# PaCkAgE DaTaStReAm
opensvc 1 26980
# end of header
                                                                                                                                                                                                                                                                                                                                                                                                                                                                           0707010001f014000081a40000000000000000000000016a102a92000000d1000000e600010003ffffffffffffffff0000001000000000opensvc/pkginfo   CLASSES=none
PSTAMP=OPENSVC-2.1,REV=2031
EMAIL=support@opensvc.com
VENDOR=https://www.opensvc.com
CATEGORY=application
VERSION=2.1,REV=2031
ARCH=all
NAME=Cluster and configuration management agent
PKG=opensvc
   0707010001f013000081a40000000000000000000000016a102a9200022c1b000000e600010003ffffffffffffffff0000000f00000000opensvc/pkgmap    : 1 26980
1 d none /etc/bash_completion.d 0755 root root
1 f none /etc/bash_completion.d/opensvc.sh 0644 root root 39189 11048 1779436975
1 d none /etc/opensvc 0755 root root
1 s none /usr/bin/nodemgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/om=../share/opensvc/bin/om
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 0644 root root 731 3508 1779436975
1 f none /usr/share/doc/opensvc/copyright 0644 root root 5124 30396 1779436975
1 f none /usr/share/doc/opensvc/daemon.events 0644 root root 5798 20275 1779444362
1 d none /usr/share/doc/opensvc/provisioning 0755 root root
1 f none /usr/share/doc/opensvc/provisioning/provisioning.agent.debian 0644 root root 2602 50922 1779436975
1 f none /usr/share/doc/opensvc/provisioning/provisioning.example 0644 root root 362 30449 1779436975
1 f none /usr/share/doc/opensvc/schedule 0644 root root 2710 60003 1779436975
1 f none /usr/share/doc/opensvc/template.cfg.DEFAULT.conf.gz 0644 root root 1147 13923 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.DEFAULT.conf.gz 0644 root root 1036 63906 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.arbitrator.conf.gz 0644 root root 817 35991 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.centera.conf.gz 0644 root root 509 60859 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.dorado.conf.gz 0644 root root 526 1507 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.emcvnx.conf.gz 0644 root root 475 58879 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.eva.conf.gz 0644 root root 424 51109 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.freenas.conf.gz 0644 root root 465 57976 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hcs.conf.gz 0644 root root 823 36867 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hds.conf.gz 0644 root root 584 10113 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hp3par.conf.gz 0644 root root 398 49663 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.ibmds.conf.gz 0644 root root 286 37139 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.ibmsvc.conf.gz 0644 root root 265 34983 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.netapp.conf.gz 0644 root root 299 38108 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.nexenta.conf.gz 0644 root root 415 49911 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.pure.conf.gz 0644 root root 724 26602 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.symmetrix.conf.gz 0644 root root 673 19934 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.vioserver.conf.gz 0644 root root 266 32971 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.xtremio.conf.gz 0644 root root 403 49230 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.asset.conf.gz 0644 root root 232 28003 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.checks.conf.gz 0644 root root 232 26701 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.cluster.conf.gz 0644 root root 1277 26928 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.cni.conf.gz 0644 root root 233 28910 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.compliance.conf.gz 0644 root root 492 60136 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.dequeue_actions.conf.gz 0644 root root 228 28022 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.disks.conf.gz 0644 root root 231 28180 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.disk.conf.gz 0644 root root 449 57559 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.multicast.conf.gz 0644 root root 477 59989 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.relay.conf.gz 0644 root root 460 56542 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.unicast.conf.gz 0644 root root 493 60121 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hook.conf.gz 0644 root root 305 35993 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.listener.conf.gz 0644 root root 1007 61561 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.bridge.conf.gz 0644 root root 269 35442 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.routed_bridge.conf.gz 0644 root root 1102 8829 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.weave.conf.gz 0644 root root 205 23557 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.node.conf.gz 0644 root root 3212 6640 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.packages.conf.gz 0644 root root 235 28011 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.patches.conf.gz 0644 root root 234 28789 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.directory.conf.gz 0644 root root 516 80 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.dorado.conf.gz 0644 root root 729 24282 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.drbd.conf.gz 0644 root root 780 30133 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.freenas.conf.gz 0644 root root 810 30642 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.hcs.conf.gz 0644 root root 785 29911 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.loop.conf.gz 0644 root root 491 59412 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.pure.conf.gz 0644 root root 713 24249 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.share.conf.gz 0644 root root 521 64788 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.shm.conf.gz 0644 root root 451 55899 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.symmetrix.conf.gz 0644 root root 684 19935 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.vg.conf.gz 0644 root root 503 61914 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.virtual.conf.gz 0644 root root 792 34285 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.zpool.conf.gz 0644 root root 504 62903 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.reboot.conf.gz 0644 root root 534 562 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.rotate_root_pw.conf.gz 0644 root root 231 28369 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stats.conf.gz 0644 root root 330 40230 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stats_collection.conf.gz 0644 root root 229 27475 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stonith.conf.gz 0644 root root 285 35291 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.switch.brocade.conf.gz 0644 root root 517 64964 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.syslog.conf.gz 0644 root root 428 50176 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.sysreport.conf.gz 0644 root root 271 31996 1779444362
1 f none /usr/share/doc/opensvc/template.comp_module.py 0644 root root 1232 29792 1779436975
1 f none /usr/share/doc/opensvc/template.comp_module.sh 0644 root root 471 33835 1779436975
1 f none /usr/share/doc/opensvc/template.node.arbitrator.conf.gz 0644 root root 817 35991 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.centera.conf.gz 0644 root root 509 60859 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.dorado.conf.gz 0644 root root 526 1507 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.emcvnx.conf.gz 0644 root root 475 58879 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.eva.conf.gz 0644 root root 424 51109 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.freenas.conf.gz 0644 root root 465 57976 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hcs.conf.gz 0644 root root 823 36867 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hds.conf.gz 0644 root root 584 10113 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hp3par.conf.gz 0644 root root 398 49663 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.ibmds.conf.gz 0644 root root 286 37139 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.ibmsvc.conf.gz 0644 root root 265 34983 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.netapp.conf.gz 0644 root root 299 38108 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.nexenta.conf.gz 0644 root root 415 49911 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.pure.conf.gz 0644 root root 724 26602 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.symmetrix.conf.gz 0644 root root 673 19934 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.vioserver.conf.gz 0644 root root 266 32971 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.xtremio.conf.gz 0644 root root 466 55744 1779444362
1 f none /usr/share/doc/opensvc/template.node.asset.conf.gz 0644 root root 232 28003 1779444362
1 f none /usr/share/doc/opensvc/template.node.brocade.conf.gz 0644 root root 225 26517 1779444362
1 f none /usr/share/doc/opensvc/template.node.centera.conf.gz 0644 root root 228 26040 1779444362
1 f none /usr/share/doc/opensvc/template.node.checks.conf.gz 0644 root root 232 26701 1779444362
1 f none /usr/share/doc/opensvc/template.node.cluster.conf.gz 0644 root root 1277 26928 1779444362
1 f none /usr/share/doc/opensvc/template.node.cni.conf.gz 0644 root root 233 28910 1779444362
1 f none /usr/share/doc/opensvc/template.node.compliance.conf.gz 0644 root root 492 60136 1779444362
1 f none /usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz 0644 root root 232 29357 1779444362
1 f none /usr/share/doc/opensvc/template.node.disks.conf.gz 0644 root root 231 28180 1779444362
1 f none /usr/share/doc/opensvc/template.node.dorado.conf.gz 0644 root root 228 28145 1779444362
1 f none /usr/share/doc/opensvc/template.node.emcvnx.conf.gz 0644 root root 227 26398 1779444362
1 f none /usr/share/doc/opensvc/template.node.eva.conf.gz 0644 root root 220 26898 1779444362
1 f none /usr/share/doc/opensvc/template.node.freenas.conf.gz 0644 root root 228 26361 1779444362
1 f none /usr/share/doc/opensvc/template.node.gcedisks.conf.gz 0644 root root 229 27545 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.disk.conf.gz 0644 root root 449 57559 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.multicast.conf.gz 0644 root root 477 59989 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.relay.conf.gz 0644 root root 460 56542 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.unicast.conf.gz 0644 root root 493 60121 1779444362
1 f none /usr/share/doc/opensvc/template.node.hcs.conf.gz 0644 root root 225 27365 1779444362
1 f none /usr/share/doc/opensvc/template.node.hds.conf.gz 0644 root root 221 26245 1779444362
1 f none /usr/share/doc/opensvc/template.node.hook.conf.gz 0644 root root 305 35993 1779444362
1 f none /usr/share/doc/opensvc/template.node.hp3par.conf.gz 0644 root root 230 26922 1779444362
1 f none /usr/share/doc/opensvc/template.node.ibmds.conf.gz 0644 root root 227 29224 1779444362
1 f none /usr/share/doc/opensvc/template.node.ibmsvc.conf.gz 0644 root root 224 26939 1779444362
1 f none /usr/share/doc/opensvc/template.node.listener.conf.gz 0644 root root 1007 61561 1779444362
1 f none /usr/share/doc/opensvc/template.node.necism.conf.gz 0644 root root 224 26779 1779444362
1 f none /usr/share/doc/opensvc/template.node.netapp.conf.gz 0644 root root 228 28228 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.bridge.conf.gz 0644 root root 269 35442 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.routed_bridge.conf.gz 0644 root root 1102 8829 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.weave.conf.gz 0644 root root 205 23557 1779444362
1 f none /usr/share/doc/opensvc/template.node.node.conf.gz 0644 root root 3807 17830 1779444362
1 f none /usr/share/doc/opensvc/template.node.nsr.conf.gz 0644 root root 220 27281 1779444362
1 f none /usr/share/doc/opensvc/template.node.packages.conf.gz 0644 root root 235 28011 1779444362
1 f none /usr/share/doc/opensvc/template.node.patches.conf.gz 0644 root root 234 28789 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.directory.conf.gz 0644 root root 516 80 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.dorado.conf.gz 0644 root root 729 24282 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.drbd.conf.gz 0644 root root 780 30133 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.freenas.conf.gz 0644 root root 810 30642 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.hcs.conf.gz 0644 root root 785 29911 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.loop.conf.gz 0644 root root 491 59412 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.pure.conf.gz 0644 root root 713 24249 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.share.conf.gz 0644 root root 521 64788 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.shm.conf.gz 0644 root root 451 55899 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.symmetrix.conf.gz 0644 root root 684 19935 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.vg.conf.gz 0644 root root 503 61914 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.virtual.conf.gz 0644 root root 792 34285 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.zpool.conf.gz 0644 root root 504 62903 1779444362
1 f none /usr/share/doc/opensvc/template.node.reboot.conf.gz 0644 root root 534 562 1779444362
1 f none /usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz 0644 root root 231 28369 1779444362
1 f none /usr/share/doc/opensvc/template.node.stats.conf.gz 0644 root root 330 40230 1779444362
1 f none /usr/share/doc/opensvc/template.node.stats_collection.conf.gz 0644 root root 229 27475 1779444362
1 f none /usr/share/doc/opensvc/template.node.stonith.conf.gz 0644 root root 285 35291 1779444362
1 f none /usr/share/doc/opensvc/template.node.switch.brocade.conf.gz 0644 root root 517 64964 1779444362
1 f none /usr/share/doc/opensvc/template.node.sym.conf.gz 0644 root root 221 27464 1779444362
1 f none /usr/share/doc/opensvc/template.node.syslog.conf.gz 0644 root root 428 50176 1779444362
1 f none /usr/share/doc/opensvc/template.node.sysreport.conf.gz 0644 root root 276 34194 1779444362
1 f none /usr/share/doc/opensvc/template.node.vioserver.conf.gz 0644 root root 226 27878 1779444362
1 f none /usr/share/doc/opensvc/template.node.xtremio.conf.gz 0644 root root 229 28669 1779444362
1 f none /usr/share/doc/opensvc/template.nscfg.DEFAULT.conf.gz 0644 root root 2014 62503 1779444362
1 f none /usr/share/doc/opensvc/template.secret.DEFAULT.conf.gz 0644 root root 1501 61163 1779444362
1 f none /usr/share/doc/opensvc/template.service.DEFAULT.conf.gz 0644 root root 9840 53749 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.forking.conf.gz 0644 root root 4736 11779 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.simple.conf.gz 0644 root root 4841 26782 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.winservice.conf.gz 0644 root root 4012 53993 1779444361
1 f none /usr/share/doc/opensvc/template.service.certificate.tls.conf.gz 0644 root root 676 17366 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.amazon.conf.gz 0644 root root 4288 23141 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.docker.conf.gz 0644 root root 5863 16202 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.esx.conf.gz 0644 root root 4116 58801 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.hpvm.conf.gz 0644 root root 4117 58983 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.jail.conf.gz 0644 root root 4204 10635 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.kvm.conf.gz 0644 root root 4410 33573 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.ldom.conf.gz 0644 root root 4117 60853 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.lxc.conf.gz 0644 root root 4701 10785 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.lxd.conf.gz 0644 root root 4229 7982 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.oci.conf.gz 0644 root root 5864 21940 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.openstack.conf.gz 0644 root root 4332 22938 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.ovm.conf.gz 0644 root root 4303 16879 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.podman.conf.gz 0644 root root 5866 14691 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.srp.conf.gz 0644 root root 4189 10786 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vbox.conf.gz 0644 root root 4147 64047 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vcloud.conf.gz 0644 root root 4363 29240 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vz.conf.gz 0644 root root 4196 2865 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.xen.conf.gz 0644 root root 4254 20811 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.zone.conf.gz 0644 root root 4662 65524 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.advfs.conf.gz 0644 root root 3995 51702 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.amazon.conf.gz 0644 root root 4106 56961 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.crypt.conf.gz 0644 root root 4272 8468 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.disk.conf.gz 0644 root root 4142 65060 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.drbd.conf.gz 0644 root root 4306 18192 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.gandi.conf.gz 0644 root root 4047 53490 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.gce.conf.gz 0644 root root 4360 21413 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.hpvm.conf.gz 0644 root root 3926 42257 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.ldom.conf.gz 0644 root root 3928 43496 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.loop.conf.gz 0644 root root 3952 39989 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.lv.conf.gz 0644 root root 4103 56334 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.lvm.conf.gz 0644 root root 4101 65115 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.md.conf.gz 0644 root root 4277 20884 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.pool.conf.gz 0644 root root 4158 2301 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.rados.conf.gz 0644 root root 4167 1111 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.raw.conf.gz 0644 root root 4298 23903 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz 0644 root root 3942 28647 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.veritas.conf.gz 0644 root root 3939 38064 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vg.conf.gz 0644 root root 4101 60574 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vxdg.conf.gz 0644 root root 3939 36925 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vxvol.conf.gz 0644 root root 4083 57914 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.zpool.conf.gz 0644 root root 4159 6879 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.zvol.conf.gz 0644 root root 4021 50495 1779444361
1 f none /usr/share/doc/opensvc/template.service.expose.envoy.conf.gz 0644 root root 1066 2161 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz 0644 root root 4915 31832 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.conf.gz 0644 root root 4905 32693 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.directory.conf.gz 0644 root root 3577 55547 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.docker.conf.gz 0644 root root 3603 2857 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext2.conf.gz 0644 root root 4914 31730 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext3.conf.gz 0644 root root 4914 34555 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext4.conf.gz 0644 root root 4914 24910 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.flag.conf.gz 0644 root root 3511 47849 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.none.conf.gz 0644 root root 4071 57591 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.tmpfs.conf.gz 0644 root root 4072 62934 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz 0644 root root 4915 34341 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.xfs.conf.gz 0644 root root 4914 40064 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.zfs.conf.gz 0644 root root 4971 46220 1779444361
1 f none /usr/share/doc/opensvc/template.service.hashpolicy.envoy.conf.gz 0644 root root 756 27958 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.amazon.conf.gz 0644 root root 4917 34485 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.cni.conf.gz 0644 root root 4211 14460 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz 0644 root root 4745 9046 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.docker.conf.gz 0644 root root 5105 61174 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.gce.conf.gz 0644 root root 4776 19749 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.host.conf.gz 0644 root root 4701 14877 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.netns.conf.gz 0644 root root 5107 62941 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.route.conf.gz 0644 root root 3512 55093 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.rule.conf.gz 0644 root root 3512 47912 1779444361
1 f none /usr/share/doc/opensvc/template.service.route.envoy.conf.gz 0644 root root 1477 57023 1779444361
1 f none /usr/share/doc/opensvc/template.service.share.nfs.conf.gz 0644 root root 3507 45767 1779444361
1 f none /usr/share/doc/opensvc/template.service.subset.conf.gz 0644 root root 1307 32778 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz 0644 root root 4224 6096 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz 0644 root root 4376 28985 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.dds.conf.gz 0644 root root 4663 5288 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.docker.conf.gz 0644 root root 4145 67 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz 0644 root root 4315 23759 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz 0644 root root 4308 16976 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz 0644 root root 4183 2324 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz 0644 root root 4320 25656 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz 0644 root root 4165 65450 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.netapp.conf.gz 0644 root root 4307 19035 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.nexenta.conf.gz 0644 root root 4302 14536 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.oci.conf.gz 0644 root root 4143 64462 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.radosclone.conf.gz 0644 root root 4131 60607 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.radossnap.conf.gz 0644 root root 4155 63229 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.rsync.conf.gz 0644 root root 4787 23013 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.s3.conf.gz 0644 root root 4525 43620 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symclone.conf.gz 0644 root root 4388 31035 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz 0644 root root 4368 25283 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsnapvx.conf.gz 0644 root root 4434 39630 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz 0644 root root 4254 13883 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.zfs.conf.gz 0644 root root 4391 30363 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz 0644 root root 4374 28744 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.docker.conf.gz 0644 root root 6315 3105 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.host.conf.gz 0644 root root 4376 30510 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.oci.conf.gz 0644 root root 6319 11386 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.podman.conf.gz 0644 root root 6317 11366 1779444362
1 f none /usr/share/doc/opensvc/template.service.vhost.envoy.conf.gz 0644 root root 413 51234 1779444362
1 f none /usr/share/doc/opensvc/template.service.volume.conf.gz 0644 root root 4316 15802 1779444362
1 f none /usr/share/doc/opensvc/template.sysreport.conf.gz 0644 root root 414 50777 1779436975
1 f none /usr/share/doc/opensvc/template.usr.DEFAULT.conf.gz 0644 root root 1813 32761 1779444362
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 0644 root root 7965 43839 1779436975
1 f none /usr/share/man/man1/svcmgr.1.gz 0644 root root 14781 48247 1779436975
1 f none /usr/share/man/man1/svcmon.1.gz 0644 root root 1346 41099 1779436975
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 0644 root root 597 51671 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.agent.xml 0644 root root 2097 49739 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.defaults.parameters 0644 root root 387 33409 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.AIX 0755 root root 439 34806 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.Darwin 0755 root root 810 64665 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.FreeBSD 0755 root root 929 10762 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.OSF1 0755 root root 464 35940 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.SunOS 0755 root root 645 50927 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.debian 0755 root root 703 52638 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.hpux 0755 root root 942 7699 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.openrc 0755 root root 803 4078 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.redhat 0755 root root 821 352 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.suse 0755 root root 707 52871 1779436975
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-agent.service 0644 root root 828 12836 1779436975
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-services.service 0644 root root 404 38007 1779436975
1 s none /usr/share/opensvc/bin/nodemgr=om
1 f none /usr/share/opensvc/bin/om 0755 root root 2450 46883 1779436975
1 s none /usr/share/opensvc/bin/opensvc=om
1 f none /usr/share/opensvc/bin/postinstall 0755 root root 50268 38725 1779436975
1 f none /usr/share/opensvc/bin/postremove 0644 root root 993 15241 1779436975
1 f none /usr/share/opensvc/bin/preinstall 0755 root root 1411 49438 1779436975
1 f none /usr/share/opensvc/bin/preuninstall 0755 root root 717 52870 1779436975
1 s none /usr/share/opensvc/bin/svcmgr=om
1 s none /usr/share/opensvc/bin/svcmon=om
1 d none /usr/share/opensvc/html 0755 root root
1 f none /usr/share/opensvc/html/index.html 0644 root root 389 34813 1756141688
1 f none /usr/share/opensvc/html/index.js 0644 root root 1485411 30874 1756141688
1 f none /usr/share/opensvc/html/index.js.LICENSE.txt 0644 root root 3169 1364 1756141688
1 d none /usr/share/opensvc/opensvc 0755 root root
1 f none /usr/share/opensvc/opensvc/__init__.py 0644 root root 86 7790 1779436975
1 f none /usr/share/opensvc/opensvc/__main__.py 0644 root root 4431 774 1779436975
1 d none /usr/share/opensvc/opensvc/commands 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/commands/ccfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/ccfg/__init__.py 0644 root root 288 24273 1779436975
1 f none /usr/share/opensvc/opensvc/commands/ccfg/parser.py 0644 root root 1141 19378 1779436975
1 d none /usr/share/opensvc/opensvc/commands/cfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/cfg/__init__.py 0644 root root 285 23976 1779436975
1 f none /usr/share/opensvc/opensvc/commands/cfg/parser.py 0644 root root 4065 25913 1779436975
1 d none /usr/share/opensvc/opensvc/commands/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/daemon/__init__.py 0644 root root 1167 28510 1779436975
1 f none /usr/share/opensvc/opensvc/commands/daemon/parser.py 0644 root root 4666 9821 1779436975
1 d none /usr/share/opensvc/opensvc/commands/mgr 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/mgr/__init__.py 0644 root root 12561 4416 1779436975
1 f none /usr/share/opensvc/opensvc/commands/mgr/parser.py 0644 root root 22927 33936 1779436975
1 d none /usr/share/opensvc/opensvc/commands/network 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/network/__init__.py 0644 root root 1219 31768 1779436975
1 f none /usr/share/opensvc/opensvc/commands/network/parser.py 0644 root root 1844 4794 1779436975
1 d none /usr/share/opensvc/opensvc/commands/node 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/node/__init__.py 0644 root root 1871 20842 1779436975
1 f none /usr/share/opensvc/opensvc/commands/node/parser.py 0644 root root 40745 51565 1779436975
1 d none /usr/share/opensvc/opensvc/commands/nscfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/nscfg/__init__.py 0644 root root 291 24651 1779436975
1 f none /usr/share/opensvc/opensvc/commands/nscfg/parser.py 0644 root root 1223 26526 1779436975
1 d none /usr/share/opensvc/opensvc/commands/pool 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/pool/__init__.py 0644 root root 1207 30424 1779436975
1 f none /usr/share/opensvc/opensvc/commands/pool/parser.py 0644 root root 3010 23074 1779436975
1 d none /usr/share/opensvc/opensvc/commands/sec 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/sec/__init__.py 0644 root root 285 24009 1779436975
1 f none /usr/share/opensvc/opensvc/commands/sec/parser.py 0644 root root 4938 28731 1779436975
1 d none /usr/share/opensvc/opensvc/commands/svc 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/svc/__init__.py 0644 root root 284 24050 1779436975
1 f none /usr/share/opensvc/opensvc/commands/svc/parser.py 0644 root root 34761 36387 1779436975
1 f none /usr/share/opensvc/opensvc/commands/svcmon.py 0644 root root 12590 462 1779436975
1 d none /usr/share/opensvc/opensvc/commands/usr 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/usr/__init__.py 0644 root root 285 24102 1779436975
1 f none /usr/share/opensvc/opensvc/commands/usr/parser.py 0644 root root 4790 19062 1779436975
1 d none /usr/share/opensvc/opensvc/commands/vol 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/vol/__init__.py 0644 root root 285 24075 1779436975
1 f none /usr/share/opensvc/opensvc/commands/vol/parser.py 0644 root root 262 21242 1779436975
1 d none /usr/share/opensvc/opensvc/core 0755 root root
1 f none /usr/share/opensvc/opensvc/core/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/core/capabilities 0755 root root
1 f none /usr/share/opensvc/opensvc/core/capabilities/__init__.py 0644 root root 5233 43501 1779436975
1 f none /usr/share/opensvc/opensvc/core/capabilities/linux.py 0644 root root 554 40171 1779436975
1 f none /usr/share/opensvc/opensvc/core/capabilities/sunos.py 0644 root root 534 38872 1779436975
1 f none /usr/share/opensvc/opensvc/core/cloud.py 0644 root root 627 48196 1779436975
1 d none /usr/share/opensvc/opensvc/core/collector 0755 root root
1 f none /usr/share/opensvc/opensvc/core/collector/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/actions.py 0644 root root 11450 168 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/cli.py 0644 root root 98574 63237 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/rpc.py 0644 root root 54756 8576 1779436975
1 f none /usr/share/opensvc/opensvc/core/comm.py 0644 root root 37849 35701 1779436975
1 f none /usr/share/opensvc/opensvc/core/compliance.py 0644 root root 29525 32512 1779436975
1 d none /usr/share/opensvc/opensvc/core/configfile 0755 root root
1 f none /usr/share/opensvc/opensvc/core/configfile/__init__.py 0644 root root 1148 31017 1779436975
1 f none /usr/share/opensvc/opensvc/core/contexts.py 0644 root root 12383 21269 1779436975
1 d none /usr/share/opensvc/opensvc/core/exceptions 0755 root root
1 f none /usr/share/opensvc/opensvc/core/exceptions/__init__.py 0644 root root 4198 10463 1779436975
1 f none /usr/share/opensvc/opensvc/core/extconfig.py 0644 root root 64752 40924 1779436975
1 f none /usr/share/opensvc/opensvc/core/freezer.py 0644 root root 2952 23878 1779436975
1 f none /usr/share/opensvc/opensvc/core/keywords.py 0644 root root 27932 27813 1779436975
1 f none /usr/share/opensvc/opensvc/core/logger.py 0644 root root 8423 36295 1779436975
1 f none /usr/share/opensvc/opensvc/core/network.py 0644 root root 19014 20909 1779436975
1 d none /usr/share/opensvc/opensvc/core/node 0755 root root
1 f none /usr/share/opensvc/opensvc/core/node/__init__.py 0644 root root 206 18606 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/darwin.py 0644 root root 267 19856 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/freebsd.py 0644 root root 1989 12646 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/hpux.py 0644 root root 673 49624 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/linux.py 0644 root root 12198 18556 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/node.py 0644 root root 200952 63613 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/nodedict.py 0644 root root 66308 6064 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/sunos.py 0644 root root 4189 39460 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/windows.py 0644 root root 3713 20543 1779436975
1 d none /usr/share/opensvc/opensvc/core/objects 0755 root root
1 f none /usr/share/opensvc/opensvc/core/objects/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/builder.py 0644 root root 8370 65199 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/ccfg.py 0644 root root 582 43273 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/ccfgdict.py 0644 root root 3468 7185 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/cfg.py 0644 root root 2071 25058 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/cfgdict.py 0644 root root 3428 6142 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/data.py 0644 root root 14339 17262 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/nscfg.py 0644 root root 3249 42371 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/nscfgdict.py 0644 root root 3306 61476 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/pg.py 0644 root root 4286 48141 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/sec.py 0644 root root 7686 33847 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/secdict.py 0644 root root 5926 43202 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/svc.py 0644 root root 210100 33420 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/svcdict.py 0644 root root 54733 60584 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/usr.py 0644 root root 4871 40447 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/usrdict.py 0644 root root 6718 48401 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/vol.py 0644 root root 1500 44867 1779436975
1 d none /usr/share/opensvc/opensvc/core/oc3path 0755 root root
1 f none /usr/share/opensvc/opensvc/core/oc3path/__init__.py 0644 root root 534 42229 1779436975
1 f none /usr/share/opensvc/opensvc/core/pool.py 0644 root root 6679 27310 1779436975
1 f none /usr/share/opensvc/opensvc/core/resource.py 0644 root root 49373 42777 1779436975
1 f none /usr/share/opensvc/opensvc/core/resourceset.py 0644 root root 15965 45338 1779436975
1 f none /usr/share/opensvc/opensvc/core/scheduler.py 0644 root root 39957 43216 1779436975
1 d none /usr/share/opensvc/opensvc/core/status 0755 root root
1 f none /usr/share/opensvc/opensvc/core/status/__init__.py 0644 root root 6448 23821 1779436975
1 d none /usr/share/opensvc/opensvc/core/sysreport 0755 root root
1 f none /usr/share/opensvc/opensvc/core/sysreport/__init__.py 0644 root root 215 19726 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/aix.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/darwin.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/freebsd.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/hpux.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/linux.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/osf1.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/sunos.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/sysreport.py 0644 root root 18494 19589 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/windows.py 0644 root root 1500 44679 1779436975
1 d none /usr/share/opensvc/opensvc/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/__main__.py 0644 root root 30 2609 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/clusterlock.py 0644 root root 4112 34461 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/collector.py 0644 root root 24591 29502 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/dns.py 0644 root root 23499 2353 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/events.py 0644 root root 4999 45650 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handler.py 0644 root root 2177 29636 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/api 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/api/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/api/get.py 0644 root root 872 58985 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/askfull 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/askfull/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/askfull/post.py 0644 root root 1205 24243 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/authinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/authinfo/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/authinfo/get.py 0644 root root 723 53484 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/post.py 0644 root root 363 27178 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/get.py 0644 root root 470 35123 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/catalogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/catalogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/catalogs/get.py 0644 root root 578 41956 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/cluster 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/get.py 0644 root root 449 33900 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/post.py 0644 root root 1003 7916 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/get.py 0644 root root 433 32954 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/post.py 0644 root root 2664 4594 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start/post.py 0644 root root 1049 12681 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/get.py 0644 root root 1747 57085 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status/get.py 0644 root root 1544 48221 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/post.py 0644 root root 3361 55656 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/events 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/events/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/events/get.py 0644 root root 2932 18360 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/join 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/join/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/join/post.py 0644 root root 3451 60575 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/key 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/delete.py 0644 root root 1157 17661 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/get.py 0644 root root 1534 48398 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/post.py 0644 root root 1361 30461 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/keywords 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/keywords/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/keywords/get.py 0644 root root 1039 10514 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/leave 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/leave/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/leave/post.py 0644 root root 840 61353 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/lock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/lock/post.py 0644 root root 1144 14834 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/networks 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/networks/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/networks/get.py 0644 root root 470 35634 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/action 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/action/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/action/post.py 0644 root root 4095 28578 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/get.py 0644 root root 1257 25303 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/checks 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/checks/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/checks/get.py 0644 root root 923 2520 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/config 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/config/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/config/get.py 0644 root root 1590 55344 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/drain 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/drain/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/drain/post.py 0644 root root 2951 16615 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/get.py 0644 root root 470 34195 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/logs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/logs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/logs/get.py 0644 root root 1031 15495 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor/post.py 0644 root root 2499 47308 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/get.py 0644 root root 474 35748 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/action 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/action/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/action/post.py 0644 root root 6512 61801 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/get.py 0644 root root 1911 3489 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/clear 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/clear/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/clear/post.py 0644 root root 1261 31932 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/config 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/config/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/config/get.py 0644 root root 3100 26996 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/get.py 0644 root root 1102 19347 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/create 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/create/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/create/post.py 0644 root root 4406 50351 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/enter 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/enter/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/enter/get.py 0644 root root 2745 908 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/keys 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/keys/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/keys/get.py 0644 root root 940 4127 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/logs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/logs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/logs/get.py 0644 root root 1508 48342 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor/post.py 0644 root root 11189 31915 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/selector 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/selector/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/selector/get.py 0644 root root 1195 20608 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/get.py 0644 root root 774 59026 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/post.py 0644 root root 1023 11976 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/pools 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/pools/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/pools/get.py 0644 root root 926 5189 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx/get.py 0644 root root 1251 21571 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/status/get.py 0644 root root 906 65015 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx/post.py 0644 root root 1663 47484 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/rundone 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/rundone/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/rundone/post.py 0644 root root 1219 19323 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/schedules 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/schedules/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/schedules/get.py 0644 root root 3052 15668 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/sshkey 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sshkey/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sshkey/get.py 0644 root root 1129 13725 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sync/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sync/get.py 0644 root root 2115 20044 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/template 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/template/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/template/get.py 0644 root root 1431 37586 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/templates 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/templates/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/templates/get.py 0644 root root 1554 43658 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/unlock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/unlock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/unlock/post.py 0644 root root 1131 16638 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/wait 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wait/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wait/get.py 0644 root root 5794 17454 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/post.py 0644 root root 1863 11612 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/whoami 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/whoami/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/whoami/get.py 0644 root root 1111 10608 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/hb 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/hb/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/disk.py 0644 root root 16057 39598 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/hb.py 0644 root root 9406 11714 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/mcast.py 0644 root root 10949 2102 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/relay.py 0644 root root 6994 46408 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/ucast.py 0644 root root 9740 55345 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/listener.py 0644 root root 84543 19325 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/main.py 0644 root root 20663 28590 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/monitor.py 0644 root root 182043 27370 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/rbac.py 0644 root root 7909 55074 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/scheduler.py 0644 root root 23028 54175 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/shared.py 0644 root root 66706 26340 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/winservice.py 0644 root root 3041 38823 1779436975
1 d none /usr/share/opensvc/opensvc/drivers 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/array 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/array/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/centera.py 0644 root root 3624 10596 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/dorado.py 0644 root root 37315 27625 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/emcvnx.py 0644 root root 4925 39828 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/eva.py 0644 root root 4713 20970 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/freenas.py 0644 root root 40393 23755 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/gce.py 0644 root root 1262 29328 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hcs.py 0644 root root 51686 35438 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hds.py 0644 root root 26575 61033 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hp3par.py 0644 root root 14348 53006 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/ibmds.py 0644 root root 3490 59854 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/ibmsvc.py 0644 root root 3039 27436 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/necism.py 0644 root root 11207 59782 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/netapp.py 0644 root root 3276 43786 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/nexenta.py 0644 root root 11354 32483 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/pure.py 0644 root root 34787 46086 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/symmetrix.py 0644 root root 52276 21871 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/vioserver.py 0644 root root 3363 53911 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/xtremio.py 0644 root root 27166 24554 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/backupsrv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/backupsrv/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/backupsrv/networker.py 0644 root root 1215 21096 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/__init__.py 0644 root root 4635 63498 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/btrfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/btrfs/linux.py 0644 root root 2466 41918 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/eth 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/__init__.py 0644 root root 1 10 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/hpux.py 0644 root root 2677 31413 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/linux.py 0644 root root 2975 62566 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/sunos.py 0644 root root 10067 1996 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fm 0755 root root
1 d none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm/__init__.py 0644 root root 1041 9968 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage/__init__.py 0644 root root 1509 39839 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage/linux.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_i 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/aix.py 0644 root root 1081 5385 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/darwin.py 0644 root root 1045 4535 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/hpux.py 0644 root root 1440 25799 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/linux.py 0644 root root 2135 9930 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/sunos.py 0644 root root 1177 11860 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/advfs/osf1.py 0644 root root 1013 4208 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/aix.py 0644 root root 1081 5368 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/darwin.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/freebsd.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/hpux.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/linux.py 0644 root root 2375 20838 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/sunos.py 0644 root root 1531 29919 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/windows.py 0644 root root 1110 11572 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/__init__.py 0644 root root 2692 51249 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/jstat 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/jstat/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/jstat/linux.py 0644 root root 6150 9041 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/lag 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/hpux.py 0644 root root 1829 53861 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/linux.py 0644 root root 4014 15035 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/sunos.py 0644 root root 6755 3012 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/windows.py 0644 root root 769 54018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mcelog 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mcelog/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mcelog/linux.py 0644 root root 1735 52476 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath/native 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/aix.py 0644 root root 2256 15635 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/hpux.py 0644 root root 2353 23750 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/linux.py 0644 root root 4203 2605 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/osf1.py 0644 root root 835 57322 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/sunos.py 0644 root root 2608 33884 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/windows.py 0644 root root 1452 34119 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/__init__.py 0644 root root 2864 48880 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/aix.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/hpux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/numa 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/numa/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/numa/linux.py 0644 root root 1419 27595 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/__init__.py 0644 root root 4497 28663 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/sas2 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/__init__.py 0644 root root 3698 47330 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/__init__.py 0644 root root 3099 18497 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/sunos.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/windows.py 0644 root root 511 38024 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/__init__.py 0644 root root 1223 19422 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/vg_u 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/aix.py 0644 root root 1334 22807 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/hpux.py 0644 root root 1851 63025 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/linux.py 0644 root root 1198 15198 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/zpool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/__init__.py 0644 root root 2252 14995 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/cloud 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/cloud/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/amazon.py 0644 root root 1262 38152 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/gandi.py 0644 root root 1359 39024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/openstack.py 0644 root root 2126 35146 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/vcloud.py 0644 root root 1888 14922 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pg/linux.py 0644 root root 22642 53171 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pool/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/directory.py 0644 root root 1771 60072 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/dorado.py 0644 root root 7136 21507 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/drbd.py 0644 root root 6553 32762 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/freenas.py 0644 root root 3921 27604 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/hcs.py 0644 root root 4769 27411 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/loop.py 0644 root root 1483 42046 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/pure.py 0644 root root 4487 3574 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/share.py 0644 root root 1738 58116 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/shm.py 0644 root root 2535 53563 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/symmetrix.py 0644 root root 6186 62163 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pool/vg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pool/vg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/vg/linux.py 0644 root root 1455 39442 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/virtual.py 0644 root root 2428 57003 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/zpool.py 0644 root root 1905 4483 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/__init__.py 0644 root root 27196 45129 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/forking 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/forking/__init__.py 0644 root root 5214 65270 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/simple 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/simple/__init__.py 0644 root root 9559 53325 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/simple/sunos.py 0644 root root 1101 14130 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/winservice 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/winservice/__init__.py 0644 root root 3731 7925 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/certificate 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/certificate/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/certificate/tls 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/certificate/tls/__init__.py 0644 root root 1855 20647 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/__init__.py 0644 root root 15756 14184 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/amazon/__init__.py 0644 root root 11321 41727 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/docker/__init__.py 0644 root root 41733 14981 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/esx 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/esx/__init__.py 0644 root root 3713 6243 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/hpvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/hpvm/__init__.py 0644 root root 4252 53621 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/jail 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/jail/__init__.py 0644 root root 4203 35941 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/kvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/kvm/__init__.py 0644 root root 21009 12519 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/ldom 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/ldom/__init__.py 0644 root root 4428 54924 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/lxc 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/lxc/__init__.py 0644 root root 35363 8199 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/lxd 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/lxd/__init__.py 0644 root root 11013 35224 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/openstack 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/openstack/__init__.py 0644 root root 11657 1577 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/ovm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/ovm/__init__.py 0644 root root 5509 3052 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/podman 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/podman/__init__.py 0644 root root 4258 65018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/srp 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/srp/__init__.py 0644 root root 11533 21829 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vbox 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vbox/__init__.py 0644 root root 4737 14798 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vcloud 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vcloud/__init__.py 0644 root root 9806 7740 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vz 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vz/__init__.py 0644 root root 5769 20025 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/xen 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/xen/__init__.py 0644 root root 3781 16303 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/zone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/zone/__init__.py 0644 root root 44662 35153 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/zone/configuration_profile.py 0644 root root 7235 59554 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/__init__.py 0644 root root 3023 44200 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/advfs/__init__.py 0644 root root 3756 17874 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/amazon/__init__.py 0644 root root 10577 8246 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/crypt 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/crypt/__init__.py 0644 root root 9519 53844 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/disk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/disk/__init__.py 0644 root root 5431 13082 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/disk/linux.py 0644 root root 3895 37245 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/drbd 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/drbd/__init__.py 0644 root root 30941 50221 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/gandi 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/gandi/__init__.py 0644 root root 6948 43246 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/gce 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/gce/__init__.py 0644 root root 10039 32886 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/hpvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/hpvm/__init__.py 0644 root root 4813 13083 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/ldom 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/ldom/__init__.py 0644 root root 4300 37738 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/loop 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/__init__.py 0644 root root 3384 56829 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/darwin.py 0644 root root 1896 14849 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/freebsd.py 0644 root root 2519 56589 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/linux.py 0644 root root 3387 64445 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/sunos.py 0644 root root 2322 47300 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/lv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/__init__.py 0644 root root 5102 51198 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/hpux.py 0644 root root 1336 37219 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/linux.py 0644 root root 5059 38736 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/md 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/md/__init__.py 0644 root root 19506 50793 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/rados 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/rados/__init__.py 0644 root root 10197 18658 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/raw 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/__init__.py 0644 root root 14997 38932 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/hpux.py 0644 root root 581 44485 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/linux.py 0644 root root 9368 21346 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/sunos.py 0644 root root 1131 12552 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/__init__.py 0644 root root 10167 17939 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/freebsd.py 0644 root root 279 24247 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/hpux.py 0644 root root 4606 14239 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/linux.py 0644 root root 860 4012 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/osf1.py 0644 root root 6026 39842 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sg.py 0644 root root 15616 55044 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sunos.py 0644 root root 229 20264 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vdisk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vdisk/__init__.py 0644 root root 2509 57656 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/aix.py 0644 root root 9564 20876 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/hpux.py 0644 root root 14879 14737 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/linux.py 0644 root root 13674 7009 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vxdg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vxdg/__init__.py 0644 root root 10418 11252 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vxvol 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vxvol/__init__.py 0644 root root 6742 49816 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/zpool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/zpool/__init__.py 0644 root root 14707 23951 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/zvol 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/zvol/__init__.py 0644 root root 5781 42512 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/expose 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/expose/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/expose/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/expose/envoy/__init__.py 0644 root root 3393 61334 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/__init__.py 0644 root root 25275 112 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/aix.py 0644 root root 4001 28779 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/btrfs/__init__.py 0644 root root 4229 54601 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/darwin.py 0644 root root 6433 4018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/directory 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/directory/__init__.py 0644 root root 6713 32185 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/docker/__init__.py 0644 root root 4326 62962 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2/__init__.py 0644 root root 169 13752 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2/linux.py 0644 root root 300 22935 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3/__init__.py 0644 root root 169 13753 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3/linux.py 0644 root root 300 22940 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4/__init__.py 0644 root root 169 13754 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4/linux.py 0644 root root 300 22945 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/flag 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/__init__.py 0644 root root 2656 4024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/darwin.py 0644 root root 239 19130 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/freebsd.py 0644 root root 239 19130 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/linux.py 0644 root root 239 19107 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/sunos.py 0644 root root 410 31900 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/freebsd.py 0644 root root 4354 57373 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/hpux.py 0644 root root 2536 58610 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/linux.py 0644 root root 23773 16596 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/none 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/none/__init__.py 0644 root root 184 14964 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/none/linux.py 0644 root root 142 10887 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/osf1.py 0644 root root 3975 28449 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/sunos.py 0644 root root 5871 29544 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/__init__.py 0644 root root 185 15086 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/linux.py 0644 root root 144 11131 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/__init__.py 0644 root root 169 13820 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/linux.py 0644 root root 311 24618 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/sunos.py 0644 root root 320 24535 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/windows.py 0644 root root 3639 14235 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs/__init__.py 0644 root root 168 13702 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs/linux.py 0644 root root 297 22959 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/__init__.py 0644 root root 4996 46153 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/freebsd.py 0644 root root 135 10606 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/linux.py 0644 root root 133 10435 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/sunos.py 0644 root root 133 10443 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy/__init__.py 0644 root root 1701 5872 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/__init__.py 0644 root root 31799 58410 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/amazon/__init__.py 0644 root root 8548 55210 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/cni 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/cni/__init__.py 0644 root root 17127 16895 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/crossbow 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/crossbow/__init__.py 0644 root root 6361 9388 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/gce 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/gce/__init__.py 0644 root root 6805 48621 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/host 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/aix.py 0644 root root 957 9556 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/darwin.py 0644 root root 947 8024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/freebsd.py 0644 root root 969 10278 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/hpux.py 0644 root root 1222 27142 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/linux.py 0644 root root 3520 2208 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/osf1.py 0644 root root 971 10443 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/sunos.py 0644 root root 920 5775 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/windows.py 0644 root root 1729 1148 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/netns 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/netns/__init__.py 0644 root root 25323 27655 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/route 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/route/__init__.py 0644 root root 4435 8975 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/rule 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/rule/__init__.py 0755 root root 4414 6521 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/zone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/zone/__init__.py 0644 root root 1911 13684 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/route 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/route/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/route/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/route/envoy/__init__.py 0644 root root 4288 15205 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/share 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/share/nfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/__init__.py 0644 root root 418 29851 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/hpux.py 0644 root root 4133 35276 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/linux.py 0644 root root 6552 22509 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/sunos.py 0644 root root 3932 22211 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/__init__.py 0644 root root 9419 44796 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfs/__init__.py 0644 root root 21421 56722 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap/__init__.py 0644 root root 10756 62412 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/dds 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/dds/__init__.py 0644 root root 15891 61622 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/docker/__init__.py 0644 root root 6624 32987 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/evasnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/evasnap/__init__.py 0644 root root 11204 34393 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3par 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3par/__init__.py 0644 root root 11265 51316 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap/__init__.py 0644 root root 3769 23570 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap/__init__.py 0644 root root 9894 57672 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap/__init__.py 0644 root root 6114 48234 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/netapp 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/netapp/__init__.py 0644 root root 10954 16574 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/nexenta 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/nexenta/__init__.py 0644 root root 9321 26619 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/radosclone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/radosclone/__init__.py 0644 root root 4496 60079 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/radossnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/radossnap/__init__.py 0644 root root 6214 55747 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/rsync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/rsync/__init__.py 0644 root root 20423 13753 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/s3 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/s3/__init__.py 0644 root root 11281 23823 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone/__init__.py 0644 root root 11846 26566 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone/linux.py 0644 root root 2295 38575 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/__init__.py 0644 root root 1508 47169 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/linux.py 0644 root root 305 25254 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx/__init__.py 0644 root root 9807 64238 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs/__init__.py 0644 root root 22630 63160 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/zfs/__init__.py 0644 root root 18764 34681 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap/__init__.py 0644 root root 7920 49713 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/__init__.py 0644 root root 13214 61961 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/docker/__init__.py 0644 root root 2861 24152 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/host 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/host/__init__.py 0644 root root 5576 10960 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/podman 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/podman/__init__.py 0644 root root 2716 13407 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/vhost 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/vhost/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/vhost/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/vhost/envoy/__init__.py 0644 root root 942 4575 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/volume 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/volume/__init__.py 0644 root root 28987 48119 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/sanswitch 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/sanswitch/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/sanswitch/brocade.py 0644 root root 4859 32045 1779436975
1 f none /usr/share/opensvc/opensvc/env.py 0644 root root 11053 46913 1779436975
1 d none /usr/share/opensvc/opensvc/foreign 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/__init__.py 0644 root root 84 7588 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/aenum 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/aenum/LICENSE 0644 root root 1525 54463 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/__init__.py 0644 root root 1133 22601 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_common.py 0644 root root 8203 56420 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_constant.py 0644 root root 5370 30133 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_enum.py 0644 root root 127043 55064 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_py2.py 0644 root root 184 16287 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_py3.py 0644 root root 406 34868 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_tuple.py 0644 root root 19103 32582 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/colorama 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/colorama/__init__.py 0644 root root 239 20569 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/ansi.py 0644 root root 2524 36689 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/ansitowin32.py 0644 root root 10326 58775 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/initialise.py 0644 root root 1915 27388 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/win32.py 0644 root root 5404 33431 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/winterm.py 0644 root root 6438 36817 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/concurrent 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/__init__.py 0644 root root 76 7038 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/concurrent/futures 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/__init__.py 0644 root root 887 61837 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/_base.py 0644 root root 23929 51658 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/process.py 0644 root root 14986 33005 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/thread.py 0644 root root 7229 40216 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/h2 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/h2/__init__.py 0644 root root 90 6306 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/config.py 0644 root root 6498 16294 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/connection.py 0644 root root 81546 57700 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/errors.py 0644 root root 1555 53972 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/events.py 0644 root root 21962 61081 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/exceptions.py 0644 root root 5379 47311 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/frame_buffer.py 0644 root root 6771 991 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/settings.py 0644 root root 11816 19155 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/stream.py 0644 root root 55949 39083 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/utilities.py 0644 root root 22818 15042 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/windows.py 0644 root root 5603 63580 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hpack 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hpack/__init__.py 0644 root root 483 40300 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/compat.py 0644 root root 679 51094 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/exceptions.py 0644 root root 974 12209 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/hpack.py 0644 root root 22771 29570 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman.py 0644 root root 2521 3994 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman_constants.py 0644 root root 4628 3530 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman_table.py 0644 root root 168580 33086 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/struct.py 0644 root root 1052 22489 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/table.py 0644 root root 9643 37661 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/__init__.py 0644 root root 891 8092 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/certs.pem 0644 root root 323173 33422 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/cli.py 0644 root root 7505 60246 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/common 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/__init__.py 0644 root root 81 6520 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/bufsocket.py 0644 root root 8330 60572 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/connection.py 0644 root root 6536 53931 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/decoder.py 0644 root root 1560 61341 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/exceptions.py 0644 root root 1996 29011 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/headers.py 0644 root root 8972 52322 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/util.py 0644 root root 1523 57456 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/compat.py 0644 root root 1775 11221 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/contrib.py 0644 root root 7098 28143 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/http11 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/__init__.py 0644 root root 101 8119 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/connection.py 0644 root root 17001 55449 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/parser.py 0644 root root 2596 4987 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/response.py 0644 root root 12882 9837 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/http20 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/__init__.py 0644 root root 99 8025 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/connection.py 0644 root root 33863 49411 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/errors.py 0644 root root 3251 23364 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/exceptions.py 0644 root root 1044 19022 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/response.py 0644 root root 8078 49510 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/stream.py 0644 root root 12084 16319 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/util.py 0644 root root 1592 64028 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/window.py 0644 root root 6155 54985 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/httplib_compat.py 0644 root root 4452 17319 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/ssl_compat.py 0644 root root 10156 2280 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/tls.py 0644 root root 5149 35818 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyperframe 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/__init__.py 0644 root root 136 11012 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/exceptions.py 0644 root root 936 8914 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/flags.py 0644 root root 1286 36799 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/frame.py 0644 root root 26664 44644 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jsonpath_ng 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/__init__.py 0644 root root 116 9740 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/__init__.py 0644 root root 605 51186 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/arithmetic.py 0644 root root 2381 42464 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/filter.py 0644 root root 3176 37865 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/iterable.py 0644 root root 2984 30614 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/parser.py 0644 root root 5208 53851 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/string.py 0644 root root 2600 6043 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/jsonpath.py 0644 root root 21629 55304 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/lexer.py 0644 root root 5321 23398 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/parser.py 0644 root root 5605 15884 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/__init__.py 0644 root root 811 5224 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/__main__.py 0644 root root 4162 57846 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/algorithms.py 0644 root root 13336 34025 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/api_jws.py 0644 root root 8115 40081 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/api_jwt.py 0644 root root 7967 28838 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/compat.py 0644 root root 1624 63064 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt/contrib 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/py_ecdsa.py 0644 root root 1771 6297 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/pycrypto.py 0644 root root 1249 32945 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/exceptions.py 0644 root root 986 19020 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/help.py 0644 root root 1609 981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/utils.py 0644 root root 2629 19369 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/looseversion 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/LICENSE 0644 root root 2490 8107 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/README.md 0644 root root 107 9974 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/__init__.py 0644 root root 9330 42745 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/ply 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/ply/__init__.py 0644 root root 103 8202 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/cpp.py 0644 root root 33639 59078 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/ctokens.py 0644 root root 3155 62449 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/lex.py 0644 root root 42905 12577 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/yacc.py 0644 root root 137736 64832 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/ygen.py 0644 root root 2246 57594 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/pyaes 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/__init__.py 0644 root root 2087 40499 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/aes.py 0644 root root 60310 16766 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/blockfeeder.py 0644 root root 8119 11752 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/util.py 0644 root root 2050 33260 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/rfc3986 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/__init__.py 0644 root root 1562 65396 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/_mixin.py 0644 root root 13214 26981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/abnf_regexp.py 0644 root root 9081 57501 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/api.py 0644 root root 3887 3806 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/builder.py 0644 root root 9577 46569 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/compat.py 0644 root root 1513 57981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/exceptions.py 0644 root root 3775 37350 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/iri.py 0644 root root 5466 13478 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/misc.py 0644 root root 4094 61565 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/normalizers.py 0644 root root 5259 23832 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/parseresult.py 0644 root root 14654 62237 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/uri.py 0644 root root 5227 18096 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/validators.py 0644 root root 13854 34299 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/six.py 0644 root root 34573 40057 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/winstats.py 0644 root root 12562 15274 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/wmi.py 0644 root root 46957 4995 1779436975
1 d none /usr/share/opensvc/opensvc/utilities 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/arp.py 0644 root root 2375 48498 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/asset 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/asset/__init__.py 0644 root root 208 18850 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/aix.py 0644 root root 3116 21007 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/asset.py 0644 root root 23544 65370 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/darwin.py 0644 root root 3193 33903 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/freebsd.py 0644 root root 2256 26309 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/hpux.py 0644 root root 8462 54124 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/linux.py 0644 root root 25262 5096 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/osf1.py 0644 root root 10949 33913 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/sunos.py 0644 root root 9437 86 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/windows.py 0644 root root 10471 12442 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/auth 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/auth/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/auth/auth_factory.py 0644 root root 777 61513 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/auth/auth_provider_abstract.py 0644 root root 269 22565 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/cache 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/cache/__init__.py 0644 root root 4249 53365 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/chunker.py 0644 root root 149 11007 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/concurrent_futures.py 0644 root root 373 33236 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/configparser 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/configparser/__init__.py 0644 root root 2053 19385 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/converters 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/converters/__init__.py 0644 root root 9522 20002 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/dbglock.py 0644 root root 1348 32554 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/devices 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/devices/__init__.py 0644 root root 501 44562 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/darwin.py 0644 root root 894 1834 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/freebsd.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/linux.py 0644 root root 11480 11242 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/sunos.py 0644 root root 1057 14455 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/windows.py 0644 root root 265 21262 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/devtree 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/devtree/__init__.py 0644 root root 212 19200 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/aix.py 0644 root root 2065 19556 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/darwin.py 0644 root root 115 9414 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/devtree.py 0644 root root 13678 24105 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/freebsd.py 0644 root root 116 9424 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/hpux.py 0644 root root 6148 32544 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/linux.py 0644 root root 20892 3199 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/osf1.py 0644 root root 3048 8277 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/sunos.py 0644 root root 11031 56703 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/veritas.py 0644 root root 8228 31021 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/windows.py 0644 root root 656 51218 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/diskinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/__init__.py 0644 root root 214 19408 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/aix.py 0644 root root 2112 16858 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/darwin.py 0644 root root 75 6543 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/diskinfo.py 0644 root root 761 54481 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/freebsd.py 0644 root root 512 38529 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/hpux.py 0644 root root 6914 20978 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/linux.py 0644 root root 13187 10629 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/osf1.py 0644 root root 2886 63926 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/sunos.py 0644 root root 5235 24292 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/windows.py 0644 root root 3626 42453 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/dns.py 0644 root root 1037 12310 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/drivers.py 0644 root root 3738 17773 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/edit_file 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/edit_file/__init__.py 0644 root root 158 13185 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/fcache.py 0644 root root 693 53580 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/files 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/files/__init__.py 0644 root root 3255 48419 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/hash 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/hash/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/hash/md5 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/hash/md5/__init__.py 0644 root root 250 18836 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/hash/md5/md5.py 0644 root root 9380 15974 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ifconfig 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/__init__.py 0644 root root 213 19426 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/aix.py 0644 root root 2727 48858 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/darwin.py 0644 root root 2947 60719 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/freebsd.py 0644 root root 1968 60931 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/hpux.py 0644 root root 4995 56279 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/ifconfig.py 0644 root root 4689 7181 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/linux.py 0644 root root 10112 35734 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/osf1.py 0644 root root 3197 11191 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/sunos.py 0644 root root 4863 56306 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/windows.py 0644 root root 1361 32983 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/journaled_data.py 0644 root root 17286 41596 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/kv_store 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_abstract.py 0644 root root 721 55579 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_null.py 0644 root root 278 21653 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_sec.py 0644 root root 973 14369 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_simple.py 0644 root root 797 59249 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/lazy.py 0644 root root 1912 16809 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/lock/__init__.py 0644 root root 7570 41858 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/loop_delay 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/loop_delay/__init__.py 0644 root root 58 4663 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/mounts 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/mounts/__init__.py 0644 root root 209 19108 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/aix.py 0644 root root 3514 28685 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/darwin.py 0644 root root 1419 34220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/freebsd.py 0644 root root 1419 34220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/hpux.py 0644 root root 1233 24581 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/linux.py 0644 root root 1459 45323 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/mounts.py 0644 root root 2941 20435 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/osf1.py 0644 root root 963 4516 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/sunos.py 0644 root root 1165 18665 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/windows.py 0644 root root 1058 10462 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/naming 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/naming/__init__.py 0644 root root 13676 23766 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/net 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/net/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/converters.py 0644 root root 1068 9148 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/getaddr.py 0644 root root 2021 30662 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/ipaddress.py 0644 root root 79927 13003 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/noop_log 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/noop_log/__init__.py 0644 root root 268 22325 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/optparser 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/optparser/__init__.py 0644 root root 16717 25426 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/os 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/os/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/linux.py 0644 root root 868 64893 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/sunos.py 0644 root root 858 59150 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/windows.py 0644 root root 263 22627 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages/list 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/__init__.py 0644 root root 238 21804 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/aix.py 0644 root root 594 43615 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/darwin.py 0644 root root 1088 17332 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/freebsd.py 0644 root root 1157 20826 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/hpux.py 0644 root root 893 64754 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/linux.py 0644 root root 2624 55946 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/osf1.py 0644 root root 1415 37185 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/sunos.py 0644 root root 3219 38145 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/windows.py 0644 root root 4534 13643 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages/update 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/aix.py 0644 root root 223 17362 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/darwin.py 0644 root root 448 35295 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/hpux.py 0644 root root 264 21363 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/linux.py 0644 root root 620 49166 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/osf1.py 0644 root root 673 50841 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/sunos.py 0644 root root 1771 4070 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/windows.py 0644 root root 178 13967 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/password 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/password/__init__.py 0644 root root 225 20790 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/aix.py 0644 root root 353 27546 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/freebsd.py 0644 root root 335 26224 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/linux.py 0644 root root 361 27968 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/sunos.py 0644 root root 1968 12006 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ping 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ping/__init__.py 0644 root root 217 19886 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/aix.py 0644 root root 289 21566 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/darwin.py 0644 root root 372 25459 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/freebsd.py 0644 root root 297 22038 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/hpux.py 0644 root root 250 18650 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/linux.py 0644 root root 373 25730 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/osf1.py 0644 root root 250 18646 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/sunos.py 0644 root root 202 16126 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/windows.py 0644 root root 263 19166 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/proc 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/proc/__init__.py 0644 root root 19025 16645 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/process_title.py 0644 root root 131 11648 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/banner.py 0644 root root 132 10455 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/cluster 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/cluster/__init__.py 0644 root root 34889 26731 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/color 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/color/__init__.py 0644 root root 12159 11865 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/command.py 0644 root root 1272 27143 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/forest 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/forest/__init__.py 0644 root root 14690 34570 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/instance 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/instance/__init__.py 0644 root root 14016 1296 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/listener.py 0644 root root 138 9786 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/table 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/table/__init__.py 0644 root root 4623 5891 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/table/tabulate.py 0644 root root 26529 21618 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/term 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/term/__init__.py 0644 root root 851 249 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/requester 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/requester/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/api_client.py 0644 root root 792 60304 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/url_util_abstract.py 0644 root root 159 13183 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/url_util_direct.py 0644 root root 203 17478 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/rfc3339.py 0644 root root 743 59603 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/runfiles 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/runfiles/__init__.py 0644 root root 1725 2648 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/selector.py 0644 root root 2787 2852 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/semver.py 0644 root root 5105 3003 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/__init__.py 0644 root root 4439 62066 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/advfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/advfs/osf1.py 0644 root root 3313 59707 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/jfs2 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/jfs2/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/jfs2/aix.py 0644 root root 4099 32521 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/lvm 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/lvm/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/lvm/linux.py 0644 root root 4569 6103 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/vxfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/vxfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/vxfs/hpux.py 0644 root root 2762 7982 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/zfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/zfs/sunos.py 0644 root root 1438 46950 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ssl 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ssl/__init__.py 0644 root root 4031 31506 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats/collector 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/__init__.py 0644 root root 211 19300 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/aix.py 0644 root root 71 5606 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/darwin.py 0644 root root 2724 45054 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/hpux.py 0644 root root 3487 40309 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/linux.py 0644 root root 5088 14561 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/sunos.py 0644 root root 5862 59293 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/windows.py 0644 root root 2309 35855 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats/provider 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/__init__.py 0644 root root 223 20556 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/aix.py 0644 root root 4274 49037 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/darwin.py 0644 root root 4777 42242 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/freebsd.py 0644 root root 4527 20815 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/hpux.py 0644 root root 6828 64149 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/linux.py 0644 root root 14379 64655 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/provider.py 0644 root root 4119 54557 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/sunos.py 0644 root root 7294 4232 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/windows.py 0644 root root 8200 19302 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/storage.py 0644 root root 589 50051 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/string 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/string/__init__.py 0644 root root 1471 46837 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/subsystems 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/advfs.py 0644 root root 6434 40486 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/amazon.py 0644 root root 1537 52459 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/btrfs.py 0644 root root 13608 45451 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/docker.py 0644 root root 29903 21339 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/ethtool.py 0644 root root 1740 56685 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/gce.py 0644 root root 814 61683 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/subsystems/lvm 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/aix.py 0644 root root 7970 44000 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/linux.py 0644 root root 582 44867 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/veritas.py 0644 root root 517 41337 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/zfs.py 0644 root root 9537 31220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/zone.py 0644 root root 3124 40219 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/systemd 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/systemd/__init__.py 0644 root root 1843 14560 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/timeit 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/timeit/__init__.py 0644 root root 1929 9750 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/uri 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/uri/__init__.py 0644 root root 2645 3876 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/version 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/version/__init__.py 0644 root root 1270 29514 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/version/version.py 0644 root root 21 1365 1779444357
1 d none /usr/share/opensvc/opensvc/utilities/wakeonlan 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/wakeonlan/__init__.py 0644 root root 1506 45784 1779436975
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 0755 root root 5447 18167 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/authkey.py 0755 root root 17241 7192 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/bios.py 0755 root root 2165 27446 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/chkconfig.py 0755 root root 3667 1530 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/comp.py 0644 root root 16963 53302 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/cron.py 0755 root root 6898 14446 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/crontabentry.py 0755 root root 8714 22567 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/etcsystem.py 0755 root root 6722 9689 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/file.py 0755 root root 13044 30723 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fileinc.py 0755 root root 12078 53091 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fileprop.py 0755 root root 9631 27377 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/firmware.py 0755 root root 6698 18711 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fs.py 0755 root root 22529 37954 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/group.py 0755 root root 7738 35340 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/group_membership.py 0755 root root 7892 62851 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval.py 0755 root root 12094 953 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_parser.py 0755 root root 6023 35836 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_with_fpath.py 0755 root root 1570 59003 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/linux_mpath.py 0755 root root 15677 40723 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/nodeconf.py 0755 root root 5978 26466 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/package.py 0755 root root 26305 274 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/process.py 0755 root root 13316 54272 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/rc.py 0755 root root 4513 59049 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/remove_files.py 0755 root root 2367 47306 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/self_signed_cert.py 0755 root root 5378 59979 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/smfcfgs.py 0755 root root 14007 16199 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sudoers.py 0755 root root 2376 45343 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/svcconf.py 0755 root root 9997 50975 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/symlink.py 0755 root root 3817 27588 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sysctl.py 0755 root root 8511 16742 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/systemd_unit_state.py 0755 root root 6101 44587 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sysvinit.py 0755 root root 7817 31424 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/tar.py 0755 root root 5836 45936 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/timedatectl.py 0755 root root 5731 16072 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/user.py 0755 root root 16530 61719 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/utilities.py 0644 root root 1260 37413 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_file.py 0755 root root 2988 45078 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_tar.py 0755 root root 4036 55666 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/vuln.py 0755 root root 23123 7508 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/xinetd.py 0755 root root 5514 57669 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/yes 0755 root root 121 9914 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zfs.py 0755 root root 8814 16796 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zpool.py 0755 root root 5546 35703 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zprop.py 0755 root root 4413 47974 1779436975
1 d none /var/log 0755 root sys
1 d none /var/log/opensvc 0755 root root
1 i pkginfo 209 17447 1779444370
1 i postinstall 369 32335 1779444370
1 i preinstall 1411 49438 1779444370
1 i preremove 717 52870 1779444370
 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!                                                                                                                                                                                                                                                                                                                                                                                                                        0707010001f014000081a40000000000000000000000016a102a92000000d1000000e600010003ffffffffffffffff0000000800000000pkginfo   CLASSES=none
PSTAMP=OPENSVC-2.1,REV=2031
EMAIL=support@opensvc.com
VENDOR=https://www.opensvc.com
CATEGORY=application
VERSION=2.1,REV=2031
ARCH=all
NAME=Cluster and configuration management agent
PKG=opensvc
   0707010001f013000081a40000000000000000000000016a102a9200022c1b000000e600010003ffffffffffffffff0000000700000000pkgmap    : 1 26980
1 d none /etc/bash_completion.d 0755 root root
1 f none /etc/bash_completion.d/opensvc.sh 0644 root root 39189 11048 1779436975
1 d none /etc/opensvc 0755 root root
1 s none /usr/bin/nodemgr=../share/opensvc/bin/opensvc
1 s none /usr/bin/om=../share/opensvc/bin/om
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 0644 root root 731 3508 1779436975
1 f none /usr/share/doc/opensvc/copyright 0644 root root 5124 30396 1779436975
1 f none /usr/share/doc/opensvc/daemon.events 0644 root root 5798 20275 1779444362
1 d none /usr/share/doc/opensvc/provisioning 0755 root root
1 f none /usr/share/doc/opensvc/provisioning/provisioning.agent.debian 0644 root root 2602 50922 1779436975
1 f none /usr/share/doc/opensvc/provisioning/provisioning.example 0644 root root 362 30449 1779436975
1 f none /usr/share/doc/opensvc/schedule 0644 root root 2710 60003 1779436975
1 f none /usr/share/doc/opensvc/template.cfg.DEFAULT.conf.gz 0644 root root 1147 13923 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.DEFAULT.conf.gz 0644 root root 1036 63906 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.arbitrator.conf.gz 0644 root root 817 35991 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.centera.conf.gz 0644 root root 509 60859 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.dorado.conf.gz 0644 root root 526 1507 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.emcvnx.conf.gz 0644 root root 475 58879 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.eva.conf.gz 0644 root root 424 51109 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.freenas.conf.gz 0644 root root 465 57976 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hcs.conf.gz 0644 root root 823 36867 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hds.conf.gz 0644 root root 584 10113 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.hp3par.conf.gz 0644 root root 398 49663 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.ibmds.conf.gz 0644 root root 286 37139 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.ibmsvc.conf.gz 0644 root root 265 34983 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.netapp.conf.gz 0644 root root 299 38108 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.nexenta.conf.gz 0644 root root 415 49911 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.pure.conf.gz 0644 root root 724 26602 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.symmetrix.conf.gz 0644 root root 673 19934 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.vioserver.conf.gz 0644 root root 266 32971 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.array.xtremio.conf.gz 0644 root root 403 49230 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.asset.conf.gz 0644 root root 232 28003 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.checks.conf.gz 0644 root root 232 26701 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.cluster.conf.gz 0644 root root 1277 26928 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.cni.conf.gz 0644 root root 233 28910 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.compliance.conf.gz 0644 root root 492 60136 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.dequeue_actions.conf.gz 0644 root root 228 28022 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.disks.conf.gz 0644 root root 231 28180 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.disk.conf.gz 0644 root root 449 57559 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.multicast.conf.gz 0644 root root 477 59989 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.relay.conf.gz 0644 root root 460 56542 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hb.unicast.conf.gz 0644 root root 493 60121 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.hook.conf.gz 0644 root root 305 35993 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.listener.conf.gz 0644 root root 1007 61561 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.bridge.conf.gz 0644 root root 269 35442 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.routed_bridge.conf.gz 0644 root root 1102 8829 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.network.weave.conf.gz 0644 root root 205 23557 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.node.conf.gz 0644 root root 3212 6640 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.packages.conf.gz 0644 root root 235 28011 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.patches.conf.gz 0644 root root 234 28789 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.directory.conf.gz 0644 root root 516 80 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.dorado.conf.gz 0644 root root 729 24282 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.drbd.conf.gz 0644 root root 780 30133 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.freenas.conf.gz 0644 root root 810 30642 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.hcs.conf.gz 0644 root root 785 29911 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.loop.conf.gz 0644 root root 491 59412 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.pure.conf.gz 0644 root root 713 24249 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.share.conf.gz 0644 root root 521 64788 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.shm.conf.gz 0644 root root 451 55899 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.symmetrix.conf.gz 0644 root root 684 19935 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.vg.conf.gz 0644 root root 503 61914 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.virtual.conf.gz 0644 root root 792 34285 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.pool.zpool.conf.gz 0644 root root 504 62903 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.reboot.conf.gz 0644 root root 534 562 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.rotate_root_pw.conf.gz 0644 root root 231 28369 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stats.conf.gz 0644 root root 330 40230 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stats_collection.conf.gz 0644 root root 229 27475 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.stonith.conf.gz 0644 root root 285 35291 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.switch.brocade.conf.gz 0644 root root 517 64964 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.syslog.conf.gz 0644 root root 428 50176 1779444362
1 f none /usr/share/doc/opensvc/template.cluster.sysreport.conf.gz 0644 root root 271 31996 1779444362
1 f none /usr/share/doc/opensvc/template.comp_module.py 0644 root root 1232 29792 1779436975
1 f none /usr/share/doc/opensvc/template.comp_module.sh 0644 root root 471 33835 1779436975
1 f none /usr/share/doc/opensvc/template.node.arbitrator.conf.gz 0644 root root 817 35991 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.centera.conf.gz 0644 root root 509 60859 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.dorado.conf.gz 0644 root root 526 1507 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.emcvnx.conf.gz 0644 root root 475 58879 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.eva.conf.gz 0644 root root 424 51109 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.freenas.conf.gz 0644 root root 465 57976 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hcs.conf.gz 0644 root root 823 36867 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hds.conf.gz 0644 root root 584 10113 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.hp3par.conf.gz 0644 root root 398 49663 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.ibmds.conf.gz 0644 root root 286 37139 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.ibmsvc.conf.gz 0644 root root 265 34983 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.netapp.conf.gz 0644 root root 299 38108 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.nexenta.conf.gz 0644 root root 415 49911 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.pure.conf.gz 0644 root root 724 26602 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.symmetrix.conf.gz 0644 root root 673 19934 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.vioserver.conf.gz 0644 root root 266 32971 1779444362
1 f none /usr/share/doc/opensvc/template.node.array.xtremio.conf.gz 0644 root root 466 55744 1779444362
1 f none /usr/share/doc/opensvc/template.node.asset.conf.gz 0644 root root 232 28003 1779444362
1 f none /usr/share/doc/opensvc/template.node.brocade.conf.gz 0644 root root 225 26517 1779444362
1 f none /usr/share/doc/opensvc/template.node.centera.conf.gz 0644 root root 228 26040 1779444362
1 f none /usr/share/doc/opensvc/template.node.checks.conf.gz 0644 root root 232 26701 1779444362
1 f none /usr/share/doc/opensvc/template.node.cluster.conf.gz 0644 root root 1277 26928 1779444362
1 f none /usr/share/doc/opensvc/template.node.cni.conf.gz 0644 root root 233 28910 1779444362
1 f none /usr/share/doc/opensvc/template.node.compliance.conf.gz 0644 root root 492 60136 1779444362
1 f none /usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz 0644 root root 232 29357 1779444362
1 f none /usr/share/doc/opensvc/template.node.disks.conf.gz 0644 root root 231 28180 1779444362
1 f none /usr/share/doc/opensvc/template.node.dorado.conf.gz 0644 root root 228 28145 1779444362
1 f none /usr/share/doc/opensvc/template.node.emcvnx.conf.gz 0644 root root 227 26398 1779444362
1 f none /usr/share/doc/opensvc/template.node.eva.conf.gz 0644 root root 220 26898 1779444362
1 f none /usr/share/doc/opensvc/template.node.freenas.conf.gz 0644 root root 228 26361 1779444362
1 f none /usr/share/doc/opensvc/template.node.gcedisks.conf.gz 0644 root root 229 27545 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.disk.conf.gz 0644 root root 449 57559 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.multicast.conf.gz 0644 root root 477 59989 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.relay.conf.gz 0644 root root 460 56542 1779444362
1 f none /usr/share/doc/opensvc/template.node.hb.unicast.conf.gz 0644 root root 493 60121 1779444362
1 f none /usr/share/doc/opensvc/template.node.hcs.conf.gz 0644 root root 225 27365 1779444362
1 f none /usr/share/doc/opensvc/template.node.hds.conf.gz 0644 root root 221 26245 1779444362
1 f none /usr/share/doc/opensvc/template.node.hook.conf.gz 0644 root root 305 35993 1779444362
1 f none /usr/share/doc/opensvc/template.node.hp3par.conf.gz 0644 root root 230 26922 1779444362
1 f none /usr/share/doc/opensvc/template.node.ibmds.conf.gz 0644 root root 227 29224 1779444362
1 f none /usr/share/doc/opensvc/template.node.ibmsvc.conf.gz 0644 root root 224 26939 1779444362
1 f none /usr/share/doc/opensvc/template.node.listener.conf.gz 0644 root root 1007 61561 1779444362
1 f none /usr/share/doc/opensvc/template.node.necism.conf.gz 0644 root root 224 26779 1779444362
1 f none /usr/share/doc/opensvc/template.node.netapp.conf.gz 0644 root root 228 28228 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.bridge.conf.gz 0644 root root 269 35442 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.routed_bridge.conf.gz 0644 root root 1102 8829 1779444362
1 f none /usr/share/doc/opensvc/template.node.network.weave.conf.gz 0644 root root 205 23557 1779444362
1 f none /usr/share/doc/opensvc/template.node.node.conf.gz 0644 root root 3807 17830 1779444362
1 f none /usr/share/doc/opensvc/template.node.nsr.conf.gz 0644 root root 220 27281 1779444362
1 f none /usr/share/doc/opensvc/template.node.packages.conf.gz 0644 root root 235 28011 1779444362
1 f none /usr/share/doc/opensvc/template.node.patches.conf.gz 0644 root root 234 28789 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.directory.conf.gz 0644 root root 516 80 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.dorado.conf.gz 0644 root root 729 24282 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.drbd.conf.gz 0644 root root 780 30133 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.freenas.conf.gz 0644 root root 810 30642 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.hcs.conf.gz 0644 root root 785 29911 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.loop.conf.gz 0644 root root 491 59412 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.pure.conf.gz 0644 root root 713 24249 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.share.conf.gz 0644 root root 521 64788 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.shm.conf.gz 0644 root root 451 55899 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.symmetrix.conf.gz 0644 root root 684 19935 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.vg.conf.gz 0644 root root 503 61914 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.virtual.conf.gz 0644 root root 792 34285 1779444362
1 f none /usr/share/doc/opensvc/template.node.pool.zpool.conf.gz 0644 root root 504 62903 1779444362
1 f none /usr/share/doc/opensvc/template.node.reboot.conf.gz 0644 root root 534 562 1779444362
1 f none /usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz 0644 root root 231 28369 1779444362
1 f none /usr/share/doc/opensvc/template.node.stats.conf.gz 0644 root root 330 40230 1779444362
1 f none /usr/share/doc/opensvc/template.node.stats_collection.conf.gz 0644 root root 229 27475 1779444362
1 f none /usr/share/doc/opensvc/template.node.stonith.conf.gz 0644 root root 285 35291 1779444362
1 f none /usr/share/doc/opensvc/template.node.switch.brocade.conf.gz 0644 root root 517 64964 1779444362
1 f none /usr/share/doc/opensvc/template.node.sym.conf.gz 0644 root root 221 27464 1779444362
1 f none /usr/share/doc/opensvc/template.node.syslog.conf.gz 0644 root root 428 50176 1779444362
1 f none /usr/share/doc/opensvc/template.node.sysreport.conf.gz 0644 root root 276 34194 1779444362
1 f none /usr/share/doc/opensvc/template.node.vioserver.conf.gz 0644 root root 226 27878 1779444362
1 f none /usr/share/doc/opensvc/template.node.xtremio.conf.gz 0644 root root 229 28669 1779444362
1 f none /usr/share/doc/opensvc/template.nscfg.DEFAULT.conf.gz 0644 root root 2014 62503 1779444362
1 f none /usr/share/doc/opensvc/template.secret.DEFAULT.conf.gz 0644 root root 1501 61163 1779444362
1 f none /usr/share/doc/opensvc/template.service.DEFAULT.conf.gz 0644 root root 9840 53749 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.forking.conf.gz 0644 root root 4736 11779 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.simple.conf.gz 0644 root root 4841 26782 1779444361
1 f none /usr/share/doc/opensvc/template.service.app.winservice.conf.gz 0644 root root 4012 53993 1779444361
1 f none /usr/share/doc/opensvc/template.service.certificate.tls.conf.gz 0644 root root 676 17366 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.amazon.conf.gz 0644 root root 4288 23141 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.docker.conf.gz 0644 root root 5863 16202 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.esx.conf.gz 0644 root root 4116 58801 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.hpvm.conf.gz 0644 root root 4117 58983 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.jail.conf.gz 0644 root root 4204 10635 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.kvm.conf.gz 0644 root root 4410 33573 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.ldom.conf.gz 0644 root root 4117 60853 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.lxc.conf.gz 0644 root root 4701 10785 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.lxd.conf.gz 0644 root root 4229 7982 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.oci.conf.gz 0644 root root 5864 21940 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.openstack.conf.gz 0644 root root 4332 22938 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.ovm.conf.gz 0644 root root 4303 16879 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.podman.conf.gz 0644 root root 5866 14691 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.srp.conf.gz 0644 root root 4189 10786 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vbox.conf.gz 0644 root root 4147 64047 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vcloud.conf.gz 0644 root root 4363 29240 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.vz.conf.gz 0644 root root 4196 2865 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.xen.conf.gz 0644 root root 4254 20811 1779444361
1 f none /usr/share/doc/opensvc/template.service.container.zone.conf.gz 0644 root root 4662 65524 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.advfs.conf.gz 0644 root root 3995 51702 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.amazon.conf.gz 0644 root root 4106 56961 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.crypt.conf.gz 0644 root root 4272 8468 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.disk.conf.gz 0644 root root 4142 65060 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.drbd.conf.gz 0644 root root 4306 18192 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.gandi.conf.gz 0644 root root 4047 53490 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.gce.conf.gz 0644 root root 4360 21413 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.hpvm.conf.gz 0644 root root 3926 42257 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.ldom.conf.gz 0644 root root 3928 43496 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.loop.conf.gz 0644 root root 3952 39989 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.lv.conf.gz 0644 root root 4103 56334 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.lvm.conf.gz 0644 root root 4101 65115 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.md.conf.gz 0644 root root 4277 20884 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.pool.conf.gz 0644 root root 4158 2301 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.rados.conf.gz 0644 root root 4167 1111 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.raw.conf.gz 0644 root root 4298 23903 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz 0644 root root 3942 28647 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.veritas.conf.gz 0644 root root 3939 38064 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vg.conf.gz 0644 root root 4101 60574 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vxdg.conf.gz 0644 root root 3939 36925 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.vxvol.conf.gz 0644 root root 4083 57914 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.zpool.conf.gz 0644 root root 4159 6879 1779444361
1 f none /usr/share/doc/opensvc/template.service.disk.zvol.conf.gz 0644 root root 4021 50495 1779444361
1 f none /usr/share/doc/opensvc/template.service.expose.envoy.conf.gz 0644 root root 1066 2161 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz 0644 root root 4915 31832 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.conf.gz 0644 root root 4905 32693 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.directory.conf.gz 0644 root root 3577 55547 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.docker.conf.gz 0644 root root 3603 2857 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext2.conf.gz 0644 root root 4914 31730 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext3.conf.gz 0644 root root 4914 34555 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.ext4.conf.gz 0644 root root 4914 24910 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.flag.conf.gz 0644 root root 3511 47849 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.none.conf.gz 0644 root root 4071 57591 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.tmpfs.conf.gz 0644 root root 4072 62934 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz 0644 root root 4915 34341 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.xfs.conf.gz 0644 root root 4914 40064 1779444361
1 f none /usr/share/doc/opensvc/template.service.fs.zfs.conf.gz 0644 root root 4971 46220 1779444361
1 f none /usr/share/doc/opensvc/template.service.hashpolicy.envoy.conf.gz 0644 root root 756 27958 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.amazon.conf.gz 0644 root root 4917 34485 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.cni.conf.gz 0644 root root 4211 14460 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz 0644 root root 4745 9046 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.docker.conf.gz 0644 root root 5105 61174 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.gce.conf.gz 0644 root root 4776 19749 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.host.conf.gz 0644 root root 4701 14877 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.netns.conf.gz 0644 root root 5107 62941 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.route.conf.gz 0644 root root 3512 55093 1779444361
1 f none /usr/share/doc/opensvc/template.service.ip.rule.conf.gz 0644 root root 3512 47912 1779444361
1 f none /usr/share/doc/opensvc/template.service.route.envoy.conf.gz 0644 root root 1477 57023 1779444361
1 f none /usr/share/doc/opensvc/template.service.share.nfs.conf.gz 0644 root root 3507 45767 1779444361
1 f none /usr/share/doc/opensvc/template.service.subset.conf.gz 0644 root root 1307 32778 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz 0644 root root 4224 6096 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz 0644 root root 4376 28985 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.dds.conf.gz 0644 root root 4663 5288 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.docker.conf.gz 0644 root root 4145 67 1779444361
1 f none /usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz 0644 root root 4315 23759 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz 0644 root root 4308 16976 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz 0644 root root 4183 2324 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz 0644 root root 4320 25656 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz 0644 root root 4165 65450 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.netapp.conf.gz 0644 root root 4307 19035 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.nexenta.conf.gz 0644 root root 4302 14536 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.oci.conf.gz 0644 root root 4143 64462 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.radosclone.conf.gz 0644 root root 4131 60607 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.radossnap.conf.gz 0644 root root 4155 63229 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.rsync.conf.gz 0644 root root 4787 23013 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.s3.conf.gz 0644 root root 4525 43620 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symclone.conf.gz 0644 root root 4388 31035 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz 0644 root root 4368 25283 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsnapvx.conf.gz 0644 root root 4434 39630 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz 0644 root root 4254 13883 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.zfs.conf.gz 0644 root root 4391 30363 1779444362
1 f none /usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz 0644 root root 4374 28744 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.docker.conf.gz 0644 root root 6315 3105 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.host.conf.gz 0644 root root 4376 30510 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.oci.conf.gz 0644 root root 6319 11386 1779444362
1 f none /usr/share/doc/opensvc/template.service.task.podman.conf.gz 0644 root root 6317 11366 1779444362
1 f none /usr/share/doc/opensvc/template.service.vhost.envoy.conf.gz 0644 root root 413 51234 1779444362
1 f none /usr/share/doc/opensvc/template.service.volume.conf.gz 0644 root root 4316 15802 1779444362
1 f none /usr/share/doc/opensvc/template.sysreport.conf.gz 0644 root root 414 50777 1779436975
1 f none /usr/share/doc/opensvc/template.usr.DEFAULT.conf.gz 0644 root root 1813 32761 1779444362
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 0644 root root 7965 43839 1779436975
1 f none /usr/share/man/man1/svcmgr.1.gz 0644 root root 14781 48247 1779436975
1 f none /usr/share/man/man1/svcmon.1.gz 0644 root root 1346 41099 1779436975
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 0644 root root 597 51671 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.agent.xml 0644 root root 2097 49739 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.defaults.parameters 0644 root root 387 33409 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.AIX 0755 root root 439 34806 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.Darwin 0755 root root 810 64665 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.FreeBSD 0755 root root 929 10762 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.OSF1 0755 root root 464 35940 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.SunOS 0755 root root 645 50927 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.debian 0755 root root 703 52638 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.hpux 0755 root root 942 7699 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.openrc 0755 root root 803 4078 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.redhat 0755 root root 821 352 1779436975
1 f none /usr/share/opensvc/bin/init/opensvc.init.suse 0755 root root 707 52871 1779436975
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-agent.service 0644 root root 828 12836 1779436975
1 f none /usr/share/opensvc/bin/init/systemd.opensvc-services.service 0644 root root 404 38007 1779436975
1 s none /usr/share/opensvc/bin/nodemgr=om
1 f none /usr/share/opensvc/bin/om 0755 root root 2450 46883 1779436975
1 s none /usr/share/opensvc/bin/opensvc=om
1 f none /usr/share/opensvc/bin/postinstall 0755 root root 50268 38725 1779436975
1 f none /usr/share/opensvc/bin/postremove 0644 root root 993 15241 1779436975
1 f none /usr/share/opensvc/bin/preinstall 0755 root root 1411 49438 1779436975
1 f none /usr/share/opensvc/bin/preuninstall 0755 root root 717 52870 1779436975
1 s none /usr/share/opensvc/bin/svcmgr=om
1 s none /usr/share/opensvc/bin/svcmon=om
1 d none /usr/share/opensvc/html 0755 root root
1 f none /usr/share/opensvc/html/index.html 0644 root root 389 34813 1756141688
1 f none /usr/share/opensvc/html/index.js 0644 root root 1485411 30874 1756141688
1 f none /usr/share/opensvc/html/index.js.LICENSE.txt 0644 root root 3169 1364 1756141688
1 d none /usr/share/opensvc/opensvc 0755 root root
1 f none /usr/share/opensvc/opensvc/__init__.py 0644 root root 86 7790 1779436975
1 f none /usr/share/opensvc/opensvc/__main__.py 0644 root root 4431 774 1779436975
1 d none /usr/share/opensvc/opensvc/commands 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/commands/ccfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/ccfg/__init__.py 0644 root root 288 24273 1779436975
1 f none /usr/share/opensvc/opensvc/commands/ccfg/parser.py 0644 root root 1141 19378 1779436975
1 d none /usr/share/opensvc/opensvc/commands/cfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/cfg/__init__.py 0644 root root 285 23976 1779436975
1 f none /usr/share/opensvc/opensvc/commands/cfg/parser.py 0644 root root 4065 25913 1779436975
1 d none /usr/share/opensvc/opensvc/commands/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/daemon/__init__.py 0644 root root 1167 28510 1779436975
1 f none /usr/share/opensvc/opensvc/commands/daemon/parser.py 0644 root root 4666 9821 1779436975
1 d none /usr/share/opensvc/opensvc/commands/mgr 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/mgr/__init__.py 0644 root root 12561 4416 1779436975
1 f none /usr/share/opensvc/opensvc/commands/mgr/parser.py 0644 root root 22927 33936 1779436975
1 d none /usr/share/opensvc/opensvc/commands/network 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/network/__init__.py 0644 root root 1219 31768 1779436975
1 f none /usr/share/opensvc/opensvc/commands/network/parser.py 0644 root root 1844 4794 1779436975
1 d none /usr/share/opensvc/opensvc/commands/node 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/node/__init__.py 0644 root root 1871 20842 1779436975
1 f none /usr/share/opensvc/opensvc/commands/node/parser.py 0644 root root 40745 51565 1779436975
1 d none /usr/share/opensvc/opensvc/commands/nscfg 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/nscfg/__init__.py 0644 root root 291 24651 1779436975
1 f none /usr/share/opensvc/opensvc/commands/nscfg/parser.py 0644 root root 1223 26526 1779436975
1 d none /usr/share/opensvc/opensvc/commands/pool 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/pool/__init__.py 0644 root root 1207 30424 1779436975
1 f none /usr/share/opensvc/opensvc/commands/pool/parser.py 0644 root root 3010 23074 1779436975
1 d none /usr/share/opensvc/opensvc/commands/sec 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/sec/__init__.py 0644 root root 285 24009 1779436975
1 f none /usr/share/opensvc/opensvc/commands/sec/parser.py 0644 root root 4938 28731 1779436975
1 d none /usr/share/opensvc/opensvc/commands/svc 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/svc/__init__.py 0644 root root 284 24050 1779436975
1 f none /usr/share/opensvc/opensvc/commands/svc/parser.py 0644 root root 34761 36387 1779436975
1 f none /usr/share/opensvc/opensvc/commands/svcmon.py 0644 root root 12590 462 1779436975
1 d none /usr/share/opensvc/opensvc/commands/usr 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/usr/__init__.py 0644 root root 285 24102 1779436975
1 f none /usr/share/opensvc/opensvc/commands/usr/parser.py 0644 root root 4790 19062 1779436975
1 d none /usr/share/opensvc/opensvc/commands/vol 0755 root root
1 f none /usr/share/opensvc/opensvc/commands/vol/__init__.py 0644 root root 285 24075 1779436975
1 f none /usr/share/opensvc/opensvc/commands/vol/parser.py 0644 root root 262 21242 1779436975
1 d none /usr/share/opensvc/opensvc/core 0755 root root
1 f none /usr/share/opensvc/opensvc/core/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/core/capabilities 0755 root root
1 f none /usr/share/opensvc/opensvc/core/capabilities/__init__.py 0644 root root 5233 43501 1779436975
1 f none /usr/share/opensvc/opensvc/core/capabilities/linux.py 0644 root root 554 40171 1779436975
1 f none /usr/share/opensvc/opensvc/core/capabilities/sunos.py 0644 root root 534 38872 1779436975
1 f none /usr/share/opensvc/opensvc/core/cloud.py 0644 root root 627 48196 1779436975
1 d none /usr/share/opensvc/opensvc/core/collector 0755 root root
1 f none /usr/share/opensvc/opensvc/core/collector/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/actions.py 0644 root root 11450 168 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/cli.py 0644 root root 98574 63237 1779436975
1 f none /usr/share/opensvc/opensvc/core/collector/rpc.py 0644 root root 54756 8576 1779436975
1 f none /usr/share/opensvc/opensvc/core/comm.py 0644 root root 37849 35701 1779436975
1 f none /usr/share/opensvc/opensvc/core/compliance.py 0644 root root 29525 32512 1779436975
1 d none /usr/share/opensvc/opensvc/core/configfile 0755 root root
1 f none /usr/share/opensvc/opensvc/core/configfile/__init__.py 0644 root root 1148 31017 1779436975
1 f none /usr/share/opensvc/opensvc/core/contexts.py 0644 root root 12383 21269 1779436975
1 d none /usr/share/opensvc/opensvc/core/exceptions 0755 root root
1 f none /usr/share/opensvc/opensvc/core/exceptions/__init__.py 0644 root root 4198 10463 1779436975
1 f none /usr/share/opensvc/opensvc/core/extconfig.py 0644 root root 64752 40924 1779436975
1 f none /usr/share/opensvc/opensvc/core/freezer.py 0644 root root 2952 23878 1779436975
1 f none /usr/share/opensvc/opensvc/core/keywords.py 0644 root root 27932 27813 1779436975
1 f none /usr/share/opensvc/opensvc/core/logger.py 0644 root root 8423 36295 1779436975
1 f none /usr/share/opensvc/opensvc/core/network.py 0644 root root 19014 20909 1779436975
1 d none /usr/share/opensvc/opensvc/core/node 0755 root root
1 f none /usr/share/opensvc/opensvc/core/node/__init__.py 0644 root root 206 18606 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/darwin.py 0644 root root 267 19856 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/freebsd.py 0644 root root 1989 12646 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/hpux.py 0644 root root 673 49624 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/linux.py 0644 root root 12198 18556 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/node.py 0644 root root 200952 63613 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/nodedict.py 0644 root root 66308 6064 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/sunos.py 0644 root root 4189 39460 1779436975
1 f none /usr/share/opensvc/opensvc/core/node/windows.py 0644 root root 3713 20543 1779436975
1 d none /usr/share/opensvc/opensvc/core/objects 0755 root root
1 f none /usr/share/opensvc/opensvc/core/objects/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/builder.py 0644 root root 8370 65199 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/ccfg.py 0644 root root 582 43273 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/ccfgdict.py 0644 root root 3468 7185 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/cfg.py 0644 root root 2071 25058 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/cfgdict.py 0644 root root 3428 6142 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/data.py 0644 root root 14339 17262 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/nscfg.py 0644 root root 3249 42371 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/nscfgdict.py 0644 root root 3306 61476 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/pg.py 0644 root root 4286 48141 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/sec.py 0644 root root 7686 33847 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/secdict.py 0644 root root 5926 43202 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/svc.py 0644 root root 210100 33420 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/svcdict.py 0644 root root 54733 60584 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/usr.py 0644 root root 4871 40447 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/usrdict.py 0644 root root 6718 48401 1779436975
1 f none /usr/share/opensvc/opensvc/core/objects/vol.py 0644 root root 1500 44867 1779436975
1 d none /usr/share/opensvc/opensvc/core/oc3path 0755 root root
1 f none /usr/share/opensvc/opensvc/core/oc3path/__init__.py 0644 root root 534 42229 1779436975
1 f none /usr/share/opensvc/opensvc/core/pool.py 0644 root root 6679 27310 1779436975
1 f none /usr/share/opensvc/opensvc/core/resource.py 0644 root root 49373 42777 1779436975
1 f none /usr/share/opensvc/opensvc/core/resourceset.py 0644 root root 15965 45338 1779436975
1 f none /usr/share/opensvc/opensvc/core/scheduler.py 0644 root root 39957 43216 1779436975
1 d none /usr/share/opensvc/opensvc/core/status 0755 root root
1 f none /usr/share/opensvc/opensvc/core/status/__init__.py 0644 root root 6448 23821 1779436975
1 d none /usr/share/opensvc/opensvc/core/sysreport 0755 root root
1 f none /usr/share/opensvc/opensvc/core/sysreport/__init__.py 0644 root root 215 19726 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/aix.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/darwin.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/freebsd.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/hpux.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/linux.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/osf1.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/sunos.py 0644 root root 94 8699 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/sysreport.py 0644 root root 18494 19589 1779436975
1 f none /usr/share/opensvc/opensvc/core/sysreport/windows.py 0644 root root 1500 44679 1779436975
1 d none /usr/share/opensvc/opensvc/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/__main__.py 0644 root root 30 2609 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/clusterlock.py 0644 root root 4112 34461 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/collector.py 0644 root root 24591 29502 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/dns.py 0644 root root 23499 2353 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/events.py 0644 root root 4999 45650 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handler.py 0644 root root 2177 29636 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/api 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/api/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/api/get.py 0644 root root 872 58985 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/askfull 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/askfull/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/askfull/post.py 0644 root root 1205 24243 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/authinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/authinfo/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/authinfo/get.py 0644 root root 723 53484 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/post.py 0644 root root 363 27178 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/get.py 0644 root root 470 35123 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/catalogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/catalogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/catalogs/get.py 0644 root root 578 41956 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/cluster 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/get.py 0644 root root 449 33900 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/post.py 0644 root root 1003 7916 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/get.py 0644 root root 433 32954 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/post.py 0644 root root 2664 4594 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/start/post.py 0644 root root 1049 12681 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/get.py 0644 root root 1747 57085 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/status/get.py 0644 root root 1544 48221 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/post.py 0644 root root 3361 55656 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/events 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/events/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/events/get.py 0644 root root 2932 18360 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/join 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/join/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/join/post.py 0644 root root 3451 60575 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/key 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/delete.py 0644 root root 1157 17661 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/get.py 0644 root root 1534 48398 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/key/post.py 0644 root root 1361 30461 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/keywords 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/keywords/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/keywords/get.py 0644 root root 1039 10514 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/leave 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/leave/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/leave/post.py 0644 root root 840 61353 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/lock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/lock/post.py 0644 root root 1144 14834 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/networks 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/networks/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/networks/get.py 0644 root root 470 35634 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/action 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/action/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/action/post.py 0644 root root 4095 28578 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/get.py 0644 root root 1257 25303 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/checks 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/checks/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/checks/get.py 0644 root root 923 2520 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/config 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/config/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/config/get.py 0644 root root 1590 55344 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/drain 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/drain/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/drain/post.py 0644 root root 2951 16615 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/get.py 0644 root root 470 34195 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/logs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/logs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/logs/get.py 0644 root root 1031 15495 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/node/monitor/post.py 0644 root root 2499 47308 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/get.py 0644 root root 474 35748 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/action 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/action/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/action/post.py 0644 root root 6512 61801 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/get.py 0644 root root 1911 3489 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/clear 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/clear/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/clear/post.py 0644 root root 1261 31932 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/config 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/config/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/config/get.py 0644 root root 3100 26996 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/get.py 0644 root root 1102 19347 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/create 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/create/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/create/post.py 0644 root root 4406 50351 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/enter 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/enter/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/enter/get.py 0644 root root 2745 908 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/keys 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/keys/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/keys/get.py 0644 root root 940 4127 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/logs 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/logs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/logs/get.py 0644 root root 1508 48342 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/monitor/post.py 0644 root root 11189 31915 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/selector 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/selector/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/selector/get.py 0644 root root 1195 20608 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/object/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/get.py 0644 root root 774 59026 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/object/status/post.py 0644 root root 1023 11976 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/pools 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/pools/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/pools/get.py 0644 root root 926 5189 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/rx/get.py 0644 root root 1251 21571 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/status 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/status/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/status/get.py 0644 root root 906 65015 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/relay/tx/post.py 0644 root root 1663 47484 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/rundone 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/rundone/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/rundone/post.py 0644 root root 1219 19323 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/schedules 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/schedules/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/schedules/get.py 0644 root root 3052 15668 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/sshkey 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sshkey/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sshkey/get.py 0644 root root 1129 13725 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sync/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/sync/get.py 0644 root root 2115 20044 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/template 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/template/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/template/get.py 0644 root root 1431 37586 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/templates 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/templates/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/templates/get.py 0644 root root 1554 43658 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/unlock 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/unlock/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/unlock/post.py 0644 root root 1131 16638 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/wait 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wait/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wait/get.py 0644 root root 5794 17454 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/post.py 0644 root root 1863 11612 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/handlers/whoami 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/handlers/whoami/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/handlers/whoami/get.py 0644 root root 1111 10608 1779436975
1 d none /usr/share/opensvc/opensvc/daemon/hb 0755 root root
1 f none /usr/share/opensvc/opensvc/daemon/hb/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/disk.py 0644 root root 16057 39598 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/hb.py 0644 root root 9406 11714 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/mcast.py 0644 root root 10949 2102 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/relay.py 0644 root root 6994 46408 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/hb/ucast.py 0644 root root 9740 55345 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/listener.py 0644 root root 84543 19325 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/main.py 0644 root root 20663 28590 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/monitor.py 0644 root root 182043 27370 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/rbac.py 0644 root root 7909 55074 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/scheduler.py 0644 root root 23028 54175 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/shared.py 0644 root root 66706 26340 1779436975
1 f none /usr/share/opensvc/opensvc/daemon/winservice.py 0644 root root 3041 38823 1779436975
1 d none /usr/share/opensvc/opensvc/drivers 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/array 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/array/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/centera.py 0644 root root 3624 10596 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/dorado.py 0644 root root 37315 27625 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/emcvnx.py 0644 root root 4925 39828 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/eva.py 0644 root root 4713 20970 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/freenas.py 0644 root root 40393 23755 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/gce.py 0644 root root 1262 29328 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hcs.py 0644 root root 51686 35438 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hds.py 0644 root root 26575 61033 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/hp3par.py 0644 root root 14348 53006 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/ibmds.py 0644 root root 3490 59854 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/ibmsvc.py 0644 root root 3039 27436 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/necism.py 0644 root root 11207 59782 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/netapp.py 0644 root root 3276 43786 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/nexenta.py 0644 root root 11354 32483 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/pure.py 0644 root root 34787 46086 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/symmetrix.py 0644 root root 52276 21871 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/vioserver.py 0644 root root 3363 53911 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/array/xtremio.py 0644 root root 27166 24554 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/backupsrv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/backupsrv/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/backupsrv/networker.py 0644 root root 1215 21096 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/__init__.py 0644 root root 4635 63498 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/btrfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/btrfs/linux.py 0644 root root 2466 41918 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/eth 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/__init__.py 0644 root root 1 10 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/hpux.py 0644 root root 2677 31413 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/linux.py 0644 root root 2975 62566 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/eth/sunos.py 0644 root root 10067 1996 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fm 0755 root root
1 d none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm/__init__.py 0644 root root 1041 9968 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/fmadm/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage/__init__.py 0644 root root 1509 39839 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fm/openmanage/linux.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_i 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/aix.py 0644 root root 1081 5385 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/darwin.py 0644 root root 1045 4535 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/hpux.py 0644 root root 1440 25799 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/linux.py 0644 root root 2135 9930 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_i/sunos.py 0644 root root 1177 11860 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/advfs/osf1.py 0644 root root 1013 4208 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/aix.py 0644 root root 1081 5368 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/darwin.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/freebsd.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/hpux.py 0644 root root 1554 39638 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/linux.py 0644 root root 2375 20838 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/sunos.py 0644 root root 1531 29919 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/windows.py 0644 root root 1110 11572 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/__init__.py 0644 root root 2692 51249 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/jstat 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/jstat/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/jstat/linux.py 0644 root root 6150 9041 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/lag 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/hpux.py 0644 root root 1829 53861 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/linux.py 0644 root root 4014 15035 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/sunos.py 0644 root root 6755 3012 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/lag/windows.py 0644 root root 769 54018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mcelog 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mcelog/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mcelog/linux.py 0644 root root 1735 52476 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath/native 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/aix.py 0644 root root 2256 15635 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/hpux.py 0644 root root 2353 23750 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/linux.py 0644 root root 4203 2605 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/osf1.py 0644 root root 835 57322 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/sunos.py 0644 root root 2608 33884 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/native/windows.py 0644 root root 1452 34119 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/__init__.py 0644 root root 2864 48880 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/aix.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/hpux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/numa 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/numa/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/numa/linux.py 0644 root root 1419 27595 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/__init__.py 0644 root root 4497 28663 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/megaraid/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/sas2 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/__init__.py 0644 root root 3698 47330 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/sas2/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/__init__.py 0644 root root 3099 18497 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/sunos.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/raid/smartarray/windows.py 0644 root root 511 38024 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/__init__.py 0644 root root 1223 19422 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/sync/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/vg_u 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/aix.py 0644 root root 1334 22807 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/hpux.py 0644 root root 1851 63025 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/vg_u/linux.py 0644 root root 1198 15198 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/check/zpool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/__init__.py 0644 root root 2252 14995 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/freebsd.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/linux.py 0644 root root 20 1733 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/check/zpool/sunos.py 0644 root root 20 1733 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/cloud 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/cloud/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/amazon.py 0644 root root 1262 38152 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/gandi.py 0644 root root 1359 39024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/openstack.py 0644 root root 2126 35146 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/cloud/vcloud.py 0644 root root 1888 14922 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pg/linux.py 0644 root root 22642 53171 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pool/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/directory.py 0644 root root 1771 60072 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/dorado.py 0644 root root 7136 21507 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/drbd.py 0644 root root 6553 32762 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/freenas.py 0644 root root 3921 27604 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/hcs.py 0644 root root 4769 27411 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/loop.py 0644 root root 1483 42046 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/pure.py 0644 root root 4487 3574 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/share.py 0644 root root 1738 58116 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/shm.py 0644 root root 2535 53563 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/symmetrix.py 0644 root root 6186 62163 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/pool/vg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/pool/vg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/vg/linux.py 0644 root root 1455 39442 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/virtual.py 0644 root root 2428 57003 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/pool/zpool.py 0644 root root 1905 4483 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/__init__.py 0644 root root 27196 45129 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/forking 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/forking/__init__.py 0644 root root 5214 65270 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/simple 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/simple/__init__.py 0644 root root 9559 53325 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/simple/sunos.py 0644 root root 1101 14130 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/app/winservice 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/app/winservice/__init__.py 0644 root root 3731 7925 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/certificate 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/certificate/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/certificate/tls 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/certificate/tls/__init__.py 0644 root root 1855 20647 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/__init__.py 0644 root root 15756 14184 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/amazon/__init__.py 0644 root root 11321 41727 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/docker/__init__.py 0644 root root 41733 14981 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/esx 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/esx/__init__.py 0644 root root 3713 6243 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/hpvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/hpvm/__init__.py 0644 root root 4252 53621 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/jail 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/jail/__init__.py 0644 root root 4203 35941 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/kvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/kvm/__init__.py 0644 root root 21009 12519 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/ldom 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/ldom/__init__.py 0644 root root 4428 54924 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/lxc 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/lxc/__init__.py 0644 root root 35363 8199 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/lxd 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/lxd/__init__.py 0644 root root 11013 35224 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/openstack 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/openstack/__init__.py 0644 root root 11657 1577 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/ovm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/ovm/__init__.py 0644 root root 5509 3052 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/podman 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/podman/__init__.py 0644 root root 4258 65018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/srp 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/srp/__init__.py 0644 root root 11533 21829 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vbox 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vbox/__init__.py 0644 root root 4737 14798 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vcloud 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vcloud/__init__.py 0644 root root 9806 7740 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/vz 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/vz/__init__.py 0644 root root 5769 20025 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/xen 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/xen/__init__.py 0644 root root 3781 16303 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/container/zone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/zone/__init__.py 0644 root root 44662 35153 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/container/zone/configuration_profile.py 0644 root root 7235 59554 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/__init__.py 0644 root root 3023 44200 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/advfs/__init__.py 0644 root root 3756 17874 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/amazon/__init__.py 0644 root root 10577 8246 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/crypt 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/crypt/__init__.py 0644 root root 9519 53844 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/disk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/disk/__init__.py 0644 root root 5431 13082 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/disk/linux.py 0644 root root 3895 37245 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/drbd 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/drbd/__init__.py 0644 root root 30941 50221 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/gandi 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/gandi/__init__.py 0644 root root 6948 43246 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/gce 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/gce/__init__.py 0644 root root 10039 32886 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/hpvm 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/hpvm/__init__.py 0644 root root 4813 13083 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/ldom 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/ldom/__init__.py 0644 root root 4300 37738 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/loop 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/__init__.py 0644 root root 3384 56829 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/darwin.py 0644 root root 1896 14849 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/freebsd.py 0644 root root 2519 56589 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/linux.py 0644 root root 3387 64445 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/loop/sunos.py 0644 root root 2322 47300 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/lv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/__init__.py 0644 root root 5102 51198 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/hpux.py 0644 root root 1336 37219 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/lv/linux.py 0644 root root 5059 38736 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/md 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/md/__init__.py 0644 root root 19506 50793 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/rados 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/rados/__init__.py 0644 root root 10197 18658 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/raw 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/__init__.py 0644 root root 14997 38932 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/hpux.py 0644 root root 581 44485 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/linux.py 0644 root root 9368 21346 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/raw/sunos.py 0644 root root 1131 12552 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/__init__.py 0644 root root 10167 17939 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/freebsd.py 0644 root root 279 24247 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/hpux.py 0644 root root 4606 14239 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/linux.py 0644 root root 860 4012 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/osf1.py 0644 root root 6026 39842 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sg.py 0644 root root 15616 55044 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sunos.py 0644 root root 229 20264 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vdisk 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vdisk/__init__.py 0644 root root 2509 57656 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/aix.py 0644 root root 9564 20876 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/hpux.py 0644 root root 14879 14737 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vg/linux.py 0644 root root 13674 7009 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vxdg 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vxdg/__init__.py 0644 root root 10418 11252 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/vxvol 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/vxvol/__init__.py 0644 root root 6742 49816 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/zpool 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/zpool/__init__.py 0644 root root 14707 23951 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/disk/zvol 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/disk/zvol/__init__.py 0644 root root 5781 42512 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/expose 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/expose/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/expose/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/expose/envoy/__init__.py 0644 root root 3393 61334 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/__init__.py 0644 root root 25275 112 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/aix.py 0644 root root 4001 28779 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/btrfs/__init__.py 0644 root root 4229 54601 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/darwin.py 0644 root root 6433 4018 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/directory 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/directory/__init__.py 0644 root root 6713 32185 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/docker/__init__.py 0644 root root 4326 62962 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2/__init__.py 0644 root root 169 13752 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext2/linux.py 0644 root root 300 22935 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3/__init__.py 0644 root root 169 13753 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext3/linux.py 0644 root root 300 22940 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4/__init__.py 0644 root root 169 13754 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/ext4/linux.py 0644 root root 300 22945 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/flag 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/__init__.py 0644 root root 2656 4024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/darwin.py 0644 root root 239 19130 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/freebsd.py 0644 root root 239 19130 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/linux.py 0644 root root 239 19107 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/flag/sunos.py 0644 root root 410 31900 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/freebsd.py 0644 root root 4354 57373 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/hpux.py 0644 root root 2536 58610 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/linux.py 0644 root root 23773 16596 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/none 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/none/__init__.py 0644 root root 184 14964 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/none/linux.py 0644 root root 142 10887 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/osf1.py 0644 root root 3975 28449 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/sunos.py 0644 root root 5871 29544 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/__init__.py 0644 root root 185 15086 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/linux.py 0644 root root 144 11131 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/__init__.py 0644 root root 169 13820 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/linux.py 0644 root root 311 24618 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/sunos.py 0644 root root 320 24535 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/windows.py 0644 root root 3639 14235 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs/__init__.py 0644 root root 168 13702 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/xfs/linux.py 0644 root root 297 22959 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/__init__.py 0644 root root 4996 46153 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/freebsd.py 0644 root root 135 10606 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/linux.py 0644 root root 133 10435 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/fs/zfs/sunos.py 0644 root root 133 10443 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy/__init__.py 0644 root root 1701 5872 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/__init__.py 0644 root root 31799 58410 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/amazon 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/amazon/__init__.py 0644 root root 8548 55210 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/cni 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/cni/__init__.py 0644 root root 17127 16895 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/crossbow 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/crossbow/__init__.py 0644 root root 6361 9388 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/gce 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/gce/__init__.py 0644 root root 6805 48621 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/host 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/aix.py 0644 root root 957 9556 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/darwin.py 0644 root root 947 8024 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/freebsd.py 0644 root root 969 10278 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/hpux.py 0644 root root 1222 27142 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/linux.py 0644 root root 3520 2208 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/osf1.py 0644 root root 971 10443 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/sunos.py 0644 root root 920 5775 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/host/windows.py 0644 root root 1729 1148 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/netns 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/netns/__init__.py 0644 root root 25323 27655 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/route 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/route/__init__.py 0644 root root 4435 8975 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/rule 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/rule/__init__.py 0755 root root 4414 6521 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/ip/zone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/ip/zone/__init__.py 0644 root root 1911 13684 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/route 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/route/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/route/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/route/envoy/__init__.py 0644 root root 4288 15205 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/share 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/share/nfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/__init__.py 0644 root root 418 29851 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/hpux.py 0644 root root 4133 35276 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/linux.py 0644 root root 6552 22509 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/share/nfs/sunos.py 0644 root root 3932 22211 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/__init__.py 0644 root root 9419 44796 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfs/__init__.py 0644 root root 21421 56722 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap/__init__.py 0644 root root 10756 62412 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/dds 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/dds/__init__.py 0644 root root 15891 61622 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/docker/__init__.py 0644 root root 6624 32987 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/evasnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/evasnap/__init__.py 0644 root root 11204 34393 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3par 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3par/__init__.py 0644 root root 11265 51316 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap/__init__.py 0644 root root 3769 23570 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap/__init__.py 0644 root root 9894 57672 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap/__init__.py 0644 root root 6114 48234 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/netapp 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/netapp/__init__.py 0644 root root 10954 16574 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/nexenta 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/nexenta/__init__.py 0644 root root 9321 26619 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/radosclone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/radosclone/__init__.py 0644 root root 4496 60079 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/radossnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/radossnap/__init__.py 0644 root root 6214 55747 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/rsync 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/rsync/__init__.py 0644 root root 20423 13753 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/s3 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/s3/__init__.py 0644 root root 11281 23823 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone/__init__.py 0644 root root 11846 26566 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symclone/linux.py 0644 root root 2295 38575 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/__init__.py 0644 root root 1508 47169 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/linux.py 0644 root root 305 25254 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx/__init__.py 0644 root root 9807 64238 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs/__init__.py 0644 root root 22630 63160 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/zfs/__init__.py 0644 root root 18764 34681 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap/__init__.py 0644 root root 7920 49713 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/__init__.py 0644 root root 13214 61961 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/docker 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/docker/__init__.py 0644 root root 2861 24152 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/host 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/host/__init__.py 0644 root root 5576 10960 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/task/podman 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/task/podman/__init__.py 0644 root root 2716 13407 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/vhost 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/vhost/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/vhost/envoy 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/vhost/envoy/__init__.py 0644 root root 942 4575 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/resource/volume 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/resource/volume/__init__.py 0644 root root 28987 48119 1779436975
1 d none /usr/share/opensvc/opensvc/drivers/sanswitch 0755 root root
1 f none /usr/share/opensvc/opensvc/drivers/sanswitch/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/drivers/sanswitch/brocade.py 0644 root root 4859 32045 1779436975
1 f none /usr/share/opensvc/opensvc/env.py 0644 root root 11053 46913 1779436975
1 d none /usr/share/opensvc/opensvc/foreign 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/__init__.py 0644 root root 84 7588 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/aenum 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/aenum/LICENSE 0644 root root 1525 54463 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/__init__.py 0644 root root 1133 22601 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_common.py 0644 root root 8203 56420 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_constant.py 0644 root root 5370 30133 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_enum.py 0644 root root 127043 55064 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_py2.py 0644 root root 184 16287 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_py3.py 0644 root root 406 34868 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/aenum/_tuple.py 0644 root root 19103 32582 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/colorama 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/colorama/__init__.py 0644 root root 239 20569 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/ansi.py 0644 root root 2524 36689 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/ansitowin32.py 0644 root root 10326 58775 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/initialise.py 0644 root root 1915 27388 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/win32.py 0644 root root 5404 33431 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/colorama/winterm.py 0644 root root 6438 36817 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/concurrent 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/__init__.py 0644 root root 76 7038 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/concurrent/futures 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/__init__.py 0644 root root 887 61837 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/_base.py 0644 root root 23929 51658 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/process.py 0644 root root 14986 33005 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/concurrent/futures/thread.py 0644 root root 7229 40216 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/h2 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/h2/__init__.py 0644 root root 90 6306 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/config.py 0644 root root 6498 16294 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/connection.py 0644 root root 81546 57700 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/errors.py 0644 root root 1555 53972 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/events.py 0644 root root 21962 61081 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/exceptions.py 0644 root root 5379 47311 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/frame_buffer.py 0644 root root 6771 991 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/settings.py 0644 root root 11816 19155 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/stream.py 0644 root root 55949 39083 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/utilities.py 0644 root root 22818 15042 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/h2/windows.py 0644 root root 5603 63580 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hpack 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hpack/__init__.py 0644 root root 483 40300 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/compat.py 0644 root root 679 51094 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/exceptions.py 0644 root root 974 12209 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/hpack.py 0644 root root 22771 29570 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman.py 0644 root root 2521 3994 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman_constants.py 0644 root root 4628 3530 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/huffman_table.py 0644 root root 168580 33086 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/struct.py 0644 root root 1052 22489 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hpack/table.py 0644 root root 9643 37661 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/__init__.py 0644 root root 891 8092 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/certs.pem 0644 root root 323173 33422 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/cli.py 0644 root root 7505 60246 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/common 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/__init__.py 0644 root root 81 6520 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/bufsocket.py 0644 root root 8330 60572 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/connection.py 0644 root root 6536 53931 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/decoder.py 0644 root root 1560 61341 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/exceptions.py 0644 root root 1996 29011 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/headers.py 0644 root root 8972 52322 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/common/util.py 0644 root root 1523 57456 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/compat.py 0644 root root 1775 11221 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/contrib.py 0644 root root 7098 28143 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/http11 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/__init__.py 0644 root root 101 8119 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/connection.py 0644 root root 17001 55449 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/parser.py 0644 root root 2596 4987 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http11/response.py 0644 root root 12882 9837 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyper/http20 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/__init__.py 0644 root root 99 8025 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/connection.py 0644 root root 33863 49411 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/errors.py 0644 root root 3251 23364 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/exceptions.py 0644 root root 1044 19022 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/response.py 0644 root root 8078 49510 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/stream.py 0644 root root 12084 16319 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/util.py 0644 root root 1592 64028 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/http20/window.py 0644 root root 6155 54985 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/httplib_compat.py 0644 root root 4452 17319 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/ssl_compat.py 0644 root root 10156 2280 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyper/tls.py 0644 root root 5149 35818 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/hyperframe 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/__init__.py 0644 root root 136 11012 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/exceptions.py 0644 root root 936 8914 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/flags.py 0644 root root 1286 36799 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/hyperframe/frame.py 0644 root root 26664 44644 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jsonpath_ng 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/__init__.py 0644 root root 116 9740 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/__init__.py 0644 root root 605 51186 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/arithmetic.py 0644 root root 2381 42464 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/filter.py 0644 root root 3176 37865 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/iterable.py 0644 root root 2984 30614 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/parser.py 0644 root root 5208 53851 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/string.py 0644 root root 2600 6043 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/jsonpath.py 0644 root root 21629 55304 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/lexer.py 0644 root root 5321 23398 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jsonpath_ng/parser.py 0644 root root 5605 15884 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/__init__.py 0644 root root 811 5224 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/__main__.py 0644 root root 4162 57846 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/algorithms.py 0644 root root 13336 34025 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/api_jws.py 0644 root root 8115 40081 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/api_jwt.py 0644 root root 7967 28838 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/compat.py 0644 root root 1624 63064 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt/contrib 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/py_ecdsa.py 0644 root root 1771 6297 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/pycrypto.py 0644 root root 1249 32945 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/exceptions.py 0644 root root 986 19020 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/help.py 0644 root root 1609 981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/jwt/utils.py 0644 root root 2629 19369 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/looseversion 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/LICENSE 0644 root root 2490 8107 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/README.md 0644 root root 107 9974 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/looseversion/__init__.py 0644 root root 9330 42745 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/ply 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/ply/__init__.py 0644 root root 103 8202 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/cpp.py 0644 root root 33639 59078 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/ctokens.py 0644 root root 3155 62449 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/lex.py 0644 root root 42905 12577 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/yacc.py 0644 root root 137736 64832 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/ply/ygen.py 0644 root root 2246 57594 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/pyaes 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/__init__.py 0644 root root 2087 40499 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/aes.py 0644 root root 60310 16766 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/blockfeeder.py 0644 root root 8119 11752 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/pyaes/util.py 0644 root root 2050 33260 1779436975
1 d none /usr/share/opensvc/opensvc/foreign/rfc3986 0755 root root
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/__init__.py 0644 root root 1562 65396 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/_mixin.py 0644 root root 13214 26981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/abnf_regexp.py 0644 root root 9081 57501 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/api.py 0644 root root 3887 3806 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/builder.py 0644 root root 9577 46569 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/compat.py 0644 root root 1513 57981 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/exceptions.py 0644 root root 3775 37350 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/iri.py 0644 root root 5466 13478 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/misc.py 0644 root root 4094 61565 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/normalizers.py 0644 root root 5259 23832 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/parseresult.py 0644 root root 14654 62237 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/uri.py 0644 root root 5227 18096 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/rfc3986/validators.py 0644 root root 13854 34299 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/six.py 0644 root root 34573 40057 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/winstats.py 0644 root root 12562 15274 1779436975
1 f none /usr/share/opensvc/opensvc/foreign/wmi.py 0644 root root 46957 4995 1779436975
1 d none /usr/share/opensvc/opensvc/utilities 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/arp.py 0644 root root 2375 48498 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/asset 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/asset/__init__.py 0644 root root 208 18850 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/aix.py 0644 root root 3116 21007 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/asset.py 0644 root root 23544 65370 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/darwin.py 0644 root root 3193 33903 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/freebsd.py 0644 root root 2256 26309 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/hpux.py 0644 root root 8462 54124 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/linux.py 0644 root root 25262 5096 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/osf1.py 0644 root root 10949 33913 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/sunos.py 0644 root root 9437 86 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/asset/windows.py 0644 root root 10471 12442 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/auth 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/auth/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/auth/auth_factory.py 0644 root root 777 61513 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/auth/auth_provider_abstract.py 0644 root root 269 22565 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/cache 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/cache/__init__.py 0644 root root 4249 53365 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/chunker.py 0644 root root 149 11007 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/concurrent_futures.py 0644 root root 373 33236 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/configparser 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/configparser/__init__.py 0644 root root 2053 19385 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/converters 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/converters/__init__.py 0644 root root 9522 20002 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/dbglock.py 0644 root root 1348 32554 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/devices 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/devices/__init__.py 0644 root root 501 44562 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/darwin.py 0644 root root 894 1834 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/freebsd.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/linux.py 0644 root root 11480 11242 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/sunos.py 0644 root root 1057 14455 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devices/windows.py 0644 root root 265 21262 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/devtree 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/devtree/__init__.py 0644 root root 212 19200 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/aix.py 0644 root root 2065 19556 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/darwin.py 0644 root root 115 9414 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/devtree.py 0644 root root 13678 24105 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/freebsd.py 0644 root root 116 9424 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/hpux.py 0644 root root 6148 32544 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/linux.py 0644 root root 20892 3199 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/osf1.py 0644 root root 3048 8277 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/sunos.py 0644 root root 11031 56703 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/veritas.py 0644 root root 8228 31021 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/devtree/windows.py 0644 root root 656 51218 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/diskinfo 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/__init__.py 0644 root root 214 19408 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/aix.py 0644 root root 2112 16858 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/darwin.py 0644 root root 75 6543 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/diskinfo.py 0644 root root 761 54481 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/freebsd.py 0644 root root 512 38529 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/hpux.py 0644 root root 6914 20978 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/linux.py 0644 root root 13187 10629 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/osf1.py 0644 root root 2886 63926 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/sunos.py 0644 root root 5235 24292 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/diskinfo/windows.py 0644 root root 3626 42453 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/dns.py 0644 root root 1037 12310 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/drivers.py 0644 root root 3738 17773 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/edit_file 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/edit_file/__init__.py 0644 root root 158 13185 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/fcache.py 0644 root root 693 53580 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/files 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/files/__init__.py 0644 root root 3255 48419 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/hash 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/hash/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/hash/md5 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/hash/md5/__init__.py 0644 root root 250 18836 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/hash/md5/md5.py 0644 root root 9380 15974 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ifconfig 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/__init__.py 0644 root root 213 19426 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/aix.py 0644 root root 2727 48858 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/darwin.py 0644 root root 2947 60719 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/freebsd.py 0644 root root 1968 60931 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/hpux.py 0644 root root 4995 56279 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/ifconfig.py 0644 root root 4689 7181 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/linux.py 0644 root root 10112 35734 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/osf1.py 0644 root root 3197 11191 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/sunos.py 0644 root root 4863 56306 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ifconfig/windows.py 0644 root root 1361 32983 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/journaled_data.py 0644 root root 17286 41596 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/kv_store 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_abstract.py 0644 root root 721 55579 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_null.py 0644 root root 278 21653 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_sec.py 0644 root root 973 14369 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/kv_store/kv_simple.py 0644 root root 797 59249 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/lazy.py 0644 root root 1912 16809 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/lock 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/lock/__init__.py 0644 root root 7570 41858 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/loop_delay 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/loop_delay/__init__.py 0644 root root 58 4663 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/mounts 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/mounts/__init__.py 0644 root root 209 19108 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/aix.py 0644 root root 3514 28685 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/darwin.py 0644 root root 1419 34220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/freebsd.py 0644 root root 1419 34220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/hpux.py 0644 root root 1233 24581 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/linux.py 0644 root root 1459 45323 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/mounts.py 0644 root root 2941 20435 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/osf1.py 0644 root root 963 4516 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/sunos.py 0644 root root 1165 18665 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/mounts/windows.py 0644 root root 1058 10462 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/naming 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/naming/__init__.py 0644 root root 13676 23766 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/net 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/net/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/converters.py 0644 root root 1068 9148 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/getaddr.py 0644 root root 2021 30662 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/net/ipaddress.py 0644 root root 79927 13003 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/noop_log 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/noop_log/__init__.py 0644 root root 268 22325 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/optparser 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/optparser/__init__.py 0644 root root 16717 25426 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/os 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/os/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/linux.py 0644 root root 868 64893 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/sunos.py 0644 root root 858 59150 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/os/windows.py 0644 root root 263 22627 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages/list 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/__init__.py 0644 root root 238 21804 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/aix.py 0644 root root 594 43615 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/darwin.py 0644 root root 1088 17332 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/freebsd.py 0644 root root 1157 20826 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/hpux.py 0644 root root 893 64754 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/linux.py 0644 root root 2624 55946 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/osf1.py 0644 root root 1415 37185 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/sunos.py 0644 root root 3219 38145 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/list/windows.py 0644 root root 4534 13643 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/packages/update 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/aix.py 0644 root root 223 17362 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/darwin.py 0644 root root 448 35295 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/hpux.py 0644 root root 264 21363 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/linux.py 0644 root root 620 49166 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/osf1.py 0644 root root 673 50841 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/sunos.py 0644 root root 1771 4070 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/packages/update/windows.py 0644 root root 178 13967 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/password 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/password/__init__.py 0644 root root 225 20790 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/aix.py 0644 root root 353 27546 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/freebsd.py 0644 root root 335 26224 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/linux.py 0644 root root 361 27968 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/password/sunos.py 0644 root root 1968 12006 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ping 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ping/__init__.py 0644 root root 217 19886 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/aix.py 0644 root root 289 21566 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/darwin.py 0644 root root 372 25459 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/freebsd.py 0644 root root 297 22038 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/hpux.py 0644 root root 250 18650 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/linux.py 0644 root root 373 25730 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/osf1.py 0644 root root 250 18646 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/sunos.py 0644 root root 202 16126 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/ping/windows.py 0644 root root 263 19166 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/proc 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/proc/__init__.py 0644 root root 19025 16645 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/process_title.py 0644 root root 131 11648 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/banner.py 0644 root root 132 10455 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/cluster 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/cluster/__init__.py 0644 root root 34889 26731 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/color 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/color/__init__.py 0644 root root 12159 11865 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/command.py 0644 root root 1272 27143 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/forest 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/forest/__init__.py 0644 root root 14690 34570 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/instance 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/instance/__init__.py 0644 root root 14016 1296 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/listener.py 0644 root root 138 9786 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/table 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/table/__init__.py 0644 root root 4623 5891 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/render/table/tabulate.py 0644 root root 26529 21618 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/render/term 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/render/term/__init__.py 0644 root root 851 249 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/requester 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/requester/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/api_client.py 0644 root root 792 60304 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/url_util_abstract.py 0644 root root 159 13183 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/requester/url_util_direct.py 0644 root root 203 17478 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/rfc3339.py 0644 root root 743 59603 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/runfiles 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/runfiles/__init__.py 0644 root root 1725 2648 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/selector.py 0644 root root 2787 2852 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/semver.py 0644 root root 5105 3003 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/__init__.py 0644 root root 4439 62066 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/advfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/advfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/advfs/osf1.py 0644 root root 3313 59707 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/jfs2 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/jfs2/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/jfs2/aix.py 0644 root root 4099 32521 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/lvm 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/lvm/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/lvm/linux.py 0644 root root 4569 6103 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/vxfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/vxfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/vxfs/hpux.py 0644 root root 2762 7982 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/snap/zfs 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/snap/zfs/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/snap/zfs/sunos.py 0644 root root 1438 46950 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/ssl 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/ssl/__init__.py 0644 root root 4031 31506 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/__init__.py 0644 root root 0 0 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats/collector 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/__init__.py 0644 root root 211 19300 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/aix.py 0644 root root 71 5606 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/darwin.py 0644 root root 2724 45054 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/hpux.py 0644 root root 3487 40309 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/linux.py 0644 root root 5088 14561 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/sunos.py 0644 root root 5862 59293 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/collector/windows.py 0644 root root 2309 35855 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/stats/provider 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/__init__.py 0644 root root 223 20556 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/aix.py 0644 root root 4274 49037 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/darwin.py 0644 root root 4777 42242 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/freebsd.py 0644 root root 4527 20815 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/hpux.py 0644 root root 6828 64149 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/linux.py 0644 root root 14379 64655 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/provider.py 0644 root root 4119 54557 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/sunos.py 0644 root root 7294 4232 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/stats/provider/windows.py 0644 root root 8200 19302 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/storage.py 0644 root root 589 50051 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/string 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/string/__init__.py 0644 root root 1471 46837 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/subsystems 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/advfs.py 0644 root root 6434 40486 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/amazon.py 0644 root root 1537 52459 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/btrfs.py 0644 root root 13608 45451 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/docker.py 0644 root root 29903 21339 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/ethtool.py 0644 root root 1740 56685 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/gce.py 0644 root root 814 61683 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/subsystems/lvm 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/__init__.py 0644 root root 0 0 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/aix.py 0644 root root 7970 44000 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/lvm/linux.py 0644 root root 582 44867 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/veritas.py 0644 root root 517 41337 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/zfs.py 0644 root root 9537 31220 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/subsystems/zone.py 0644 root root 3124 40219 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/systemd 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/systemd/__init__.py 0644 root root 1843 14560 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/timeit 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/timeit/__init__.py 0644 root root 1929 9750 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/uri 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/uri/__init__.py 0644 root root 2645 3876 1779436975
1 d none /usr/share/opensvc/opensvc/utilities/version 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/version/__init__.py 0644 root root 1270 29514 1779436975
1 f none /usr/share/opensvc/opensvc/utilities/version/version.py 0644 root root 21 1365 1779444357
1 d none /usr/share/opensvc/opensvc/utilities/wakeonlan 0755 root root
1 f none /usr/share/opensvc/opensvc/utilities/wakeonlan/__init__.py 0644 root root 1506 45784 1779436975
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 0755 root root 5447 18167 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/authkey.py 0755 root root 17241 7192 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/bios.py 0755 root root 2165 27446 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/chkconfig.py 0755 root root 3667 1530 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/comp.py 0644 root root 16963 53302 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/cron.py 0755 root root 6898 14446 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/crontabentry.py 0755 root root 8714 22567 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/etcsystem.py 0755 root root 6722 9689 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/file.py 0755 root root 13044 30723 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fileinc.py 0755 root root 12078 53091 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fileprop.py 0755 root root 9631 27377 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/firmware.py 0755 root root 6698 18711 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/fs.py 0755 root root 22529 37954 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/group.py 0755 root root 7738 35340 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/group_membership.py 0755 root root 7892 62851 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval.py 0755 root root 12094 953 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_parser.py 0755 root root 6023 35836 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/keyval_with_fpath.py 0755 root root 1570 59003 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/linux_mpath.py 0755 root root 15677 40723 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/nodeconf.py 0755 root root 5978 26466 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/package.py 0755 root root 26305 274 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/process.py 0755 root root 13316 54272 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/rc.py 0755 root root 4513 59049 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/remove_files.py 0755 root root 2367 47306 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/self_signed_cert.py 0755 root root 5378 59979 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/smfcfgs.py 0755 root root 14007 16199 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sudoers.py 0755 root root 2376 45343 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/svcconf.py 0755 root root 9997 50975 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/symlink.py 0755 root root 3817 27588 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sysctl.py 0755 root root 8511 16742 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/systemd_unit_state.py 0755 root root 6101 44587 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/sysvinit.py 0755 root root 7817 31424 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/tar.py 0755 root root 5836 45936 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/timedatectl.py 0755 root root 5731 16072 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/user.py 0755 root root 16530 61719 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/utilities.py 0644 root root 1260 37413 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_file.py 0755 root root 2988 45078 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/volume_tar.py 0755 root root 4036 55666 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/vuln.py 0755 root root 23123 7508 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/xinetd.py 0755 root root 5514 57669 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/yes 0755 root root 121 9914 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zfs.py 0755 root root 8814 16796 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zpool.py 0755 root root 5546 35703 1779436975
1 f none /var/lib/opensvc/compliance/com.opensvc/zprop.py 0755 root root 4413 47974 1779436975
1 d none /var/log 0755 root sys
1 d none /var/log/opensvc 0755 root root
1 i pkginfo 209 17447 1779444370
1 i postinstall 369 32335 1779444370
1 i preinstall 1411 49438 1779444370
1 i preremove 717 52870 1779444370
 0707010001f5f5000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000000800000000install   0707010001f5f6000081a40000000000000000000000016a102a9200000171000000e600010003ffffffffffffffff0000001400000000install/postinstall   #!/sbin/sh
type python3 > /dev/null 2>&1 && PYTHON=python3 || PYTHON=python
su root -c "PATH=${PKG_INSTALL_ROOT}/usr/local/bin:/usr/local/bin:/opt/opensvc/bin/pkg:/opt/csw/bin:/usr/gnu/bin:/usr/xpg4/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/sfw/bin:/usr/sfw/bin:/usr/local/bin $PYTHON ${PKG_INSTALL_ROOT}//usr/share/opensvc/bin/postinstall"
   0707010001f5f8000081ed0000000000000000000000016a102a92000002cd000000e600010003ffffffffffffffff0000001200000000install/preremove #!/bin/sh

ARGS=$1

OSFLAVOR=`uname`
if [ $OSFLAVOR = 'SunOS' -o $OSFLAVOR = 'FreeBSD' ]
then
  if [ "$PKG_INSTALL_ROOT" != "/" -a "$PKG_INSTALL_ROOT" != "" ]; then
    echo "skipped pre-install when PKG_INSTALL_ROOT=$PKG_INSTALL_ROOT"
    exit 0
  fi
	ARGS=0
fi

if [ $OSFLAVOR = 'Linux' -a $ARGS = 'remove' ]
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" = '0' ]
then
	# uninstall package
	if [ $RUNNING -eq 0 ]
	then
                echo "stopping opensvc daemon"
		nodemgr daemon stop
	fi
	exit 0
fi

   0707010001f5f7000081ed0000000000000000000000016a102a9200000583000000e600010003ffffffffffffffff0000001300000000install/preinstall    #!/bin/sh

if [ "$PKG_INSTALL_ROOT" != "/" -a "$PKG_INSTALL_ROOT" != "" ]; then
  echo "skipped pre-install when PKG_INSTALL_ROOT=$PKG_INSTALL_ROOT"
  exit 0
fi

# 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 
type nodemgr >/dev/null 2>&1 || {
        echo "adding opensvc daemon restart flag"
	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
 0707010001f015000041ed0000000000000000000000056a102a9300000000000000e600010003ffffffffffffffff0000000500000000root  0707010001f5c4000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000000900000000root/var  0707010001f5c5000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000000d00000000root/var/lib  0707010001f5c6000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000001500000000root/var/lib/opensvc  0707010001f5c7000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000002000000000root/var/lib/opensvc/compliance   0707010001f5c8000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002c00000000root/var/lib/opensvc/compliance/com.opensvc   0707010001f5e4000081ed0000000000000000000000016a100daf0000270d000000e600010003ffffffffffffffff0000003700000000root/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)
   0707010001f5dd000081ed0000000000000000000000016a100daf000066c1000000e600010003ffffffffffffffff0000003700000000root/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 = [pkg.strip() for pkg in 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 += [prefix+x for x in 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 = []
        _pkgname = ""
        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 = [x.strip() for x in 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 foreign.looseversion 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: hp_del_pkg")
        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 getattr(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 as exc:
            try:
                os.unlink(fname)
                os.unlink(dname)
            except:
                pass
            raise Exception("download failed: %s" % str(exc))
        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, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            raise Exception('can not fetch installed packages list: %s' % err)
        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)
   0707010001f5ec000081a40000000000000000000000016a100daf000004ec000000e600010003ffffffffffffffff0000003900000000root/var/lib/opensvc/compliance/com.opensvc/utilities.py  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

def ssl_context_kwargs():
    kwargs = {}
    try:
        import ssl
        if [sys.version_info.major, sys.version_info.minor] >= [3, 10]:
            # noinspection PyUnresolvedReferences
            # pylint: disable=no-member
            kwargs["context"] = ssl._create_unverified_context(protocol=ssl.PROTOCOL_TLS_CLIENT)
        else:
            kwargs["context"] = ssl._create_unverified_context()
        kwargs["context"].set_ciphers("DEFAULT")
    except (ImportError, AttributeError):
        pass
    return kwargs

if __name__ == "__main__":
    print("this file is for import into compliance objects", file=sys.stderr)

0707010001f5d6000081ed0000000000000000000000016a100daf00001e3a000000e600010003ffffffffffffffff0000003500000000root/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)
  0707010001f5ea000081ed0000000000000000000000016a100daf00001663000000e600010003ffffffffffffffff0000003b00000000root/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)
 0707010001f5d2000081ed0000000000000000000000016a100daf00002f2e000000e600010003ffffffffffffffff0000003700000000root/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."
}
  """,
  "example_value_replace": """ 
{
 "path": "/etc/resolv.conf",
 "replace": "^search .*$",
 "fmt": "search foo.com bar.com baz.com"
}
  """,
  "description": """* Verify or Change file content.
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
* The fmt must match the check pattern ['check' statement]
* The fmt is used to substitute any string matching the replace pattern ['replace' statement]

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. Alternatively, the 'replace' statement can be used to substitute any matching expression by string provided by 'fmt' or 'ref' content.
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: No
    Help: A regular expression. Matching the regular expression is sufficent to grant compliancy. It is required to use either 'check' or 'replace'.
    Type: string
  -
    Id: replace
    Label: Replace regexp
    DisplayModeLabel: replace
    LabelCss: action16
    Mandatory: No
    Help: A regular expression. Any pattern matched by the reguler expression will be replaced. It is required to use either 'check' or 'replace'.
    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: strict_fmt
    Label: Strict Format
    DisplayModeLabel: strict fmt
    LabelCss: action16
    Help: Consider a line matching the check regexp invalid if the line is not strictly the same as fmt.
    Type: boolean
    Default: True
  -
    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.replaces = []
        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 and len(self.replaces) == 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 'check' in d and 'replace' in d:
            perror("'check' and 'replace' 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 'check' in d and d['check'] is not None:
            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, 'strict_fmt': d.get('strict_fmt', True)})
        if 'replace' in d and d['replace'] is not None:
            self.replaces.append({'replace':d['replace'], 'path':d['path'], 'add':c, 'strict_fmt': d.get('strict_fmt', True)})
        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:
                        if ck["strict_fmt"] and line != ck["add"]:
                            perror("pattern '%s' found in %s but not strictly equal to target" % (ck['check'], ck['path']))
                        else:
                            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

        for rp in self.replaces:
            pr = RET_OK
            m = 0
            ok = 0
            lines = self.files[rp['path']].split('\n')
            for line in lines:
                for current in re.finditer(rp['replace'], line):
                    if current.group(0) == rp['add']:
                        pinfo("%s : string '%s' found on target in line '%s'" % (rp['path'], current.group(0), line))
                        continue
                    perror("%s : string '%s' should be replaced by '%s' in line '%s'" % (rp['path'], current.group(0), rp['add'], line))
                    m += 1
                    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 ck["strict_fmt"] and 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()

        for rp in self.replaces:
            need_rewrite = False
            lines = self.files[rp['path']].rstrip('\n').split('\n')
            for i, line in enumerate(lines):
                for current in re.finditer(rp['replace'], line):
                    if current.group(0) == rp['add']:
                        continue
                    newline = re.sub(rp['replace'], rp['add'], line, count=1)
                    lines[i] = newline
                    pinfo("rewrite %s:%d:'%s', new content: '%s'" %(rp['path'], i, line, lines[i]))
                    line = newline
                    need_rewrite = True

            if need_rewrite:
                self.files[rp['path']] = '\n'.join(lines).rstrip("\n")+"\n"
                self.upds[rp['path']] = 1

        r |= self.rewrite_files()

        return r


if __name__ == "__main__":
    main(CompFileInc)
  0707010001f5f2000081ed0000000000000000000000016a100daf0000226e000000e600010003ffffffffffffffff0000003300000000root/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)

  0707010001f5c9000081ed0000000000000000000000016a100daf00001547000000e600010003ffffffffffffffff0000004000000000root/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 utilities import which
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 = []
        if not which('ansible-playbook'):
            perror('ansible-playbook binary not found')
            raise NotApplicable()

        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(d)

    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)

 0707010001f5f1000081ed0000000000000000000000016a100daf00000079000000e600010003ffffffffffffffff0000003000000000root/var/lib/opensvc/compliance/com.opensvc/yes   #!/usr/bin/env python

from __future__ import print_function

try:
    while True:
        print("yes")
except:
    pass
   0707010001f5e8000081ed0000000000000000000000016a100daf00001e89000000e600010003ffffffffffffffff0000003800000000root/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))

   0707010001f5ee000081ed0000000000000000000000016a100daf00000fc4000000e600010003ffffffffffffffff0000003a00000000root/var/lib/opensvc/compliance/com.opensvc/volume_tar.py #!/usr/bin/env python

from __future__ import print_function

data = {
  "default_prefix": "OSVC_COMP_VOLUME_TAR",
  "example_value": """                                                                                                                                                     
{                                                                                                                                                                          
  "ref": "/some/path/to/file.tar",                                                                                                                                         
  "path": "/home/user/bin",                                                                                                                                                
  "immutable": "true"                                                                                                                                                      
}                                                                                                                                                                          
  """,                                                                                                                                                                     
  "description": """* Verify and install tar 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 tar archive is extracted on check action                                                                                                                          
* Extract tar archive on fix action                                                                                                                                        
* Immutable boolean is used to know if extracted tar content can be modified on filesystem                                                                                 
""",                                                                                                                                                                       
  "form_definition": """
Desc: |
  A volume_tar rule, fed to the 'tar' compliance object to extract archive inside docker volume. For tar files, a reference content must be specified or pointed through an URL.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: file
    Type: json
    Format: dict

Inputs:
  -
    Id: ref
    Label: Tar uri
    DisplayModeLabel: ref
    LabelCss: fa-map-marker
    Help: "Examples:
        /path/to/reference_file.tar
        safe://safe.uuid.8dc85529a2b13b4b.626172.tar
        http://server/path/to/reference_file.tar
        https://server/path/to/reference_file.tar
        ftp://server/path/to/reference_file.tar
        ftp://login:pass@server/path/to/reference_file.tar"
    Type: string
  -
    Id: path
    Label: Install path
    DisplayModeLabel: path
    LabelCss: fa-map-marker
    Mandatory: Yes
    Help: path to install the tar reference content to.
    Type: string
  -
    Id: immutable
    Label: Immutable
    DisplayModeLabel: immutable
    LabelCss: fa-lock
    Mandatory: Yes
    Help: "On : extracted tar archive must not be modified on filesystem
           Off: extracted tar archive contents on filesystem can be modified"
    Type: boolean
"""
}

import os
import sys

sys.path.append(os.path.dirname(__file__))

from comp import *
from tar import Tar

class CompVolumeTar(Tar):
    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()
        Tar.init(self)
        self.vol_path = os.environ["OPENSVC_VOL_PATH"]
        for rule in self.rules:
            rule["path"] = os.path.join(self.vol_path, rule["path"].lstrip(os.sep))

if __name__ == "__main__":
    main(CompVolumeTar)
0707010001f5f0000081ed0000000000000000000000016a100daf0000158a000000e600010003ffffffffffffffff0000003600000000root/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)
  0707010001f5d8000081ed0000000000000000000000016a100daf00002f3e000000e600010003ffffffffffffffff0000003600000000root/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": """
{
  "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, separator=" "):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.cf = path
        self.separator = separator

    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"]
            separator = rule.get("separator", self.separator)
            if path not in self.file_keys:
                self.file_keys[path] = {
                    "separator": separator,
                    "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, separator=data.get("separator", self.separator), obj=self)
            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: %s is set %d times, should be set %d times"%(path, keyname, current_n_key, target_n_key))
                    return RET_ERR
                else:
                    if verbose:
                        pinfo("%s: %s is set %d times, on target"%(path, 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: %s is set, should not be"% (path, 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: %s[%d] is set to value %s, should not be"%(path, keyname, i, target))
                    return RET_ERR
                else:
                    if verbose:
                        pinfo("%s: %s is not set to value %s, on target"%(path, keyname, target))
                    return RET_OK
            else:
                if is_string(target) and target.strip() != "":
                    if verbose:
                        pinfo("%s: %s=%s is not set, on target"%(path, keyname, target))
                else:
                    if verbose:
                        pinfo("%s: %s is not set, on target"%(path, keyname))
                return RET_OK

        if value is None:
            if op == 'IN' and "unset" in map(str, target):
                if verbose:
                    pinfo("%s: %s is not set, on target"%(path, keyname))
                return RET_OK
            else:
                if verbose:
                    perror("%s: %s[%d] is not set, target: %s"%(path, keyname, instance, str(target)))
                return RET_ERR

        if type(value) == list:
            if str(target) in value:
                if verbose:
                    pinfo("%s: %s[%d]=%s on target"%(path, keyname, instance, str(value)))
                return RET_OK
            else:
                if verbose:
                    perror("%s: %s[%d]=%s is not set"%(path, keyname, instance, str(target)))
                return RET_ERR

        if op == '=':
            if str(value) != str(target):
                if verbose:
                    perror("%s: %s[%d]=%s, target: %s"%(path, keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s: %s=%s on target"%(path, keyname, str(value)))
        elif op == 'IN':
            if str(value) not in map(str, target):
                if verbose:
                    perror("%s: %s[%d]=%s, target: %s"%(path, keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s: %s=%s on target"%(path, keyname, str(value)))
        else:
            if type(value) != int:
                if verbose:
                    perror("%s: %s[%d]=%s value must be integer"%(path, keyname, instance, str(value)))
                r |= RET_ERR
            elif op == '<=' and value > target:
                if verbose:
                    perror("%s: %s[%d]=%s target: <= %s"%(path, keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif op == '>=' and value < target:
                if verbose:
                    perror("%s: %s[%d]=%s target: >= %s"%(path, keyname, instance, str(value), str(target)))
                r |= RET_ERR
            elif verbose:
                pinfo("%s: %s[%d]=%s on target"%(path, 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: %s unset"%(path, 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: %s truncated to %d definitions"%(path, 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=%s set"%(path, 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)
  0707010001f5cf000081ed0000000000000000000000016a100daf0000220a000000e600010003ffffffffffffffff0000003c00000000root/var/lib/opensvc/compliance/com.opensvc/crontabentry.py   #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_CRONTABENTRY_",
  "description": """* Verify crontab content. Fix if appropriate.
* The collector provides the format with wildcards.
* The module replace the wildcards with contextual values.
""",
  "example_value": """
{
  "user": "opensvc",
  "check": "/path/to/mycron",
  "entry": "3,13,23,33,43,53 * * * *  /path/to/mycron >/dev/null 2>&1"
}
""",
  "form_definition": """
Desc: |
  A cron rule, defining a Unix crontab entry, fed to the 'cron' compliance object.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: cron
    DisplayClass: raw
    Template: "%%ACTION%%:%%USER%%:%%SCHEDULE%%:%%COMMAND%%:%%FILE%%"

Inputs:
  -
    Id: ACTION
    Label: Action
    LabelCss: action16
    Mandatory: Yes
    Candidates:
       - add
       - del
    Help: Define if the crontab entry must be installed or not installed.
    Type: string

  -
    Id: USER
    Label: User name
    LabelCss: guy16
    Mandatory: Yes
    Help: Which Unix user should this entry be installed or uninstalled for.
    Type: string

  -
    Id: SCHEDULE
    Label: Schedule
    LabelCss: time16
    Mandatory: Yes
    Help: "The Unix cron format schedule : minute hour day_of_month month day_of_week."
    Type: string

  -
    Id: COMMAND
    Label: Command
    LabelCss: action16
    Mandatory: Yes
    Help: The command to schedule.
    Type: string

  -
    Id: FILE
    Label: Cron file name
    LabelCss: action16
    Help: Some Unix systems support split-file crontabs. For those, you can specify here the filename you want to entry to be added to. For systems without split-file crontabs, the crontab file is based on the user name specified above.
    Type: string
"""
}

import os
import sys
import stat
import re
import urllib
import tempfile
import pwd
import grp
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class CrontabEntry(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.crontabs = {}
        self.checks = []
        self.upds = {}

        self.sysname, self.nodename, x, x, self.machine = os.uname()

        rules = self.get_rules()
        for rule in rules:
            try:
                self.add_crontab(rule)
            except ValueError:
                perror('syntax error in rule ', rule)

        if len(self.checks) == 0:
            raise NotApplicable()

    def fixable(self):
        return RET_OK

    def parse_entry(self, x):
        if isinstance(x, int):
            x = str(x)+'\n'
        x = x.replace('%%HOSTNAME%%', self.nodename)
        x = x.replace('%%SHORT_HOSTNAME%%', self.nodename.split('.')[0])
        if not x.endswith('\n'):
            x += '\n'
        return x

    def parse_ref(self, url):
        f = tempfile.NamedTemporaryFile()
        tmpf = f.name
        try:
            fname, headers = urllib.urlretrieve(url, tmpf)
            if 'invalid file' in headers.values():
                perror(url, "not found on collector")
                return RET_ERR
            content = unicode(f.read())
            f.close()
        except:
             perror(url, "not found on collector")
             return ''
        if '<title>404 Not Found</title>' in content:
            perror(url, "not found on collector")
            return ''
        return self.parse_entry(content)

    def read_crontab(self, user):
        if user == '':
            user = 'root'
        if self.sysname == "Linux":
            cmd = ['/usr/bin/crontab', '-u', user, '-l']
        else:
            cmd = ['/usr/bin/crontab', '-l', user]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out,err = p.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if "no crontab" in err:
            return RET_OK, '# Created by OpenSVC compliance\n'
        if p.returncode != 0 :
            err = bdecode(err)
            perror("cannot get %s's crontab: %s" %(user,err.strip('\n')))
            return RET_ERR, ''
        return RET_OK, bdecode(out)

    def add_crontab(self, d):
        r = RET_OK
        if 'user' not in d:
            perror('user should be defined:', d)
            r |= RET_ERR
        if 'entry' in d and 'ref' in d:
            perror('entry and ref are exclusive:', d)
            r |= RET_ERR
        if len(d['user']) < 1:
            d['user'] = 'root'
        if not d['user'] in self.upds:
            self.upds[d['user']] = 0
        if not d['user'] in self.crontabs:
            rx, text = self.read_crontab(d['user'])
            if rx == RET_OK:
                self.crontabs[d['user']] = text
            else:
                self.crontabs[d['user']] = None
        c = ''
        if 'entry' in d:
            c = self.parse_entry(d['entry'])
        elif 'ref' in d:
            c = self.parse_ref(d['ref'])
        else:
            perror('entry or ref should be defined:', d)
            r |= RET_ERR
        if re.search(d['check'], c):
            val = True
        else:
            val = False
            r |= RET_ERR
        self.checks.append({'user':d['user'], 'check':d['check'], 'add':c, 'valid':val})
        return r
        
    def check_crontab(self):
        r = RET_OK
        for ck in self.checks:
             if not ck['valid']:
                 perror("pattern '%s' is absent from %s's crontab" %(ck['check'], ck['user']))
                 r |= RET_ERR
                 continue
             pr = RET_ERR
             if self.crontabs[ck['user']] is None:
                 r |= RET_ERR
                 continue
             lines = self.crontabs[ck['user']].split('\n')
             for line in lines:
                 if line.startswith('#'):
                     continue
                 if re.search(ck['check'], line):
                     pr = RET_OK
                     break
             if pr == RET_OK:
                 pinfo("pattern '%s' matches %s's crontab, entry: '%s'" %(ck['check'], ck['user'], line))
             else:
                 perror("pattern '%s' does not match any %s's crontab entry" %(ck['check'], ck['user']))
             r |= pr
        return r

    def check(self):
        r = 0
        r |= self.check_crontab()
        return r

    def add_2_crontabs(self):
        r = RET_OK
        f = tempfile.NamedTemporaryFile()
        filen = f.name
        f.close()
        for user in self.crontabs:
            if self.crontabs[user] is None:
                continue
            if self.upds[user] == 0:
                continue
            f = open(filen, 'w+')
            try:
                f.writelines(self.crontabs[user])
                f.close()
            except:
                perror("fail to write temporary file %s" %filen)
                r |= RET_ERR
            if user != 'root':
                os.chown(filen, pwd.getpwnam(user).pw_uid, -1)
                cmd = ['su', user, '-c', '/usr/bin/crontab '+filen]
            else:
                cmd = ['/usr/bin/crontab', filen]
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, err = p.communicate()
            if p.returncode != 0:
                err = bdecode(err)
                perror("could not append-to or create %s's %s" %(user,err.strip('\n')))
                r |= RET_ERR
            else:
                pinfo("new %s's crontab installed" %user)
            try:
                os.unlink(filen)
            except:
                perror('could not remove temp file: %s' %filen)
                pass
        return r

    def fix_crontabs(self):
        r = RET_OK
        for ck in self.checks:
             if not ck['valid']:
                 perror("pattern '%s' is absent from %s's crontab" %(ck['check'], ck['user']))
                 r |= RET_ERR
                 continue
             if self.crontabs[ck['user']] is None:
                 r |= RET_ERR
                 continue
             pr = RET_ERR
             lines = self.crontabs[ck['user']].split('\n')
             for line in lines:
                 if line.startswith('#'):
                     continue
                 if re.search(ck['check'], line):
                     pr = RET_OK
                     break
             if pr != RET_OK:
                 pinfo("trying to add to %s's crontab, entry: %s" %(ck['user'], ck['add'].strip('\n')))
                 self.crontabs[ck['user']] += ck['add']
                 self.upds[ck['user']] = 1
             r |= pr
        r |= self.add_2_crontabs()
        return r

    def fix(self):
        r = 0
        r != self.fix_crontabs()
        return r


if __name__ == "__main__":
    main(CrontabEntry)
  0707010001f5f3000081ed0000000000000000000000016a100daf000015aa000000e600010003ffffffffffffffff0000003500000000root/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)

  0707010001f5e2000081ed0000000000000000000000016a100daf000036b7000000e600010003ffffffffffffffff0000003700000000root/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
import six

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 beyond')
            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.data) == 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 add_fmri(self, v):
        if isinstance(v, six.text_type):
            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
        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('FMRI:%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, '=', '%s:(' % self.smfs[s][p]['typ']] + self.smfs[s][p]['val'].split() + [')'])
                                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, '=', '%s:%s' % (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('FMRI:%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, '=', '('] + self.smfs[s][p]['val'].split() + [')'])
                                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)

 0707010001f5d3000081ed0000000000000000000000016a100daf0000259f000000e600010003ffffffffffffffff0000003800000000root/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)

 0707010001f5d0000081ed0000000000000000000000016a100daf00001a42000000e600010003ffffffffffffffff0000003900000000root/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)
  0707010001f5e5000081ed0000000000000000000000016a100daf00000ee9000000e600010003ffffffffffffffff0000003700000000root/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)

   0707010001f5d4000081ed0000000000000000000000016a100daf00001a2a000000e600010003ffffffffffffffff0000003800000000root/var/lib/opensvc/compliance/com.opensvc/firmware.py   #!/usr/bin/env python

import os
import sys
from subprocess import *
from foreign.looseversion import LooseVersion as V

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)

  0707010001f5e0000081ed0000000000000000000000016a100daf0000093f000000e600010003ffffffffffffffff0000003c00000000root/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)
 0707010001f5f4000081ed0000000000000000000000016a100daf0000113d000000e600010003ffffffffffffffff0000003500000000root/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)

   0707010001f5eb000081ed0000000000000000000000016a100daf00004092000000e600010003ffffffffffffffff0000003400000000root/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, 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)

  0707010001f5ce000081ed0000000000000000000000016a100daf00001af2000000e600010003ffffffffffffffff0000003400000000root/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 as exc:
                perror(str(exc))
                r = RET_ERR
            except Unfixable as exc:
                perror(str(exc))
                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 as exc:
                perror(str(exc))
                r = RET_ERR
            except Unfixable as exc:
                perror(str(exc))
                return r
        return r

    def check(self):
        r = RET_OK
        for e in self.ce:
            try:
                self._check_cron(e)
            except ComplianceError as exc:
                perror(str(exc))
                r = RET_ERR
            except Unfixable as exc:
                perror(str(exc))
                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)

  0707010001f5de000081ed0000000000000000000000016a100daf00003404000000e600010003ffffffffffffffff0000003700000000root/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)
0707010001f5ed000081ed0000000000000000000000016a100daf00000bac000000e600010003ffffffffffffffff0000003b00000000root/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)
0707010001f5e1000081ed0000000000000000000000016a100daf00001502000000e600010003ffffffffffffffff0000004000000000root/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: self_signed_cert

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)

  0707010001f5cb000081ed0000000000000000000000016a100daf00000875000000e600010003ffffffffffffffff0000003400000000root/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)
   0707010001f5db000081ed0000000000000000000000016a100daf00003d3d000000e600010003ffffffffffffffff0000003b00000000root/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)
   0707010001f5df000081ed0000000000000000000000016a100daf000011a1000000e600010003ffffffffffffffff0000003200000000root/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)

   0707010001f5d5000081ed0000000000000000000000016a100daf00005801000000e600010003ffffffffffffffff0000003200000000root/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)

   0707010001f5da000081ed0000000000000000000000016a100daf00000622000000e600010003ffffffffffffffff0000004100000000root/var/lib/opensvc/compliance/com.opensvc/keyval_with_fpath.py  #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_GROUP_",
  "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.

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: dict
    Class: keyval_with_fpath

Inputs:
  -
    Id: path
    Label: Path
    DisplayModeLabel: path
    LabelCss: file16
    Type: string
  -
    Id: separator
    Label: Separator
    DisplayModeLabel: separator
    LabelCss: key
    Default: " "
    Type: string
  -
    Id: keys
    Label: Keys
    DisplayModeLabel: keys
    LabelCss: key
    Type: form
    Form: keyval
""",
}


import os
import sys
import json

sys.path.append(os.path.dirname(__file__))

from comp import *
from keyval_parser import Parser, ParserError
import keyval

class KeyVal(keyval.KeyVal):
    def __init__(self, prefix=None, path=None, separator=" "):
        CompObject.__init__(self, prefix=prefix, data=data)
        self.cf = path
        self.separator = separator

if __name__ == "__main__":
    main(KeyVal)
  0707010001f5cd000081a40000000000000000000000016a100daf00004243000000e600010003ffffffffffffffff0000003400000000root/var/lib/opensvc/compliance/com.opensvc/comp.py   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 string test.
    """
    if sys.version_info[0] == 2:
        l = (str, unicode)
    else:
        l = (str)
    if isinstance(s, l):
        return True
    return False

def bdecode(buff):
    """
    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
    return buff.decode("utf-8", errors="ignore")

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

def encode_list(cmd):
    return [str(w) for w in cmd]

def list2cmdline(cmd):
    import subprocess
    return subprocess.list2cmdline(encode_list(cmd))

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), in_json=True)
            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, in_json=False):
        """
          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, in_json=in_json))
            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)
                    if in_json:
                        _v = json.dumps(_v)[1:-1]
                elif 'OSVC_COMP_'+s in os.environ:
                    _v = self.get_env('OSVC_COMP_'+s)
                    if in_json:
                        _v = json.dumps(_v)[1:-1]
                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

        p = re.compile('%%SAFE:\w+%%', re.IGNORECASE)

        def _subst_safe(v):
            matches = p.findall(v)
            if len(matches) == 0:
                raise EndRecursion
            for m in matches:
                s = m.strip("%").upper().replace('SAFE:', '')
                _v = self.collector_rest_get("/safe/%s/download" % s, load_json=False)
                v = v.replace(m, _v)
            return v

        for i in range(max_recursion):
            try:
                v = _subst_safe(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"
        if not data["url"].startswith("http"):
            data["url"] = "https://%s" % data["url"]
        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)
        s = "%s:%s" % (api["username"], api["password"])
        base64string = base64.b64encode(s.encode("ascii"))
        request.add_header("Authorization", "Basic %s" % base64string.decode("ascii"))
        return request

    def collector_rest_get(self, path, load_json=True):
        api = self.collector_api()
        request = self.collector_request(path)
        if not api["url"].startswith("https"):
            raise ComplianceError("refuse to submit auth tokens through a non-encrypted transport")
        from utilities import ssl_context_kwargs
        kwargs = ssl_context_kwargs()
        try:
            f = urlopen(request, **kwargs)
        except HTTPError as e:
            try:
                err = json.loads(e.read())["error"]
                e = ComplianceError(err)
            except:
                pass
            raise e
        if load_json:
            data = json.loads(f.read())
        else:
            data = 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"):
            raise ComplianceError("refuse to submit auth tokens through a non-encrypted transport")
        from utilities import ssl_context_kwargs
        kwargs = ssl_context_kwargs()
        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://", "")
        try:
            int(uuid)
            isint = True
        except ValueError:
            isint = False
        if not uuid.startswith("safe") and not isint:
            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)
        from utilities import ssl_context_kwargs
        kwargs = ssl_context_kwargs()
        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()
        return backup_f

    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")

 0707010001f5dc000081ed0000000000000000000000016a100daf0000175a000000e600010003ffffffffffffffff0000003800000000root/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
        if out == "None":
            out = None
        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)

  0707010001f5d1000081ed0000000000000000000000016a100daf000032f4000000e600010003ffffffffffffffff0000003400000000root/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
import codecs
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'], in_json=True)
        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()
        if sys.version_info[0] >= 3:
            with codecs.open(tmpfname, 'w', encoding="utf-8") as tmpf:
                tmpf.write(f['fmt'])
        else:
            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("file %s differs:" % f["path"])
            # discard header not to create fake differences due to tmp filename
            lines = [line for line in out.strip("\n").splitlines() if not line.startswith("---") and not line.startswith("+++")]
            perror("\n".join(lines))
        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:
            if sys.version_info[0] >= 3:
                with codecs.open(f['path'], 'w', encoding="utf-8") as fi:
                    fi.write(f['fmt'])
            else:
                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)

0707010001f5e3000081ed0000000000000000000000016a100daf00000948000000e600010003ffffffffffffffff0000003700000000root/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)

0707010001f5e9000081ed0000000000000000000000016a100daf000016cc000000e600010003ffffffffffffffff0000003300000000root/var/lib/opensvc/compliance/com.opensvc/tar.py    #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_TAR_",
  "example_value": """ 
{
  "ref": "/some/path/to/file.tar",
  "path": "/home/user/bin",
  "immutable": "true"
}
  """,
  "description": """* Fetch a tar archive from a href
* Verify tar archive is extracted on check action
* Extract tar archive on fix action
* Immutable boolean is used to know if extracted tar content can be modified on filesystem
""",
  "form_definition": """
Desc: |
  Point to a tar archive.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Class: file
    Type: json
    Format: dict

Inputs:
  -
    Id: ref
    Label: Tar uri
    DisplayModeLabel: ref
    LabelCss: fa-map-marker
    Help: "Examples:
        /path/to/reference_file.tar
        safe://safe.uuid.8dc85529a2b13b4b.626172.tar
        http://server/path/to/reference_file.tar
        https://server/path/to/reference_file.tar
        ftp://server/path/to/reference_file.tar
        ftp://login:pass@server/path/to/reference_file.tar"
    Type: string
  -
    Id: path
    Label: Install path
    DisplayModeLabel: path
    LabelCss: fa-map-marker
    Mandatory: Yes
    Help: path to install the tar reference content to.
    Type: string
  -
    Id: immutable
    Label: Immutable
    DisplayModeLabel: immutable
    LabelCss: fa-lock
    Mandatory: Yes
    Help: "On : extracted tar archive must not be modified on filesystem
           Off: extracted tar archive contents on filesystem can be modified"
    Type: boolean
"""
}

import os
import sys
import tempfile
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class InitError(Exception):
    pass

class Tar(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        self.rules = []

        for rule in self.get_rules():
            try:
                self.rules += self.add_rule(rule)
            except InitError:
                continue
            except ValueError:
                perror('tar_archive: failed to parse variable', os.environ[k])

        if len(self.rules) == 0:
            raise NotApplicable()

    def add_rule(self, d):
        if 'path' not in d or 'ref' not in d:
            perror('tar_archive: path and ref must be in the dict:', 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"])
        else:
            return self.download_url(d)

    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 check_output(self, data, verbose=False):
        lines = [line for line in data.splitlines() if \
                 "Mod time differs" not in line and "Size differs" not in line]
        nberr = len(lines)
        if nberr:
            if verbose:
                for line in lines[:10]:
                     perror(line)
                if nberr > 10:
                     perror("... %d total errors" % nberr)
            return RET_ERR
        return RET_OK

    def fixable(self):
        return RET_NA

    def fix_tarball(self, rule, verbose=False):
        tmpfname = self.download(rule)
        path = rule["path"]
        immutable = rule["immutable"]
        try:
            return self._fix_tarball(rule, tmpfname, path, immutable, verbose=verbose)
        finally:
            os.unlink(tmpfname)

    def _fix_tarball(self, rule, tmpfname, path, immutable, verbose=False):
        if not os.path.isdir(path):
            try:
                os.makedirs(path)
            except Exception as e:
                raise ex.excError("failed to create directory %s: %s"%(path, str(e)))
        opts = '--keep-newer-files'
        if immutable is True:
            opts = '--overwrite'
        cmd = ["tar", "-C", path, "--extract", "--file", tmpfname, opts]
        proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = proc.communicate()
        pinfo(out)
        perror(err)
        if proc.returncode == 0:
            return RET_OK
        return RET_ERR

    def check_tarball(self, rule, verbose=False):
        tmpfname = self.download(rule)
        path = rule["path"]
        immutable = rule["immutable"]
        try:
            return self._check_tarball(rule, tmpfname, path, immutable, verbose=verbose)
        finally:
            os.unlink(tmpfname)

    def _check_tarball(self, rule, tmpfname, path, immutable, verbose=False):
        cmd = ["tar", "-C", path, "--compare", "--file", tmpfname]
        proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = proc.communicate()
        out = bdecode(out)
        err = bdecode(err)
        if proc.returncode == 0:
            return RET_OK
        elif immutable is False:
            return self.check_output(out+err, verbose=verbose)
        else:
            pinfo(out)
            perror(err)
        return RET_ERR

    def check(self):
        r = 0
        for rule in self.rules:
            r |= self.check_tarball(rule, verbose=True)
        return r

    def fix(self):
        r = 0
        for rule in self.rules:
            r |= self.fix_tarball(rule)
        return r

if __name__ == "__main__":
    main(Tar)
0707010001f5e6000081ed0000000000000000000000016a100daf0000213f000000e600010003ffffffffffffffff0000003600000000root/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
import codecs
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):
        try:
            with codecs.open(self.cf, "r", "utf8") as f:
                buff = f.read()
        except:
            with codecs.open(self.cf, "r", "latin1") 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)
 0707010001f5d9000081ed0000000000000000000000016a100daf00001787000000e600010003ffffffffffffffff0000003d00000000root/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, separator=" ", obj=None):
        self.obj = obj
        self.path = path
        self.data = {}
        self.changed = False
        self.nocf = False
        self.separator = separator
        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 + self.separator + 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 + self.separator + 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):
            self.nocf = True
            return
        with open(self.path, 'r') as f:
            buff = f.read()
        self.parse(buff)

    def backup(self, path):
        if self.nocf or not self.obj:
            return
        return self.obj.backup(path)

    def restore(self):
        if self.nocf or not self.obj:
            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.bkp = self.backup(self.path)
        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()

    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

            try:
                key, value = line.split(self.separator, 1)
            except Exception:
                self.comments[self.lastkey].append(line)
                continue
            value = value.strip()
            key = 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 + self.separator + 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")
    pinfo(o)

 0707010001f5d7000081ed0000000000000000000000016a100daf00001ed4000000e600010003ffffffffffffffff0000004000000000root/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)
0707010001f5cc000081ed0000000000000000000000016a100daf00000e53000000e600010003ffffffffffffffff0000003900000000root/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))
 0707010001f5ef000081ed0000000000000000000000016a100daf00005a53000000e600010003ffffffffffffffff0000003400000000root/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 foreign.looseversion 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)

 0707010001f5ca000081ed0000000000000000000000016a100daf00004359000000e600010003ffffffffffffffff0000003700000000root/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)

   0707010001f5e7000081ed0000000000000000000000016a100daf000017d5000000e600010003ffffffffffffffff0000004200000000root/var/lib/opensvc/compliance/com.opensvc/systemd_unit_state.py #!/usr/bin/env python

data = {
  "default_prefix": "OSVC_COMP_SYSTEMD_UNIT",
  "example_value": """ 
{
  "name": "lvm2-lvmetad.service",
  "disable": true,
  "mask": false
}
  """,
  "description": """* Controls a systemd unit masked and disabled states.
""",
  "form_definition": """
Desc: |
  A rule to set a systemd unit masked and disabled states.
Css: comp48

Outputs:
  -
    Dest: compliance variable
    Type: json
    Format: list of dict
    Class: systemd_unit_state

Inputs:
  -
    Id: name
    Label: Name
    DisplayModeLabel: name
    LabelCss: action16
    Mandatory: Yes
    Type: string
    Help: The systemd unit name, including the suffix (.service or .socket)

  -
    Id: disable
    Label: Disable
    DisplayModeLabel: disable
    LabelCss: action16
    Mandatory: Yes
    Default: No
    Type: boolean
    Help: Should the unit be disabled.

  -
    Id: mask
    Label: Mask
    DisplayModeLabel: mask
    LabelCss: action16
    Mandatory: Yes
    Default: No
    Type: boolean
    Help: Should the unit be masked.

""",
}

import os
import sys
import json
import pwd
from subprocess import *

sys.path.append(os.path.dirname(__file__))

from comp import *

class SystemdUnitState(CompObject):
    def __init__(self, prefix=None):
        CompObject.__init__(self, prefix=prefix, data=data)

    def init(self):
        if os.uname()[0] != "Linux":
            raise NotApplicable()

        self.rules = []

        self.rules = self.get_rules()

        if len(self.rules) == 0:
            raise NotApplicable()

        self.unit_data = {}

    def mask(self, unit):
        return self.systemctl("mask", unit)

    def unmask(self, unit):
        return self.systemctl("unmask", unit)

    def enable(self, unit):
        return self.systemctl("enable", unit)

    def disable(self, unit):
        return self.systemctl("disable", unit)

    def systemctl(self, action, unit):
        cmd = ["systemctl", action, unit, "--now"]
        pinfo("systemd_unit_state:", " ".join(cmd))
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        _, err = p.communicate()
        err = bdecode(err)
        if p.returncode != 0:
            perror("systemd unit: command failed (%d): %s" % (p.returncode, err))
            return RET_ERR
        return RET_OK

    def show(self, unit):
        if unit in self.unit_data:
            return self.unit_data[unit]
        cmd = ["systemctl", "show", unit]
        try:
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            out, _ = p.communicate()
        except OSError as exc:
            if exc.errno == 2:
                raise NotApplicable("systemd_unit_state: systemctl is not installed")

        out = bdecode(out)
        data = {}
        for line in out.splitlines():
            k, v = line.split("=", 1)
            data[k] = v
        self.unit_data[unit] = data
        return data

    def fixable(self):
        return RET_OK

    def check_rule(self, rule, verbose=False):
        r = RET_OK
        try:
            name = rule["name"]
        except IndexError:
            perror("invalid rule: no unit name")
            return RET_NA
        data = self.show(name)
        if data.get("LoadState") == "not-found":
            if verbose:
                pinfo("systemd_unit_state: %s not found" % name)
            return RET_OK

        if "mask" in rule:
            if rule["mask"]:
                if data["LoadState"] != "masked":
                    if verbose:
                        perror("systemd_unit_state: %s should be masked" % name)
                    r |= RET_ERR
                else:
                    if verbose:
                        pinfo("systemd_unit_state: %s is masked" % name)
            if not rule["mask"]:
                if data["LoadState"] == "masked":
                    if verbose:
                        perror("systemd_unit_state: %s should not be masked" % name)
                    r |= RET_ERR
                else:
                    if verbose:
                        pinfo("systemd_unit_state: %s is unmasked" % name)
        if "disable" in rule and not data["LoadState"] == "masked":
            if rule["disable"]:
                if data["UnitFileState"] != "disabled":
                    if verbose:
                        perror("systemd_unit_state: %s should be disabled" % name)
                    r |= RET_ERR
                else:
                    if verbose:
                        pinfo("systemd_unit_state: %s is disabled" % name)
            if not rule["disable"]:
                if data["UnitFileState"] == "disabled":
                    if verbose:
                        perror("systemd_unit_state: %s should not be disabled" % name)
                    r |= RET_ERR
                else:
                    if verbose:
                        pinfo("systemd_unit_state: %s is enabled" % name)
        return r

    def check(self):
        r = RET_OK
        for rule in self.rules:
            r |= self.check_rule(rule, verbose=True)
        return r

    def fix_rule(self, rule):
        r = RET_OK
        try:
            name = rule["name"]
        except IndexError:
            pinfo("systemd unit: invalid rule: no unit name")
            return RET_NA
        data = self.show(name)
        if "mask" in rule:
            if rule["mask"] and data["LoadState"] != "masked":
                r |= self.mask(name)
            if not rule["mask"] and data["LoadState"] == "masked":
                r |= self.unmask(name)
        if "disable" in rule and not rule.get("mask"):
            # can not disable a masked unit
            if rule["disable"] and data["UnitFileState"] != "disabled":
                r |= self.disable(name)
            if not rule["disable"] and data["UnitFileState"] == "disabled":
                r |= self.enable(name)
        return r

    def fix(self):
        r = 0
        for rule in self.rules:
            if self.check_rule(rule, verbose=False) == RET_ERR:
                r |= self.fix_rule(rule)
        return r

if __name__ == "__main__":
    main(SystemdUnitState)
   0707010001f016000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000000900000000root/etc  0707010001f017000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000001b00000000root/etc/bash_completion.d    0707010001f018000081a40000000000000000000000016a100daf00009915000000e600010003ffffffffffffffff0000002600000000root/etc/bash_completion.d/opensvc.sh 

svcmgr="abort boot clear collector compliance create delete deploy disable dns docker edit enable enter eval freeze frozen get giveback install logs ls migrate monitor move oci pg podman postsync presync print provision prstatus pull purge push resource restart resync run scale set shutdown snooze start startstandby status stop support switch sync takeover thaw toc unprovision unset unsnooze 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_install="data"
svcmgr_pg="freeze kill pids remove stats thaw update"
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 status"
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_compliance_fix="--attach --color --debug --env --local --module --moduleset --namespace --node --ruleset-date --status --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_list_moduleset="--color --debug --env --local --moduleset --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_detach="--color --debug --env --local --moduleset --namespace --node --ruleset --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_attach="--color --debug --env --local --moduleset --namespace --node --ruleset --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_show_ruleset="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_env="--color --debug --env --local --module --moduleset --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_show_status="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_list_ruleset="--color --debug --env --local --namespace --node --ruleset --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_fixable="--attach --color --debug --env --local --module --moduleset --namespace --node --ruleset-date --status --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_show_moduleset="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_compliance_check="--attach --color --debug --env --local --module --moduleset --namespace --node --ruleset-date --status --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_compliance_auto="--attach --color --cron --debug --env --local --module --moduleset --namespace --node --ruleset-date --status --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_purge="--color --debug --dry-run --env --interval --leader --local --master --namespace --node --nolock --purge-collector --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_status="--color --cron --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -r --refresh -s --service"
svcmgr_print_status="--color --debug --env --filter --format --hide-disabled --local --namespace --node --show-disabled --status --waitlock -h --help -p --parallel -r --refresh -s --service"
svcmgr_print_config_mtime="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_logs="--backlog --color --debug --env --follow --local --namespace --no-pager --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_deploy="--color --config --debug --disable-rollback --env --kw --leader --local --master --namespace --node --nolock --restore --rid --slave --slaves --status --subsets --tags --template --waitlock -f --force -h --help -i --interactive -p --parallel -s --service"
svcmgr_monitor="--color --debug --env --format --interval --local --namespace --node --sections --stats --status --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_ls="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_takeover="--color --debug --disable-rollback --env --interval --local --namespace --node --stats --status --time --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_sync_update="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_print_base_devs="--color --debug --env --filter --format --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_migrate="--color --debug --disable-rollback --env --interval --local --namespace --node --stats --status --time --to --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_print_resource_status="--color --debug --env --filter --format --local --namespace --node --rid --status --waitlock -h --help -p --parallel -r --refresh -s --service"
svcmgr_push_encap_config="--color --cron --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_move="--color --debug --disable-rollback --env --interval --local --namespace --node --stats --status --time --to --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_sync_resync="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_freeze="--color --debug --env --interval --local --master --namespace --node --slave --slaves --stats --status --time --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_abort="--color --debug --env --interval --local --namespace --node --stats --status --time --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_resync="--color --debug --disable-rollback --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_shutdown="--color --debug --dry-run --env --interval --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_sync_split="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_run="--color --confirm --cron --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_push_resinfo="--color --cron --debug --env --local --namespace --node --status --sync --waitlock -h --help -p --parallel -s --service"
svcmgr_print_sub_devs="--color --debug --env --filter --format --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_scale="--color --debug --env --local --namespace --node --status --to --waitlock -h --help -p --parallel -s --service"
svcmgr_oci="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_unsnooze="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_presync="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_drp="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_nodes="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pg_freeze="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_support="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_boot="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pg_pids="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_start="--color --debug --disable-rollback --dry-run --env --interval --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --upto --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_sync_break="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_resume="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_all="--color --cron --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_toc="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_push_status="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_set_provisioned="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_prstatus="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_dns_update="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_full="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_enable="--color --debug --env --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_thaw="--color --debug --env --interval --local --master --namespace --node --slave --slaves --stats --status --time --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_resource_monitor="--color --cron --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_giveback="--color --debug --disable-rollback --env --interval --local --namespace --node --stats --status --time --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_pg_thaw="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pg_update="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pg_remove="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_stop="--color --debug --downto --dry-run --env --interval --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_print_exposed_devs="--color --debug --env --filter --format --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_sync_restore="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_startstandby="--color --debug --disable-rollback --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_podman="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_disable="--color --debug --env --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_unprovision="--color --debug --dry-run --env --interval --leader --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_print_devs="--color --debug --env --filter --format --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_snooze="--color --debug --duration --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_print_resinfo="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_provision="--color --debug --disable-rollback --dry-run --env --interval --leader --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_restart="--color --debug --disable-rollback --dry-run --env --interval --local --master --namespace --node --nolock --rid --slave --slaves --stats --status --subsets --tags --time --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_sync_revert="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pull="--color --debug --disable-rollback --env --local --namespace --node --provision --status --waitlock -h --help -p --parallel -s --service"
svcmgr_set_unprovisioned="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_install_data="--color --debug --env --local --namespace --node --rid --status --subsets --tags --waitlock -h --help -p --parallel -s --service"
svcmgr_clear="--color --debug --env --local --namespace --node --slave --slaves --status --waitlock -h --help -p --parallel -s --service"
svcmgr_sync_establish="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_quiesce="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_sync_verify="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_frozen="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_switch="--color --debug --disable-rollback --env --interval --local --namespace --node --stats --status --time --to --wait --waitlock -h --help -p --parallel -s --service -w --watch"
svcmgr_postsync="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_pg_kill="--color --debug --dry-run --env --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_enter="--color --debug --env --local --namespace --node --rid --status --waitlock -h --help -p --parallel -s --service"
svcmgr_print_schedule="--color --debug --env --filter --format --local --namespace --node --status --verbose --waitlock -h --help -p --parallel -s --service"
svcmgr_docker="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_push_config="--color --cron --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_pg_stats="--color --debug --dry-run --env --format --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_collector_disks="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_log="--color --debug --env --local --message --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_networks="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_list_actions="--begin --color --debug --end --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_checks="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_tag="--color --debug --env --local --namespace --node --status --tag --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_ack_unavailability="--account --author --begin --color --comment --debug --duration --end --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_list_tags="--color --debug --env --filter --format --like --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_untag="--color --debug --env --local --namespace --node --status --tag --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_show_actions="--begin --color --debug --end --env --filter --format --id --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_show_tags="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_list_unavailability_ack="--author --begin --color --comment --debug --end --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_create_tag="--color --debug --env --local --namespace --node --status --tag --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_ack_action="--author --color --comment --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_alerts="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_events="--begin --color --debug --end --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_collector_asset="--color --debug --env --filter --format --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_set="--add --color --debug --env --eval --index --kw --local --master --namespace --node --nolock --param --remove --rid --slave --slaves --status --subsets --tags --value --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_unset="--color --debug --env --kw --local --master --namespace --node --nolock --param --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_eval="--color --debug --env --format --impersonate --kw --local --master --namespace --node --nolock --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_get="--color --debug --env --eval --format --impersonate --kw --local --master --namespace --node --nolock --param --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
svcmgr_edit_config="--color --debug --discard --env --local --namespace --node --recover --status --waitlock -h --help -p --parallel -s --service"
svcmgr_delete="--color --debug --env --interval --local --master --namespace --node --nolock --purge-collector --rid --slave --slaves --stats --status --subsets --tags --time --unprovision --wait --waitlock -f --force -h --help -p --parallel -s --service -w --watch"
svcmgr_validate_config="--color --debug --env --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_create="--color --config --debug --disable-rollback --env --kw --leader --local --master --namespace --node --nolock --provision --resource --restore --rid --slave --slaves --status --subsets --tags --template --waitlock -f --force -h --help -i --interactive -p --parallel -s --service"
svcmgr_print_config="--color --debug --env --eval --filter --format --impersonate --local --namespace --node --status --waitlock -h --help -p --parallel -s --service"
svcmgr_update="--color --debug --disable-rollback --env --local --master --namespace --node --nolock --provision --resource --rid --slave --slaves --status --subsets --tags --waitlock -f --force -h --help -p --parallel -s --service"
nodemgr="array auto checks collect collector compliance delete dequeue drain edit eval events freeze frozen get logs ls ping print prkey pushasset pushbrocade pushcentera pushdisks pushdorado pushemcvnx pusheva pushfreenas pushgcedisks pushhcs pushhds pushhp3par pushibmds pushibmsvc pushnecism pushnetapp pushnsr pushpatch pushpkg pushstats pushsym pushvioserver pushxtremio reboot register rotate scan scanscsi schedule set shutdown snooze stonith sysreport thaw unschedule unset unsnooze update updateclumgr updatecomp updatepkg validate wait wol"
nodemgr_array="ls show"
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_dequeue="actions"
nodemgr_edit="config"
nodemgr_print="capabilities config devs schedule"
nodemgr_rotate="root"
nodemgr_rotate_root="pw"
nodemgr_scan="capabilities"
nodemgr_schedule="reboot"
nodemgr_schedule_reboot="status"
nodemgr_unschedule="reboot"
nodemgr_update="ssh"
nodemgr_update_ssh="authorized"
nodemgr_update_ssh_authorized="keys"
nodemgr_validate="config"
nodemgr_pushnsr="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushcentera="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushibmds="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushhp3par="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushxtremio="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushpatch="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_pushhcs="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushpkg="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_pushvioserver="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushasset="--color --cron --debug --filter --format --local --node --server --sync -h --help"
nodemgr_pushibmsvc="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushnetapp="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushgcedisks="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pusheva="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushnecism="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushdorado="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushbrocade="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_sysreport="--color --cron --debug --filter --force --format --local --node --server -h --help"
nodemgr_pushemcvnx="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushfreenas="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushhds="--color --cron --debug --filter --format --local --node --object --server -h --help"
nodemgr_pushstats="--begin --color --cron --debug --end --filter --format --local --node --server --stats-dir -h --help"
nodemgr_pushsym="--color --cron --debug --filter --format --local --node --object --server --symcli-db-file -h --help"
nodemgr_checks="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_pushdisks="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_compliance_fix="--attach --color --debug --filter --force --format --local --module --moduleset --node --ruleset-date --server -h --help"
nodemgr_compliance_list_moduleset="--color --debug --filter --format --local --moduleset --node --server -h --help"
nodemgr_compliance_detach="--color --debug --filter --format --local --moduleset --node --ruleset --server -h --help"
nodemgr_compliance_attach="--color --debug --filter --format --local --moduleset --node --ruleset --server -h --help"
nodemgr_compliance_show_ruleset="--color --debug --filter --format --local --node --server -h --help"
nodemgr_compliance_env="--color --debug --filter --format --local --module --moduleset --node --server -h --help"
nodemgr_compliance_show_status="--color --debug --filter --format --local --node --server -h --help"
nodemgr_compliance_list_ruleset="--color --debug --filter --format --local --node --ruleset --server -h --help"
nodemgr_compliance_fixable="--attach --color --debug --filter --force --format --local --module --moduleset --node --ruleset-date --server -h --help"
nodemgr_compliance_list_module="--color --debug --filter --format --local --node --server -h --help"
nodemgr_compliance_show_moduleset="--color --debug --filter --format --local --node --server -h --help"
nodemgr_compliance_check="--attach --color --debug --filter --force --format --local --module --moduleset --node --ruleset-date --server -h --help"
nodemgr_compliance_auto="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_prkey="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_list_tags="--color --debug --filter --format --like --local --node --server -h --help"
nodemgr_collector_disks="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_log="--color --debug --filter --format --local --message --node --server -h --help"
nodemgr_collector_events="--begin --color --debug --end --filter --format --local --node --server -h --help"
nodemgr_collector_search="--color --debug --filter --format --like --local --node --server -h --help"
nodemgr_collector_list_filtersets="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_list_actions="--begin --color --debug --end --filter --format --local --node --server -h --help"
nodemgr_collector_cli="--api --color --config --debug --filter --format --insecure --local --node --password --refresh-api --save --server --user -h --help"
nodemgr_collector_tag="--color --debug --filter --format --local --node --server --tag -h --help"
nodemgr_collector_list_services="--color --debug --filter --filterset --format --local --node --server -h --help"
nodemgr_collector_networks="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_untag="--color --debug --filter --format --local --node --server --tag -h --help"
nodemgr_collector_show_actions="--begin --color --debug --end --filter --format --id --local --node --server -h --help"
nodemgr_collector_checks="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_show_tags="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_create_tag="--color --debug --filter --format --local --node --server --tag -h --help"
nodemgr_collector_list_nodes="--color --debug --filter --filterset --format --local --node --server -h --help"
nodemgr_collector_ack_action="--author --color --comment --debug --filter --format --local --node --server -h --help"
nodemgr_collector_alerts="--color --debug --filter --format --local --node --server -h --help"
nodemgr_collector_asset="--color --debug --filter --format --local --node --server -h --help"
nodemgr_set="--add --color --debug --eval --filter --format --index --kw --local --node --param --remove --server --value -h --help"
nodemgr_unset="--color --debug --filter --format --kw --local --node --param --server -h --help"
nodemgr_eval="--color --debug --filter --format --impersonate --kw --local --node --server -h --help"
nodemgr_get="--color --debug --eval --filter --format --impersonate --kw --local --node --param --server -h --help"
nodemgr_edit_config="--color --debug --discard --filter --format --local --node --recover --server -h --help"
nodemgr_register="--app --color --debug --filter --format --local --node --password --server --user -h --help"
nodemgr_validate_config="--color --debug --filter --format --local --node --server -h --help"
nodemgr_print_config="--color --debug --filter --format --local --node --server -h --help"
nodemgr_delete="--color --debug --filter --format --kw --local --node --server -h --help"
nodemgr_updatecomp="--color --debug --filter --format --local --node --server -h --help"
nodemgr_update_ssh_authorized_keys="--color --debug --filter --format --local --node --server -h --help"
nodemgr_thaw="--color --debug --filter --format --local --node --server --time --wait -h --help"
nodemgr_logs="--backlog --color --debug --filter --follow --format --local --node --server -h --help"
nodemgr_auto_reboot="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_unsnooze="--color --debug --filter --format --local --node --server -h --help"
nodemgr_freeze="--color --debug --filter --format --local --node --server --time --wait -h --help"
nodemgr_schedule_reboot_status="--color --debug --filter --format --local --node --server -h --help"
nodemgr_schedule_reboot="--color --debug --filter --format --local --node --server -h --help"
nodemgr_array_show="--color --debug --filter --format --local --node --server -h --help"
nodemgr_scan_capabilities="--color --debug --filter --format --local --node --server -h --help"
nodemgr_shutdown="--color --debug --filter --format --local --node --server -h --help"
nodemgr_print_schedule="--color --debug --filter --format --local --node --server --verbose -h --help"
nodemgr_snooze="--color --debug --duration --filter --format --local --node --server -h --help"
nodemgr_array="--color --debug --filter --format --local --node --server -h --help"
nodemgr_scanscsi="--color --debug --filter --format --hba --local --lun --node --server --target -h --help"
nodemgr_array_ls="--color --debug --filter --format --local --node --server -h --help"
nodemgr_drain="--color --debug --filter --format --local --node --server --time --wait -h --help"
nodemgr_updatepkg="--color --debug --filter --format --local --node --server -h --help"
nodemgr_unschedule_reboot="--color --debug --filter --format --local --node --server -h --help"
nodemgr_frozen="--color --debug --filter --format --local --node --server -h --help"
nodemgr_ping="--color --debug --filter --format --local --node --server -h --help"
nodemgr_reboot="--color --debug --filter --format --local --node --server -h --help"
nodemgr_stonith="--color --debug --filter --format --local --node --server -h --help"
nodemgr_wol="--broadcast --color --debug --filter --format --local --mac --node --port --server -h --help"
nodemgr_collect_stats="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_ls="--color --debug --filter --format --local --node --server -h --help"
nodemgr_rotate_root_pw="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_updateclumgr="--color --debug --filter --format --local --node --server -h --help"
nodemgr_print_capabilities="--color --debug --filter --format --local --node --server -h --help"
nodemgr_dequeue_actions="--color --cron --debug --filter --format --local --node --server -h --help"
nodemgr_wait="--color --debug --duration --filter --format --local --node --server --verbose -h --help"
nodemgr_events="--color --debug --filter --format --local --node --server -h --help"
nodemgr_print_devs="--color --debug --dev --filter --format --local --node --reverse --server --verbose -h --help"

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



om="setns getns unsetns node cluster svc vol net pool daemon array"

load_svcs()
{
    if [ "$OSVC_NAMESPACE" != "" -a "$OSVC_NAMESPACE" != "root" ]
    then
        svcs=$(cat /var/lib/opensvc/list.services /opt/opensvc/var/list.services 2>/dev/null | grep "^$OSVC_NAMESPACE/")
        echo "$svcs"
        echo "$svcs" | grep "^$OSVC_NAMESPACE/" | sed -e "s/^$OSVC_NAMESPACE\///"
        echo "$svcs" | grep "^$OSVC_NAMESPACE/" | sed -e "s/^$OSVC_NAMESPACE\/svc\///"
    else
        cat /var/lib/opensvc/list.services /opt/opensvc/var/list.services 2>/dev/null
    fi
}

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=()
    exe="${COMP_WORDS[0]}"
    COMP_WORDS[0]="${exe##*/}"
    a="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    if [ $COMP_CWORD -eq 1 -a "${COMP_WORDS[0]}" == "om" ]
    then
        svcs=$(load_svcs)
        COMPREPLY=( $(compgen -W "$om $svcs" -- ${a}) )
        return 0
    fi

    case "${prev}" in
        --color)
            COMPREPLY=( $(compgen -W "yes no" -- ${a}) )
            return 0
            ;;
        --format)
            COMPREPLY=( $(compgen -W "csv flat_json json table" -- ${a}) )
            return 0
            ;;
        --service|-s)
            svcs=$(load_svcs)
            COMPREPLY=( $(compgen -W "${svcs}" -- ${a}) )
            return 0
            ;;
        --node)
            nodes=$(cat /var/lib/opensvc/list.nodes /opt/opensvc/var/list.nodes 2>/dev/null)
            COMPREPLY=( $(compgen -W "${nodes}" -- ${a}) )
            return 0
            ;;
        *)
            ;;
    esac

    case "${COMP_WORDS[0]} ${COMP_WORDS[1]} "
    in
        "om node ")
            unset COMP_WORDS[0]
            COMP_WORDS[0]="nodemgr"
            COMP_CWORD=${COMP_CWORD-1}
            ;;
        "om net ")
            COMP_WORDS[0]="nodemgr"
            COMP_WORDS[1]="network"
            ;;
        "om pool ")
            COMP_WORDS[0]="nodemgr"
            ;;
        "om daemon "|"om array ")
            COMP_WORDS[0]="nodemgr"
            ;;
        "om svc "|"om vol ")
            unset COMP_WORDS[0]
            COMP_WORDS[0]="svcmgr"
            COMP_CWORD=${COMP_CWORD-1}
            ;;
        "om getns "|"om setns "|"om unsetns ")
            ;;
        "om  ")
            ;;
        om\ *)
            if [ $COMP_CWORD -ge 2 ]
            then
                COMP_WORDS[0]="svcmgr"
                unset COMP_WORDS[1]
            fi
            ;;
    esac

    action=()
    typeset -i skip=0

    for word in ${COMP_WORDS[@]}
    do
        # prevent "Bad substitution" on action deref
        word=${word//[^abcdefghijklmnopqrstuvwxyz0-9_.]/}
        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// /_}"
    prev_action=""
    opts=""

    while [ "$action" != "" -a "$opts" == "" -a "$prev_action" != "$action" ]
    do
        opts="${!action}"
        prev_action="$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
complete -F _comp_handler nodemgr
complete -F _comp_handler om

   0707010001f019000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000000900000000root/usr  0707010001f01a000041ed0000000000000000000000056a102a9200000000000000e600010003ffffffffffffffff0000000f00000000root/usr/share    0707010001f11d000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000001300000000root/usr/share/man    0707010001f11e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000001800000000root/usr/share/man/man1   0707010001f121000081a40000000000000000000000016a100daf00000542000000e600010003ffffffffffffffff0000002400000000root/usr/share/man/man1/svcmon.1.gz   ‹     V[7~Ÿ_qöiÚÍ0ìnÓ‡"@"ª¬”Û¨U6ÌŒ7{b{`‰¢þö~ö\`“<õÆçês?NÞÐêÝíý|F×tsusõØ»z‰ßoQºzC³Éý4²û¬ÐŠz4/¹/±-W.¶d¹Ù‹Œ[Ê:¦ü	_WY2üsÅ­jK™.
¦ò4è[ý5›/Vw«(}EµÚHäµ¿§ÇÍÝ|ñp7Ÿ­7úø_OW·Ë»€î¹ïŒjc˜u¦Ê\exhJçœ„%¦H—Ü°`€=ZÇ‹ÎF:·óÏ¼©ÉRò<¥‡VSÁŽ´ÓÖu¬ß;\s?n^ÕÞxËK£·†ø
ål°+[Dƒç”É
_ƒëó“²:fu€nç÷÷“ÙëUý.ÙÖ"¢9QUâ«	œùúXCåú pzjñ|At`ÆŸT0g/¬ÐŠç@]mŒþÂ=ùo"ÉYs‚·Ú´&Q)YÆ¥UO—NLûšEéÃÂç°×Ûsã•Gv§­ë(Mª*Ö¿üI¸Nn—@vÇeYº²æA*¸µÈÈ÷½LKmF·ó·óeÎâŒ®\Y¹”Jm­XKN{&QyÄ§±ÊéÑ°¥5³ˆ?,rî;¹åH_BLØÑ~=r;ªÔªNHqxñUéQ8t„“E>\ÜŒVÓå»é2ò…Pc¨2‚œ'XÛÀ¤t·AHÙ’gb#`‘/©3&»âuÌl¹ó…¸ªÊR_76Û!µ[;çJâcØ¡®¿œoX%]Ãæ•¦çD¯Ê“®o®_ÒÙöWCE#•• þˆg
¼7f¬Ð¦’²dè øÇH
”³‚ÇVgŸ8òš—dwÝ“'›ržWœA5©š>±¢”Ü¼9ƒ~ÿ:½II_¼¥Im	°†KvLÑ×
½–b¬$Ôß3Ó—bÝo°}i•éïnR¯9=%Ê‡v4›¿žFmo[o.,*ýèG‰6¦Ê§ï³äëD6r> ÏÏï`HP‰¾ù¡¹ZLn›ÛZ|¨Ÿ,Ä®M Ëüµ)ÍZ¶ú†vP”Ì8á9‹I—Ê“Î&/–»N£E¹{O€ó/ML¯]ÄÕ^­B‡#„‚¡sÎ\ñÉF†oÐ&» !ßGpaº¡Ú™<	|S<üÂ‚îfè¤ÉÛäŒ>j‘QÛú$8ê·¹§Ó×Æ($ø*ñÿ-O}=\ÖJÏÝå!|[O7¯9ì-Ðæ[Ð—:éµÜg”’h
†»ºT:¦çÕrÂ‡zê nLa›0³wic“PjI»!N¶ZZL–÷‘ÍUÒ 0Œ:d4Áæ[ÿƒ[p•Ä5ÁŸü¸%¿¿ø0ôðøýž;ðCL‡‡5QÔ£ø"sA8“U|[§°Þ¬ÚÖ &nÊÒ„gBœÄ4_žÉÚÿ"&ì¶ç¯#˜ðFû@ÒVê5…j®WS\£oèsSŒ‡ºÃÜŸÜ :gy¦¼%‘ñ9fktU~ƒó\éð?ŽÀúŒàÑºüñ]1Q<öÃQ8†ÿÑ¹Æãúebøñ¨÷Xu£1Dê2W6Ù±Ë¬,S,eŽõ7¾ŽM”ƒŽÙOƒ¼¸äO"˜¹€gÚR[…ýê—’0þõ½ù¹ÒÝ´8Ë:ŠçÐ5u>0Ü‘û£ça*lþð®›Niòv5G‘.Ãœ,¶†~ºþ9ÀþiôÍ„èä‡7óå*êÞu~Qì¹Ôe‰®ó;©`ènü â•sþDÂ
ôò¥NjÍÆ
    0707010001f11f000081a40000000000000000000000016a100daf00001f1d000000e600010003ffffffffffffffff0000002500000000root/usr/share/man/man1/nodemgr.1.gz  ‹     ìûsÛ6¶Çç_{;lw-ÅNÒÝÔcyV±•Ô·~å´³“ä¶	I¨)’VÜíöo¿ß€e+sÓÇvªötêHÂûqÎç  ô¯?çÇ£³—WbO<Þ}¼û¦·ûŸEý1¼†g£(/Rµ˜Ñ¥ÊÇ_	9Sy[A>b!sü^ÀE$Å¿Ò¾‹;þÇùÅåødõŸ‹DT,|œ×âÍôäâòúäâ|üfz)ÞŠ£‹³³áù±‹y<]8ßè$Õ\µ9ë|j¤­LTµQ;ÎÏ¥¨­¹(Jed¥ó™°w¶R„·•Ì%–ºšSˆµ*xï,Si_\7)-ä˜¶jƒZenu¢l›˜õ¡ßLŸ‡zQJSÌŒ\Ðç­N:4†UïùTÏPb!êJê
ß³ÌUÀ—eZçI¥‹æ’ŠV¡.IåKÞÔÓ7mh¬q½™^]œžŽŽ®/®Ü†/Gg£ók(Š„hš;)PÇ¤*ŒÉþ(›(&7y±DågT¬à,”1Wä«,Å<}Ø™ïgŸP²‹2S•JÅäNôz²®æˆúqª¦²Î*WqSÕß)•\.Ô'®ö½µ’‰¢þõ%IGu0|uýùÅUD‹•‹’ÉŠ™XÎÕªéŒ:9™|]±í*ÅÇ€š
EpÙ”ÜæÔBÞ^¥J5¾ =×²V…†UïJ£¬E}1ZLTš¢Yt.Œúg­,cŠ@Ô¤©Tj]j'h’šK£)ß¹!
‘"ÓøVL]n»úV	[ªDO5² Y‘È¬UƒJšuÊªØó}®²2Ï‹%¢ ý”XÎ¡ÔÕª¢(íBVƒWgÃëhì²»CqRYIá=!ž}qY Î“L‰[™¡ÐBBÄ¿µE¾#¦™¬¾ö_{+¨öûîeè~®Òòqå­Ô™ó(òìÎ5Z«GFAçsê)D­3i\iúÝ>Ï DÐÈÐ}×ß!º*ëjcI÷£èÏ¢Wì‹œ¬˜H‹&¥Þ©îõ§‚¶S˜l)ïì÷wÊî‡ï"	éÃ7W·Ê|Ÿ#÷måÕŒ¢Ì`<ºúräÅÛ»ˆÚhêfä“¢qWÿã:ZŒë²,)¢Mæ@±oËyU•Ö5µ‘K¬ š!%â­{RRäµ÷xïi+¶H"ÄÚqIÂóSçÙI€jÐ !©³¬”„®FŒUŽÛŠîp\G&	šÜc|U&¨N 2S€9¯iôNqÐ(Îþ£G{ýÇý'ý§ûTÒ_¸•É»>LAno“>ÄgG<º•æQ¦'‚ë£ÌææÑüqŸRîHÐTgÅÁÿŒ/Î/‡×Ÿýâäô=6Î…j³Òsª˜ú‘#j«äR5©g0a‰Q¨¦€ÝR°tþ¤°bV—”#…d(ë¤áú<+ÇƒT[§IV£=Mo	KÓ=XŽþ{ Ÿ)SÙ(:Ö¶DÓ¬Œ¦÷èG8Ž·µ€³VUë|sN4]pêGµ¡‘^Þ¥Ëðcø1ü~Û?4lr³itç=p8n‹—é(¢ÅXôÈk“8Õ¹B' ‘©LÔƒQd‚8¼<ñ={tzM–B_Âº%5ÐD½ìåYÓ¡Ý¥.IS]»xhPg¢½ÅÂ.âX·Æéâ»•Ú¼q¯WCCâ{…‚l ñfž¬kËºMœ"g-³5`S2ƒWcêÃa ‘•ZÅ]¥][G’¶Vç(v7e·€­dJµÞ˜³¸‚
k8øâ¦/Œš‘˜Ø¯š>¨U»¼:Ã ¦È-Räw‹¢¶môŽX”Ÿ/“.‡ãñWWÇ¿R/Ñ%õ› TBÄÜ‰I]­ãªSRYê©cD:óŽRƒäš„Ë-â7:_äDIšøEtj¢Nª((È¨h˜e°id$êœêî ø‡Œ<ÌÝƒ ªÎ¡¾ÆãS‘(Sá5Üš5¡>99qò²Szj³^¨hr¯.ˆƒFqŠÑt©Ÿ;‘à÷Åqga?þáQÃ¨4îäŒþGqçÔrÑ‹Â$ÊÙ†¬XõK›kl]¾©žA_;IXy«¢1þ¹'È‰Z•Â6JOeëÈ„{áKOV{½˜äì¯êó°ˆ‡E<,ÚÞa²i*9‹¢#ÿ]BV—ä†"ôôtŒ™À7î1ð{p=|éä—bR¤ÖØ9­—Q¤‡y;~!´£ŒQÆ(ctk1ŠXWÏœ»ëšv£‹Çj÷¡‚¶¶öh•Æ z)UüP©ÈTd*n1‘bÞÝ2â¼½£HÑia)Éè"½?ûž¨™ÎõzÑ¾p¢»'0/–ý&ÔšÇß Ð9+º uáÏG/OÎÑª!vC»úE¸øø¯wvÖ;>óùþbC€ºjJZéªœÏüŠBØê'Ë2ÓŠòî´Š;#/Wðÿ`NlØ4°i`Ó°]¦Á1Äcâ´ó«»k|G,ç@'‰e$T'peãÁÆƒ6\ãá‹jM0TK—•çšÞÛµIBÙ
ä­k·ñÄAé´8?lµe–L…/|'¨|2oM’Éë7ÌYæ,svË9ë ²±Î}|+z–½yÚ<-lÝcf"3‘™ÈLÜr&6/óoÀbûž?“‘ÉÈdd2þ±ÈHÛ¹šõÜ,ë(yôÅ+”ØËôŠ;5s{Cµ:eB¨ÁéÉ„AOšT³ËÁø£ØÉ)Òp„ë%sIgž Ý¥ÎÒDÈnüuˆ¶€gjc¨S%S¿=š`PtúAé;h@…iÃ
Ì ³y•‰ÍÄfbo/±‹X\e«ñ:ðn…‹„ ]4‡àƒ³Ñx<|9r¢Û$ÑÈíƒ×©¦>K:&‰ÉÉädr29·•œ¹ª–…énù.pXˆëc%“y»òþÖàMIµwr9<sZJšÅ‹R)ƒt{Aj•4É<Š®”ëÚuP“oñ¥³”–Äëƒê®T‡ûol=AÑáwÓf1ÙúÆ{~ÓÌ÷}*‚…L½O¾z3íþx7 NgŠW#Ø°)`SÀ¦àW1¤õí–àqçò¬ phkZ†Ã Ífg+úâÞö_iƒ º-°};›ƒÝæZ»…[„u:89^à\è¨o°·³Ña£ÃF‡ÎÿÐ2kZº ÞéÆtcº1Ý¶”nîXJA¶Pwv ¹ƒ{>áƒ|˜˜LL&&R–;f¾Ê-S“©ÉÔdj25×©é.<»<=ž\ˆÖÓîªD¨œ¤{C÷)LV¥7eïŸ$<b©"}_¸Ö+öµÑ¶¹WÐe Ö"“pRo“Cêuœ®1 F6Nõ»µÍlMÄÁÙÅñ«ÓÑx´’{Ž·J˜®ý£åÑîj()Óus¢4´Ç©¿ˆe–Å°ÖÖ³:)òoA£ßêAÛÉ¼{ìq(ÉÕ†r˜_­l…Ø
±b+´c÷•ÍŒGÑUwý6¤˜À¾²;Èã6V„!‚Iœ§»w‘Šì5¨k)h÷HD‡F¨j§_º78©j·£½ÔA/À Û‡iI"QC¥L\&.—‰»½Äup}r»à³0¸7¨^‰¢ÎÝ…%mB¦îîe˜Ò5 Íe ¼;BÏò‚û2§*7ßÚ‡qý¦AýúXùµé&ÀB‚GeËˆÚwan³6åèÿ¦§=ÚøÑÌ)¾>^¢WîrM‘¡]„Íôbg}êE'¥ë—Â°­b[Å¶ŠmÕ–Ù*Ç(:V¿øŠTè£vMÊgñpM*ï‹|F°auÊÝÇËS¼<Åˆ ßRùmçÀ(üÒ¦ÈéªP(ƒÑ”¢u·=†Ó­×fBamÊMˆxzÃ†€6[j0À°fæNð’/™±¡bCÅ†ŠÕoÄPQ´(ú’®Š¿{`¯HÖIø4JÄÖ‹­[/¶^l½Øzý6¬—ƒˆ‡o8’ØõsoQÍX¶nƒ´qøõ_& 	ø»  zÍ¹ì­ª=D!lò¬Âik­³˜~ôþ‹Ð:Üµ[/¾™~ôÍ¦ÛÐø“ÉÎdg²ÿ<²›ÿŸëfÕÍf¦Ûù:Œý(šóž æ8sœ9Îÿ	wçuFè÷ùí\¼sêUðúÓéÇôûýÐÏ0û˜}Ì>fß}¶’Um¢¯y2åý™vL;¦Ónk;;Ý;¬47ê®óú‹M¬~"è„×/=Q‚D†‹ÓÔ‡‡{@ pû H|Ã£ë“‹óñ=Jc$@x)!ì²Ñ,ßC¶•!H,çmÚLRŒ…ªæEê:uIL-3/e{ÖQ%\6¤µ¾„ôðdªgµ/ £”QÊ(e”nÛÌ™"5)ŠŠn‹¤Ïìô´aßJÂš6øü>F)£”QÊ(½w[\´Qô•Ñ•{ÛÉJ®ÜhÓèÄ:Q›uÞ.)O¥IÛí…®«­
	,u–QOUúðô‰ß—µS)â bf5³šYÍ¬fV7¬NIšjµº÷¸qpÚîð×^“Û6uKÚØ6Øô±¼ „UU—mþ$9Ö	uÍøeü2~¿+ü"œÆÈ/ Ôðà*°Çû`xK²s½,Ä&ýeœOÓ‡F%Jßªt?8S·—´«ÊD«µ»Yø‡ÿTãÄ5üŒ!ÊeˆnD§F)aôÂ}¶£Mçé"îˆ›Ñã
M…Ag ¸€‚Ï‚úWÛdóL¬ô{”	õòô¤kÈšüz¡>[õB®OÎFÑyP¢›UIA*Ž¬—RWN° HÒÞåÉê½%4w®íœzö’Š"=ÙÝmbw‰¾jS¢dæYu;0Gë-tîšñÎxg¼3Þ·ïÄ[z(Gj#véi\5ïpžd$@ù§!?¤¼'
ü6Km„•ŒJFå–¡ÙÐ®¶%šcµ}:/0ˆBtÇ¨™ÜÀmð|xôÅéÅKTÛ’uªì^«o(z[d5:pòTP•;¶°í5ª'=Ÿƒ êlmøº·»Öèn½£»ìA£ÍñÝ	jÐ‰‚‰©²^Bí@×£S—/õ2›™ÍÌæmc³í¶Î:Œ¬ƒâ©Ö’ÌaGÙAU4º	süs‘¹È\Ü:.–Ð”(º7u¸Hº(ÍDÓL½0áæ;ê?ŠÑ²°9.pQT®S¦µ?ÆIfD"‘¸u@4˜ê"Ñ[/Ý÷v"G·â‰ò¬=­‡ûàxôåÉÑhŒ:ûP¢éD.Á”N:¹w@^ç˜=Ò«:YR.¨–œöª¢gŠ¢ÛB¾µQV§t €¡=¾ž
¡‘-fñ¡6-­~áÐë¤r™z5‰sõŽNÌcÒÅz‘7›‚},Âi¼ÞJÍ6°˜˜õÌzfýV²¾¡ØÞWÒÞØoš1™¿Ì_æ/ó÷gñ÷=/üòÒãŒqÆ8Û:œnS*ÍÕE¹Œ\"ºNÒÚeaü!/tMš¾jÆx«÷lù--Æ*c•±ÊXXµè
:O@¥NiŽó‚p4Y%M2wpk‘ôÍÚãøùD>>ì(?\œ´RrÔõm‚òÝ4K¼mí‹Oww÷ž>y¶»ûøéü¿K ÐÿÌû{Ÿ}ö¤·û¬_˜YÖ2ßßÝÛÿë§ŸMžN&é_ŸMº¸õ>¸^½u)äÝLYvw?ûÛ“OŸí=ût÷Ù³¦,I£Í¶ª_Íª½n£×ùàôÕy'Ktš»¶Îa|rÿfÄ‡æ¿Ç„-[¶ [gAü@¸]p8“æfµÎK}ç}è…2çL+±’vÇ¢í‘¦.Rß¯ën¾‡üéŒbBì“‰éU1?R÷/EôéF^Ö`z2=™ž[NÏöVƒk•eÍÛ`(5aÓT™{Ì=æsoë¸7¯«´Xæt­ÿÖÙP ——Ê´O§þ4æsŽ9ÇœÛ:ÎåEA¸ŒÝ'äV`¨È×'È1J.ˆ»{_ƒÛàøÕÕ.D °Ámý=Öt_¼7ßÛ]ðv%%ƒ’A¹u ¬Š\Wóªà¯uiŸ½‡¯‰¬pU¦$ï¢TÍKUu	ÿ)4² a]ÎU‡²tdŠ.j-5už·OñýA+nv]ÉŒ‡™LO¦'ÓsëèYÍå2‚°QÇm8#Ð=Yùà)±æ|À´Pþ&ƒvOTµ–I8œŠÏ
dÄ3âñŒøÿâëüÁ“öWùâ'<kç!.óùÇüÛ>þ…µÔWáÛ†ÕTf³ÙÆlÛ6¶•þì;;'Ÿ;‘LÅº³ŠD+“™ïY÷ªS=».E&ó byGè\¨ÚReÛÄ¾¦ÄxèÇxd<2·¹˜ÿÊ™‘nÍÓE Fh×ƒz†
åF&užº‹YÆõh«™5×yºz7Þ¨²xÔ”¨I«6Ù)},ÅBSÐî¤6Ny3£`ÌWæ+ó•ùº¥|-å{è
ŸLKzÚ¾(h2ôbø±¬Òú0'Ò]eÝR6n‘IiÆM†&C“¡¹½ÐÄÈo33¡÷yµ>-erçˆ¶áyÊ0e˜2L¿0uûwVxü¡MyªÝÖ™¾U9mmê,ZÏCÊiBcW:Ô¹¦œ«ˆûñúOoH±©w_åátêðmÔ8
h‰h4F…åÃ4isY¢=&týªéC+–ÄQQRþ%â{qHôå€þèËøC~ÿŽ¢ø‡Ø;åäÐÝ˜B¥‚¸Â"û™CH§?ë<#Äÿ­âÿŽµØbÇÂz]³žKPíRÒf¯”âP=±}ÁåO®~ßÀÌ¿eàû‹NÎ‚’Çmû¹€k:Ošvçy%}¸|Ðv×è
n± ²ZÒ_#>Î‹¼÷2•¢ŒNìŽ 'µ(!lT¶Žƒ¹#™Qí'ý_ð	>m–8p6àlÀž/²(ƒ~«ÓR|%oTï"ïÏÝÈ§MEXÈ„ˆI5„­„Œl¯ßØ¥…ïSÈW‘™›Õt×úUÿàùÕÅðøh8¾F364k}W¹íxE­ER¡Â$ìa¡íú•(ÝàlxÔÉe­6Ò¯­·‡áX²`à7dÔÉ…”cpyqÕ­3è¿PúlaØÂ°…aóÛµ0o¦WÄ'qtqþâäeâ¿™^vmO
hU*ŠŽÝ§w£3Áô¬™„Ã,dîÚMýdòf‰¼ªûLÞ,_|LDüú ¤sØ{p£îè¼áÃ×?°	Š|øöõë§êÝáÛ·Í<©V˜)-ç˜ôj¶ÑƒTíS%þìÅµ¡×Óû.©Ñ¤X3Àµ¹„@}<ýu¹#úýþ'NF.ËL+÷~=ioálÒEQ”r½(qo»çnWV2›ÔÆÐŠ&I¾û„E>¤ë>lè÷‡!¿ÿ€tØ‘{Ï(£Ó”ï`kÜ|,¢³`M|…´^€ü½O½¹×ëÉ\æ3·7Jú››—£‰– _F»sEZ-tø,Ë>fô0»S™aZ(Ä5 íÎ!—p^þ#4+¹´-ê#ÿ×^ˆN‹mìü~d
H¨%+ø—}<Aà¡o#÷“jç¼›`¯wß®B^á|–M;›v6ílÚ·kò¨R]vÇô£}¥ièkë~Ú&Ò¤Ñ±ÿô_I;§¹ÎÝª;÷µk:) ºòŸÝ$„2¦È½?ú0	oQâ¥0‡™ÃÌaæð–q‚ ‡§7.?^Md{VÊ:Ã<¨‹æÕe)jprv9ºBM‡×£èdåŽäVç¶¸“ZÂ3#ÒÕdw¡Š'e<)ãIx0Àƒü*ƒÈF½TUÛ}¡À†%Öã 7œø¿öÎ¶7q#ˆãïùûŽ¶:8U}W•J|ªð LÒÜ«ÈØ¶`Ù„oß™Ù]Û¤:Uº´>ýOw-v¼Þ§™ß³›“aÍP^¶FÔÞß¶‡ó˜	”qTÕÓîtxu+‘Í&åž’JL»ÉôÆûIVôô,¡½®â².gäL]õ×“ô­x1c½#Xt¿©_#ýëÍúóþøë½›ÿ­÷wîÜ5¸kp×à®Á]{_wÍmO5Aô¡.ö4,µ8º5oÆÓÁ:°¬ë‘<^ó ç­ÖtyÄ75·¬Óªk\åþ6‡Ûš¿þj&;&y|yÐ¡z»#uÊ²¢©Ç”Ÿ¸Ü.Dnh¯?›µ¦²“ˆd¸üöéZéÔ¼«æîN½‘‚RØw~¡!ÎÒ€¹÷
óOV:'Ýr÷#|úO»y•Óèì+§É”Nß:}ÿÏé|Øê×û_6¿êü±(K/Û)ÇÑäG\¼|j
ÕDnß©1:ÕÏ‰R4#{É¥Î©„Cæs~RËãá¼¯µ–r½[Ÿ¥ô]Z8'HiV©¡})míêHùªf]-aÊãÕL ÉÒSÂëÇ®8¶ÝÂÖÂÖÂÖ6-/‡¹Ü[ÀÂŒp5«E½þpè~m!‰]*tù¹¬¶´\Û‘[Æ{EnéJBý¥Vð®j;×øIÅ&1»;ê¸’ñ=‰M,òNMè~¿ai[lý‚­"Š½ÑdèÝ‹m{M‹Ï÷ï‹i-ÑbÙ ;@poŒ…çé¾ÓÕ£½¹7žÞy/åÙ¾R“ pí—¤ù¡Þ]ÿæÖ“¹)•‚›æl» Wßß¾/|_ø¾;r>–4Bà×v=¼êübQ~&üL,êÃÈ¿w@ä·ØxÍÃ­ë6q¸µÆÌä5dÓg$Äm/Á‘’Xßö€=`¯1G}Ìnýk5ì/új1U‹k>öãæÆ,¦ó‹#?Bæ yuócjnlâ`G}4÷IeŽÅ†=P
ñõ«] 5$†y–Ú… 36U¬.ªl·eòjów¹Ôlìc)c×¼€-`Ø6mã(¡2($Ð%Ô”Ï—' ¾ÊOÎ²Ýºå×Òm¿±gêâ Ôö‹ÚíûÚn¡3¨gÈ	óI°åS¹Ù£Åiî4ôµ±§FTõóXSÝE<9_æÇ‚ONõ!~fÐs†´.C…æeN¥9­®˜†+s¥Š'ÍÇwŸ•]èú}=ílùÕÖ›^ýA_|^0w¸9;h^·áŽüäªdÝ¨ÎíÓ¿Ùÿëè÷jM§¨ÇÔÍ& ·D¡
ÒpÜwÀ½pcòóÀÂÝÔÀÜ9'{´í@;Ð´7íô<‡ÇìòYEá/Â:ÿÕAR$ICÒ,¢Ì¡T.àÃ=Ôu@½©P“ð1}®>î?ÞMîv€`ØöÆ‚ýÑ…³¯gÊ»ëƒè :ˆ¢ƒèM%ú*ã4p±ìOt5éûÀ:°¬ëÀzS±¾¦a¬­Q~Î2†Û KöŒ/]ÞìÊ%XÖƒõ`=XßPÖo"‡ùë!\wà8Îóæâ|ÿ±®
³ó¨ªƒê :¨ÞTªëeRºé£«±‚§¦ƒé`:˜Þh¦SokP÷ï :¨ªƒê zS©ž’ô‰¥úÄ¨‘?ÕAuPTÕ›KõC°ß;ªË ¨ê€: ÞX¨yí—L‰êOY¾¥K:$ð<ÏÁsð¼1<ßsº8Ktùü‘*¶‰ðap¢ 
ˆ¢€èÛÝ®K„†[Ru@DQ@ôë!Z‚ƒÛ®GO‰²q¦úžëð"ç™º:U2`òæHyÂÕEjC»Öi¡HèFL6¹/šI¤àîvÙ“ Ð–Â°§™ˆ¢®½ÕŠŒI7©#¨ãòÒëF:0’Æƒ"H—äÐ¶™œ£ØHþ.Ë¶ü¦ý[ï–HŸ™©°wå}MhÆåJIZM;çT>(TûýéŒÇáPm6¿&IÛÄCœõ8è„¦ƒ'×,O•Oº6³Ô±ž7R]ÜÅoYSÙÝž¿è/ü‡áhÞše4…<‹œìpò='!Õ ô+%é|)ZÈ†££££[7º'·í†>	GŸ±F{Ó§¡	wº-yúãžÿe<¸=¯xö=›.Ÿ ¯¸¨JxxŸ4Ë6÷²TQ.n4!wÏ5{EBC­d%Î¸]«ì˜ŠqâÔèšÙãöq†l2U@C÷UNÝëÊ+3¾¦Ÿ>®ôóCP<,uúÀ5™'`«`«`«`«h«uf$­¶ýÿn4uâ³…¥hð|ßÊ÷gª1Ñ.1É=_Ýu`XÖõb½8yÌsY™¨H¢Ì½,—¹òp£YÃxŠ#½ZÑ‡`w*´aCÈU
iEC"^ÅQÄ4mÿ&²B÷Íx¼´ÄEjJ1×DÍúvAÿå
q84+»€T¢ìÌûØéeë“ôÕ¼ôƒÒë4ãå åi£²á¶€i€i€i€ih‚ièú×j:[Œ¦_>ûž§ú7þ”Þ5ÁIÖ¹úáçåšâò’º%—T´»¸žÎýÖ”†Œ‰¡¶eû=/I3¶è].OÒ÷,ú>HOÝÖß­)®¬2    0707010001f120000081a40000000000000000000000016a100daf000039bd000000e600010003ffffffffffffffff0000002400000000root/usr/share/man/man1/svcmgr.1.gz   ‹     ì{“Û6’Àÿç§@*›¢cKŠ'Þ½½s¦VÉŽ’™ÑÔH¶oËvÙIÈP$—ë*uŸýº 	JòcîœlrÛ®òH"î@ ìÍ~Ó§çÏ®Ä‘øþá÷_wþþÿGÐ›þ .ç£ ¸YlV¹èŠI¦ˆ+äJ%eXˆBå7z¡
¡“¢”	~ÛÈB7.é~E=Êhú÷‹Éåt<zO„É/H7øM¼¯—ãÉål<¹˜¾^^Š7âtr~>¸Rºáhzz5¦Ð`œˆr­êRèd™Ë¢Ì«EYåªCaI)¡!‘f*—¥NV¢Ø¥ÚÔ…·º\cŒVuLp«¨'f.§ÜŠuZ”uÔý›Ø¯—OL­°Yž®r¹ÁÏAd+‰B”©¹Xè4éÀåd©WTö¢L3ü+óJ‰\e±^ÈR’qjä§S9ˆ­Ø¦Aðzyu:9;Î&W®žÎG3([aå¾H¡²‹2Í…\\ÃÿÊƒÅu’Þ‚V
Ed.•çÏÞ×–¨'n¢¯L‹CŠ¹Âêf±*U$æ[ÑíÊª\Cê{‘ZÊ*.I
yš–Ã¢'r£¾¥*w»(&È¦½Ù%*ŠKÛ<Ÿý0¹
PÒpG•L†ùÄéJÜ®USh…-n‹¡Tô¢ÉÑÞ¤Ò™a¯Êîpnbçš¾ÉËSdr¡úØc¦—ƒÓ•¶¾Ž¹ x‹5~[B&%Usî‰47Æhl.2P1@ËÂÊÏËÓÊ»Pec¡
T.¼FºO4÷m].(ýÎÓ„ê|#s-ç±jªRˆËÁÕù[H2í@Ålc÷ë‹ 0{>­©÷Ynï¾úêÍ1þ>yu\¨ì¤þù&DÉæêqtEøUˆ½ç¥MÔJ’¤M×MsŠJÙ„¶®Qáb' ÚMÚÂ^
èí ÌƒŠ°“‰ÅZÅ±XÅé\ÈLµH¾6¿ch¹99N³“ãWê¤©†~”Væ.(×ÑÉãÐ¿²ÊÓ*Û¹†±zÇ×j{r ê€6V‚Ê#~.Ò$“`Ð<AÚŽú§Þ&M4È±çŒHï£žˆH“âÉ|ÉJ•DÐW+økt(ÄŽY„nŒ=XIPÚÚx†ðYVŠ}64&,‘,¥+$1Í$"<Á?Ç}úJû~íþ;4z=Ô­S²±ÚF:»‘Ð‹”Ñž\­ ÞF~&ýV..…û`ÖV'mv£÷YaTö~”µ¼ÿ@fY¯Ô•VåÉQHa:{ü ”Åõcóó«ûê=ôaªùý§ž¼‘:îßÊ<qWHÆ½û=#Îv„‹´lúIU€‚¢ÿ£ÒÐ¾Â6þ?ª´îß^Ë“ƒQ7X£Ük¨^JÍœÉ£46ËÜÙöìÙ`ö|L¨ãl’xëw	ã1­Ç©UA[©…^jP!ª„œëX—[a²÷*plQzþk×½^ï[ßr‚ú“¡µ—èuÑT‚NÂ±Öª@ÓÜã%„–ÞÝÚ>ÚNæ+ô;^öZR-*¨—ç¾œUo9SæûÚxÛz•¤¤IÖ2woÁ¯£ÿ.à3—ÆDû>&§.|Ö)~×ÿ2­Ê¬*{â2…¦š»þKÚú¾ W—>+¸Tˆ¹, ‚X4$6®‚Â`œøVn‹_¶ “ö»XØü!4†ÏI úÖÕ‹Ô¼ZG-rwÀî¡€S°'/u¬ÈÃAÂyZ`V¶
‡’‰2½Z‚é.^€p³°#V(Ð‚
ï<‹Ó’´ðøø—¨S‚…)½ö@£K“˜ ¾™ëdezEÝÂš¢ŒëÀzôÄ9øI]W„TLA6´¼6K"õÄS´ÐYRÈ7·ñÚ3CGˆ÷`Šƒ©4‰:I­‘«	¿'Â|ÿÖý1Œ˜ËÖg£Kct’j3‡»§Ë:©È«$!€­æ€qÑ‚Ž[©KÐÛkÓ…_Æ³³ÉéOà #'Ï2ÄúÄñ—"4À
eÒ›j#0ÖÌqšgXvº
Þ«¹ý¥6&¦ëôâCÄŸ¼'™,T+õ^c[ :«Ä·{ðÙ Z:µtn¾cŸo¤®
š{®€ÙBÓðQˆV¾uÑ‰ÄÐ\÷É
ðë¨\,Ò*)°g¨åÔmêŠÖÕ¤AŒ‰	UÛQù–X¨>Ðž:)á¿‘u+'jOc¨¾ý'£gãPú…®»v!hÃþþuÏÏ»Ã¡X¯o6¡ÑS§¨.¹LVÊcX^1Öè•Ó–‰ˆÀDá^hÏÍ;ý:Xï´¥?|~5 1è‡;WG„Gë£‡›‡<,àaxXÀÃð°€‡wÄ*/ÁÍu‘ÅrÛÜ‹®ûdM
ÍÞÿq:¹¸Ì~xût|6]A5éÊŽëC¸¢ø >îº—]šƒ?è?\fÁ”o±Ñ…	ÉöSr¶±ŒeùÖ|]7èHJb(ú…µ¦ŸM^&­5Ž±µ®lõã‚\•UNMI«XæTæKæKæKæKæKæKæKæKæË»ð%T¦lð’~âJ‚2¼N&N½9TOOOOOOOOOÏ»€'äÅõ‰Msù’ù’ù’ù’ù’ù’ù’ù’ù’ùòŽ|iZ½”« 8µ z{‹W|º„ŸýÙàYøNÞ¹ƒk1‹5N|â"Ú&S2æ«àEÌfÌfÌfÌfÌfÌfÌfÌfwa³H‡¦þè2ÏüñÌÓ%Ó%Ó%Ó%Ó%Ó%Ó%ÓåÝè’Œ”‡—þýLVÃ•Än/om›·Ã¿£Û=³o{oS¼‹Õ
ø+ø+è:r•úûÿw›9:::::::::ú§	ì®•bœy¿vÎTíˆ—kÐëœPv†w†w†w†w†w†w†w†w†w†w†÷ßÞq¬#wƒ`ÐÏjA,zè‘#©“ÅFÆPCQRý³ñO#T*(US4•ü¦Æ¯ö”îb*·@qÜê8ZÈ:Eø¶Žd|ÎÁXgJFtê+Ö'‡¢ãÌŸê&Ï Óãúœ’‰™‰™‰™‰™‰™‰™‰™‰™‰ù®Ä¼óò¹¸®g¾ë—<DvB»ðÇ¼[ìW}¿_nÆÌÌÌÌÌÌÌü…	8]í‚ÂÉ:mØ&Ž»?Äõq×ÆïÐLÏŒápy8Û±{:-äƒ@ÇÜÆÜÆÜÆÜÆÜÆÜÆÜÆÜÆÜvnKTy›æþYöÊg¼"€h‡ÜRÝa3~ìÏ«O™H™H™H™H™H™H™H™HïF¤x*k³ulêýu+ÁŽ€<qÀÇš<[
¸ÍÎ61a,ó y?ÔÛDF¨‹?àV2õÇÃ†…¡—Ì1
MS¦Rø†7ž1ú3ú3ú3ú3ú3ú3ú3ú3úÿîÐgã™»m;ãÉe&L&L&L&L&L&L&L&L&Ì;&½,lJÍì¥¸—¥:)ÝÄ0\ù–_ Æ Æ Æ Æ Æ Æ Æ Æ öÛ‚Z•ª=O
†5†5†5†5†5†5†5†5†µ.¬½^^áa;“1yòãèt&§³ñäbúzyé‘\¤²8ÝÁ©UHÕ¾ÑT~	J}{èä£(ßvAP®uiõyRX&M¹—óè:ÔSükãÛN•–	j?8ŒÅµ/íÄžœa‹ù÷)€+®§˜Hµà@øi•/Úç¥(Ç`˜&aiåÝZIRC\2	 PtÄ¼*ÎV…ZV±1eÓ%
±ÌSìÇ.p{+HîÝÃ¨ÉÕ×rº‚ÕÊ›ïëÊ1†¼ê˜Ï7aÇðüû}áå¨#úåmAŒ9…ê8ÎŽXBð•¾Æ´œì}B”‚NÍ²F~¦”:Qù×G}}„ÉQy¤4è)HËÕŸž^Œö[Ï¶Û^ƒí9·ŽyÆ@Œb/ñeì7­¹ð9xZ±°—U5‡&vÞùù“éh6ý`éïTj°2ö½Ö0ÈÞCS{,–—¶NZ·5¦ C³¨`u¡Œ‰A·iq…B­Á´ÌXo²´°çÕfæ„`lÇÈwžÒ°‚T£#B=ÀZ}ß?Bßl6’h¤@¸2OÑ¿@B»›«G-WÞÃÔútrñtlFm§™C¹q@X*('²–ä¹,¦2Ät^n=ô4r0¸iëvCŒ~©›ïŠ2ÒIhÊ.Ð ÎwÝzh×¹a/§DTi¼6"=ý®r-hœKö;…pèefsøØXîÞ`6] ¡6]ÄibÎ=À/1tñÆé•k	øŠMJV*]÷Nèø7i\my Eæx@õV!£ˆ”hŒ'Y¤U%–(w0y•ù´†%iü­“©£›Ãxï 6‚š#‚¢½-…QÜ6ÔÓÔ£Òò¹Zbá%líéNäXòùt\s[>…bP6*¸„–Ê,A)ix.Á§¤Ž.±½¡'Uˆóõj“¥=uY#ïÖƒ~;– Fn¯oû?½ü<¾©Ü'½7?·i¼úÛ1Y¢“7¯^kî½?yófh‹†¸Nîâî‘7f~çÊ‹Æ^g–ù©Oc[’Tf¢¦&)~Ç–Ù$áP,žK…bBáÛ%qDIÙf(G!]ÊÕ&½q]’º©S•ç8dBûfâ>€¸à€P?M\·y/æ/ŸÓ´%®MÃ~¾u$
‚T±1ýùì¾YÎ­¶mS°X›*‰³vˆf‡‡·¹.‘’ZÞ®¤Óý¥Œ”Ê¬Ê¡O-—älº;ïº_øýõ‘M^æU“:ÙMŒq4‚Ü}üx‘FFôÓl’‰(_ú|õðMó2W Íûq½AHPljçÓð•O'šî:Wky£¡ôäž(>¨¢±XÃÈ+ò, Ù/œ~Ã¦ªã%fÒÐöCŸ¨Ž¬Uð“R™QkTùzI›chŒs´LÛÐ4X®–ÐÙ`\Gùï„ŒïpÝ¿©Ž&‡»R‰©,Ö¾Up7´®|t~y6˜¸5*F]<ê±èÈl ^¹ÜÜgÇyÂÏó~<=ÊÓ£<=ÊÓ£<=ÊÓ£<=ÊÓ£<=ú©gÙ aÞ©Jþ=0('>Ô	1ÌlY­r„ìâÞþrèB”þ“ÁéOg“gH(ØáZ•÷6l›1:
ÚìcQè¢f°„½À#X³ž6Ô£‡×­Ý1qœÞOéÃ¥,,ã¤ÝF™WB-ò2î.hRÈ±[tk´?LÉÀEžpì®dx*†(2£'£'£'£'£'£'£'£'£ç§ÑÓm ö³'‡{ØÆ@S¦ø£ú‰t‰æàã”ì“'¼úy5ïÑæ=ÚÌ¬Ì¬Ì¬Ì¬Ì¬Ì¬Ì¬Ì¬ŸÃ¬Ö!53¦ ù[éÖÛ>J½hƒŒHÇ¬,Uù2“¾hd5¤èOGfjPÏ4X	çb£nÛ™Éž[íAËŽÌqD6Ò˜…ŠR_7˜]
ŽKDŠŽÌçº$l):dÜ;µ×mÛÜÂ¬Ø‚%Ùx«Œ¡·f–t	ÁF6þAýØ»·ný(usƒÛé4¸{âúR|ÿbpá·96K}\Öu­3SvÌú8ÇÜ1)©c313131313131313131’˜3È»tËP7xß-7Àîx`¹è&@Ö»/‚Ò3„1„1„1„1„1„1„1„1„}„Óp¿ÜD¥59õ;Œn´ºEá4µ~WõÜßÓX®Ð`ß;úV¼»zîàÊ±#šïzïØX±¿‡ØçûÜxLÜ-¼»‚ø þãÍ¦K/:Å%tbþbN0æ„¶qÊ¸³_ÑÿÑG}”,dæº‰NA÷ß î%ÆÅ‹—n×–)Æõ.Aì¿Bì)ÆN¢ùö@lÄþwˆ}œœ¼Wj#5i!U¿#Þ=x‡½Í>×£‡“ÅÅ³™ü'^}ŽA§e
æ!SÚ~¯‰ùmÀ‹ Z‹ ÖàUÜvÞÈ­þuÛ‹É Û0oK(ôjè€©Ù+\T`‡Á
[J
ñ0¯·.Qhècv´)LÞ¡4ó4sŽ}!‰©ÌôzWažhÆíBêèPEÊ
ÑÔjüÏ©Ôx¤Æ#5©ñHGj<Rã‘Ô>4R« ‡Á Ø&‹5èB
"ÍÑ8—fˆ;;¬Jbì¶£±6¦Bãß“Ö›d{xD­w`½=6]±„âµLëZ¨u6>}TbG›ÈŒŽç€;bu¼ƒ“–0´)Ö­­|>DUNÔwZ0x‰ÙQN¾T\n*ßèd7y
VÇ‡Õñau|XÝÿí°:rGÝú8ž`Ò ¯ñ ¸Þ°HÝ)Wmì°‰ºÈ€¿³£”x:‚§#x:‚§#x:‚§#x:‚§#x:âðt„{d|EÏÎ>Òóà_N¨nS¡È¼QÆ©>×SMù
Rní”:üÒæ¡sgUxÎ#vß2‡‘Li¨« ™fzãË?5c@d@d@d@d@d@d@d@d@<ðzË³ñàâtÔ~§Î"«ÉžÈ²Wú„ÚƒíU¥÷`Š^tÄfŒÚ¤Ñ‡âÕA¡Qd|ÆåÞ{aî¡Zé‰?*s°?=¶ Os©ßûˆZ'ìŸO†ÏÏFÓQ³r^4×sº­)VÓTh0=Æ)…ÐðakvWÓ*~N“Œ,1.·\¬ýç¶$WÊ‘ÿf¥`ffffffffþÐÁä}‚«*ñ/šÅ2ôª-ý^á
…¯&"W`šÝâ¨eL|€*Újz“O©–©-RïpiÓht2¹Wzï·žý_/%¢
Y
?„àmò…ªÕØë"lÀ aw õ#æXr€Ðû]Ã7ÂW*ÙÀÛ!¾Q	¯‡ÜŒûÓhÛmÜN{¬„½S3Ë3Ë3Ë3Ë3Ë3Ë3Ë3Ë3Ëß…å	L? ó_É™•™•™•™•™•™•™•™•™•™•ÿH¬lÀ,†ê‹/õ°ïõ{˜»ì/öH 7SÜzpÙÞ’×}ðºæ_æ_æ_æ_æ_æ_æ_æß/Á¿Ð.AsÆÊ!()H 2ô"R?­]ôAóÁ<³ËÊÊÊÊÊÊÊÊúÙºÔï÷–+ÐŠc^­À«””””””””ÿÅAõ#^¨9p——QïÉê22=3=3=3=3=3=3=3=3=3=ÿkÓ3Þü‚à7oÞÜ[Ù@H)¦–küÅ½bùMëmPe|»¾þ&õÑ’Ü€«Û»å7ï@ÒøÒÏä÷ºŽ—©’©’©’©’©’©’©’©’©òó¨2ÿ4Sæ‡ˆ2?Ì“õ›åò;‘$ïÂb†d†d†d†d†d†d†d†üƒ0d©¼™IÊe—ñÕÉñ–Ê³ó‰€á‹á‹á‹á‹á‹á‹á‹á‹áë®ð•3z1z1z1z1z1z1z1z1zýVèeŒÄ>y¹½Ö:2i1i1i1i1i1i1i1i1i}ä%÷“'?ŽNgâtrñtüìùÕ`6ž\ì¼îžô N­>€ßšÕŠòm$Óœi‹g^³IºË/±IXbWÎv—¦ÕKáh+Eª%ÒN«|Ñj¦$EÁÃ4	K+`åçGbC‰§­§UÑóªôÖ¸-«ØXÄ²é…9Ê]&õr¹\¯ ¹¿ÅWGF/®ÆCP	W°Z[ó}å8Æ°“Wóù&ìÔqžC¿/¼uD¿£\£Ñ!j!†÷ÀSvÄßÒª‹kcù[^õ¾!J*€½¾6òÃ0è!¥Ô‰Ê¿>êèìë#ŒHžÉ -Ã@OAŠXÞ¨þôlðb´ßz¶ÝölÏ›uü7Å^âÊØoZsá#*rð6XÙý¬ª9.†´îøù“éh6ý`éïTj"0+öÞ=K¹²7œž!Ùï¤­“Öm)šõš;k5‘¾mZ|S€·JRèM–‚ËÙ8Ë9a;Fn|ó”Æ¤BèÕêûüyÔZ‰ÚH¢‘ñÉ<E‡	I'(1BfÐòÍè.L­1"ðh{È¼ÂðpG%BsDæ‘L”åRb„86gÐÖïS˜9|tõ»!Æ¿‹ÔÍwEé$4e—†`ßÑ:ýO{g×5’¥á{ýŠŠØwï”i}·‰qƒÙö`Â6°=´\¥¢´¨¤
©dgvþûž””Uþ‚7<7àª’R©Ì<'ßçä×B'ÏN7{3µr»É^%õnZ5¼}në|´¬òrÆRÉT~]b!…›àí»½““ýçê™½D'EeýxñÆžQˆ‰½ÜjžŠd.Mé
›WKËÁ:ÅðÏª¢]¨È‘9M'ï2³ÖQ:ÖÖ›©Oð®cRµ…ä8ÕrwQ·ËXžiN†6Ör­ÊŒíº*
}BóÔüíJ´¯wø5j3ÚŽÎ¬•Ÿf3Í|ª’µïÚåÅÄÑj6¬“—?«Ø½¾;ßýëëdïŠ.V¹×RxxïWÕçU=}øËŸ˜Oxøë/¿<È…´þþð×_/S¥ºÄþö˜ŸþÓÄðàp7¾¹Â÷ªÛÍ—An›u¹Û[Y]´¥ýÕ‹˜Nsé5!ÏRPWÕOôŽ:7É¦d~=‡[
s–lc]·S”ý²-?ÕÙ¢:ëŒ#œD"ÛÖµÒŠz¿öOr­tÚRüÚàó._ùïq¥·­ØÐíÑšë4Iz¨•‡RÔtpmðUëF9™kWí“˜=:
dv^çº-Æz?§Ìh­kw––ÊI[KëžÍ¬Ûó†×øfG(Ÿÿã~¸}U·ÃÝåæÍz¡‚€²åŸvõ¿åâ=/#û¨og?w—ý²óëpå‹:“Ö|ùÚ¾Ýçª¥Ì2Ì|²ä…Tä2ˆ±NÏ†QÕa”ú9éAZ-.„Õ˜/š	,ê—VC}tK\ü)îcâ$‘bÇ!€6·sÝÏr³º`®§Ù<=Ë¥Ì¬{²ëÅ â›¹ Ö4ò æ¿ü°›Õp]éœ„üD™è¯Ð—i^-%ÒÙÊðK[7ï84Ô÷8fó‰¶…X³…vö_=Ú…›•J\“uqÇþhæm@*í"–¶K‹÷Ïõêb™y\Çº‡»Mß1öþSo®®tÿ²ªÓ²)ÌªÙÉ…¹dÄ}¿þ®ÒûdÉ_³léÅ¨Éö >µÎW¾î;àj½Dël&S°ØjëGúwjh¹üÛ¬ô@‡CF·µîAí?{ñT7Æ9¹úÍúìY©¨,P(oåKúëjÇ®x?-At™è2Ñe¢ËD—‰.]&ºLtù¶qü©xZ ï5åd.¡QÕêWÐêB¿mY¨%Voí¾bô@ÿ}¨æ>Ô žÅ©	ÚÌøÅÜØz4îÒùtëf¶µ vOžíßXÈ÷7³á—´@)Srhæ=|èëý¸³£­¿*§›•ž¼Öä,¥¸»Ô²z‘—3ëÞ¾ñªj.Ä‡-¢Bñ.F¬«Î'	í.?»}æ¥£‹½«¸‡‹0Åœ†œ-sÒÑ&~sÿÕîÁó“ý£W{OërBŠCä£KºO¢V-Ä±îü¢ô„Úî1:Áè£ŒN|ìèÄ²¹ÝG’ÃA%{ç6–úiª.˜º®TÂMÛ*‡$Ûr\½þ¾¦…Xxii\½Ùão`© RA¤‚H‘
"D*ˆT©¸:R‘‰²5&þudô•UéùÔaª+†ŸrãòVŠé¢D2ƒÑ³Q®PfÓ,¤Æói˜Ò„£Ý<íTZ{÷W©ÅLÅÅNV–Kãüµé“´ž&ýÿÐ»¤âdEËç¥=g|U[ŒGþ&úäÈÿ“°ie¦á†«^VGÜ·¢¢Ûr$(§P(Š E€"@ Ð[¨T8Vmúi°ÇàgÑ$áõ*CY‹Áh£Œv|üh‡Þî>9<z¶7ìžšˆuËê+{j“ÛÒ ‹tõÆÿœ4gþ¥(ûäÖûú´üÞa7tkÂ3›Zj ©ÒtÕÖæìåÖ¶HkËM<˜¼®Už¨SDž½Ø?:>|®D†ïå–™Î”U9dî<sG«É/Ú°˜€Åßöb‚5kÖ¬!XC°†`Á‚5×kÞê6°ÿÝMRMÏ	Õª!TC¨æwÕX¼Ø·VðÛLòŠcÚêý‚/ËÖ¹óÓÁAmy°0r	QkZÕS{¤h~-¯	–õg4›ÏèˆÜŸ'ÛÍ^]ˆSÐÅ´„š.…š¬2´ñî={ÿ€Ó—P"°F`À5kÖ¬X#°F`ÀµK5ÃÕ~þã¼YéÅzu•º>%?’ZRÃÒ
vÿ"höbïäç7OžžìÉ[Û7=¡-Å×ë¥5éï~áœú¥î€³Qà(p8
ŽG£ÀQàW+p?áÔ*÷êúë«™¡l†²Êf(û£‡²dPð[L§ÓÝ½ÇÚXé.2“ZdÓiWþ^¶9|¿‰¥‡hLooé^ŠbëvÊ†ã,h=m87¥«Xª„{×›¼–,|M$-•Ýƒç÷ÿÇ°åª×‹5d;t¯]„B\#Pm)£Áâ¿Õ™î‚vöŸ¾Ú¿Ü„§uµ<Ó­xHÑ.Ú}µ÷ô¥“fï‡âèY—Ç«Ò"ØG°`Á>‚}ûöì#Øw[°ÏÔ`’¼tQè†Ëº‚}ûöì#ØG°ï“û¾­è0`À€! C † ˜k0vx’¼´ÿ£#ÅT5Çâ°ì¯¯^b3ÄfˆÍ›ùèØŒ4Eí¢¶ëª(NS±KŸœ¡zEís%ºI“¿FÏÐçžÅ32Ò ×Ý‘&z~gÝ.WÖAè™"±9Çá½¸å0¼t¶²~¼?Ï;Ë[OÁëRØ=Ú?>|yôh?¶úÁéj‹Q~ˆyÀ—ef^S¡o–¢sÏ*·^],3›˜à*aÓèÇ^øzÃp}t¥;†U-Â´°ÂÔ-vùŠX‚øô½AoÐô½AoÐô¾½Ã	|Y¿ÛÀ#…Üð0Šu¿vs· Ü4‘ÝyÝ\H.$’É…äBr!¹®“\›ï½:õ¡Ýó«Ã§/ŸíúËþ£“ÑÞ£“ƒÃçÇ›½ˆ´Yz*m*IöN»¦ò6åd.­¨jñQÓªÌ´²vTÓ4ÍVQ(W^p÷äàÙþ…sqCñØ¤—ðü(ÊªÃfÞÇÕý¸³£­¶*§›••¼Öä,¥è5úÔ²z‘—a7«5/Ýx7ò~‹(ê]ƒXEO\¥ÖÙL~v»ÊK\Ú»ŠY_Ø¢8yŠ˜Áð€ó±eN:ÈäÃoî¿Ú=x~"¼÷4±®"¤8Lê’î“èÔ¬kwÆ°ökü‰¶Î®¸ ¬‰ÆFc£±ÑØhl46}kXó´ªD9?*2©i_])ú a€ÞIe©_Ÿe0j&M®RSåšå¬®ZyöÖç:ØÂUsÏÍ²Þä½ª]¤e+%}Á%æ(1G‰9J?G	„á@8„á@8¼š'Âkµƒ ÷5¡‡ê¼…¼}üà`œfßkÛâY	ªKð?Õø×œÃØêÓ|lPTò@­êÍf8²bSLJ;3Æ‡ÔÚq¬Bqžj	xÒÑL|Ö¹gÜŠ÷·°vÛ~ùíÞ—ˆZD-¢Q‹¨EÔ"jµˆÚ«EmX h'ÃYë¤ÕPŒ¾%°ôŠVX}¡n^~
‡ºi¶Ë*ZèÏðâ=ŸWEÿF±Þü†ãôßbøùs„ù¡è :€ è :€ ƒkè@ŠlcC%ß6£RB/¨³‰-ä4KúñRŸFªå?ÐN[úJñÍòo”\µuüð7&71¹‰ÉMLnbr¤éAz¤éAzÞ‘žÎ’~ýu.½iÙbW¹ú¬0ëhÜË5
]Î<Ñµ(ž'½!‚m£ÿ[­·‰íeís”Bê¾^Æ_üŸió&\ö¯ñèŸùB²ÚüË2ûÏÞìÿ5òMƒB²çµº1í'E©F«3áÁ‰JU3.µŠ\Œ»ŽI<¯8^±)IEôj­ºâ‡eÚ4šö™ h³—ê_±¿´‰poÕÛ¼Óþóm6F´z‘™[HµßžÒaÛæS½¢{Ôè»óy®]¸(Úyjœ´É®dÞ*ÝÌs™•6û¬‡ä¼q•žõ—×'²ë^lv/ùý¸?ŒBšo(ô¾›6còÂ¶Û{ØŠô¶f¸kzŽcGN»¹‡µq-±0SÍóx6¹¢ÆùðYT¤ÁÔ×~Ýü©a„Œq©IÌF{åµ²
ùú.êt8ªvc±Vhá¡í„&%=U«Ž°	ç÷‚ (J€ (J€·¡„Ha›Q¶_Þù„2óÉ˜OÆ|2Ð 4 @Ð 4 @ƒ?&è+!ƒUèk†hóšö÷à²õŒãn¢—:¸WCÜ#:ˆND'¢Ñ‰èDt":o³:Ë¤A&{ñÃæ8êCÂ
¶,Ô‚«·[ôØ‡jîC&ÉOš¹mvÖZåÌ„¯ìÌ„OµH„=• €à 8 €à 8¾*à¨þ‘•Ir”Y›	Æ7iëZ]–mÄinöÞì€òBy¡¼P^(/”Êå…òºZy½ÍÏ²Ótò.‘b¬–›Û±kQt*ÌlZ“ðmÏãÍÚ'óJ¢;×š÷^VE>Y?wË÷³Ü®«¢°ÇŠ‘¦¥I7ÑU†S¿F¶n¯ö~=ZòŠÂB”u»\Y?«x¯"Éœ¾‹¤GÒ#é‘ôHz$=’IÿÕJz3,)ií/iˆþIê¸Ö'ì$¤¸:­$Ïª¢]d›ËÇº{|“‹ø–…8h³ÕðµÍî`Š1ëY? À 0  À 0ðya`‘¿­m3ú§¢»OkÏêuÙB7…ó£VEÇVÝn/í‹Í‹ô¾«“#ÞO¼ÿSÄûWÕîÉ¡uRÍ.@Õ×µ¿Ñ¡Ë*ï";ï;RMDºo­0LSÀ0LSÀ0ÅmLQÙâÐwÞ®lGrmeÑ
Q“ëÕR"ÎÃ`’]©T^¹…K¾È}Á8švõ•{r^>á›üò>‰ÞØäãáÏ-àx^€àx^€àx^¾"x©&ytfÓ²š.¤2ì ÝÉ»ìoê5Ðx4)ómo‚R~K¯{º:Â¶¿i[XB<ˆŸ$ekŸºëU@õõ7L¬²ï»S Ô•Ê›qþÓïsþShÃž› ÅP@	P” %@	P” %Ÿ J–oû=7ŸØÿ!Ü¼Š/¼§¯pX£¨/$º‡õ™
[Àk)Ò(D?Óì‰*»y¢ÿö;yš-Ë»¥¥6ré&ïš;ØÆ±¬´¸’Ç6‚âÅšÅéYaŽ§¥ˆóªÑ|Ú®¢uA7ñ±¹n´°ñŽ06Qçoåö{,HùR÷Íü+J¾Å•;ð|ŸÁgð|ŸÁgðÙµ|ö./Š$ù«ü›Áf°l›Áf°l›Áf°l›}.6[æSé]çðÔÅ:žÅÏ\5ÛÕÌåF6'n­]ãœO‘ÚýËñáó{'?¿yrðôdÿHÊÆ¾Ùè/mµ^/mNïl5JÎ–¬ì>9<z¶w’‡]Rë÷Fþ£Ü~¥q[-ÀR¤«7þç¤9s	©ÂË>¹´Ôûú´üÞàQ‹à’}›ÍçÒÞlÕÖÖ>äÖ¶HkË¢QŠ(E”"J¥ˆRD)¢ô}Déjžž'bìé90`À€°l›Áf°l›ÁfŸ‡ÍlQ÷åM XûÏÚÖþ³öGÀpGÀpäÎq¤Yé»ÉšIvßê†¹ójÚMeêÃÕaLB5ŽuFWk½½a“…ÔTi0I¥Ä{×•PFÔäzk“6kH{ŠI&Ïœú å±!,ŽÔêŽa,†±Æb‹a,¸n„áF¸n„áÆ»âFÉÎelô/ï˜ÛåT›ÙÀ†e–MõMu4)Ÿ¤7ðFàî>„áCø>„áCø>„áCø>¼3>ÌÅ5ió–Fu&½ìûÂ,0¨ÂîG+ÈëMsÔ	ž¾ô££NECéEV›±]Âúv	ß0Ã}‹hò9‡À!p‡À!pº	‡œ„öÞ¾­µÇw³ÌK—üÖf£­p¾’Âðo¼ò/åE+muöüÿÀ?ðüÿÀ?ðüÿÀ?ðÏ†bZ¹jDèãƒ<u ‚€  ‚€  ‚€ ?IžtÌgýÄ Í…~Ô¤”âÉ…
–­8¸i×Bûíæ`ÎB˜"L¦S„)ÂaŠ0ý aBÃæ#šv…9¹¬SýÚñhYI¶‘³ÇœQ¢±­mæl&o9O×Õ²óÃ“Ôº@}hmgšUµº°ñ±Õ‹¤J85šGÍ£æQó¨yÔ<j5›šoÄOÛ"‹çÙtÏòÓ@»+ˆ¯GÝ:35aí_F•âñ2õÕ"Èó!áÎù¬MõH§ÓÐß‹ÐÆ¶¥.£]”½ÍÚ]ÑÒ®Z¶d#}‘¾H_¤/Òé‹ôEú¾¯ô+e?ýsöbª9SÍ™jB€ B€ ô¡ê,77”ìé‘si•x‰ªV½rMÚdÑ–…C0buØî.FvXúCµø¡ªBÚqÞïžféÙºåKÌêê™<¢YMO­¥«·ZqÐ2Ù=9x¶cyÞ_ÜP¢3ëbF©=2ÈÎY^æÍüÞèqÐsò?îìhC±¼Y¿ÉkMÎRŠË«K-«yiy»·îØ¯•æBÜÕ"R¹Þ›ˆ!Õù¤›ßcÓ.mú¤.D'Méü©Z,gxÀùØ2'}jòá7÷_í<}µ÷4±Þ%¤è¦ï]»'Ý'Ñ	`Ã“±ó_ãO”NO½Ç=ÎTáLÎTáL•$HST³]WEqšŠ]Š•bQ9«ö¹§I‡_ãpœ<÷ÌJ%L˜LµéUƒ»´º]®L?ÈŸUlŽE–Š¨LŽC´ÀÎ¯êz¶î âlžžåò4+`»^|T|a3Oëµájž®,¸ "º¿®ô©±Á³Ð  A@ƒ€4hÐx€F·*é(ë[Mð*¤'MþãHÚP#n+óÅLJ³CYËÆ<ö,c×[ï=V Ðh4†@C Ý.ÐÚ¢H’­F¯«A¯mX4UÒ*Ïë|4“¶®¥‰—2S•Õ\Ö$ÙgŠcjWŽ€ÅÏ™­ÌHõåÝþÌL>ŽWž¤E“Dk¢5ÑšhM´&Z­‰Ö|­ÙÌC©ä”WWãMûcN¤õÂ1ÄÎM&¶RtaúŠÙêÑ™¿Ž¯Ï•·SË]Õ©¨F\<µñIRH:$’I‡¤CÒ!étHº÷‘tÒ˜Óå	»0éTP5æö¤
-•¦-,Ð×MOGù¡üP~(?”Êå‡òCùý¡”_(Ž‰¾6g‰YÕ,—E>ñ+Ò¶—^7¶@®;0G¼º™ó¹ºª0y©-/ì3dË|Ú2¼ËæÒ›¸ˆBR[Ã¶Ü*¥à¥Ó–Rï´OhÃŠ¶pTm3O¥>Âê¥áÑú“z—.EëD[éä²ð:*³EHQˆç@â"q‘¸H\$.‰‹ÄEâ¾§ÄíÖ¯¬…5{¯üÆæˆu¬uÇ´Úí…öB{¡½Ð^h/´×mÚ«ß''ôLº€xØK+|íà¼ˆæ9o¢X`w,‡¶°±¸È´ý·yÙ®ïjÆ†XlˆÅ†XlˆõïmˆÅ„!AH„!AÈÏZ|Iòhžª4¾nnŠ8­¥%.‡¡Ahü2·G†¡Cè:„¡Cè:„¯¥CÚŽm>×¿TAEPTAEPTAEPTüjQQÀ.9j]$«Çl‚ˆÓci†‚<½pÄ	"Ö’ê4´á†xò´ž†|æu?7•¨€$ 	H~13P­÷ª"ìÕ<mo­]×æ%#>Ò0/ÜöÉ°Iúqšo&u¾ì¢&©oèÆ¯ßêIßÝYé2‡d!YH’…d!YH’…do%ÙF¬L<ë#«òád,uÆSqÂò][ßf(q‘I›è¥‡¦ì=¦×Uµ{rhž¤ÎâE‡bµ&YäoÍÅÅï§îåÐû>O2ªŒÞÛi"þÔ
á‡ðCø!ü~?„Âáw»ðÓ {'ì²i’g«`kÚÄW0&Á˜cŒI|ü˜ˆ¢h ˆ¢h ˆv=¢EÑ÷k!mí0LÓÀ40LÓÀ40LÓÀ40íÎ0mÞ®Ô$É^|$¤ ”øç•ËÃ¤Ú²P[6¬þÚ½ÅèþûÐö§ïëPpÏ6"ìò,6[ªö™ïz£r*Ù€RVÚâ›y¾\Ú+ožC¤Or	-B‹Ð"´øñ´¨þv÷äàÙþ¾úþâo=3ù:J}O>;³¼Ì›¹Xr`Eñ`?îìh'*¯¹Ùw$¯59K)vÆ]jY½ÈK_r´.w{Í…¸ET®TÅ9ÔùÄpÖÑ©#zŽoXÂNÙÞ¶6¤Wp>¶Ì‰^O>üæþ«Ýƒç'ûG¯öž&V+!E—Þv=é>‰®­aíŒ½fý¢j5{äùA~äùA~ÿvä/«JdrlÿK¡gµo¨¶êŒ_…ŒÔZ÷
[1g‡ïv¿<Ú;98|~Ã«Šd¿?¿¿³ØB¤!Òiˆ4D"‘†HC¤Ý.Ò|Gî;”±Jérìïh“4kžfKu,ÕzZ"¦Ìÿ/«"Ÿhó«"§Ðû»Ezá.HÓUE6wWEC"º_Rp
Ù´¾•lUFû•çM÷£%SdŸ$Ö[Éå·“[O³Õy&—þö@}³ÈË‡¿YEt_¤øL01ÀÄ Ó»%:ãZ_Á¸–TnµûòÅÉaè¼cG2HÏA\éž­ºNä	À€! C † 0`À¼_ &LRíÂ&×ª6ÓY‘
O»É­Äˆ? ~À‘jÀ"°,‹À"°,‹_,VË;[AY\aÀý¸ýúJ“Y>ò^I›öOÜ
,¹µùø>…OáSP2Ð¬rvj–®'6iý‘±fÂ„> |@ø€ðáƒO>RétÿG¡ÒÕfOÞêÉ\¤•¾¿×èX›%¢ÍRëU@P?¶Ë¢J§z¨phžâµJ‘&]ââ"WkG³#Îgˆ3Äâq†8Cœ!Î®g&’!"Ö½âåE—Ñdš~­Fÿu°Ûe&÷˜ýÕ }Ö–ž7ìAÚ?ÜZaýaÝ3¬Ë)ÖdY@dY@²0I\IòhžJIØgs…cÿ{Z/%ôC»”~%cÊS¶˜²Å”­Ÿ²5‘Þ§#DéåÖ­ÔÍEð$ÖûÊ§|æE,bÚâµÏ_ÕÒ WŽj8ÚÆ'–ƒÀ 0ƒÀ 0ƒïƒ§Ráï’ä'ýÏÛ—ð‚Øú²È'A ÌìÊ{óåRÎ‰÷òÓÅ´iÊt>—ÙÊˆÃ>4‹IQ•Ùð±©§³Ð Jˆ¢„(9sÔõ@=PÔõ@=PïQoZ/“ä¨Õfµåÿð,KÏ«©ƒõ;×ì;hétJm¿wˆ:xèþÀð < Àð < Àð < À»KÀ‹––‘7sq±ÝŸ×ç1,µAmPÔµAmPÔµAmPÔöûR›°G‘$'RÍúYªqy¡/oþ·*Ú…ù+³jXVƒÕ`5XVƒÕ`5XVƒÕ`5XíNYÍ\ßÝN¢ì÷jd<Æƒñ`<Æƒñ`<Æƒñ`<ïnO‘5Òž“©è¸+§Nún'ýF™L¥Ý@7ÐtÝ@7ÐtÝ@7ÐtûÑM²$>)‹gS†¯º¹”kcoõ.¡-æuwê”uzß=Þ{þßûG‡/ÿK^IÞ°i%•‹ªõîL­¸?à¬+MË©¿Ò÷ B€ B€ B€à]`»ÿz”m÷»¡HmŸÖÕ;É‚ú+½}žÖSñÙv*/Ð¨nÜ˜É	½AoÐô½AoÐô½AoÐô½Ý5½É‰èPÍRtù–SŠšy@)Té¢Ã¥„¾[ÍÅ¯¤Ót©bR…C÷ƒžS¤ƒVcsÐtÝAwÐtÝAwÐtÝAwwMwÒÈV:6§ÿwLrº=ÿQiÓµù˜ß•ˆ;iâm]«GÓ/a6˜fƒÙ`6˜fƒÙ`6˜fƒÙ`¶»e¶f)"IŽõ?o_'±AlÄ±AlÄ±AlÄöÇ 6Ÿ0oR•Ù¶eÂ§Wr¬Ì³Ál0Ì³Ál0Ì³Ál0Ûgc6ibùìâJfs\jÛn~åÂ™6‡á`8†ƒá`8†ƒá`8†ƒáîœáVé»L—¯%RŒÕrí!ò -Šn)›Ù´gå½f¦V~1»å6µíº*ŠÓT¨H|…´ õ;JG+ñ*ìÂ¯‘ÂÓ;3MÎ'HÃãÂç¥4¸º]®¬¢åÏ*†!-¶Ý“ƒgû7ùýÅ…>³^h”Ût–—y3|’OŠöÇµÑ¶›M y­ÉYJÝÞœUÛô©eõ"/ÃVk¾¿ñŠk.Ä£-¢BñGl­Î'UAÍäg·Ö¼”'-Â»JM^HjVLb\ÃÎÇ–9év“¿¹ÿj÷àùÉþÑ«½§‰u@!E÷Þû{Ò}F6šØ»œ÷kü‰åÊÊåŽrG¹£ÜQî(w”;Êývå>OÏ“d/V˜U­¾yåAÝnø£-µƒ`¿ê«ÝSŒè¿ÕØ‡úK’—å©f.J+2íð±ÔGsšûS}‘C2 Àp  Àp  Ç5ÀQMî€7ºycqž{/%%ð&ôƒozWçË•epãiaóJZÄO™ž5ö·Zäoç+i¦©ªßZœÒiU­n¹`Ö³Î˜uÆ¬3f’ $(	J‚’ $(	J~B”lKy‘³ÜÑ§GÊãy»RWcŽåÚxÓ¶`´ÜÏs¹èT@N»Ô¶äã?¶{`”š”ã`_Ù8ôýCÿÐÿ¿GÿE–ŠLŽßÛ!}×ãIŸfóô,—¤íMízqñ…Í<Õ"‡¬æéÊÂ*{ûëJ?D2¸8B„ A‚ A‚!Bï‚hÊª’&™¼IÁgµ/,[ «ÐUè*tº
]…®BW¡«®ÖU÷Ž¾898|~lïïöžJ:GV‹·õè»ûßÛg‘_›åÙöQnÝ{yòóá‘yªRäˆZýTÛe%-oêóþÒÜƒ´3ý®Tq‚÷’ÿüê¥O­±    0707010001f01b000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000001300000000root/usr/share/doc    0707010001f01c000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000001b00000000root/usr/share/doc/opensvc    0707010001f088000081a40000000000000000000000016a102a8a000001ed000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.hb.unicast.conf.gz   ‹     Í–ÁnÛ0†ï~
¹nA{èe]
ìÒ'È­è–èX˜"¹”œ6o?RNœ ±añ ËÃAýüô‹öj5ç¨V0ãÐtmýò±#èƒ3˜òéæU7/»ê¥­Wì‚¥×ê±½9—]Uªþ'ß#Ûoh-ËÄ×‡RJ&vX{WÚrO:ÁôÖ;¦‹„gô©ÌXj°÷ù¢ín].h"ƒw)Spa,ä¹%É•¢?…-ÜS	M¬®Ï2¤bâ!oêÈ¸Æx‚bg
Y<‰ØJV×(”ÄM[òK¾êQ'„é­Ê&öÂ…Ü,t/LÀÜ3z•IÜ Šú®Lžþö˜KÈÕbÝ‚YAæ?.:Á±‹œÊñþNÆ_Ã01ˆ¯(‡ñ'ZsA3V‰ªãô>úb(„Køæ$gÚŽÄôEùpF+¦Ê.†ßRŠ†šò;	°ü!'š	m‹Òtvéº-u#ÒBþð~Ð·¾…¤úêÅ’:äìŒë„´´ÇSklIÞÖ„¹07×Z&Hf·§Ø/ö\ÿ7Zòx„ä‚6HyöúÃdÈéÇeD	Ç=àpìk’6@W¿*²\r"©lBëÒ&÷]ÃgíÄV‚8ù©iOmÒ	     0707010001f075000081a40000000000000000000000016a102a8a0000010a000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.node.array.vioserver.conf.gz  ‹     íÐ±NÄ0à=Oa©+ÐS™nC!ýï.¢$='-ôí‰‹îº0¡ˆ9K+»qýMSó˜†*g™írCyA³	<ƒ?®îvuíÌó¶az|¾˜ÝºÛb#Þ°|Dîï·MJ¥Ôo+‘J.ŽöuÀõGvHã<yÆ¶Âž§µÑã`§!o«=Å€Ë,PÙüÝL#œ?xGtD {·f+’+_ìO ÑæåHYÞÙÏ6C’JiJÇäÃ]¹¹“FG­ÜislED±ëO±Ê]öÿPìíg¢k·#Ž1«ú¨ú¨ú¨ú¨ú¨úÔóùnsµ    0707010001f03a000081a40000000000000000000000016a102a8a000000e8000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.cluster.checks.conf.gz    ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:ßzB¬qØµÅ]ÃÛq—Ñz
;«Ù™q]—DUSA,r<€OZL®¬»²ÝUÏkØ—jqzÂå#ˆën5Ó.ÈdSK+Êa¶ÇÛµ';êrˆïÉÜÆ¡·iŒ7ƒŸÆtÆ4æ.¿WQPÎ Y—tûÞ3=Ò+&ˆçï¤YI9oìòÑlÅ¾!B¨Bq uÜXŽ>LÝaN:¬=h
´;Ú¿>8©´:XAë·×ê6Ám —)Úó.¹ßf£T_&±é($  0707010001f103000081a40000000000000000000000016a102a8a000010d4000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.sync.hp3par.conf.gz   ‹     í]ßsÛ6~Ï_I¦“dNVd·½7Î4wm§™kšN“Ü=t:&DBÆ$Á euúÇßî  ù‡([Šª–~Hl‘\‚û}»X¬°Ë'O¶ùóè	ÛâŠÓ‹20³¨›UŸW¼~˜¸íŽn»º{ô>ì“Z–™¸úõÑWôÐgî±=Âá_ˆÅ\ÕÙér¼®ùŽmñõ¤SUñq.Ú[}¨jñ[#k‘];PÕêRj©JYNéàw<×t$Þäf9êUId9µ4¼LÝ}rÁ'ì›	žùA,j{\W"•™Â9SQÂµ)©î S8ãG^Àéff‚}ÿ#º‚˜QL‹2c©*
^f>Â¥_Ù£gl¢Ôm.„™©l¯*n5¹ŽµžmMÅŒ¥ 4™q#ôi+žýÁÒ\F | Å[}¡Æ-HñÍ¸&T=Dª×„‚»äŒ}*Ÿ„V£:¢ß€G8à?‹*—)70JÒÜ){§ÌjUªFÃØk½ü› €“  xu:í=Ííž¦…2x^-Ø´VM5${@Ñ0PÄÈð+V4Ú°±æ"02—I“çºA-Ç‚D×ŒFU*WÓY@q·×ç*½€ÛžWJ›óVcçÆvàkïÐPã•AÄ•H ‘OŒ¨Z5uvšñ19MÚ'I˜ýhÈ¾­kUk0H¨‹ÎÎDènAf8µáµé¡¼/”!€¤Ê$ ÷~0’˜!„Êl¼è‘Ü®Q†ºM¨“±®ªêñÜŽeªj†©ªðƒøç<«{·m“N¯÷¶GwýÆX–ØêÍ Iš}ž$acDahFÕ¢Çt˜:Ý>U'ã>¸Ò2¶‡u°ÂEs›‚ÚT˜¯èAÝ¨VµÕŠØÔ¦ìs	;5Pí}ADtµ}vhSDÇb‚ÜŽÓC!2 Ù'‡†ä–³C‘MìsC»1É‡&‡VðÙÖ>5´5»|hnÈá±	|}fhGù€ÔP€Ë¦Pö‰¡yïÌP„Í¦€öy¡Bú€ÄÐ
>÷€µOíÕ{æ…bt6Å´Ï
íÓû§…bt6À´O
íÓf…btîÆ,J³ß]v÷Ápø}/òJãÞ,ÐsSÂU´à³€©¼ÝüÇ/%,Nð˜RØ|&ÓY ¨Ô¬ÄÓ€ª<Jyž3ÝT•ª«„ª@ÞŒ_â.1<¡5ÚÀÅ½ü@ÜuÁ8p§TÆ×pÜ5·®T©%è®‰NÀü6À3©•ƒ³ÙöÈ ?dC%“ý±¼AªÊKQ·+ Ÿ—+¦î¹¤ ›K€x,˜œ–`ç‚é©Béƒ¦"ÊèYc25G«moñÆh<Ç4iÃó9_hÜ	dAIR¾àI2dì½0  öÍ·ß½þøÃ‡Ñq>SyHOäÇ8Çëg
âm$ÔLÁxïÈœ$4ìÕ¥¸ó=šv<jïŠth ÀÅL‚¦‰¬¦©AM
¶×€¸";MTÁ^VÜÌ^ùá% 58]J:½˜Ÿ&î¡‡þLx(½´¿àùZ’´Ï–Îx9Ù z¼øÎ¢´7nõa?¸°Ý¸ì>³Œ¹ÅÎD™òê/fe[Ø½|/cCÖƒ“LW'	þnmø/—@Ë@ƒ°+IlÄ•Aîà§è1aÐL:!]c7ñ.Åj8,¨”iã……á‰Á€3Qåj„q‚½¡¡T.A'–@t¿uô) -CVÙh+Þ®Wê5Ÿmj9'8Qœ”L#LÚé~	År:–å…F
é™jò¾sîQöOeLŸ¡+WlwÞ“Èu$PºTžÐN1&a†ëp‰¡ãx	Î3ö´Tvæx:`ä±É¶´J@›¦ˆ˜!4¸¹Ì#ÎÝ^ûÁ˜1Ë{Ü ÁKIe þZr<Ào˜»€©*×¶æšÔ`ÞGa •ZLax¡Ÿqå3²Ô¤ö”_Â@í… 7Ëò:wvpùS€˜Ñ]0d?û{;`n‡ßN…ñ]¦-‰ÔÀ£
ndJ±¨w«õ¸²ZLšyp£”\^À$Û›È\è…6¢ÐÄqÅˆÉTZ}mU·ØY5=çRÏ…œÎþb_¤n`*0‰2ÓKÁi-€QçÕ§´_pÌ¿ÞhR¸(YÔ¿p=ÊÞ¼ƒGÌ'—àkI»Cö_ž70À±0s!Jv<"G£ÑýGÔ¥È¾´KÇ‘+h1¾ÝlZ5ç¿5ÊðÖ;a])éƒiŠ+É+‘påƒàkZÏ‚Í«£lç˜à¡0¬·%P8o~¯ƒÝ¸ ·ÖHAÓX«Ó"—H[­ÙÆd‘À@VÅk^#ê!ûné(è®esÅRºœC’C¨X@(€‹ù\ÒÄë $g•Ò’Ø¹2{!…“M1†à TPÈ´VZ ý©¤1”å"x:CÝI‹®$ùrôÙ×p¬ü‚0€º„qÆá©½âdõ|3Wö|o -©‰ýt5& gàµuoÛpmÖIùÁ:ÂHÊe.ÔfiàããÑÉÌêÈÞ”Œ‡ë‘ª9«g[iªú)uã…×ëX`[Ø¥D—2Z&Ó=/ çD;)ÚÕL‡0 oÖ×·V%”¦ðäù<·¡„‚aÔ~íUÈÀã“»Ù×ó®³ï}šoa÷~pKÌ+cr„xá=*b´ú7ü‹qYÈ†öŽÁ–6˜•®1ÂNÙhp<8ÁFG'KHLüàv0Qœ“/íÝÔ“hù{¼æø–Ì\w±ó%– €ªì™,£<,Ÿ[C×˜£w“£·ö¼Xß‚ùN1ƒbW¹6?‘rž"\c Ã¨¸Ö Joå-ÚÖÈGô³†Jç˜ñ¨UÞsd³Ík6Éù”=¡¹?GBø%•õ»„¤å @„~ 2®ž¸’`.š89H>°ë*+ŸXÙ¯¯pm×–(GD¢gûý„Ÿ7ð÷0ýKÉ|¾ìÝ»· ;HGÞwo=eaâôƒ/Ú¹Ô'@e÷`&€jüM‰™nÆvQù>…OÛJ34`+ØqÈò€µÀõã5×s^U²ƒé)¾Šc‚—³¥V]HEkýÕÕ@ˆZpÅûbgê±Ú]âLŽ²èá÷ZoƒÏÉ)l” nkƒ’Ë>*ùSD%ÿ@{Üuhb¬´wv«uÛa<>uMÃ[ýþ0ŒgïE.Ãjkbœ¾³Ê^:«¸Lû­›_6é«Ò·Sù´íT:a×¡^¦ï¡²Ï*ÝQìT%Ó7Nù”S:‚·®6¦ï–²n)ÝÀëTÓ·HÙW‹”î v¨ƒéû¢ì³/Jw(;U¿ôÍPö×e#(×Ö¼ôPö×¥;’]*]ú¶'ûk{Ò	ÉÎõ-}¯“ýô:Y‹b÷N'}ƒ“OÛà¤t]Ò5}W“=v5éb·lMßÊä¶2é†ÝÚdMß¿dýK:a×-WÓ7-ÙSÓ’ÎvIÕôJöØ©¤3’Ý25}{’½µ'ÙÉõ‰š¾'ÉÞz’t²Sž¦oD²·F$]€Ü Ms¨zaÆ‚y*z¤ÊªYÅ« d	%6£xe¢‡ì¼¶Êø©½JU.±ñÌu“R•íc‚—Pyg¼óËEq×Ç€t±ZèåV3n¸ãÏ’9¤äuÔ9wx^¬¼0ŸI QÅSq¤Îam°Ýr9AeÒü(„%OS–\7eo«æìiû<O[Oðz)†¶k_U¸QövF5m/k™½zö’ª®_†Ãáslfòfb[uØ¡ªôàô¦hƒ9‰¦J¼úÞ4n7'ú-áp[‘ßøCÇäiIqGKW›p´í²Jþ¦mÚ|¯ß6^²éÔi½ËËò3² ­»©<Q3jÌÓ_>êÛ!,yÕJòÂ±Çr“è„Æîa¯œ•ÚÞ-èÚdÔ\‡¶±Vƒ¶[µ|ˆH†gLjõ»(—gµMu|	¾“dÏkoƒU aõ²å&•Àú&®Z@XºÎf\»f Ó;U>Ää¯Élx|oµänÂÝ®ØW£AüðáÈðr	ÜNÝ“Ú6+ ZYÚ²‹¨ñŽs®Ó™Èš\$Q1mfý ÏAØÿb,ðH[­ÔˆD;,eíq÷`ç™Èù"q#Ó+í(Çi+Ê±\Ö]	S×{—á\6	˜q¬÷Åbb#Ó&ça…L[ìŠ0Ç ‡‰ÉÛvd×«BÞ>¯ÉðO,‘ìßX!cw(³r}®GŠ?±oz>¿>Zã³¬bzÏz®¬±!Häº¾!æ°B–²h
Fj‹ø²ô.ž9T¨Í4/nt4Ïh*šÏàò©	vÝ²öó|ˆw+„GìY©¢î0Žç!èÑ» ÷6ò—]íø‰½}tZ½q0Dñðlµ]”®üÝ¾mÕß\†Q ó¢Ñõ*w~‘©ôÜ¼Ô—é‹Ö[%m©’ÿÈ½ú5bÎè…Í`íö¼3öËãÑèt4:Ã¿_ÿó§ÃÇöxtBŸ~î>5ÄqMùø×[yãËúŽQ»j9öÐE”ï'åzÑÌ×ðp;çgØa¦˜˜Nôï=ž×ZçlïœË)7¤½ý#=º-…iGBm¢üÆF<¶v;QÚö“tßåS’@Žý‹@*–¢NW#|{…åµ½xMc«þ+áMZSÙn6$ù7ÀRØ’ú7¸hÄG§¶)#¬¥EAK³ö°k¨Kïª^h¢8Ü…r`†¸»K,mš1†Ÿ0iÚ¯E(ÞóØû7bqaj¶Âxö(l…u_)ƒ[~
šPkŠR•GŽTÄïS§zïöDßðníB5¥kƒ…ñ4ö¾š`9#^G]]óŽl‚](‡C6w™Sö¬mÍê#ÆÆ ý3Ÿºá…C»Ö»*øn}5À¼ßg/v™½ Bæ"&ÃY\"ö¤Ù1iTÕ‰3·1%ìðÛ•371åZFa•3î¢¹è¿itH­å´Œc9J›´×YMYåÚyíÎ¯/h2†Xî¢·ÈÝZd«èƒðå×iq—uºmA=…vO!PóÁ(¤Ä:úüêÓ¥·ûB‡ãìóÙ§Hž²lV–XKIIz{ˆVO6eO¬²9>VØÃ”šûQ¦·™¸ÌzÜ­bµw8-œƒ¹Ôü–÷¾u4N/ÀXx$û6ˆ@Ö¤0^U9,˜\Gaû=DK¿œñi-¾ðÁuÅpI%Û!7üòµMÉëè®>JLK Yúd{Ü™28Ås,Yr´ý»h w‘”Ö‡½—Û½—#EŒŸ‹i±ÎÓ¹{=vO#§êƒ!Ò*5:PÉþ×3i÷LÂäÞ)$Æ:Ù‹=vÏ#«éƒáÑ
1îà‘áÓ¿1onjO‡ùªˆOž:¨*@¾KSsÜ‰û¯) ’mìæ_ÆACˆÂëeZ.Í,L 4ÑU!–re9ÉémôZ^F­UÇ_çâ¢Öô7½ZãRÉß[±p¯ª¢ý+Dw¯#©UÜÍn3rÛ.“Ä¿ãÌ~G¿ãÊßÕ®PèêÔwõ³‹ü~ä…ÝkIÏx;+y‹õáîÏ7O_Û‡`Ým;m¼û®µÁ™ý\¶Ó¹,Ðô!Ìe7ƒ¼Æÿ(5eQ   0707010001f0a3000081a40000000000000000000000016a102a8a000001f7000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.pool.vg.conf.gz  ‹     å•MoÛ0†ïþríšûŠîØã.ë­(E¦b-²¨éÃMúëK)Ž“6p°-F[ ºØ2e~</MÏfS®j®ìÎ™+ˆ[‡Ð­.w7mvÓ²«r±3¯m›Çê¦}ËeWUN}Û'òõ÷CV´È†o®Œ(Hrbipˆt'LÈÀãŸ¤=r¸÷©jT"™xÈí'YÜûBà´ÑïŒÁ¡ÔJKø+´èµ,Å±‡ ùÄ}ƒ¥, ‘ï;2‰w+OÉA$Æ‹1ãêO0´ÒR˜#÷m#]s”›âøÑQ™úÇB,'T7*ü/T¥†mˆØfŒŠ|+âb–ŒêIÇ¦ êA0­s„V»^šõ‚\ü¬¼ÿ€$Û¡?z;47' Ÿ»<LwÂÖ@ŒÐÃ’›rÝCéQDM–¶m9ãò6dîi—h¿øóè„Ç%Š_–KUÅúb"äšGøžiïê×y¦6¾Òýý¢—×–’²~Êf˜¹k‘a2þp;&çhˆ"¦°²Á:™O;H/!Ø	“J—Œù’a½){æõ_ìèË§åo”1¿³”§và~¿¹™ý<4<æ5Éùà››;<ò6˜ÂÖF±)j½Í¨Wí=&¯Rï	   0707010001f11a000081a40000000000000000000000016a102a8a000010dc000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.volume.conf.gz    ‹     í]mo7þÞ_A (’àdÕ6ú¨±q¹k‹®©Mz÷¡(,j—’ï.·Ë]ËêË¿y!wIÉ’åXJêvý!°w¹Crž™áp8Ã<y²ÏŸžˆ=þ ¹k“5¹Ú¹ýŽn¿¼ûèGžì“J©ºùé#ð•Z.L•Žº~e’(káÕÑ561¥œdªíëmÕ(|Q©Ÿ]©nßÈÌÒ›²2×ÚjSèb6
?IÕT6YÝ»Z|®‹¹ªt-‹Äu“)9çb®dêÇ LXUüÞ–*ÑS@›™*àÛ›%²Hu*keG-yñ›¨ý»¸¡ßoˆ0›@«·såX'r“BSQÃ#fùPŒÇ@b<ÚŠïa,â0J%.`œ|gà] >¾ÕE‘-»F‹›5¯e±dá;ü, ††m#˜ZKGj…¬”°ªºV©˜,Å4S7^-ð©†9aª_ºÙ¯7ÏÔT¹¬›ðøû’S\«* ?1(‘¼¼ù€n!¿ea3Y›J,t–	˜ˆÁY¤M²%¦&kMS%* 7“KÓº$b©¶WðifßæDOK¡ÈŠ4é0bëÇâw¦ØâÎè­ht!óV•‘)¢6-oÅ´29ñ”ÞœðoÔ«Q*†îA+˜'+Ç‡“Wå—œ‰“ãã|³ëeù×fö
O‘Û…˜Y¯2• É 6d&dŸk£`>’9Lˆéâr’H`ŒÎt­q5]Ï©»\Þè¼É…¼–:Cæ´l)E˜¡­ú3Ñ^AŸ—¥±õeËÖ?(Êí›÷óKP¯<GÖÃâa“J—5¢¬nTÒ ÈrZ»Õá–d”É¤†áÆíLÀM GCñuU™ÊÂ€BÕ Yrtè%b·™ûÀikYÕ=”ï
e ±2ô$ßF"soáŸ",{$÷«”!oÇÔÑ¸®¦ìñÜfšrŠiÊûà×ýZy µXû®ZØÔJõÞÏ}(ØôªC»?!2÷@³w~†äž½È}ì}ŸÃ¨äCŸ|îkïúìM/êû8<î_ïùN)èúÄèlÇ¬Š÷²@¶xìÃCà÷/••V4VŸ›¾"«Æ€™¬ñ¹SŠÄy í@,æ:™Ç‡L6	0ÅQ"³LØ¦,,~¥2%Ð›ËkŠóAƒRU-¸¤§‚^—tŒT˜Æ× ­%v]šÂjà1ž‘88óÍ€S=³Ngüé ±òy¦ÖÏÑZQ0)eE>™¶5JÂxü‚yI‡çŸ¼ ^Ÿ^ðYS€]¥2Yëk%JYÏ¡)Qeíùxì#< 1˜áÌRà^R›J«·7ã~°¼DÅK*Õ‚¨¹seD(üâ”@‹ó~5 @†h‹ã 	L6I-3´b•`@'%e	Æ~	Nj+vÌkhM0ÆYHxx|°Àæ‹ÏŽ7ƒcÝñÈã§}ó°c×[R.p˜â·®ƒNÕ_zFv+_{’¥g…¡“ò¢]!ikÐ”´RÚyS§fÎJÛÅ«Úb›º±¸ZÊl!—xk¤Â,Šâ	fRˆ7ªFéùêëo^þðíÛ‰Êbnâã.×'òc†ãõïSzŒëèÜÀx·ìŠ8•cÕÍv.—¥3=ë=0J*,Á©NÓ]7ð£¡ÜŽ!v’§£±ÉÅZüðÆÀ5hnUR]-Fc7é¡o	“êl0¿šÏ-™Ëb¦ÒA4½¸gUpÇ-?øÁ:À^Ëè1hKÌ=SE"Ë?™–í!¿é”¥ìâxŒ‰,ã1þÎ:ò¯;ðGø%åZÝ¬ZÏE´ÐµoNHEßð1uGÈ9h&ˆR
òPƒ}>m>`Æ À©*3³ìRa¼¢!U©',@Ôß]â3«LÓGÂ”8âòd >ÜÈšm.ÓÛÍn© ›êÌå¬.ŸÜe|œl‚%.Ôd,{½ÞË"ŠÐ9¦®-¥u¥g3PUIk‡N@u…æÍgE·9ÔÅ•EÍ¶sÓd)®ÅnÍF0x²óz†+¬(Žä]º‰2øè$àWõïG "G¿r^îï‡ÒÔ8§“Ø…Hký´ÈWjª*°†þ»¾û°¨îH Ëá!pvë|6á…¶ýÏeKß-(ÇjDÙW åê¦ñ§ÈÅÚcð£­ 4à5ÛTÕ¸})V"1hj7ÄDSÔ*pÝ˜õu)ï ¯…˜¡KÏ„…ëYyòÅ&L9x ³G+åâN%µ´W°´Ë"±q^ÝKÅÓÂ°Sýt ÐÓ}[ñU#—¼çXnhÐ¹Î#nk<÷ƒ1cP.34îISƒagaÀXz¬Ò<%“ŸÔ}°˜}ŒÂ²F5Ø!1àJ¥fÄå¿¥¥MÉSÊ äo8–î;×:øü)¦3ƒ´º†â{ß·¶=ðÛhì±àÍ¢…¶B6È¦69ìëŠNz7„n‚CñƒUÓ&C9¸•J¦¯`ÿÑä%»+K[«Ü’Ô¨™—[Â–_g{ªM	Ú} çaœ.\CPr7Gi>ÿ|c”¦œ]N²+m.JÏæõ_tñBÂ¥*RÛæ0åe9CÞÿˆcþéVK†§Cà;Fðý†Ä«‹.ÍÜŠÿÊ¬NT½Pª€ ©lŽ‡âßª*T¨i;YzOxÆh´ŸmC6)›ËŸ˜Ö­°®k^‚G:jF°1G<¡(@fîÔ&EED¼©ƒ-ÃÇñ”—J¤müÄ‚c4®Q’Um” "ÐÂ°zŽþÑP|ÓÙç®’ßê¢¹	}a±xêèK¡`„§j™ÎÁÇ‰"S(tR”œ”Î•ñð‡àhò	ì‹€¹N*cˆŠžZHË3DÉdŽ¼Ó&ÅÚ¬ÏŽ?þ;¼k#!tØ—0Î8`Â_œ®¶¯†Û{h…š¤Ÿú¸CìKÛëÀ>L)?X'0šŽpSw Û—Âã“ãÓOóüöBÈ0BV6GÓ “£ëÆ­jª°;±_C°§·°³ÓQà–ú@yªèŠ°/2tEŒ@>ój­1âÊ,¡À¹ŽPžìÁF—F"…xrº]úz¹ÛÙö¾DÎ·°{;8L4&xàNžäÂ[TÄÄêŸð/ºÃQ!Žo"­;‡¬Àœ3%Fâxp28ÅÇG§¤&>Øf®òK²¥=¢÷µ$k…q_“šÛ]ô¼Ã 0ÕR<ÓE¯•}ÎŠ~ÑÔGÓ£×ÜîJƒ7\Áøkàãˆy"-ZŠpk‡£”àl3¯å-Ú¬äÇôs‡t“_b¾2Y/#ûXm^Ši&gâÙ1ªëÉsŽÄó©™ÅGîˆŒåÐÇ B?ŒÇð²“D1$¦“õçÞÎ2}YÃ&˜óÈpK'”Æ€”DÏ6QÈ¯ø{x IIý	ÎÅÅë€Ò	ïÅk/²°púÁÁg~­ö)`ÙÒÍÔP]	”¹‘ÙfÂ{ù/)ªæu+4ÙŸ„RH-JÜ!ãv»N]üq/Txd"ŽGŽRt\u.…XVw!jÁgâÓ;,SÕáœ§r>²ÜÝQ<ç)îé” nw:%×½Wò‡ðJþ†úxh×„‹ÚÛžÝn½±tÂ8£YW4¼ÕŒ–ÐŸ{+rº8¸Q»ÃÇéëÔ?Hº;àØ˜…~Ÿ*õ¾8ýý§ï„ÝÕY}Eú‡¬HßÅj²ú2ô÷Y†¾#xwUbõµç®ö|'w®¿êÎ?LÁù(î^nÞW™¿ß*ó] ÛÅ‹éKË?`iùÎ îæÄôõäï±ž|7ìîôaú"òVD¾‚÷pa+„û¾—vŸU?”žHÉ8WºŒ5 é AA‰Õ(ÖZ;ÿ“3ã?íW:/3¬g…>ëªI(ï†kÐñJÄŒ«0±c~?RA›¥í¢“²–N~:ÉÙz®owéð||G‡1]	óÑz	3¦=i
ä#W,0x}4hrÕE|'ÅÓv>O[Kð²#C'|œOÇQÔ‹J§çÏ^P~ôù`8>ÇŠÌWS®7ä³“ësº ySlëuSÒ…Ù­†©ó%Êîºå;Q™Z,<­Pl¹ÎÀU²<:±9>d>ûm'{o}Áž/2¢T]Ç>‘Öº¼(ÎE­±,Å-5`‰²X¢&2¹ò‡¾x¨“«–’'Ž…„Ý¹ÂTŒâ\ô°àW¥¶ •jÙ¢
a:ù(mmUvDB†-¦•ùE]«¶2Ø'Ë;JÜ®íÃ„W–MÊšô…˜®j
5 L2si]iÐC×§¤ÃòXø+R%[.¹N¤;H9?Ä“G†MÈ$H^º§—Ðñ¼.ø¤>ªf2—6™«´ÉÔ8Ê¿LÙÈ, „åJ…ï€Úêá~DÚI@G{ÈïÝÄ.S•IØ ðÈìJ!¹ 06	3,Ý—°t½qÞ—Î?—˜"Šù§µNšL†Im«ËÛ› ¡¦S¼r%v»Jtàõs€Ê‚ÄcRj‰S2}®ÐÉŸ’õòœ‰ã;l3¦·\¡åJvA"ÓõIŽÈuAw]Û"yé¬‹—Êí¥2ºÛÍ3ZŠsø<”Ô1^Àúó|ˆ½ÆI¥ÇâYa¢„xÇótFtôV%×ø÷·¸<ôöæåÃnßqxl½×…²kÓ¾öùPw<tƒã+£]…­ªY“[òzœbÁ˜{X4.Âo<žkh®@»ž‰I£AõÚ›—ßÑÿøaÛÌ†v$tÕ	Wòe·–³q*–—òµ^Ôo7K"Hÿ	PÅÌÂÙª÷Í_° òÇw”h[={ŒÅ¡‡¥¼`Ž€âƒ}ž©ú|³UÁz…‰QôÝGBÁOý=®å™ºŠ“˜7%Ekô~{î<OêoÑPÑUy.o±hL5-ÐÂ»¯Ü5<‚îFñ¥|ÁÆ““A÷ûésò3»Ü°v>AñŒD¶Uí@x}Ñ2Žwé¼ÅKGœÌq‘VàbW¦‚Ñ52©T9ê'H¢Ð]£î*w[éÝÍìGY÷_Ì¸¼˜9
&¹Qêû þ=Êšù’%vRC«;À|Þ‚ŠPœì÷K|×‘ƒ3Þ‰¬ÜAÈdÑâ­ùØÑÎÐm.`Ëª˜û„e3Á‘™º”EÚ´Ò‡¡p­—%–£E2mÉkÃ’à§X†JrX˜âÈ™R²ê#Ç‚ÜàÕÒù0»Ú4…+¡ÆÖMO1'¿£;"³%ŽlŠ—;‡bábm#ñ•)žÖ«SŒ— ëç<rÃ‡¶V÷œ„l]Èíãi‡Œ§C,-†-q4ZôBs`¡1åN2³IRÂ‹óv•™Û$e-Æµ*3(l2Ñý3/-:,ñ†yíwÌ)f.¯k[Ôj9û«àmñU¯¨¦×BdèR5Ñu%1¤{ï‚$€à¤rÿ
!*}ÞzâÔ`»hrÕÑÕÅ4£\X¡*}•uMÞéä¢²øÛnS¹6èçËbé.n$xÅf¸h*“½q¼ÚßÇþ6”3ÞPÇ7>ú^9ú;F—"ñWF·æ>´£9nV÷Ç|VÿxzÃSøµÝ ëÝÎeã3ým'³AËÞ-8¨[pú1x”·	Æ6«aUÕÇ£‚¨62ä×y}›çÿÇŠ…Ð~  0707010001f02a000081a40000000000000000000000016a102a8a000001db000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.array.emcvnx.conf.gz  ‹     í”QOÛ0Çßó)Nêkñ
+Ò@â!íMÊÅ¹4‰í®•øð;;iË†*AýÒÊgÿïî÷¿x2seqE9dÆÕÂÊP«fù*¹q«—]v›š°6%-f§©éÙÐv–ÅúïhõÛry²-£¥PÛRB#®HÊ+ë°hh“ëO1Âô«ÓLåãHIvMØ–çIUº¡µOÜÇ½#¥+­àædˆµŠÇšR—ÈŸü%÷ $'™ %[‚!é¼’S75v¡Ž1…A[3P`¡ót(§O‡Ù¦¤<z·Þ+Ñ+kžóHëÞ.ñ§±sÐf
ºeM¥ç”1Z:&ï©ô€ðÕa¨Ï|?L1•402e
‡)IÿÚÎ(HÄAœ<÷+¨Íó*[òEELïoÔ¸ áÂºÚ<6%7ˆIîH²¥Ïæxoè'14z¯n~y©•?®.†{b€t|' ¢~s&Ê»x8ü¿4n¸´ã(­Òè|'^hªk¶J†Ø2|ëÑ80ý´àn>Å§ãs>ð)¶|Š]|äáà¨öÈuoO=	×æðØÚ°çµçõ¶¼þ e™ß}   0707010001f056000081a40000000000000000000000016a102a8a00000209000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.pool.share.conf.gz    ‹     å•MoÛ0†ïþríšûŠî¸ã.ë­(E¢j-²©éÃ7ì¿’'kàbk"Àt1b2$ß‡4µZ-yª,xr8Gd¯ ö!ÔÂãÃ-[Ý²ìªû,våM«pÿPÝÑ·ƒìªÊÕï°"¯>‹p"Ölø°àÉ”‚$'¶§LŸ…˜-¿'ãQ[j‘l<÷³þ×:kZÆÀ¬ýàJ£„Oðˆ-z#‹RŽ${ÜÕ:Y›uiˆõ8
—!#ùj
Ñ´Å˜ÓAG65Nú}p68;X"ÚX×œî¦D¿=/xº›Ü›Kå¾×áÕ¨3’>Dl hòˆG¬
;#à“‰uá6‚`t9ç­f·µ»¹x©¼îþ€¤¶CòïP[ÜŸüÑåµe»+­b„¶–än„Ò£ˆ†ZØ4ÅÇåŸ!sOC¡ãáÎ£Ë»ïy'J&¾Ü–2ÛnÖÖ„¬y†ïã=Bp½Ì´ï…ôÎ§%ú…ÚW¯Ü†R'dãÞÍ0óÔòî%Æ?n`ÂÜ4Ñ¸ELad*Ù‹]¤o!Ø	›Ê”ŒùQ.®?eÀø·ÖÉ—OÛo|i–7Gá®=5÷køŠÈÃì‡Ûj­H®§Ø<ÜÙñ$Úd
}Å¾tëyEc×~$„µë
     0707010001f09a000081a40000000000000000000000016a102a8a000002d9000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.node.pool.dorado.conf.gz  ‹     í˜ÍnÛ0€ïy
¹¦?çv-0lvØzYw*ŠT‘èD‹,z¢ì&{úQ²ãzMm–« ND†?Ò"­áp—×`(vxEs¢‰°,@hôRãËÌívu»e7¸‰Î½q·ƒóäôEãö`—?‡å=z}ö°
é½\²äh‡WäD
9±ÐNuíKˆ?KãA¯	4d²´áamWè`e
/|-¤”ÉŒ—b
¼QÉ9¶@Š5®g œÌù/™ü=¹8s‡÷NHgê(Ý¥Ûð]T¾¼Æ%u‡:‘F/”-) ßÑefZzºcžó<`Æö€{À=àpø +ÌDü—}`þ$-=Í¹•¬n%E:ºä*ð®ý ÝQx¯‚©d€. ÁåGµ°¥£Ä°+¿hÖÒ³üç,5è²x›µ²ößªô,?É2)ô÷BÑÐ|êñµIî§%oErWÒZT‘g…¶ÌDæ1¯Ñ­¨l/0=¼ÞëÀ›ñK¾Ï!xÔ˜Ks°5ù¹?¤ÍL|ù~ÛÆÏÑÛ¯Ñ[ž»°F¥®ÆÓH”dÜ”I5‹Äñ1 Æù`åcŸžžnâ›Ñ8ž£*ÖEFÏMÍÌX %7ÕyLÄ}.CÊÓxŠÄŠ•Qœ÷&ÌÇã‹sn •Ï'v>Æ"*¯›Û—ÔbšYX¬üUÅ#F[„tZ #ôbÂõ¼XäXœ¹5Ì“NRä^R÷E‡¸w,¤‡µH¤ ´|9ìÊÆ(p°ÞX¢ÏønIïjk;Sö…tã[úîw×DKZdM­Š0›6ÿp5“íŠ‚%IÍ¸µð¿Õ§H°’¶LYFâ-Âzäö
ÌŠo]î»G“ µN@ËýX|àdö'4ãÝáD£:imsrGÅŽµVDKä"EëñŠš¨ýhØ*Á     0707010001f03e000081a40000000000000000000000016a102a8a000000e4000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.cluster.dequeue_actions.conf.gz   ‹     ­Q1RÄ0ìý
Í¤>Ò‡’’æJ†á|ò†xvìáî÷8\’c Äê¤•wWë¦©Y¦¡Š5Ó9|dd¼XN>ý/]]wu³3O¿Ž}6³å7œ?£¸îª¬<ÀåÜU¬9å8ÙãˆMíÁŽ:‘o^àþ"½Ícº|Œ+¨x‡\@À¾÷L÷ôŠ ñü}aaP.ûå.š¬Øw$õQ( Žw—PºÃ-!(D·v7?¾xPViu°‚ÖEn×Ü6Öm çì©¼6·Ûì®¬Ec¾ q½q!  0707010001f0e0000081a40000000000000000000000016a102a8900000ff3000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.vxvol.conf.gz    ‹     í]msÛ6þž_™LÇöœ¤Ê¾ö‹g.×—¹L›ææœö>t:&DBÆ$Á duîÇßî É²¤FNâ–™¾8$± vŸ}°?~Ì?Ïž³#þAr™Ô·fV•`‹»…Êß“ÜqGw\Þ=û'û¼–e&î~}öMúÊNûÙ3ý­X-U]vƒ(y!àÅðˆK:UŸä¢íé]Ý`?¬¿5²ÙÆ‹ªV©¥*e9£—ßñ\Ó›LLy“›nÐ?ª’^Èr.jix™º~rÁ§ì%›žùAóµ}¯+‘Ê©Lá›™(¡mJ\t
_¼›âSSfàç\ÍdÊsìkàé¬VM¾¢o®ØT©­\U7U-DQ™>Qµù¨ny ‹Û7Gá1c)/3™q#ôe7Zö¿®ŸT•Q#˜(]•‘€^O™†Å’Ä $°·•(¯þš-ež3Çt•–À-ê70axkæŒúë¿LUQÀØ`žÚÀ”Pô¼}‰/è_”áˆ]+;‘6¼ž	“E•‹B”†ºÓ,S€GSUÐÐ$5Ë¹6¬P™`§Bßºé„’dŠ,Ià©%!Ì€]Þ./“uP%ÈU6Œ§FÂ<Œ¾Œ')ê!pA5u*Ø„k©G„ß5B€e+‡-h®jxöä üXVâ1•¥Ð “öCà3ðÖ€ø#Ð×ÀHÔdK¼FxÞñ[ SÕRÁøVLÐ·HA¦b˜‹…ÈqbÐ_f¥@p„o*% jê3õãñß’¬m«ð´PFÜÔË!ãV`e;eê¾uœ'¡ƒz
h‚×±I¨a
Ã%LN ~k°=Æ‰ÖËm—£Í²Àì%üa%ÌSbÆ†_²Sd ]q—æ–1 ~ÍÀŠÔÌˆ*óÕÉíïÛ¬üö¡^…öžƒ—ƒ^é9E¾Ø“gl²².¨µBìÛ; 	ÜI‚M«Å¦{b¬;›ä*½õæÉ"qØTäÈÈn¹lÉ5~ÊajFÜøÏ±3hbÝ6à8ÅŽ¦€ipi80dLÚšE|›(˜(>=ŠD±Õwvà½f@-öŸÛ¼fGb—º-f½³ÜR‡¡4›+ÁÍX›¾˜áâev¾Í)F0ª"ì~¢,÷M68þË¯Ç4jë6KÏsqIâU–IdðØqŒ<×ôÿ{–;–½>@Ôåô2-²Ë$_Ø/HÛì£Å»Æ0`3”.è©|6®†¨÷Zþ)4„4<Gg‡zKˆ%h ¦msq–ê'éÜ¶bà¨Zw
ˆ)kê¸&füf # ÀÑ×8xŒ\Á^[Ó‡¯®Øùx\lã?¹˜ðMJ}Ó² 7„^ýÚ•&h‹NkY‘Ëw"m@ËøÔˆx¡¨Èe:D¥ÊË¤IÂì#ðäu­j
uƒdçÂ¿Dám‘ÌÃk‚¸9ö^”T”¡ ‰•ÉZðq¸‰ÌÁ"„ÿ”Ù¤_ÊY)CÞ&ï#PGã0¹ªª—çq4SUÇPLU"¿¦ì}å#©eÀÚ?ª•‰}…Z‹>ú9T¢Á¦xìð'”ÌÒìƒŸ÷“ä‘£/‘C%ØÇ>£’ïü¬Éç ±ö¡ÏÑôò}c'Ä×G>§”ïúÄÒyX¦8`Qš§w°ôòû—È+Í-€ÏM	­ÈªY©¼Ý÷'(øÎRØr.Óy˜T Y‰ŸT9Lyž·	•PÐ›óm Ã•¨¹=!äž~@
z]Ñ+‹4ºZ+ìºR¥–ÀcÜí³[®Vœ ómÏ¤F©üÉRZ>ø	â+ÏÈöäp"˜œ• çt¶5AFÏ“©eæ¼6¿1FØð|ÉWxÜ‡`BIR~Î“dÄØµ0¸ÙûÍ·ß½úé‡w‚ãr®ò,¾O äÇ8
ÇëßgJÐ9›+ïáABÃ^÷7ÎöènKkoŠt”USˆL§	¬¦©M
ºmÜ‘ƒ*Ø<ö|é‡—ÐÙ)!„”èÐMzä¿„IuG¢:˜ŸAM’vnéœ—3‘¢éÅ=‹ÒvÜòÃ>Ø°=Htw"Š2åUŸ8ve»ŽÏêñg«cñ±8j*,#e#î(,~ŠÍ¤ñŸ“¤¢6é€¬†ï@3Jà!<È^Y/3ÎD•«•È<a¯hH•Kà‰õ·>HËVö :Šµƒë™ºa³M-g3À'è€‘’)`„Iëî;QtîX–·!¤çªÁÁœqBtLU Äz†¦L1BÀ‘Ü{rÊó'‡ÔS›;Çõ-„FzU¦:Ž—àð<c'¥²žãdÀÐcÓc#h  mr1BhÐ¹Ì#únÏý`Ì‚åŠÆ=iL›&Š+§8«ÏN	,Ào¨» W•k{L¿A5ðûH¸R‹¦³„€³m1ËÙÁNøjßp,];÷uÐü“p ftFì?¾o'ðíðæ¨Ú[R‹ÙB6ðÆ¨‚<‹íÍ*F=n‚#ö“Ó&GÜK%—·àd›¢bS™½ÒFšP#î8&ä’ª´üº:ª¶¥PÎn&ù­T7K!gó?Ùnáªb³>ôåzŽN5C—öŽù×{U
¥`@#ú'e›½~SÌ'°µÄÝû™çp"ÌRˆ’I†çãñxÄ¾u)ò /ídé=%NÆÒ`|9~@²iÕÜüÖ(Ã{±>(Öµ<X¦”³ˆ¹…¸ò…Á€àkZÏ‚Î«”²­a‚IaXïÃÀo~¯ƒÛÔf§4M´Êq[dp@JZ´1YD0J‘¬y!Œ¨Gì»ÎPÐ\ÿ ËæŽ¥ÔŒC’Ï!T, ÀÅ|.iâu •°JiIè\mHádSL 8 2­• ÿl-É·eˆàéy',º’äËñgÿ€wí@àïT£ AòÆ‡§¶ÅÅú÷f©ì÷^ZPú©* ç`µu¯Ç0mÖHùÁ:ÀHÚ9ÊÜ>P»KÏÇ_0Ëÿ{×x€h†Õ ·•¦¡<^Ð‘R7n£pc±}@DJo!º”Ñ2™ú@¼ Uô‰>[øÝ)„yµ–¸¾µ,¡m
ŽÏKJ(FíÇÐ¶Bž_<Œ¾w{ÛÞWÈùVìÞ‚&÷•qs„pÑ‹ {GìkøïZþw÷	Ç`KÜ•®q;„]²ñà|pÆÃ‹N¤(L|°]˜…(nÈ–ö=Ô’l¤s~Kj®÷ÑóN–  U¯Ø©,£}X ŸYEÛ˜áÛéðýîÖ· ¾3ÜA±«\»?‘b½Šš†k4˜P¬ôZÞJÛ*ù˜þì@‡RÅîxÔ*ï1roóŠMs>c§cT×ó3„ß£ÔøÈmHZôÑDÒ„Œ«'î‚$ª½°t2 ~6°ë*KŸC5”FÑÚS)D‚g{>áýþnÿÒ@2¿_ööí›€¤ïÛ7²à8ýà ™÷µÒ•Í¸9€š€Tã“?2ÝLì¢ò+œ…ß¶•fÀl5Öyˆò µ˜*½ãzÉ«
Ë{Çvˆã/gW]Hå«/£Õ@(µ Åûb‡eêeõxAˆS9[Ó¨ã›à¹­;0(A¹íJ}TòID%C}|ìÐ„(u=»Õz£i›}F³®ixëç‡a<“x+²C\¨íˆqúò˜RãvÚ·&¿RÓ×Ä|Øš˜½d·GRh_ó1aö—â^© }õË‡¬~ÙSx»@û’—Wò²—÷Nûìë\>NËN)î_åÒ·|Øâ–}D·OÓW´|ÄŠ–½…¸_Ó—±|À2–ýd·3†ékW>ZíÊ>< „yª"ôÄ>ÅkÊ“£dœ[YÅš
éDƒ@‰Õ(ÖZ=bÿåµeÆ¿ÛVxÏ¦Ä\}¡MÝ¤”wcK_è† ÌŒ«0Ã÷ü~
PAÕJw»“Üp¥^‹bò.èÜ8y>½£Ç1Ë¹U<C-0×
ÓIí.ýadï ¢“€BXÀð4 ÉuS–ñIg'í|NZKðª#C'|öÂ":ˆÒ ^Ô2{yú‚u_F£ÑÖ¿àeqøÄž%¨¯˜Ë0×«©Ú ¿nª$Â­ÆR··X¹SDèÖ^Xg©ÅàiAñ@•Kç~r°?fbõ}'{ï|yDt¯¢c`¤µ./Ê—ÌÈ¸î\X¢<FÔ„§·>ãÐgÐw¸j)yâX¶Ñ+LÙeœ–w WjË}è>á¨‹N>ªA›àOUÈð‹i­~e÷U[‡å³¶%û]Û&æÑ’ˆMÊšôe/®t 5 Ìvfs{Q#pL””…^ÑayþšÔ†Ç­xË%×	w)/ÇƒxòáÈð2	ÜºîimóÛWí-· Q­–%s£Ó¹Èš\$Qþew¥]GÈß‰œe@mýp?"íÐÑÙ÷nb7™È9, 2wßo,|
A`:m2fXº–àº®]ôßå•Ï9¦ˆbþ©‘i“ó0©¢M`uy{ ÃÄtJWGw`×ëD^?7hð/,ìß1©Âj±2}®¬É_õò\±ñ›eÓ[®ÐreAî¹)š²”ES0b[„—ÎºxäØûÇµ½”ÃÐœ’+ZÎ¡yˆÔ5­þœ°×8©tÌNK%ÄÃ8ÎB¡[‰>$zÊÔÌúb²ÇªF|ß`Ù—š¹²!²Ðt—æÊÚö“ïÁtàí¶hÐ¯½<7ªj¶¬f1c“Fæx1»~õ£»LØŸ’·#¡"e[ž”ß[£ÝÊŽºYAŽ¥M@³Ôfë‘œma¯¡´w]vÝo‹PµfÅ­éñ7À,¹Ò]8N^ÇG!¶^ÖL¢ <{x¡€%‹²ßð\Q¼å\¶Ž~£‚obaÓL0Ì ãhÈ¯{ÙGh‘DÍª¢ÛMÁRÃ,yL	ÁˆhU¦'xÏ4e­—ª:P¾/èRöi÷0gQ5ep!6–ÅM1Ó	ÛÑ…ù
G6ÅõÑˆ-Ý
ö’}k³>ÅX´Ÿó¥^8´²¶`q‡6€}ïW©¹J%?…j†V§¸èAóÈ QÕ^˜Ù†”ðò}1sR6VŽë˜	 ðdÈDÿEÓ'C h-geËÑò¸mg9e™kýÚƒÛÔ†ÏþÂ*x_Â)Â,RM¯…È*Ð¥z"MÍq£þî£B>Â¦júòzÂÚo[ñÞØþÎ¨nT@Íý¨–®,§9ÕçWÜ‡ûM/hrQ±é}Åò%3¬D_¹Ëghy¾f3Üø2ºÞì.ÛO_ì~å~)I|Äâzµ{*	†©ÏÓµ»5Ö|n·ÂiŽÛÕý)Ÿ€=Ýã“ðlkã˜$õ~§ñIÙCçÁ—}Xð¨aAÀé§QÞ²ÿ6e2…t   0707010001f025000081a40000000000000000000000016a102a8a0000040c000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.DEFAULT.conf.gz   ‹     ÍVÛn7}×Wà‡´€¤&iÑÅ1 øR¤ÈÅ‘å¾…E‘#-¡]RåE²Š~|g¸Ki¥Dh…÷–y™Ë9g†<;;å×9ƒ~lîêúfxÿ~|*s§î´Øu~o’=sÚ(|ü£Ã/p³¶NvŽ•öbZ"­õNø18^Ú%›Þ:»È~ÀáŸQ;ÜEq#JŸVÎD,Ã.¼í
C ÐÑÕ«~‰RÏ´„˜£A§%o“Â(­D@?Øù„¿wv¤5+t-SkK&¡Cþ½¤¥aFEQ¬ÞF'Öº,aŠ çÆRì`xt+MK>âÈ7ø"e×¦ß’Ç»àyOˆ´Q®ÅÆ“á¥uM&æ1™ôî0€6Y¤]¹-l"'Ù'Ê1öÛñæueÑƒ±
KñÖ!r€mSÁRÌ2hk|ÆzÌÿ‚®*Tšp,7iˆŽ²R¢§0•LlÕ²u¾¡¸ÈaL:è‘,ÖƒI“R?¯SÈ~­oÙÙåAx¥,9Y3GÕÝzÎþÐÔîr¶-KõÒ—Dzb¦ó¦™†·2Ž‡[«R„Ï±:¾¥0u_jJ»F·/ý2 3ŒýTÈ‰:åß%¢Ã¤È¬0ic©¸"X3+ÁR&ñŠW£[ð:DÁš=KÇ_4˜¾ÈäŠ•Ð%#‘5˜<‚pmÙ¨®ø‘…³FÿU›%aÏYg3Rù@öˆæÁ„÷Lo¨$Ê±fõ½åó˜>÷¢"¤}‡½xJS¼¼©)^]ÿÆ#±DòÓˆÆwyþÃý·Ÿ.yÓx•Æ_y¼Õ#O}¾¾§?£K>7ºæwã_h`«ã;ž¿ŽÚ«±¦Ç¶Z&iÔ4u6Î‹ $=bW$×œt¦1Ôÿ¥²é®§“ÙI‚ì;Öæ¾"¿O‚k7ŸuX‰2¦ÞÃzÍ€'Ñ_ÒÏWÈ8¢(­ž­ ‰‚iŒZýw-í‘8º¹„Ÿ^½~-SõÖÔ¦›„Ÿ ™@H[ˆ>æpF­ÄÈ6òDÎ¶Z9VmtsPÐm;Ûë=†èµ„ÖdË\£òBû]Ý¡°,žÙ©W™y:¶ß(ô· á´´rñt…dò¹²ûóË§\*ºT ‡o¢fðqIàyúÙ¥kdô„yUýøÒó#†*
*ñ¨«XÁZè ŒTjÎa¯Öê‡F“~§ÄêÃõßëña^¥·Ä2mµ¤ÓÜÁC¡Ûo†¥pD3][‰È6=D)!q„Çç|±ŸÖo¾ŒŽÔRÐOá6eýà±¤J°î‹¶¬¸œêµÍñM.~;pd²ŒTžneþ8zÞ»}¬R“K-_Ãÿ’dç"2ûM  0707010001f093000081a40000000000000000000000016a102a8a0000044e000000e600010003ffffffffffffffff0000004700000000root/usr/share/doc/opensvc/template.node.network.routed_bridge.conf.gz    ‹     íWMÛ6½ûW°‡öàj×Æ"6uIQôÔ^Ò^ŠÂ¢¨±ÌZ"U’²k ?¾3Ci—ÎvƒfãNPv-‹Î¼÷æƒWWçüÌ®àŒ6g1œßÍ!{ï†ˆõºò¦nð9æÎëÝy±›ý6{å­ñ¯ßg/%èÕiØ³²Ã#-­ïüQuíéÅ7gü0`A»^U-ÞŸôÖÈ/<þ9.ü Ú ojÜ¨¡¾}u„!Û€«:¢ƒk‡hœ·ï¬«‘UÀk<(s0ˆ[„éMÚ±Ç ÑÁbùmqS,Š8?>Ð£¬7½é!ÖbZYÚQ-¦höÔè“{¡Gm6FÃ+hÐ¢7Zà¥‚¦oÅ `‹!PXƒ
Ð:­Z@[÷ÎØrâÑÑÎnL3@™ìªã$h‡žCQZ³iÞ_)½#£†Š–1.=¢‚ÄálfŒw&-§øf/ÙQÌ… þ„RéÄã…Šõ'gñ¹R#cÀH*üÏ£ÒÛë=ñM‡VØÐÎHI2„×ÉðŠ´æžÂÕôaÝ£_&ÿspï1üt7ËÛ.píÑgû)Ý°Aÿy;t¥
ªZÎÎÈž2"”B)ƒ(yr
x“lÔÑ½—VºuC„Þ’QÚ"<äÍlŒç\.ÅrÉY|½xñ\¹ëv‘ šêÖ=´üò´W×ÞØ°ñJÔÏõÎ„õ);ˆ_w»Ã]™CJ¨ˆÕ]k]2¦=«Ó`þçã³à#íü›€6ÔƒÇ¾U¶n©ÂPãg€¥1ôµWžš,ÓÕkPa:,x®þ‘¶ý*nÇyêü	x Î=•<nCtŠFóBú“ÁXÍ-'#ÈüáòAæ…½©17ÅQ)ž|N­Ñá2§Òß±hŠúŠ4®´&¤‘d´ðÁÎ%Õ:\jjvÊØOéYÅ#•ðÜY8-õ„u}2NtIó
ÉçŒ‰É”Ú+ÓJßfÊò£¾6½X^û¸NïËR¯RœO1$óÝ¥2Ä²ÿ†(+MM	îöÃ;Ê#š±}±¸oÆxCƒ\¤L#>Fˆ^±Ý¸¥Çf;â#üe3{ïZ£EÆUYòYe9ÌãÌ-{LòÇtÊFû9oßÊ2ŸT’	òÕžØ	ˆxB5å±1ø:8zØºÀºËÌ‘>ö†ò©dÑÊ±r°cý>^‘Dóê ê?”æ1W€H:Kî®[ÔÙº»à–ï;Ÿ"6¹/½ƒÆã?]5G”:©ø´F®TÓZQ¦ó}”dhúýªÕ‰v:‹\ÙäÀòö5lQ‘{ót&[r–úÂ¤½~Tèá`â–7ÜL
j€çù½„5óó¯?’¸üž5¢]W«ø> vö«Húê{çc:LìrÙãH¦¡—„ HÎþ¾b‘ƒ    0707010001f04a000081a40000000000000000000000016a102a8a0000044e000000e600010003ffffffffffffffff0000004a00000000root/usr/share/doc/opensvc/template.cluster.network.routed_bridge.conf.gz ‹     íWMÛ6½ûW°‡öàj×Æ"6uIQôÔ^Ò^ŠÂ¢¨±ÌZ"U’²k ?¾3Ci—ÎvƒfãNPv-‹Î¼÷æƒWWçüÌ®àŒ6g1œßÍ!{ï†ˆõºò¦nð9æÎëÝy±›ý6{å­ñ¯ßg/%èÕiØ³²Ã#-­ïüQuíéÅ7gü0`A»^U-ÞŸôÖÈ/<þ9.ü Ú ojÜ¨¡¾}u„!Û€«:¢ƒk‡hœ·ï¬«‘UÀk<(s0ˆ[„éMÚ±Ç ÑÁbùmqS,Š8?>Ð£¬7½é!ÖbZYÚQ-¦höÔè“{¡Gm6FÃ+hÐ¢7Zà¥‚¦oÅ `‹!PXƒ
Ð:­Z@[÷ÎØrâÑÑÎnL3@™ìªã$h‡žCQZ³iÞ_)½#£†Š–1.=¢‚ÄálfŒw&-§øf/ÙQÌ… þ„RéÄã…Šõ'gñ¹R#cÀH*üÏ£ÒÛë=ñM‡VØÐÎHI2„×ÉðŠ´æžÂÕôaÝ£_&ÿspï1üt7ËÛ.píÑgû)Ý°Aÿy;t¥
ªZÎÎÈž2"”B)ƒ(yr
x“lÔÑ½—VºuC„Þ’QÚ"<äÍlŒç\.ÅrÉY|½xñ\¹ëv‘ šêÖ=´üò´W×ÞØ°ñJÔÏõÎ„õ);ˆ_w»Ã]™CJ¨ˆÕ]k]2¦=«Ó`þçã³à#íü›€6ÔƒÇ¾U¶n©ÂPãg€¥1ôµWžš,ÓÕkPa:,x®þ‘¶ý*nÇyêü	x Î=•<nCtŠFóBú“ÁXÍ-'#ÈüáòAæ…½©17ÅQ)ž|N­Ñá2§Òß±hŠúŠ4®´&¤‘d´ðÁÎ%Õ:\jjvÊØOéYÅ#•ðÜY8-õ„u}2NtIó
ÉçŒ‰É”Ú+ÓJßfÊò£¾6½X^û¸NïËR¯RœO1$óÝ¥2Ä²ÿ†(+MM	îöÃ;Ê#š±}±¸oÆxCƒ\¤L#>Fˆ^±Ý¸¥Çf;â#üe3{ïZ£EÆUYòYe9ÌãÌ-{LòÇtÊFû9oßÊ2ŸT’	òÕžØ	ˆxB5å±1ø:8zØºÀºËÌ‘>ö†ò©dÑÊ±r°cý>^‘Dóê ê?”æ1W€H:Kî®[ÔÙº»à–ï;Ÿ"6¹/½ƒÆã?]5G”:©ø´F®TÓZQ¦ó}”dhúýªÕ‰v:‹\ÙäÀòö5lQ‘{ót&[r–úÂ¤½~Tèá`â–7ÜL
j€çù½„5óó¯?’¸üž5¢]W«ø> vö«Húê{çc:LìrÙãH¦¡—„ HÎþ¾b‘ƒ    0707010001f071000081a40000000000000000000000016a102a8a0000012b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.array.netapp.conf.gz ‹     íÓ±NÄ0à½Oa©+pbåTFF¦ÛCHÿëE”$8éAßž¸ô®T'{ie7®ý©­ë5£ªiÅv†ÙŒW”ÇòÈ&Æ?µ[wºuíª§iÙšoñù\m§¥›yíª’ù_1~nï–1J¦ä¯WaJ6DóÒãü¢Ó'H…ñ>8Æ2ÂŽ‡©Ðbo†>/£=S/P™ü]LÖí¥{êàÁÎN»•É–'vP4ù@9P–{vG“!›JjHK:rþ¦œÜJ¡¡œÙä°ÅR¬ÅJà#ø½Rl:ÌŠÞÃæ	hÎ6dá3ØÜªÏï|Ê·ÇÞ¼á…N«ýü‹«qY}ÔG}ÔG}ÔG}ÔG}ÔG}Ôg=Ÿ/£€ò   0707010001f114000081a40000000000000000000000016a102a8a00001116000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.sync.zfssnap.conf.gz  ‹     í]QsÛ6~Ï¯À$ÓI2'Ë²Óv¦jâiîÚÎe®in.ÍÝC§cB$$aL,AÊVæ~üí. ”b‹²¥èÔÒ‰-’`¿oÀ
»|òd—?ž°þ 8³Tñ€•Ë\°ScÏ$n·½Û­îýŠƒ}RH•ˆ›ß}Kƒ~å‡ýèöÿJ,¯u‘Œ›n$¼äF”píd‡?¨*ëœORQ7öKQ	¼Pˆß+YˆdíB^è…4R+©ftñGžº’ˆ)¯Ò²é÷ÏZÑ©æ¢%W±k'|Ê.Ø\ðÄwB0²(ìu“‹XNe÷Ì„‚gc¼-Öj!Š@~*MIƒ¦MŸ¿f×sY
“óX0#r^ðR$tÓS¯FÃJÍPÝf®Ë!øÖ]  Ì">›šSüÀÿžêÙm¸\	‘”Z÷[ òbŸHUŠ™(Z¨ü2,ã72«2¦ªl"
Ã@h¢äR¨S âÅm:W<G§óZB‹ð¨§UL—Š• m¯[{ôËZ&¨e¡Jè0â6Me\šÀ]NDy-„bô\æ)ùâFR!Œ®ŠX˜!4:­Òt¥Ššê"ã%š7,Š^šj²ÐéÅð%Þz1Ä'‡/ÁÆD)áï(²nxmŽ!hƒCèÑUº´OœÎ¾>½8}3<GßŒ¿:GÂPó¯˜½ó6Ö"®
#ÇG/l_;Ñ$©­Þƒ/Ö”Ð´÷•Ó%qÌ;S2Úú" A¾†Iªã+óe®MyYk¡·çÚžce\%ð<\Èœ€7 ßR0>-Áy¢ö½9ö2ŽOxŒÖ=Žê‘DÌ~Vº0äŸ‹¢B± Å]DoAðœjÝNSò¢ì¡¼/”!€¤Ê( ÷~0’˜­!„T2YöHîÖ(CÝFÔÉØW÷xîÆ2u¾ÃÔùVøÁ¦ñ2)zwm“N¯÷¶G÷üÖX*éÑÜš¤ÙáI¶FºVê¢ßšîS§Û¡êdÜWø¯‡u?°ÂEþÛÔ*ÇèCê@µª}¨VÄ6 Vª%ì	Ô@µ÷5ÑÔBôÑ¡mˆ)Np{…Èlfz’;ŽyD¶E°íÇ$ZÁg+XûÐÐÎìò¡±!‡Ç6ðõ‘¡=YäBC.ÛBÙ†öæ½#C-l¶´íÒ†Vð¹¬}Xh¨Þ3.ÔFg[Lû¨Ðþ0½X¨Î˜öA¡ýaúÀ¨P»1ÅõYb5;Ápøý]¤¹a• çJÁS´á³€éTàÁJ: (Š…„Í	^«Oîðtl< •†)¼ ÕIÌñT_•çº(Y.4žœó”nÈž¦…÷òQÐê’qàŽÒ%ô¯YKl:×ÊHÐ1ž	$88ó[O;Kƒ¨ÍÖW>	ú6gô 6‰ÎÎŒ›n²ÿ6t:Ä÷Ú+²¡ »– ñD09S`ç	‚é©Báƒ*'Ê˜yU&ú­¶nâMiðž²2Hž^ó¥ÁHEê”ãAN:;(ûþ‡_øé—Ññz®Ó,¾Mäû8ûë¯'ÖÛH¨¹†þÞ9‰¨Û«[qç{Ì ïŠÂƒª¸˜HÐ4‘µ¬
ÐGƒ­À3 .KÆ‘ÎØËœ—óß½´·ãÙÇ°GW×ãÈzèï„A™Æþ‚ñ•hIÒŽ-žs5É 5¼vËBÙ†k}ØÖ¶§1ÝÇ`b–1·Ø™P1Ïÿ`Vv±;16wb6ŠðÌláïÖÆ€ÿ²á€? mw’ ¹7%r7 ?F	f²ô·R­gˆÒX÷e•àCxªÛžÖå0b0àDä©^Å¡¡T.A'–@ÔÞ&úd€VIVÙh'Þ®WêšÏ.9›'8Qœ”Œ#LÚé¾¢™Ž¥º2H!3×Uš ÓwÎ½½„Î“C™ÐgèJÁ#œÈM$Ð9ºTžèl;¬IXÉÍ,p‹aÚë%¸:OØS¥íÌñtÀÈcÓ]3h• €6Mm†P q™VÐGœ»½öƒ>ã,ÕÔïIU©^
*ðkÁið ¿W`î¦ªÔh"ÇšÔ`ÞGa •BÌ0³*$œ}V*Cê`Où:j½a_šçÜÝÁãO5h ÖŒî!û—oÛ s;ü6Ž<vÆw1˜&Ë#T¯JñRÆ´õnW=n€CöÁˆi•">)%•W0ÉVYÎ¦2fiJ‘b°ù*d*µ¾^í€U·ØY>»œ¤WR_^9›ÿÁ¾HÝÂT`*1à¸À¨Ë|†SÚ¯Øçß:'¦ý÷£ìÍ;b
<Y€¯%íÙ¿yZA}JÔÙˆ0<FCöQ(‘†‰J~°t±‚ã«ÑÈÆyuù{¥KÞÃz'¬+9…°!q'	ÛR©qçà›ñ–¸¸ÊvŽ	…Ëz6+4Ìƒ0o~ÑÞ»~Á:ÜZ#-š&F§Y (JÔk²–À@&f¢ÅýØ8ŠºëŸ¤ªnXLO€sˆ¢“3X*f°ÀÍ|*3Y¶÷H:Îrm$±s¥?öAZNÖY•™ŒmÐ?ÁÄÊP–Wˆàñu'5lº¢è«ÑßÁµº#ð7
ÂX êúÙ^žÚ'ÎWï/¯µ½ß@Mjb?µ±ÁÌ¼¶ém`®Í:)ßYGI‘£ÄÅê(||6:ÿ’YýÙÅx¸É«“‰®p¶•e…¶¦ÀF”©\ pm³mÀŠ”®ÂêR¶¶ÉÔò¤âœh'E»›é:äÍZâþÖª„Âž!Ÿ¯íRBC7
ßíSÈÀ³ó»Ù×ó®³ï}š¯a÷~p˜HŒ++— Z{TÄhõ7ø×e!ê[¸qYú¸s5lÌFƒ³Á9~0:9o E0ñƒÛÁÌDvI¾´Gt[ObäÇöžã2sÓÅÎ, ],Ù3©ZqØ ?·†þ®*OÞMOÞÚû®`æ;ÃŠÝåÚøDÌzŠp#çÆ€*½•×h[#ÑÏvh]bÄ£ÐiÏ‘]Ì6¯Ù4å3öl„æzö	ác”?rIË#@'€úíÚ°æ±‹$˜‹¦NNÒŸì¾ÊÊÇ™ýú
÷vUfÙˆrD$zÖßOøyÃ¿Ô‘ÄÇËÞ½{²tä}÷ÖS&Nß9xÌÏµ >*[º1€™ ªíoJ|ÏL5±›Êoq>l+K˜¡[ÁÎB–¬®Ÿmà¸¹æy.LOñ]P¼œ5ZuK*Úë¯îBÔ‚'^±/7x¦«ý-BœÉQ=ü^ëmð99…m%ˆÛÆEÉ¢_•ü_¬Jþ‚ö¸ï¥	IjZv»õÊP˜}F£.¨{«ß†ë™È{‘E¸ÄÁÚ†5N_Yå •U\¤ýÖÃ/ÛÔUéË©|Þr*°ë/Ó×P9d•î(vÊ’é§|ÎÂ)ÁÛ”ÓWK9Dµ”nàuÊˆéK¤ªDJw;äÁôuQY¥;”²_úb(‡+†²”s^ú
(‡«€ÒÉ.™.}Ù“Ã•=é„dçü–¾ÖÉajlD±{¥“¾ÀÉç-pÒº.áš¾ªÉ«št±[´¦/eòK™tÃnc°¦¯_r€ú%°ë«é‹–¨hIg»„júJ%¬TÒÉn‘š¾<ÉÁÊ“lƒäæ@M_“ä`5I:Ù)NÓ"9X!’.@n¦9Vwý©]&ÌSÒ#eV]É¼½@h(m3jïLÌý‡Vÿ¬Ÿ’YžJ,¼ sgQÅ”Deë˜à#”Þ¹öÖ2:Àõ!`=¬—¦9j†/&³üi˜s×[¯êû.žÇ·VÞhÞÖx²þ¶FÌa“(àG&,ax°ä¢Rª}¬š³§õxžÖžàu#†ŽkßäxPÎv¶rÚ^2¹xö’²®/Ãáð93y3µ¥:ìÁPI|§&îUùÀ”“¨ò(j¿û®²Òí;ÒÜ‘phVÄ¥/üaÚä©IqGI—›t´â]“TëÂ—MXÚ×KÚh o¼ËKuÁðíƒÆO5à‰Ò6£&<¾òé£¾BÃ«Z’Ž58šC¢S6ng¸‡µ:pVªk· k“­â:tŒ5ÔÕ¨äC‹dxÇ´Ð…jîª‹êø|'ÉÞW7ƒY aö²å&¥Àú&®Z@˜ºÎæÜ¸b Ã;9e>´É_ÙðöS¼Ö’k„»S±£A{ðaÏðr	ÜNÝÓÂ+ \©lÚE«ðŽsiâ¹HªTD­dÚÄúž‚°þÅDà5¶š©ÑíÐÈÚën`—‰Hù2r=3+åh	Ã©3Ê1]Ö=	S×{álŠÌ9æûb2q)ã*åa†Lì’0' ‡‰éËvd7«BÞ>×døç–HöoÌ±'”Ù9¹>W£ÅŸ»>úøúhƒÏ²Šé=Wè¹’Ê.AZ®ë{bË¤¢7å’ÚZ|i¼‹g%j3cßˆ»æhžÑTt=‡ÇC¦FXuËÚÏó!¶ÚÎ±gJ·ª@?ž‡ [Dï‚ÞÛÈv·ã'özè´†½õbˆÖÃóÕrQ(v^@Ï?r[­Åµ .CˆÖæ´2Å)¥;Ÿ&:>…Æ•YÄ§µ·ŠêT%ÿ
/ùM‹9S ƒA´ëû^±_FãÑèdtÿ~÷õN‡ìñèœ>}á>-+XÇUêño·òû—ô£öUrì¡›(_OÊÕ¢™;-`pK;ç'Xa¦˜˜Nôï=žk¥s¶vÎbÆ&•Ciï_ÿŒ‹ž+S§ÂÔ=¡J„¶QúÉB<6w+QÚò“Ôn3JÈ±~HÅTÔÙê
ß>aymÞPØªÿJx›ÒT¶¤]„ü`*¬¢ún5âW§¶(#ì¥EF[³6ìaÕP+±_[Ñ´Öán)Ìw÷ˆ¥M5Áå'LšökZïyìI#´ù¡å2Ç²Q¸ž=	Ka`Þ®”Á-?M©4…ÒêÄ‘Šø=v*Èt)ðG÷01YWÊ•ÁÂõ4Ö¾šb:#>GU]Ó%ölŠU(‡Cví"cö=ìmËÕ!¶Áø1]÷Â®­Õ®
¾[ß`0ï÷Ñ‹}F/HÇÇ¹h“áŽ¨n{Òì™4:ïÄ™Û˜VøíÊ™O1e-¢°Ê™€
wQ†\ôŸ4G:$Š1r¦Úk9
›ÔÏYMYåÚyíÎ¯/hƒ2µÜUo‘ûµÈZÑGáË×iq—uºcA=…öO!PóÑ(¤Ä&údüæó…wûB‡³äÅüs„/3©ª•-Ö%)Ho/ÑîÉ†ì‰U6"Ç'k˜Rq?Šôà1YoW«X­Nç B.ÿ‡í½/Ó0†dßÈZ‚Æó<…“«(l¿‡Haë—2>+„À>¸ª.¨d+ä†_¾Ö!yÓjÕ@)‚i	$•¶·+S·xŽEGëÏ°ŠyIiØ{¹ý{9RôÑø¹6-6y:wb¯§ÑþiäT}4DZ¥F*Ùÿz&íŸIÜ;""…ÄØÄ#{b±çÑþyd5}4<Z!Æ<*ùìOÌ›O•§ÃxU‹Ož:¨*@¾˜È²àx	þö_S:@$[ØÍ¿ŒƒºÐZ^7aýkYÎÃ Hƒ%ºÎD#WªiJoó° rÑ*­:ø:7×*Mÿ©Wk,´Lð½K÷ª*:ÿ±Bt÷:’BgAkö˜‘;vEþÕ¯ìw”íw\ùVí…¾ Ž}U?»iÁïGNíYKãí¬<æ#ÖÇ{>7<<½v7ÀºÛqÚöQì»ÔwösÙ^ç²@ÓÇ0—}Šä5þÂeÐ|¡    0707010001f0d6000081a40000000000000000000000016a102a8900001007000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.disk.lv.conf.gz   ‹     í]msÛ6þž_™L'öœ¤Ê¹ö‹›dÎwMî2×4:Í}ètLˆ„$ŒI‚%@Éêõþûí. ,Kjä¤n™é‹C`÷Ù »ðãÇÇüóè1;â$—I}=`fU	–/>œÜqGw\Þ=ú'û¸–e&n~zôMú9LûÑ#úµX-Uw#(y!àÅðˆE:UŸä¢íé]Ý`?¬?7²ÙÆ‹ªV©¥*e9£—¯x®éM&¦¼ÉM7èoUI/d9µ4¼L]?¹àSö‚ÍÏü ƒùŠÚ¾×•HåT¦ðÍL”Ð6%®@:…/ÞÍñƒ©)3ðs®f2å9[¨¼)Ä¾ýŠ^#?Ï¶2T]UµEe®øDÕæ“2·åáÜmß…½Œ¥¼ÌdÆÐçÝhÙ¯]?©*¢F0Q
º*#Ù¼ž2-3Š%‰I2`o+Q^¾ÿ[Ê<gŽé0*-ZÔn`ÂðÖÌôÖ™ª¢€±Á<µ)¡Ôyû_Ð¿(Ã»T w"mx=& '‹*…(u§Y¦ 0Ž¦ª ) Ij–smX¡2ÁN„¾9uÓ	%ÉY’$ÀSKB˜;¿^ž'ë J«l"O„y
}ORÔCà‚jêT°	×R[ü®,[9lAsUÃ³áû2_‹©,…˜´Ÿ·Ä¸Æ¦ F4#^£@<ïø5Ð©j©`|+¦@è[¤ S1ÌÅBä81è/³R 8Â7•5õ™úñøoIÖ¶Õs‡Ú.^U(#®êåÇq+°?³2õ
ß:Î“ÐA=´GÁëØ$Ô0…á&'P¿5ØãDëå¶KÑfY`öþ¸æ)± ŒaÃ/Ù		2®¸IóFË… ¿f`EjfD•ùê”äö×mÖþûP¯B{ÏÁËA¯ôœ"^ìÉ36YYÔZ!öòH÷C’`Ój±é#ÁžëÎ&¹J¯½y²H692²[î[rß¤r˜š‘w`þsìšX·8N±£)`\“¶fŸä&
&Š†O"Qlõx¯P‹ýç6¯Ù‘Ø¥n‹Yï,·DÓ6Šf³ZLæÊBpK˜½˜£³­Av
†ÌŸª»¿S–û&ÿñ§cµu›¥ç¹¸‰$q‘e<v#Ä5ýSŽ½>@Ôåä<-²ó$_Ø/HÛì£Å»Æ0`3”.è©|6®†¨÷Rþ)4„4<Gg‡zKˆ%h ¦msq–êƒ‚ÀQ•î6OÖ´Þ¾¶u‰üˆ]ØOÅ¸­ÉãÔÜž•/>ûï«ï_¾üõ»÷—¿¾ÿçÿ¬‘Ä&ÏÙÙ¸Ø&(ò!Àš«
´ÿªí¯·˜^OÛ%)¨•NkY‘o7"m@ùÔˆxEÈä<¢/SåyÒÎ$aö¸üºVµ†…ºA²sá_¢ì¶HæîÅCÜˆ"€^”¿U”¡ ‰•ÉZ”r¸‰ÌÁ"„ÿ”Ù¤_óY)CÞ&"PGã0¹ªª—çq4SUÇPLU"¿¦ì}å=©eÀÚßª•‰}…Z‹>ú9T¢1Uµ¸ïð'”ÌÒìƒŸ“ä‘£/‘C%ØÇ>÷£’ü¬Éç ±ö¡ÏÑôòCc'Ä×G>÷§”úÄÒ¹[¦8`Qš‡wuòû—È+Í-€ÏM	­ÈªY©¼Ý óG-øÎRØr.Óy˜} Y‰ŸT9Lyž·™•PÐ›óí´Ã•¨¹=Jäž~@
z]ÑV,žŸ4ºZ+ìºR¥–Àc<Ð¶{³Vœ ómÏ¤F©üÁr_>úQã…gd{Ä8LÎJÐs:·ŠNåšŠ £çÉÔ²“^ß˜F#lx¾ä+<D° ¡$)?çI2bìR` ûúå«‹¾y7 8.ç*ÁâûB~Œ£p¼þ}¦²¹*£Mäˆ†½îoœíÑ#ÜÀÖÞé(ý¦™NXMS?štÚ¸³	U°gx>úÂ/¡CV<k)Ñi¡›ôÈ	“êÎNu0?ƒš$íÜÒ9/g"DÓ‹{¥í¸å‡}°)`{âèï:neÊ«>Ãì(ÊvêãÏVÇâósÔT:‚FÊFÜPºX ü-&šIã?'IEmÒYßf”2ÀCxâ½²^f
œ‰*W+‘yÂ^Ñ*—À êo|
–!­ìtk×3uÃf›ZÎf€	NÐ#%SÀ“ÖÝw¢èÜ±,¯5BHÏUƒ™‚9ã…è˜Ó@ôM)˜b„€#¹öˆ•ç¨§6ÉŽëkôªLu/Á+àyÆž”ÊzŽ'&€›Aë i“‹ˆBc€ÎeÞÀÑw{îcÆ,W4îIcÚ|R\9ÅévJ`~n@Ý¸ª\ÛóüªßGbÀ•ZÌ0ï%œm‹é°Èö„/` ¶!ðÇÒµs_ÍŸ`¶ÄŒ®Áˆ}ïûvB ß?a2«¸õø(µ(ã-doŒ*¸Á“jð×Þ¬bÔã&8b?h1mrÄÁ­TryN¶)*6•¹Ð+mD¡	5â†cæ.©JË¯çG@Õ¶\ËÙÕ$¿–êj)älþÛ-<@Ulzˆ>_Oæ©fèÒ~Ä1ÿt«Já¢h¤Q§´´×oaŠ9àd¶–¸;bïyÞÀ 'Â,…(ÙÙ˜dx6Gìß¢.Eà¥,½§ËXZ Œ/ÇwH6­š«Ÿex/Ö;Åº–‘Ò”’1	W¾0|MëYÐy•RÚ 5L0)ë]øÍÏâup›í´‘‚¦‰V9n‹,HI‹6&‹F¹”5/„õˆ½êÅ Íõ7²lnXJ-À8$ÉðBÅB\Ìç²&^P«”–„ÎµñØ†N6Å‚`A!ÓZiðÏÖ²[†žÎ‘wRÁ¢+I¾ö7x×þNÅt /aœqxj[<]ÿÞ,•ýÞ+@jB?õ±Cô¬¶îuà¦Í)?XI;G™ÛjwiàñÙøéÌòÄ^ÇÅ  šáD5èm¥i(át¤ÔÛ(ÜX¬C‘Ò[ˆ.e´L¦>/@}¢O+~gw
a@^­%®o-Kh›Âƒ#ÄóÒ†
†QûqD ´­gOïF_»½mïr¾»·ƒ ‡‰Ä}eÜ!\´U%ÈÞûüw-Q¼û„c°¥îJ×¸ÂÎÙxp6xŠÆÃ§HQ˜ø`»0Q\‘-í%z¨%ÙHü|Ij®÷ÑóN–  U¯Ø‰,£}X ŸZEÛ˜áÛéðýîÖ· ¾3ÜA±«\»?‘ba‹š†k4˜y¬ôZÞJÛ*ù˜þì@‡RÅîxÔ*ï1rosÁ¦9Ÿ±“1ªëÙ)ÂïQj|ä6$-Ž@úè "éBÆÕwAiX:P?Øu•¥Ï¡bK£hm‡‰ÈˆÆ€”"Á³=Ÿð~·i ™ß/{ûöM@ÈÒ÷íYpœ~pÐÌûZéêkÜ@M@ªñI‰™n&vQùÎÂoÛJ3`¶lë,Dy€ZÌ”Þq½äU…õ†½c;
Äqƒ—³Ž«.¤òešÑj ”ZÐâ9ûb‡eêeuAˆS9[ü¨ã›à¹--;0(A¹íJ}Tò»ˆJþ‚úxß¡‰­—i{v«õFÓ6ûŒf]ÓðÖÏÃx&ñVd†8¸PÛãôå1Ÿ¤<Æí´oM~9¤8¦¯‰ù¸51{Én¤Ð¾æSÂì/Å½RAûê—Yý²§ðv%€ö%/Ÿ®äe/îöÙ×¹|š:—RÜ¿Ê¥/nù¸Å-ûˆnŸ(¦¯hù„-{q¿ ¦/cùˆe,ûÉngÓ×®|²Ú•}$x@óPEè‰ýs¬)OŽ’q®ek*¤%V£Xkõˆý‡×–ßµ­ðBN‰¹úB›ºI)ïÆ–¾ÐUB˜Va†!îùý ‚«•îv'¹áþî½9Ää]Ð¹rò|xG÷c–s	0ªx*†Z`®¦“Ú]ú)ÂÈ^VE'…°€ái*@“ë¦,ã“8Îž´óyÒZ‚‹ŽðÙ{Œè8 JƒzVËìÅÉ3JÔ}1F§Xÿ‚·Êá{– 
¼‹.Ã\¯¦hƒþº©’´KÝ^wåN¡[{³¥ƒ§ÅUT.ûÁÁf|Ÿ‰Õ·ì½óåÑŒŽ}€‘Öº<+_0#àºs5`‰òQž^ûŒCŸAßáª¥ä‰cÙFw®0eçqRtXÞ^©-÷¡‹‡£z,:ù¨m‚?U	D Ã/¦µúE”ÝWm–ÏÚv”ìwm7˜8˜GWO"6)kÒ—½¸ÒÔ€0Û™ÍíŽÀ1QRzE‡å1økR·â-—\'Ü¤¼âÉ‡#ÃOÈ$pëº§µÍo_µ×á‚FµZ–Ì•Nç"kr‘Dù—ÝÝw!yr–µõÃýˆ´C@G{dß»‰]e"ç° ÈÜÅÀ±ð)é´IÈ˜aéZ‚ëºtÑ—W>ç˜"Šù§F¦MÎÃ¤Š6ÕåíM€Ó)Ý1Ý]¯xýÜ ÂjdÿŽIöP‹=%ÓçÊÚüS²^¾“çl¼ÃfYÆô–+´\YcC[®”f…,eÑŒØá¥³.9ö¢rm/îß04'äŠ–sh"5ÁBM«?§#ì5N*³“RE	ñ0ŽÓPèV¢w‰ž25³¾˜ì¾ª?4Xö¥f®lˆ,4]º¹²¶=Ãä{0x.ôK/Ïªš-«YÌØ¤‘9^YÌ./¾u·ûSòv$T¤lË“ò[kt¢C¹ÃQ7K"È±´	¨b–Úl=’³-ì-”¶ñ®[±ûm±ªÖl¡¸5ý!þ˜%Wº›ÉÉëø(ÄÖkÃšI‚Çb/°dQöž+Š·œËÖÑ¯^ðM,lš	†`m"ùu/{â-ò€¨YUt÷)XÊa˜%)!­Êô	^HMYë¥*‡T„ïsÇº½}ÚÂ=ÌYTMÜœeqSÌtÂvtáC¾Â‘M±@}4bK·‚=g_ÃÆ¬O1Víç|î†m£¬-Ø_Ü¡`ßûUê}®R‰Ça…ƒáŽÕ).zÐÜ3hTµf¶!%¼üc_ÌÜ†”•ã:f(Ü2ÑÒôÉ(ZËYÇr´<nÛYNYæZ¿vç6µá³?±
Þ–pŠ0‹TÓk!²
t©žHSsÜ(‚¿ûè‚‚°©š¾¼ž†°ökY¼7¶¿\ªPs¿,ª¥+ËiNõùÁ]øá~“Àš€\Tlz[±üBÉ+ÑWîòZž¯ÙwÁ þ*®7»ävÅ“Ä»?w¿½$>bq½Ú=•CŠÔçéÚÝk>·[á4ÇíêþOÀîñIx¶µqLÈz¿ÓŽø¤ì®óŽàË>,¸×° àôCˆ(oYÿúÌ“8t   0707010001f077000081a40000000000000000000000016a102a8a000000e8000000e600010003ffffffffffffffff0000003700000000root/usr/share/doc/opensvc/template.node.asset.conf.gz    ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:ßzB,Bvmq×ðvœ—Ñz
»£Ù™q]—DUSA,rV±œ\Ywe»«ž×°/ÕbôçÏ ®»ÝSàÒˆ¼l
b)E9Ìö8b»ödG]‘à#yû¿qèmãÍà—11¹ËïU”3@.$Á¾÷LôŠ	âyMš•”3cÿ“f+öB}Š¨ãÆrôaêsÒa­é@Sp Ë|G{à×÷&•6ó­Ü^›Ûô¶ž§hO»ìã~›=üMR}•="  0707010001f10b000081a40000000000000000000000016a102a8a0000103b000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.sync.radossnap.conf.gz    ‹     í]ßsÛ6~Ï_I¦“dN–e·½7É4wmç2×47—äî¡Ó1!’0&	– m«süí. ˆ²%«né‡Ä&‰°ß·Àb	,Ÿ=ÛæÏ“gl‹?(N/ËtÄÌ²¬æ™ÒºäÕ=Äm·uÛÕÝ“Ÿ±³ÏjYfâò—'ßP§_wÝ~ò{p&–ªÎNº†È‚Ï…†[[üA]éTU|š‹¶®x®Þ©Å¯¬E×ŠOuC7ªZK-U)ËùIT$3Þä¦k÷Oª¤²\ˆZ^¦®¢\ð{Ã‚g¾‚A—EmïëJ¤r&Sxf.J(›âc©*ÏEÈÏ¥6¤1¨Z§pýÓÂqÈjŒ•¼š-x™å"cÓ%3©‰pÐ?­š:cv±FèŠ§"`¯¹Ù*øÆêš)uFÓ\¥g “ÓJisÚjé!@óÐ\Á¬…f_ Ø¼üŠ €2P¸–•aF1q)ÒÆÆgFÔ€h‘	Ð8Ixj ù'IÛ“„ÙKcö}]« /ABÝ Xân"z7 ³	œÚðÚPÞÊ@Re€{7IÌÆÂ?e6]Hn×(CÝ&÷ÔÉØWUxnÇ2UµÃTÕFøÁl|šÕ†Û¶I§×;Û£+¿1–¥ÊÆWýÓ¡Iš½ž$acD¡iFÕbÀt˜:ÝÞU'ã.¸Â¬»þ»/ª¸PÝÔ¦Ê`Ý:€ºP­jïª±	¨M9Ävj Ú»‚ˆèj-†èÐ¦ˆNÅ'¸‡‡Bd6@sÝÉ-G‡<"›"8Ä†vc’÷­à³¬ChhkvyßØÃcø†ÈÐŽ,ò¡¡ —M¡C;óÎ‘¡›MâB;„ô¡|î ëÚªwŒÅèlŠéÚ¦wÅèl€éÚ¦÷Œ
ÅèÜŽ)6X”f¿»î‚á.ðû‡È+Í-@ÏM	¥hÁgS9HšÑïpÿ\Ââïy õwq¥‹ P©Y‰Tyò<gº©*UV	U¼?‡ŽÒ•À½^ð ã^~ 
j]2Ü)•ö5 k‰UWªÔt×D'`~à™ÔˆÊ£³ÙöÎµ o²‡`“8Øé“®™ì]W6ùM€ÏËSwŠì(À.$@<LÎK°óÁôT¡ðASeô¢1™º@«m«xg4>c´áù_jŒdAIRò$3öQP ûîûÞ~þñÓˆèx±Py¼qÐÖ	‚|Ça{ýýL¿„Z(hï-‘“„š½ºwcãžGí‡" p1“ i"«ijÐG“‚­@Wd'‰*Ø«Š›Åß¼´kaBI'g'‰ëôØ?	Òýý3hIÒö-]ðr.²QÔ½¸fQÚŠ[}ØWÖdbî2˜˜eÌv&Ê”W0+»Ã†Ù­²É$1P:Iðwkcv7mk€`hv%	’¸4ÈÝ üGLh4“Æ?NHEeÆvo+VÃsM»‹7FûyúÐc0àLT¹Zaœ`oh(•KÐ‰%Õ·Ž> eÈ*me´†×+õÊ˜mj9Ÿ'8Q)™G˜´Ó}E7ËòL#…ôB5y†ƒ¾Ü£è%4ž”)]Ã¡Ôíæv"×‘@U8¤òüÑ± í}f¸>×—:ö—àè<cÏKegŽç#&@›m›A«´iŠˆBm€ÊeÞ@qîöÚÚŒ.X®¨ÝÓÆ ©^
*ðW‚Ó0üÚ€¹˜ªr­ˆW¤ó>
­ÔbŽûþCÂÙ²²Ô¤öœŸCCmAÐ¶¥+çžŠ?W ð]1û·¯Û s;üv’x,ìŒïb0þ'VoŒ*¸‘)ù¢~XE¯ÇupÌ>k1kräÁµRry“lSTl&s¡—ÚˆBkÄ%/À?&Siõõz¬ºÁÎªùé4?“êôBÈùâö"uS	L”™î§µ FVsœÒ~Æ6ÿr­Iá¢ÐÈ¢þ†ëQöît1žœÃXKÚ³ÿð¼N…¹¢dGÂðh2™ŒÙ?E]Š<àKÛYºŒXAˆñõädÓª9ýµQ†°Þ
ëÊ!&X¦¸’„e©T¸ò…Æ ð5­gÁæUŠ^¶˜ SèÖ³y­`„yó‹xìÚ~¸µFrš¦Zå9G: $-ZŸ,ÈÂ#Q…0¢³ºb„Ãõ²l.YJ%`pH’ƒ#ppp1ŸËBšx€¤ã¬RZ;WÚc’;ÙSp@…Lk¥Ð?ƒ[*”å"xº@ÝI‹®$ùzòÅ·p¯mü‚0€º„vÆî©-q¼ú¼¹Pöyo -©‰ýTÇÐµõ`ÛÚì åë#)r”¹8P¥ËG“ã¯˜Õÿ˜½+×#Us0UÎ¶Ò4hk%ØH©(¼²X‡:À#¥»à]Êh™Lu _@*Î‰þ|á')„y³–¸¾µ*¡0…'GÈçëJ(hFíÛÐ–BßÎ¾w½ÇÞ·¨ùv?S‰qeŽ/üˆŠX­þÿ¢_²¡}„k:¨ŠQéÃ!ì„MFG£c¼098î E0ñÂÍ`¢8¥±t@tÓ‘DËßâ5Ç÷dæºwX ª^²²Œâ°@~iýCc>ÌÞÛçÎ`}æ;ÇŠ]åÚøDÊ5ŽáŒŠkªôVÞ¢m|B?kØ¡TqŠZåG¶1Û¼e³œÏÙ‹	šëÑK$„Qj¼ä’–G€>N úÈ¸zâÎI‚¹hæäd ýåÈ®«¬|n`5f__áÚ®),QŽˆDÏöý„Ÿ7ð÷0üKÉ|¼ìÃ‡÷ ÛHGÞï=eaâôƒb~®õ	PÙÒõÌPß”ø–éfj•ß`/|ØV˜¡[ÁŽB–¬®­á¸¾àU%K0˜âÛ 8x9ë´ê\*Zë¯®BÔ‚¯ÙWkF¦«Ý9!Îä(Š¾×z\§AaS§q[ë”œ^ÉïÂ+ùÚã®]’ÔÕìVë¦0ûœz]SóVß†þLâG‘óÐÅÁ…ÚgÈ¬²—Ì*.Ò~ãæ—MòªéT6J/ìzœ—r¨ì3‡J{’§<dâ”žà­;3dKÙG¶”~àõ:3¤HÙWŠ”þ ö83äEÙg^”þPö:ý2$CÙ_2” \{æeÈ€²¿(ý‘ìsÒeH{²¿´'½ì}¾eÈu²Ÿ\'kQìŸédHpò°	Nú@×'\3d5ÙcV“Þ ö‹Ö©L0•I?ìÖk†ü%{È_Ò»~±š!iÉž’–ôÆ°O¨fÈT²ÇL%½‘ì©Ò“ì-=É&H®Ô9Iö–“¤7½â4C"’½%"éäašÇ
¡ö{<0O‡édÕ™¬âÕ ÒAƒD‰Í(^™è1û/¯­2þÕ–’E•KL¼ sgÝ¤tˆÊæ1Á"t¼3ÞyŒÇEq×ç€TX-u·ÕŒîøÓ1‡”¼Ž:§ÏÇç+ïfè¾*vÐ~KÌm¹œ!2i~Â†§© K®›²Œ·Usö¼íÏóv$xÛ‰¡íÚ—nÔ£½Ñ™¶WµÌÞ¼xE§®ßŒÆãñKLfònfSuØ¡ªèÁãM5ÒcM•$ 8ÕXéÄe¿%ª©ñ‰?tLž–·¤Äqgóm&»<%Ý6íO>×…O›°DÒ8õGÚÑåUù†‰_¾sSŒDyÌ¨)OÏüñQŸ¡ãU+ÉÇÝ&Ñ;‰O¸‡¹:pVjs·àÐ&£ä:´µµÙ(åCD2|bV«ßDÙ=Õ&ÕñGð$û\[žO/[nÒXŸÃÄå@®³×.YÈÃðNE'bò×d6<.Å[-¹J¸Ûûf2Š;¶¡!Û©{VÛdtÖB–öØE”xÇŠ9ÕéBdM.’è0mfÇž‚0ÿÅTà=¶zR#íÐÉÛû®c§™Èù2q-Ó+éÈî´'Êñ¸¬+	S×Gáì’,8ž÷ÅÃÄF¦MÎÃ2íidws
r˜˜Í0mW@v½*täíóŠ, ÿØÉþ'dìevLCŸËQ„âiôò•¼f“5c–UÌ0r…#WÖX$º¾#æ°B–²h
Fj‹øÒ.ž9tP›i^\;Ð¼ ©èbÅC¦&˜uËÚÏË1ÖŸž°¥Š²@;^† [DoƒÞÛÈvµã'ö¶ë´º½±3Dþðb5]Š]ÔÐòß¸ÍÖâj€!CˆÈ9lt}HÇ3•Bå¥>OÛÑ*i*ùK(ÜðËˆ93 &ƒA´Ûç^³ŸŸN&'“ÉÁäþýö¯G8>±§“cºú¥»jðãšòé/7òÛ—£v•rì¾‹(ŸOÊå¢™;¯¡sK;çg˜a¦˜˜Nô=žWRçŒlîœó9›6¤}|û:=gº=
Ó¶„2ÚDùµ‰xìÙ!ÌDiÓOR½]/I ÇüE ¢ÎW=|[ÂòÚ^“Øjx%¼Ij*›Òº!ÿFx¶¤üÎñÞ©MÊkiQÐÒ,†=ÌjÅ"öW<šÈw®8˜!î®ˆ¥M3E÷&MûZ„ü==i„ÿ Ô,+L…þìA˜
Ï}¹c?Í(5E©ÊG*â÷‰SA¡ŒÀGÝÃƒÉª)],ô§1÷Õ3b9Êêš/±e3ÌB9³Ù8aßÁÚÖ¬v16íû|âš6íJîªàÝúk€yˆ^ì2zA:~‘‹˜·D-p‰8fÇ¤QU/ÎÜÄ”0Ão_Î\Ç”+…UÎT¸24DÿIÏH‡DÑZÎËØ—£°I[ÎjÊ*×Îk·¾¾ Ê|¹³Á"wk‘­¢ÅX~•·Y§Û4Ph÷5?…”XGŸ‚_>\Hp»t8Ê¾\<DXð´e³²Äú\J
ÒÛ[´z²!{b•Èñ©Â¦”Ü"=¸ÍÄEÖãl«¹ÃiádÈ¥äÿ°¼÷©£qzÆB—ì× YKÂxUå°`r…í{ˆ–~9ãóZüàƒËŠá‚J6CnøòµÉë¨V ¥¦%,}°=ÎL<â9–tm¯aò6’Òúpåv?Ê‘¢Í8ÓbÝHçvì4Ú=œª‘V©ÑƒJö¿I»g÷‘Bb¬ã‘Ý±8ðh÷<²š~4<Z!Æ-<2|þ'æÍuéé0^ñÉSUÈ×SijŽ;‘àoÿš‚Ð"ÙÄnþcÔ„È½îÂúÒ,Â H]¢“+ËYN_ó° ×ò<J­:ø97¥¦¿îÓçJføÝŠ¥ûTíÿX!ºûI­Š 6»ÍÈm»LÿiŒ×öeü+_«]¡ÐêÔgõ³‹|?rh÷ZRofåcÞbýx÷ç†›§¯ìÃ°î·6ÞŠ}Û†ÚàÉa.Ûé\hú1Ìe×ƒFÿ*r.à›   0707010001f0fc000081a40000000000000000000000016a102a8900000db3000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.share.nfs.conf.gz ‹     í\ßo7~Ï_A (œàdE6Ú5	.wmpÁ5Éá’Þ=…—Ú¥$Â»ävÉµ¬âþø›’»¤üCR-Çu»~0lírHÎ÷ÍpHÎèéÓCþ<yÊøƒâÌ’7bÄìºLÍÍ]Åvt‡ÕÝ“Ÿh²O©
qùó“oiÒ¯pÚOžààÏÅz¥›bÚA×ÖÀƒãþ ’L®k>+E×Óç¦ø ¿´²Å•u£/¤‘ZIµ ‡oyièI!æ¼-m?èZÑ©–¢‘–«Ü÷S
>g¯ÙRð"B0˜¯hÜsS‹\Îeï,„‚¶9iz09¼ñy)Ø‡·Ÿc˜¸¬ucQA0(3bÜ0»k¶ÒmÁf‚aaì…°ù÷®¾"†ÔÜQ0«Ù']òF/8×UÅU1†¾¿Eõ@s­o¨æv9 Ô4oËuÂôñ`6·ºY£¢IÁ¤XzåVÅÎJŸÃ\ÎjmìY7»Õu§Òß²ß¢BhÜÈÚ¢ŠÅ¥È[+Ÿ[Ñ0ºmrQšóífšu3É˜ûhÌ¾oÝ0HhZRüCÄîdöÓXÞØÊß
e ©2‹Àým0’˜½!„_ª˜­$k”±n³» êeì‡«®<c™º>„aêzüZ5¬•÷d–‘j«UF"vµCô³/¢31×Êßsø#³šCðs7$ýDöEpˆ}îÇ$ïülà³¬Cès0»¼kìãñØ¾!ò¹?£¼cè“¢s;¦8`¡¾ÈÙáqï¿ˆ²6¬5ôÜ*hE^Í¦KNÙàù…ÄgH3b«¥Ì— Ò0…¯´:ÎyY2ÓÖtŽZ]ƒ¼%¿€‰Òµh8PàâA~$ŠÎZñ¸TiãkAÖ»®µ2t7D'`~à…4ˆÊ£³ÙîÉµ ïŒ60`“(ÛLûa²ÿõäZ]ˆ&êz¦|®6LÝ+²§ [I€x&˜\(°óÁT¡5²­‰2fÙÚB¯Ðj».ÞYƒïØÖ mx¹âk‚‘, (ËÔžecÆ>¹söï¾ûæÇ>ˆŽ«¥.c²„>APã8ox^haˆPK­’³ú+{s½ñ¾ÇŒñ$ÚWdb .4Mdµmúhs°hâªbšéŠ½ÄÓé×axh^7ÂÆ’¦ç«iæ'=oÂ¤LoÑü,Z’tsË—\-D1J¦—ö,”ë¸Ó‡ûà*À†LÌ&æsƒ	•óúfeûºÖC²œd–Yheø·³1à¿ì9€–¡4´CÉV\Zän~ŽÍ¤¯RI¢t$/»À2Jð!’/­Â0c0àBÔ¥^a¼à`h(•KÐ‰#õ·> eÉ*Ä[ƒÃJ½â³m#à'ê€“’9p„I·Ü÷PôË±Tç)d–º-éÔ;÷$D‡Á“C™ÑgèJ…»éô"·‘À]·òòÑ± ícf¹9‡ÐÈ¬UnÒx	Îv¤´[9ŽFL€<6?4ƒ6	hÓ‘2„Æ Ë²…1âÚ´C°RÓ¸g­R¼´sà¯ìÀÀüÒ‚¹XªJ£‰W¤Fë>
­4bÃ‹ýŒum¥2¤vÄ/` ®!èÇÒ·óoGÍ4h bFß`Ìþúö ÀÚM³€…[ñµHJªÞZ]q+sŠEƒ[Å¨ÇOpÌ~4bÞ–Èƒk¥”òÙ¶ªÙ\–Â¬•!ÖˆK^A|’œ¾^€U7¥,Îfå¹Ôg+!Ë?Øiá¦˜P…éç FÕ\Ò~Â1ÿ|­Iá¦hbQÃý({÷¦XO.À×’vÇì?¼la€3aWB(v2!O&“É˜ýS4J”_ºÉÒsÊfHÑb|3¹Ù¼nÏ~iµå¬·Âº‘ZÒw’°-•w¾0 ¾¡ý,Ø¼Î1ÊöŽ	&…a=[4ÖAX7¿J÷Á~\‡;k¤ ift‰Ç"HJX]L–LÒˆ^	+š1{Û;Šºë¤j/YN-À9dÙñ	„Š„¸™/e%mº@ÒqVk#‰ãq)œl« ‚Jæ6è_À#Ë

<_¢î¤†MW–}3ùê¯ð¬ü‚ð, u‰™TIxêZœn¾oWÚ½ #5±ŸúØb”d8„ksN*ÖFÒÉQáÏºSøødrúµËÆ‚ï;Åx¼©Ûã™nqµ•¶E[S`#Ê´þ ðÊfú€ˆ”žBt)“m²K©¾€T\Ý¢èv3°Â€‚YKÜß:•Ð1E GÌç•%4£	ãHèZ!ONogßÀ»}ïÔ|{ðƒ€ÃLâ¹2Ž/‚GE¬V‡ß—Ålè^ál‹§Ò‡°)›ŒNF§øÁäø´‡ÁÄn³ÕùÒÑ}=‰‘¿¦{ŽïÉÌÍ.vÞc	 `~ç3©’sØ ?w†þ±µÇçÇïÝ{ç°¿ó]à	ŠÛåºó‰œôñ¦é‚*ƒ•wh;#ŸÐÏvh]á‰G£Ë#‡XmÞ°yÉìÙÍõä9"œQb’u8§t<ôqHÐ@ÆÝ÷A¬Es/§ éÏGn_åäs»1w}…{»¶rlŒDy"=»û‰°nàßññ/¤çe?¾¹Azò~|(g4k-¨O€ÊÖ~`&€jzSFfÚ™ÛT~‹³Ç¶ÒÂ
Ø
v³<b-pýdÇÍŠ×µT`0ÅAq<àå¬×ª©h¯¿¹ˆQ‹Z¼b_oñLV÷„x“£Sôø^ë}ô99…}ƒÄmkPr1D%¿‹¨ä/h÷š¤¾g¿[o³/hÖoóþ0Žg²àE.â7j[bœ¡<æAÊcüIûÉ/ûÇ51_¶&f'ìvH

a²fwwJª_¾dõËŽàmK J^®äe'wNûê\¦Îe+Š»W¹Å-_¶¸eèv‰b†Š–¬hÙÄÝ‚˜¡Œå–±ì†ÝÖf¨]y°Ú•]Ü#„y¬a¿ÇkÊ“£dœsY§–
€ôÐ QR3J­ÖŒÙyã”ñ¯®•¬êRb®¾0¶isÊ»q¥/Ø„2ÓË*Ì0Ä3¿#VPc½6ýé$·Üó§g)yuÎ<žïjà~\Àj)F5ÏÅ±˜k…é¤î”~Ž4*¤
øQ	Gžç,¹i•Joâ8;êæsÔy‚7½ºá»¬ñl—®’4¨—,^?{I‰º¯Gãñø9Ö¿¼›»êw— +i1§^oë‘±¸^·u–àHVë¤»ïGò·ˆÐ­Èm¨1)y:RÜREåÓ¹m&÷™X}ÝÍÞçP2íé«©¼ú€#wy©^3++Ðº_jÀ•)£f<?‡!ƒ¾çU')Ç²þ^aÎ¦iRt\Þ«RWîƒ®M&õXtóQºªHH†oÌý«Pý[]VÈÚö’Ü{]7˜8'¼:nRÖd({ñ¥hq¶3[rãë@†>5]–§äoÈlxÚŠwZòp‘òz2J'_!—ÀÝÒ=o\~;]ÏKånê“Z-'æÌäKQ´¥È’üËÂù^F‚°db&ðHÛ¼ÜOD{ô²Çî¹ŸØY!J 72³‘O!L§KBÆKß–®O>úïóÊ—SD1ÿÔÊ¼-yœTÑ%°ú¼½Èab>ÇJÏˆìfSè(ØçY þ©#’û“*Ü¥;%×çËÚPü)y¯ÐÉ+6Ùâ³œbÏ{®¢u!Hâº¾#æ°J*Yµ#µ%|é½K`åö2Ã«kÍ3ZŠVKh35ÃBMg?ÏÇØkšT:aÏ”NâaÏcÐ¢·AO™šÅPLv_Õˆw–C©™/"]60¹µóí&ßƒë $Ð¡
x^©ª¹²š‹›µhÒ>½ù€‹Û¹énÉ»‘P‘²+O*¯­ÑqiX¤î*Ó©ß~–$ciHÅ,µÅf$çZ[}ã-5oÃ±Ø>Uk®PÜ¹þ˜#Ì’S”ÚíW…¸zmØ3‰ŠBðöøœXÄþÊÊ•Ä[~É†@"ÆÝ7q´igf€st‰@´®ìI#´É¡v]cEÆ-Çq–<¦„`D´VùphNYëJ«cO*â÷Ô« ÒVà+žîqÎ¢n•¯Ã¸	Ëâæ˜é„íèÊ5ŽlŽêã1[ùì”}{»9ÅÔL˜óÔ/Ú•²¶è|q‹5€v©÷¹K%?†jJ†[v§¸HsÏ¤ÑõNœ¹‰)ñ—ìÊ™ë˜reç¸É™ˆ
·Q†\ôŸ4}2&Š1r¡ÒXŽ¶Ç];§)§\·®ÝzLmùâOl‚×%œ"ÍÓVˆª[jfÒ6Šàÿ]´þÛã]ªf(¯§!$…˜ýj¼’vãÒ pÖ•èåJ5/©>ßT#/’b‰™À/hqI±éuÅòZX‰¾ö_>CÛóŸá¿` ÑUÔ›;ò§âYŠÝ_¹­Eú­5¡Ww¦’aH‘‡<]wZƒaÍwNs¼ÙÜóØã½>‰ï¶®\“DXïvÛ‘Þ”Ývß½9„÷Dš~åuÄ ¯ñ´2=ºÈf   0707010001f0bf000081a40000000000000000000000016a102a8900001015000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.container.ldom.conf.gz    ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²yÁåú‚®i—´ýP-Q6±’¨ˆ”½.îÇßÌ”Hïzííz“n£E$z‘ó<3’3ÜÇùóè1;âŠKUe¸¬D3bfSVdª¼ƒ¸ã¶î¸º{ôk×ÙÇ¬2qùÛ£gÔéÔíG°b³VMvÞ7dÑ
m”†{ã#þ ²tªj>/D÷±÷M+ðF#>´²}+¾ç…¦;u£VRKUÉjqÝÉDÎÛÂôÿQUtCVKÑHÃ«Ô}¨<g/ÙRðÌ·B0è³hì}]‹Tæ2…g´%S|,åU&3n„vŸh+yÉþÇÖ JµÖ¤<h„Náöû%¬EÃ4“é6¢„v0×W²1-/XÉÓ%`1Ÿ9(tK¥MÅKñy#©YjV	‘1£Ø\0-“ùuZîtÇàLæ¹hDeƒÍ°{|Ñ¿€6Ý›/X®Ô.l$.HÓÎ'ø¦w=,ý×®¤‡áJ³=s \×è] ¨YÝQÖfÆçª1 îÎ}ù0l-ø°î; Ø•h‚Ì•‚OUž¯s²4°¹$1  IFìm-ªw?Þ°(˜S:´JKP„Í
<¡ªà®Y2˜2Ue	mƒ~‚«äS9ãÝM¼Aÿ#†öNH´áÍB„V,Ëº%‰>ÄRÀhG[×ð*ÈVpmX©2Áž
}y2é»J’U’$ S+B˜;¿XŸ'Û¤JP«è}xj$ô¼ô4ê¤hÆ Õ6©`s®¥¶|ÞÜ¶8ì`³Ò«tÖ(ef57ËaèÙˆÃ/Ð ÎH%^ ÇJ—xz·ü †}@Š,Þ€K1àZÊˆ‚ØÛ‘eÂ¾ƒ®ÙÁ…`6vg
x=o‹ÂaŽÙàm×´®]äì,-,#b„_ªÚÀÿ¢‚»h”ÊˆY³þ”èðýœý›i6x×iž°³ðþJ–±+i ã5tN _ ìÁ‡!Ø=nû}õ¢Âaž’
`,ÂyÒøkö”€Ð—iÑj¹#Ð×BjÓp°qU›ÂíïèýÁã4ýp(„ÿ4t¹0zø*]‡ H_hPO‘±ùÆ]~Wt)­_	DjÆqulµÑ1ù±B¥Ÿ–Z&ŽÛzâ=¨¿ÀÖ\ãó¨ºfdÉÙAÿ?¯ØáxœïÑæ†Š‚N3¡ë¸¹‚ŽV0ëIÅÎ1·· ?ÚÒâqw×hÛ‹Økn¨‡tW¨öá…Œg_MinÛÖ”µ‘-2§_¸„ÐP{îo¿ˆàºÄ´àû¸A#¸r.‹¶±~¤y€	û²$U?XŒNÏ2F½â"ìÊˆÈÝ€ÔY1Ð¬SÏ¸:D^u³@E§¬ÉŠK‘¶àhyŽA("â½YàóÎÓ±Eç<éz’8À`thÕ`šÅ.…¿‰î@ææŽø%2ÕÊ?
e ©2ÙÐn#‰¹5„ðG•Í7’Ç5ÊP·É] u2n‡«ª<c™ª>†aªú6øµÕ0VÞ“Yªý£Vˆ8ÔFÑÏmu¡é=‡?!2·@s~î†ä‘£ÈmbŸû1É»?[øÜ
Ö!ô9š]Þ5öqxÜ¾!ò¹?£¼cè£s3¦Ø`›]ñÀ6+î¿‰¢Ö¬ÕôÜVðy5˜*îtÓâ»[•Ç{H=bë¥LÃíDÌwÁÇ€ª§¼(ºÍíZ¨ä-ùŠVò”K@vUÜÉDÁW7´ˆKí­nAÖ?]«JKÐ1.	œ€ù.À3©•¿XzÅGß•zåÙSÀîFÍ“‹
ìœ¶8¢œ¶&Êèek2µ®&À¯ÆgL«‘6¼Xón!!Y@P’T_ò$™¸ôœŠ}ûÝ÷¯~úáýˆè¸^ª"$‹ÿ&%ëØ6NÂöúû™´wÄ–
Ú{CxP³·Çç{ô³|´wE:Êð(E&AÓDVÓ6 6[w@\™'ªdÏq+í¥o^Bûq¸J¢%×é‰:Õo³é -IÚ¾¥K^-D6ŠºYTöÃ>ì…« ÛÍ)wyßÎ”¨R^ILG1¶wñþ/þÝÚX¼ÕêFh·’öFÄ%e$à§è1¡ÑLÿ8!½C”Äjx,¨”ÂÍÑMŸ,u¡6"ó‚½¡u»4–@ô½}ô)-CV9è(Þ®WêŸm¹Xà&Qœ”L#>²‡¢Žeu¡‘Bz©ZÜôÌ9÷(DÇío—”×Ð•‚+F
8‘{³Èjt©¼xp,@;¥LÃõ„FzS¥:Ž—àè<cO*eGŽ'#&0i+?6ƒ¶	hÓ3ÄîŸºÝSc·×~ÐfÁ
Eíž·¦KYÄ™ eàCæ.`¨*´Í;»"5÷Qh¥L‘	gß¥t4Çžð4Ô¾zÃ¶ôï¹§ƒ×Ÿ`bÄŒî…	û¯ÿ¶ÆvøæKÚ†ÛQ‹’£B5ðÖ¨’™R,êÝ*F=®ƒö“y[ ®•RÈdÛ²f¹,„M¼×ÄqÉ19ÔæÙy}½8«v¥å-fóâBªÙZÈÅò/¶ZxSLT™î§ FÍêi¿b›»Ö¤pR
4²¨RÓë·ÐÅx²_KÚ°Ÿ1%ó¼¢YQ±Ó)ax:N'ìß¢©Dð¥ë,Ý§d¼- Æ×ÓMëvö¡U†°ÞëVILHSÊƒÃüœùBc ø†æ³`ó*¥3ë˜ SÖ³E£`„qó‹xìÚq¸µF
šæZ¸,b3tA’]L	¼.ãwÂ¾ïÅÝõ²j/YJo€sH’ñ)„Š%„8™/d)M< 4uV+-‰[í±/R8Ù–s@¥L¥Ð?ÛJí"xºDÝI“®$ùzúÅ?à^×ø7åËCÐº„vÆá©}ãlûy³Vöyo ©‰ýô=& —àµõ`ÇpmÖIùÆ:ÂHZ9ÊÜ:P·J—O§g_1«ÿ	{× 4ã¹jq´•¦¥\/°‘J·n¡ðÊd¾)Ý…èRFÓdúò¤â˜è3PßÛ•B¬±rf-q~kUBËž!Ÿ×6”PÐŒÆ·#" }xzv3ûÞì{_¡æ;Ø½Äš9‰ëÊ¸8B¼ð±Z}nå÷p¶´ÁUé—CØ9›ŽNGgxa:>ë!E0ñÂn0KQÎÈ—ˆÞÖ“hù{<çøŽÌ\bç=– €j6ì©¬¢u˜ ŸXCÛšñÛ|üÆ>wó[0ß® ØY®]ŸH±BåáFÍµUz+ïÐ¶F>¥Ÿ=ìPªœáŠG£Š#Çm^±¼àötŠæzz‚„ðk”/¹IË#@€ý°tVá*‡’(ŸßÊÉ@úÉÈÎ«¬|nÕóEs»¶´lD9"=»ý	?nàßÃå_jHæ×ËÞ¾}²tä}ûÆSNß8xÍµÒ•b¸>€™ ªñN‰o™nçvRù{á—m¥1[ás²<`-fJïá¸^óº–Ì@ñcPx9ëµêB*šëoÏBÔ‚7^°¯öx¦«ûBœÉÙ:¡Àß×mÒ-ƒÄmoP²¢’?ETò7´ÇûMHRÿe7[Çúß¹ ½n¨yÛû‡a<“x/²
Cœ¨í‰q†ò˜ORãVÚw&¿Ü¦8f¨‰ù¸51aw@RèPó)aGñ TÐ¡úåcV¿Þ¾Ð¡äåÓ•¼áÁiŸCË§©sÙ‹âáU.CqËÇ-n9ºC¢˜¡¢åV´âaAÌPÆòËXÃno3Ô®|²Ú•C¼EóP!ôÂþŒ9Ö”'GÉ8²Ž- é¡A¢Äf[­ž°_xc•ñŸî-<óQb®¾Ð¦iSÊ»qgïâ™X˜oV•t*ï„ý°‚^VÝ¯NrÃý1msHÉû¨3sx>¼­ûqë¥Õ<c-0×
ÓIí*}Ž4Ê¤
øQ
Kž¦,¹i«*Þ‰ãìI×Ÿ''xÕ‹¡¾Ë×vi; JƒzÞÈìåÓç”¨ûr4™LN°þ Ã+v/A•xlY†¹^m=ÒÇë¶NÙZéö04·‹Ÿµ‡ Yi1y:RÜPEåÒ¹mîõ,«ëvöÞwgi†gõ9õG:ïò¼zÉð¨*í‡ðDEÌ¨9O/|Æ¡Ï ïyÕIòÂ±l£ßWÈÙyœ–wà¨Ô•ûÐÙ¶Q=í|Ô£.ÁŸª"’áy£~UÿTW‡å³¶$û\÷L,¢S
‘›”5éË^\é Z@˜íÌ–öð?Ð˜¨(½¦Íò˜ü™ßâ–ÜG¸ÛHy9Å[†KàvèÎ›ßNÛó²²;õQ­–3ÓéRdm!’(ÿ2³~€ >o–´íÍýH´c@/{bï»ŽÍ2Qp˜ Ø–é­Œx
A ;]2fXº7aèzç¢ÿ>¯|É1EóOLÛ‚‡I]«ËÛ›ƒ&òœŽ1îÉ®·…Ž¼}^‘àŸY"ÙcR…ÝÔbgäú\YŠ?#ïå?ò‚M÷ø,«˜Ásí=…ï[b+e%Ë¶d¤¶ˆ/½wñÌ±gak{¾üGó”†¢õ^™š`¡¦µŸ“	~5N*²§•Šâ¡'!èÑ› §LÍl(&»¯jÄ»Ë¾ÔÌ•‘‡.ð0âõí&ßƒëÀSÑ¡¿óx^©ªÙ²šÕ‚Í[Yàé¶ìÝ«Ýµ~—¼k	)Ûò¤âÚ›V€Eê¶2¾Û÷’r,m©˜¥¶ØŽäìö4Jûòþ]‡e±ƒ«Öl¡¸uý!ÿF˜%W¹C¬iÔñQˆ­×†9“()a°bé—¢l\Q¼ÕŸ­âî^±´içf€s´‰@4®{ìI#4É¡fScEÆ-ã0KSB0"ÚTé<»˜²Ö+U©ˆßçNtÐwÞÑ=ÌYTm²Œeq9f:á{tàC±Á–åX >™°µ›Áž³oac¶»ƒö}>wÍ›v¥¬-X_Ü¾ñ0K½ÏY*éø!ÌPc2Ü0;¥sÒÜ/iT}gv1%<üãPÎ\Ç”+3ÇmÎT¸‰2ä¢?ÓôÉ(ZËEÇr4=îÞ³š²ÊµãÚËÔ†/>c¼.ái™¦·BTØR3—¦á¸PÿöÑ1€°©š¾¼žš°õ<ühlQß*æ~Q'WVyAõùÖ ¹ŠŠ%æhqQ±éuÅò+%3¬Dß¸Ãghz¾å3Üþ—fÙ¯ÙU ·*ž$¾Øý…ûEñ‹ûª]SI0¤H}ž®]­Á°æK»N}ÜmîyìánŸ„{[W¶I¬ÛíˆwÊnÚïžÂ‚{M?„ˆò:b×ø?}8&ßv     0707010001f068000081a40000000000000000000000016a102a8a0000020e000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.array.dorado.conf.gz ‹     í˜±nÛ0†w=Å^]ÉîÖ¤ÊX K¦lE ŸÉSLD"’Rì·IY¶,ªÖAp\DèŽ?ï¾" .sŽb3Ž$‡Îáa	á`	¤q(Í_ÉÍ[Ý¼ìŠ?¹Ù…SZÒþ±¸ÍM×Ç¶‹"ÕÿL‡WãäÍ¹´*¾ÿ6ãH˜¼0·-6ú…­§qôÒ+Gç\Ÿ’ìÛp.íÞhš´bÕäÆ ·$T£ÜÁirJäÞ¢‚1ãaG£íq/RƒÐ»¶Œ9·i^Ã.ëoª*'•Q\ûA”ÂtUŒWÃº\1,†Å°ÖW€¥±£ëÒ:EfÅ•úÓ@˜Ð•ð»mXg%I.¡Á¶Ý¢x†`rž'”Ñ“iø¾iÔ>ÓÎª5àêóü?<-zŸ^}Á/zj-áê=¥Gkž@é%ÐÞÆ/Ý“ô€ðÓbØÝÅ²r¤ENÅK¢$…2«ŽsèúxPl)ªe+6ðºÍ&»á-FÔrÌÛápiÐ¸`*/.‰¶d»N×0ÊUq·ñÐY³l È²l È²l ÈÎd`P™>|Ö_ëõ÷Õ‡ýFä.–ËÞaúU~ëcU.#]ûd„Çy$˜vffÌìŠÌâ©ã®ïõO×©µw‡k¦sŠÖàŒ	Ì‡ù0æÃ|˜óa>Ì‡ù0æ3Ÿ7?×šÄP)    0707010001f0bc000081a40000000000000000000000016a102a8900001015000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.container.hpvm.conf.gz    ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²yÁåú‚®i—´ýP-Q6±’¨ˆ”½.îÇßÌ”Hïzííz“n£E$z‘ó<3’3ÜÇùóè1;âŠKUe¸¬D3bfS¶¬WåÄ·uÇÕÝ£_»Î>nd•‰Ëß=£N¿ n?z„=¸›µj²ó¾!‹Vh£4Üñ•¥SUóy!º½oZ7ñ¡•è[ñ=/4Ý©µ’ZªJV‹óèN&rÞ¦oøª¢²ZŠF^¥îC…à9{É–‚g¾‚AŸEcïëZ¤2—)<³ -™âc)¯2™q#´ûD[ÉKö?¶Uªµ&åA#t
·ß/A`-n ™Lo´%´ƒ¸¾’iyÁJž.‹	¼øÌ©€@¡»€X*m*^ŠÏ‰HÍR³JˆŒÅæ‚ia˜Ì¯Ór§;od2ÏE#*lÞ¨’^ô/àÃ„M÷æ–+µ›‰Ò´ó	¾ÇÇ‡é]Kÿµkéa¸Ò¬GÏ×5z(jV7B”µ™ñ¹jÌƒ¨»s_>[>¬û(v%š s¥àSU„çëœ,l.IH’{[‹êÝÏß€7,
æ”­Ò¡E³O¨*¸k–ŒfçŸLUYBÛ Ÿà*yÆTÎxwoÐÿˆá„½SÀmx³¡Ë².D	D¢Ï±p ÚÑÖ5¼
Ä²\VªL°§B_žLúî‚’$G•$	èÔŠfÄÎ/ÖçÉ6©Ô*zž	ýo=:)š1hAµM*Øœk©-Ÿ··-;Ø¬ô*5J™YÍÍrz6¢Ãðäô#€3R‰À±’À%ž^À-?‡a"‹7àR¸–²¢ övd™°ï kvpá˜ƒÝ™^ÏÛ¢pØ„c6Æ xÛ5­k9;KËˆaàÃ—ª6ð¿¨àÆ.Z ¥2bÖ¬?%:|?gÿfšÞuš'lÁ¬¼¿’€eìJèÂxè {ðavÛ>ÃG_g½è€ðÇE˜§¤‹pž4þš=% tÅeZ´Z®ÄôµÚ4l\UÅæ„pû;zðøM?
á?ÝD®Œ¾J×!(ÒÔSdl¾±C—DÀ]JëW‘šñF\[mtL~¬PéÃç¥–‰ã¶žxê/°5×ø| ª€®YrGvÐ?ÇÁ+v¸§Æ{´¹À†¡¢ ÓLAgèz n® £ÁzA±sÌí-À¶´xÜÝ5Úö"öšêaÝª}x!ãÙWÓcšÛ¶5emCd‹Ìé.!tÔžûÛ/"¸€.1-xãGÀ>nGÐ®œË¢ml„i`Â¾ìIÕ£Ó³‡ŒQ¯x€»²"r7 uVC4ëÔ3®‘WÝ¬PÑi#krƒâR¤-8ZžcŠˆxoø¼ótlÑ9Oºž$0šF5ƒ„¦E±Káo"†;¹y…#~‰Lu€òBHªL¶´ÛÃHbn!üQeóÍ€äq2Ômr@ŒÛáªêÏãX¦ªa˜ª¾~m5Œ•÷d–jÿ¨U"µCôs[D]hzÏáOˆÌ-Ð‚Ÿ»!yäèÇ#r[‡Øç~Lò®ÁÏ>·‚u}Žf—w}·€oˆ|îÏ(ïúÄèÜŒ)6ØfW<°ÍŠûÀï_¢¨5kµ =·¼E^Í¦
;Ý´øîVåñžRØz)Óp;ó]ð1`€ªÆ)/Šns»ªyK¾¢•<å„]wòQðÕ-âãR{«[µÁO×ªÒtŒ‹…D'`¾ðLjDå/–^ñÑw¥^yEö°»QsÁä¢;§-Žh§­‰2zÙšL­«I ðk£ñÓj¤/Ö|ƒ[HH”$Õ—<I&.=§bß~÷ý«Ÿ~x?":®—ªÉâ¿IÉ:¶“°½þ~¦í±¥‚öÞ$ÔìíñÆù=Á,í]‘Ž2<J‘IÐ4‘Õ´è£MÁVàWfç‰*ÙsÜJ{é›—Ð~n'…’hcÉuzâŸ„NõÛl:èŸAK’¶oé’W‘¢îÅ_•ýp§{á*ÀvsÊ]Þ·3%ª”×CÓQŒí]¼ÿ‹·6oµú„Ú­¤½qIIø)zLh4“Æ?NHEï¥±žË*eÀ‡pstÓ'‹ E]¨È¼`ohÝ.%}o}J@ËU:Š·‡ë•zÅg›F.¸‰FÔ'%SàˆÏƒì¡è‡cY]h¤^ª7ýsÎ=
ÑqûÛ%%Á5t¥àŠ‘NäÞ,²]*/ÐNiÓp}¡‘ÞT©Žã%¸:ÏØ“JÙ‘ãÉˆ	LÚÊÍ m‚ Ú4DÄ±û§n÷TãØíµ´C°BQ»ç­éRqæÀ_™øÐ‚¹ª
móÎ®HÆ}ZiÄS$BÂÙw)Ä±'|µ/‚Þ°-ý{îéàõ'˜Ø1£{aÂþë¿í@€±þ†ù’¶ávÄGÔ¢ä¨P¼5ªäF¦‹z·ŠQëà„ý¤EÞÈƒk¥òÙ¶¬Y.aï5±F\rLµyv^_/ŽÀª]iy‹Ù¼¸j¶r±ü‹­ÞÂT` U¦{Ái#€Q³zCÚ¯Øæß®5)œ”‚,êŸ”Áôú-t± ž¬À×’v'ìgLÉ<¯hÖBTìtJžN§Ó	û·h*Q|é:K÷)/FˆñõôdÓº}h•á¬7ÂºUÒ”òà0ÿg¾Ð ¾¡ù,Ø¼J)ÃÌ:&è†õlÑ(aÜü"ž»vAn­‘‚¦¹V.‹Ø]¤E“E¯Ëø°ï{G1Bwýƒ¬ÚK–Òà’d|
¡b	¡ NæYJÏ(MÕJKbçV{ì‹N¶å‚PA)ÓFiôÏ¶G;…ž.QwRÁ¤+I¾ž~ñ¸×5þMùòt .¡qxjß8Û~Þ¬•}Þ@Gjb?}c	è%xm=ØÀ1\›uR¾±Ž0’VŽ2·Ô­ÒÀåÓéÙWÌêÂ^Çõ Íx®Zm¥i)×l¤Ò­[(¼2Y‡o@DJw!º”Ñ4™¾|©8&úÔ÷v¥k¬œYKœßZ•Ð2…'GÈçµ%4£ñíˆhßBžžÝÌ¾wûÞW¨ùvï±fNâº2.Ž/¼GE¬VßÀŸ[9Åý#ƒ-mpUºÁåvÎ¦£ÓÑ^˜ŽÏzHL¼°ÌR”3ò¥¢·õ$ZþÏ9¾#3×‡Øy%  š{*«h&È'ÖÐß¶fü6¿±Ï]ÀüÌw+(v–k×'R¬Py8Ç@‡Qs­A•ÞÊ;´­‘Oég;”*g¸âÑ¨bàÈ1F›W,/ø‚=¢¹žž !ü¥ÆKnAÒòÐÇ B?,U¸Êaƒ$Êç·r2~2²ó*+ŸCõ|FÑÜ®--QŽˆDÏnÂø÷pù—’ùõ²·oß‚l#yß¾ñ”…Ó7^óc­t¥®`&€j¼Sâ[¦Û¹T>Ã^øe[iFÌVøœ†,X‹™Ò{8®×¼®e3PüÇ^Îz­ºŠæúÛ³µàì«=žiÀêþ‚gr¶N(0Ç7Áu[…tË qÛ”¬†¨äO•üíñ¾C’ÔÙÍÖ±þw.ÀC`¯jÞöþaÏ$Þ‹¬Â'j{bœ¡<æ“”Ç¸•öÉ/·)Žjb>nMÌAØ:Â|ÊB˜ÃQ<(t¨~ù˜Õ/‚·/t(yùt%/AxpÚçPçòiê\ö¢xx•ËPÜòq‹[î(f¨hù„-ƒxX3”±|Ä2–Ã°ÛÃµ+Ÿ¬våoÂ<T½°?cŽ5åÉQ2Î…¬cK@zh(±ÅV«'ìÞXeü§{Ï|”˜«/´iÚ”ònÜÙ»x&fÆ›U%Ê;a?¬ —ÕF÷«“ÜpL[ÇRò>êÌžokà~\Àz)F5OÅXÌµÂtR»JŸ#2i~”Â†§© KnÚªŠwâ8{ÒõçIç	^õbh‡ï²Æµ]ÚˆÒ ž72{ùô9%ê¾M&“¬ÁÈðŠÝKP%[–a®W[´Áñº­“„E‡D¶Vº=Íí"Âgí!hVZLžŽ7TQ¹tîG›{=Ëêº½÷ÝYšáY}N}À‘Î»<¯^2<ªJû¡<Q3jÎÓŸqè3è{^u’¼p,Ûè÷rv'E‡å8*uå>t¶mTE;õ¨Kð§*ˆdøDÞ¨ßEÕ?ÕÕaù¬m'É>×}‹è”Bä&eMú²W:€f;³¥=ü4&*ÊB¯i³<&CfÃã·x§%÷î6R^NGqçÃ–á#ä¸ºóÆæ·Óö¼¬ìN}T«eÅÌtºY[ˆ$Ê¿Ì¬àE ÈŸÏ›e m{s?íÐËžØû®c³L& ¶ez+#žBèN—„Œ–îMºÞ¹è¿Ï+_rLÅüS#Ó¶àaRE—Àêòöæ ‡‰<§cŒ{²ëm¡#oŸWdøg–Höß˜Ta7µØ¹>WÖ†âÏÈ{ù¼`Ó=>Ë*fð\{Oáû–˜ÃJYÉ²-©-âKï]<sìYØÚž/ÅÑ<¥¡h½„×C¦&X¨iíçd‚_“J§ìi¥¢„xhÇIºEô&è)S3ŠÉî«ñ®Á²/5seCä¡<Œxc}{†É÷à:ðÄTtèï<žWªjF¶¬fµ`óVxº-{÷êGw@­ß%ïZBEÊ¶<©¸¶FÇ¦`‘º­L§ïö½$K›@*f©-¶#9û†=Ò¾¼ÿD×aYìàª5[(n]È¿fÉUîku|bëµaÎ$J
ÁcØÃ¬Xú¥(Û#Woõg«‡¸»W,mÚ9†àm"ë{ÒMò@¨ÙÔXQ†qË8Ì’Ç”Œˆ6UúÏ.¦¬õJUcG*â÷¹SôwtsU[‡,cY\Ž™NøøPl°e9¨O&líf°çì[˜Ã˜í.ÆÆ }ŸÏ]óÂ¦])kÖ÷Ÿo<ÌRïs–J:~3Ô˜7ÌNéÜß4÷KUÄ™]L	ÿ8”3×1åÊÌq›3n¢¹èÏ4}2$ŠÖrQÅ±M»÷¬¦¬rí¸vã2µá‹ÏØ¯K8EšE¦é­U¶ÔÌ¥i8.Á¿}tAÌ lª¦/¯§&lý?Ûß_Ô·
¤¹ßGÔÉ•U^P}¾5¨F®¢b‰¹Àš@\Tlz]±üJÉ+Ñ7îðšžoùwÀ€ÿ¥YökvÈ­Š'‰/vá~ÑE¼Åâ¾j×T)RŸ§kWk0¬ùÒ.…Sw›ûCÞ{¸Û'áÞÖ•m’ ëÃv;â²›ö;‚'‡°à^Ã‚@Ó!¢¼Žä5þ«9»v     0707010001f043000081a40000000000000000000000016a102a8a000001ed000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.hb.unicast.conf.gz    ‹     Í–ÁnÛ0†ï~
¹nA{èe]
ìÒ'È­è–èX˜"¹”œ6o?RNœ ±añ ËÃAýüô‹öj5ç¨V0ãÐtmýò±#èƒ3˜òéæU7/»ê¥­Wì‚¥×ê±½9—]Uªþ'ß#Ûoh-ËÄ×‡RJ&vX{WÚrO:ÁôÖ;¦‹„gô©ÌXj°÷ù¢ín].h"ƒw)Spa,ä¹%É•¢?…-ÜS	M¬®Ï2¤bâ!oêÈ¸Æx‚bg
Y<‰ØJV×(”ÄM[òK¾êQ'„é­Ê&öÂ…Ü,t/LÀÜ3z•IÜ Šú®Lžþö˜KÈÕbÝ‚YAæ?.:Á±‹œÊñþNÆ_Ã01ˆ¯(‡ñ'ZsA3V‰ªãô>úb(„Køæ$gÚŽÄôEùpF+¦Ê.†ßRŠ†šò;	°ü!'š	m‹Òtvéº-u#ÒBþð~Ð·¾…¤úêÅ’:äìŒë„´´ÇSklIÞÖ„¹07×Z&Hf·§Ø/ö\ÿ7Zòx„ä‚6HyöúÃdÈéÇeD	Ç=àpìk’6@W¿*²\r"©lBëÒ&÷]ÃgíÄV‚8ù©iOmÒ	     0707010001f0f7000081a40000000000000000000000016a102a890000125d000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.ip.host.conf.gz   ‹     í]msÛ6¶þž_™ÎN’¹²j{šýà:žúöenf7M§IÛŽ	‘ „1	°(Eýñ÷œ€d[’9©·ôn=±Hxžóàè³Ïùóä3vÀ'›	³ëF°…6öÃÅ¶u‡»'¿Êæ³VªB¼ÿíÉ—Ôé—Ôí'O°é×b½Òmq6´€W’¸rtÀ#“ë†Ï*Ñ¿ê]Û	¼ÐŠß;ÙŠ¡ßñÊÐ•¦ÕKi¤VRÍÏ’+…(yWÙ¡ÙA˜TÑJËUî_T	^²¶¼­z,ZwÝ4"—¥Ìáž¹PðlŽ·åZ-EÉŸi’¼Ýäpé'#€InÀ …SöZƒX…ŸËókfº¦Ñ­e54U6ÞýyÍÍ5kDÑF*+Ú’çbÂŒfg×«³ŒdfÌ,tWl&˜–YÍJ ¶ZÅmŒ„ñœB›¾¤ûX†;€Í"¿¾ÊyÛJzýð.€/s+—Ü
f‚UR]3?zn,§ìmŒŠ,ßB,™4Œ³@¨"‚yÖÊbŽMcf%m¾@ÜLvàW(s¥x-®LW–òý£Cð{­‡`ŒRQ@K^u‚q|7B^øæû·Ðƒ$2¿){úòV À1L€ž]t†•º­¹…k(2ËÎñÑ‹£ó.¦ç¼iàwÍŸ'‚þ€þ^déçÆS€t©õ6 »¦€v=:Œû+‡RS®
‰#aÎ†Ö²ÿïÙKAM-´ppÞ`q™må|.Z$RÄùmì ND¸ÞJŒð'¬S•0†0uB‰)ß|ßEÑsÅµhâ:zQÄ{p$¿þö†¼’Û$öü¥5àaŒ@ü<¼éD'}ñù9tÁê\W¿žS`HŸÿ–eZ(³•3d“Ñ`á-xæv)sVƒƒ«Â"rœ½ýñg´0*Sö‹´‹H½áŠÞ€Î}B–J6Ó\IV´r‰NF«RÎ»d»0(/†7iMLqøK`Dï y¤OÊpá/à)P³û`‹|Ó8ÐÀ|ÁÞ4B½ýùk¶’U¶aîÃÅq‡9VÜfÊ¾æ
£³B–¥h…²¬luÍ ùLA,†_í^íß”sC1E$,Ë¾Â'°MY†f‚¾Þ.°WeäãÀd ôÀCF(óôúW^œS@xáXE@+Öw¦(lYW þ‰$,5u„â½;(Ü ‚åÜ»YÎF~å7å¨y¾´¼Ù@£ð7¢¤`zyCÝå3¹“4Rwmuª²ñvwš‚* ÓsJÐ²W?0^ˆ]Àf1ð§î€ wð<Âe/Æïàx$î:jøV¸3¢ì*ÒFºwüÍ¤éÐí:
Ôo­Áv|óã ®€9ÜÞÂ½ÝL	¸Ä[í¯EEúžôºŠdÙ[?: aÑåhÙØ{ 0ÙyºÎkþ^#K>ÙPvê¯ÐõðªÒ9b¯EVT•	ïp4ÁXEƒF9WÐy%V‘°¦usŸ×Âu«B@ïq.:j„×0eœæî8}«ÀúkÖT]=k£¿Z­md²žm3è~xQZ!€* …|Å ²y>e¯)TÐàh¤×¸Á>›$ä(dN¶Ð+—§9{Vtu½Ž»Áe©è}‘0lésãº8–¬¶Due…¦¨¬Î-Ð³$»°übfý,’qúâÅÔýw:=ÆÛOO'h§ÓÇþI±~AŒÃñ2zúÅôx9á“‘œ±¹öcBVÕC¥´åhƒ&Ž[Î²yãYØz{c¦ì²2kÁfkz¨À€¶Åg} 
$*D%üÈ€IøiugiÝƒÖ¬àÖ+¸çŠ>Íh^ã–G²ÌÂû™°—D89žÒÿîâÀŠK{“¡GG‚ã‡œÞ]K'ôø†ŠôqÊ«ÎàôvXö0è;4¸œÈˆßÇôhÁ[ÆKÀ‡›È’ŽÏ¼'â½nqþ|Ó÷bìJÞÜ»{â¶ÞdÐv@-—
§á4òxÇ!1“üš3nøæj‰0â=>x£x/r¤&|Øˆ¶ZóŸ€zwrnC´âë¿(åÂ#oiÖ1k‚3ßa	…à”>úZYäå<,à’xÓÅ.§€÷Ö›>&õ,>%uÃ»xØ¶<©Aüûâ{ËªZ®«Jä‹°ÿøwmøžW?\¾Ž¼Bg6tÎ/^ qÔúr|*3ðG×0 W­‡„‡GÏß«`®ëš4¡e¸öÔOÎYêØ Gzr–qš£œe}O2æ>š²oÛV·Æ…m‡bqÞâ."w ³]ÉÒ‡ÈŒPþY(c i(ã•È?£sä÷…~©b¶‘<¬RÆc›} ^ÆýpÕÍˆça4S7‡PLÝÜ¿N¾òÔ2Ú?«•‘ˆ}AmÅýÜÑh2ú€áOŒÌ=ÐƒŸCòÀÑO@ä¾Ž±ÏÃ¨ä‡?øÜÖ1ô9˜^~hìãñ¸|cäópJù¡OŠÎvL±ÁB}Ùãq¿ÿUcpYÆ¹SðY5¿éXõ»Îaç¯ ÍÄmB¦)eJÒÒ>Óê(çUÕ§"7Bc"ò‚/)!nhæCá'¿eÞº¦}E¥q'¾Yk|u£eãÊ,‘ÀÃ	˜ß™Ö(¢ò_–ÓøÑ“/Ã@p)K¸ß;W ç´_¨B>²kˆ2fÑÙB¯T¼,ÿÊ¼ÇvÆm|¯øÚ€`$Ê2õ9Ï²)åPRÅ·ß]þôïw.±hµÐU²ããß‰ëû¾Ó¸½áz¡…!B-´JöoD|ÔìMãm¡mlLQ¼Ë-‹nwYm×ÂxtyŽéø’º8ËtÍÎn¡yŒÜn„%ÑÎ¦ïô4Ü	2ƒþEý³¨IÒõ-_p5Å$é^úf¡Ü‹ûñpÜØeuøwf…ªœ7cæð¡2‡ãlü·Ó1—ÇÑ+ ßÃô&C[š¸5š¦®åh1Ü_·ávB*y&d±>¨TTIr¹ß²çÐc‹»iM¥×ÑVœW´asÕå‹âûvÑ§´¬nGÊZƒÁƒzÃfûtLêÂì9ÓÉ8‚(w,ÕµA
¥AÞ¸'!:î¶£AqÛä~#)àEî"nÐ¤òêÑ± õÔeBps¡‘Y«Ü¤ñ\šcŠþS¥çx:axÊC3h“ €6¹ˆEFà¯K.+JõÆ?úQ›)µKS»gR¼4sJóÎ\—ÀüÞºŸæC9™›R#¿Â`TZ1ß¨†ñÙ"˜ŒÃÁžò%4Ô=ã†mžówÇùÈ˜_1£`Ê~ìÓNàÛá_gYÀÂy|D-Î3J†wV×ÜÊœbÑ`V1êñœbe¦Œ¦YƒÌ‘cEW7¬”•0kcEM¹Ð0aâuãkêúñzy VÝ•“0¿šU×R_­„œ/þËVï¡*àÀ„*Ì Ø¥_5sti¿b›»U¥|&z¢Qÿ‹óQöêt±ž,ÁÖÒèNÙÏ˜a{VÑ®„Pìä˜0<9>>ž²‰V‰*É»ô¥ë”Ú¢Äxq¼Ù¼é®~ï´å#¬[aÝÈ5	)¦S3ÊsÃ™/¦ åsé>1&è†õlÞjðƒà7ÿqGi•×F
šfFW¸,â®5%*úÐ)§Y‡œ§)ûn04×ÿ–ª{ÏrzŒC–@¨XC(€“ùJÖÒ¦ó $g®4hy£=îA
'1û¸Å!¨eÞj#€þ…ÙÈØ"x¾À±“&]Yöâø_Áµ¾!ð7
Âµ K3ÙOÝ§›÷Û•v÷èIMì§wìP³ «mF8„isF*4v¨»Á<<¿Ô¯ÒÀÇ'Ç§_07þSöJ1ÏGšîh¦;ô¶Òv>X(Óù…Â“uLúªÝÔå“i2½ùRÑ'†º‘wn¥ÔZâüÖ	-SrÄ|^¹P‚ŠHúúº˜€î)dàÉévö¼ÛÛö^âÈ÷°;ˆ¹¶×•qq„x,*b´ú~c\¶YßB·pãë,[0çj.Ø;žœLNñƒã£ÓRÊÊ=Úf-ê+²¥#¢÷µ$Fþ‘Î9¾%57ûèù€% €Å*Ï¤JÖ!`‚ìJÞtöèMyôÚÝwó[Pß9® ¸Y®[Ÿ Z]Æs4–7QÆ0Q¢GÛ)ù1ýì`‡Öõ®x´º9rosÉÊŠÏÙ3*ò9yîôÝ%åîûIÇ#@@‚~Z51’¨nÔÉ)@úó‰›W9ùXZ_»í+œÛuµcc$Ê‘èÙïO¿ÿŽ—©!}‰Ë›7¯#A®‘ž¼o^ÊbE”o<|-Ÿ€![û>€š ªéNIh™éfnRù%•Ðøe[iÁC¶‚Ä,X‹E1;8nVT
3RüÇ^Î†Qõ!U¨¥If1jÑ/Ù;,ÓˆÕÃ!^åh=Þ×z}NFá¾A	â¶3(YŽQÉ_"*ùÔÇ‡MHÒðf?[ï-³Ï©×ífÑ8íÆñL¬È2qp¢¶#ÆËc>IyŒ_i¿3ùå>Å1cMÌÇ­‰Ù»=’BÇB˜OY³?Š{¥‚ŽÕ/³úeOðv%€Ž%/Ÿ®äe/÷Nûë\>MËN÷¯r‹[>nqË>ÐíÅŒ-Ÿ°¢eo÷bÆ2–XÆ²v;c˜±vå“Õ®ìƒà=B˜Ç
á¡Q?dŽµ;D“q®e“j* 2@ƒDIÕ(ÕZ3e¿ðÖÆýS²n*‰¹úÂØn\
_ú‚PFà#žiÍï§ˆô°^›au’[žz³ã÷þ¾+çãÛx°õˆfL{’(àG-axžÐä¶S*Ý‰ãìißŸ§½%¸Äl9©6ËÎ[Y\<;§DÝ‹Ét:}Žõ/¯JWÝáöt-ñü=Ìõêš‰±è¯»&ËÒƒ;'Ý­çwáµ"·¡VÄ¤äéI±¥ŠÊ§s‡âíÚÙ{Ê#’ýðÅgÛ«fe£î]X¢*eÔŒNÖqR~Ä«^RŽeÃ¾BÉÎÒ¤è¸¼½R_îƒ¦M&õX´óÑLúªHH†w”­þC¨á®¾+dm{Iî¾þ5˜8'¼:n&7úÒÔ€8Û™-¸ñõ CŸ†6ËSò»ó¡yúïGÉ¿„û”‹ãIÚù¸ex‹;Ú¹î²uùí´=/•Û©Ojµœ˜+ƒ§Ew•È’üËÂÙ<X®ä(„k mss?í0Èžºë¾cW…¨8L \ËÌFF<… Ð>	3,ý“àºÞúèÈ+_pLÅüS+ó®âqREŸÀêóöfxB·(K¬ôŒÈn6…N‚~ÞàûÃ\Ýß˜Tá6µØ)™>_Ö†âOÉz…—l9U1˜Ñrí<Îób«¥’uW3¶„/ƒu	Ì¡Ü^fÒ¯–èï¹":';fjF1’þ<Ÿâ[Ó¤ÒcöLé$!Úñ<Ý!ºzÊÔ,Æb²‡ªFüÐ`9”šù²!²Ðþðk²í&ßÏðÈõF Að¼QU3qe5Ë9›uhÒÞ^~ÎíÚô»äÃá˜ÌáÊ“ª[kt\Z©»ÊtzïÐKÈ±´	¤b–Ú|3’sO[ýÃ;jÞÆe±ûT­¹BqgúcþM0KNQj·÷:!
qõÚ0g5…à)ìñN,}ÇÊ¦çJâ­ðÂ¦ÇÓ#Ž6ÝÃ0Ž.ˆüzÀžF„&y Ô®¬(Ã¸åhã¼tŠˆÖ*ŠGSÖºÒêÈ“Šø}æ‡ ÖVà-žîqÎ¢î”¯Ã¸	ËâJÌtÂçèÀ‡j-+±@}:e+?ƒ=cßÀÆnv1Uú|æ›7íFY[´¾¸CÀ¾³Ô‡œ¥Ò?†jJ†-³Sœ
Œ¤y`Òèf/ÎÜÅ”øð}9sSnÌ79QaeÈDÿMÓ'c¢¸¯wÙ8›}XxÎ”\ç×¶.S[>ÿ«àm	§bó‹á‚âP.µ3i[ŽEðwò „KÕåõÔ„¤sðÆ+ü 7³®Å Wª²¢ú|§Pt°z¼Þ$ð€&—›ÞV,¿Ô¿ÞA­ýá34=ß°þ€÷í3ámnÈ¯ŠgY(vé¦é©5á­nM%sßfÂw‹áj†5Ÿ»¥pêãÝêþ˜wÀïöI¼·uc›$Âz¿ÝŽt§lÛ~Gtç<hXôcˆ(o#YÿÒÑzÀ;~     0707010001f067000081a40000000000000000000000016a102a8a000001fd000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.node.array.centera.conf.gz    ‹     í˜ÁnÛ0†ï~
¹v1v]—[†]ÚÛ04ŒÌ8êbI£ä¬~ûQrœzÙ|
¨‹%Q¢Èï§È‹Åœ­ZÀŒ-»Cfn ÀKÄø?îæn^vÕ÷’ì‚­kèåGu[’^MiWUNà'¿=7ïÎq<ãŸ6Ö‰ñÍŒ-ÃŠÆÜìétÚ'ÜGÊ¦_½e:ÇñÈ}14´Å~ŸÎñ}óŽ&_:ñhŒŒÝZwÐ’#¶¦$(¢‘;‚€iÉC’~Nè…LŸrHyºåÁ½++îåÑ¡Ã–:]Ø·ŒÝRÎ¸ˆ	ÛÚ‡Tçq-ãÒy³ÁŸË×ÊÙoEÉLò<ÀÎÇd][f¿Üxx0lCZ>#' ÆcÖ_?|b-yê*Ž©]æÞ·`ÝTm`Š‘Àï3î;	kKLÎ”¥xQª‘SZ¯cº>&Øx+j¬×qˆ‰ºõvŠtÍ¸n‡ºp7n˜Â“-"Kðñ
FwµœV—oÐ[PTU@PTU@PTUÀ™ŒÄºÆ›t”›3¶tL0Kb¼sr¡.¨Ž³§_:ÊçùÈ›Á¹d¯Ð”Ú_€Bèd]{Ÿ”òQ>ÊGù(å£|”òQ>Êg>> >p— "     0707010001f0eb000081a40000000000000000000000016a102a8900000db7000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.flag.conf.gz   ‹     í\mÛ6þž_A (6Áy]ï¢ùâ&Áåú‚®é.éõCQ¬h‰¶‰•HU¤Öëâ~üÍI‰ô¾ØÎz“n«|K’ó<3’3zúôž<eüƒâæfÄìºl^òÅýÅvt‡ÕÝ“_ææi#U!®~}ò5MúMûÉú…X¯tSLûËí¹••Ð­…ŽøUer]óY)º?4­Àø­•è‡ò=/=©})ÔJªÅ4yRˆ9oKÛþþ*ÕR4Òr•û^JÁçì5[
^„!³{nj‘Ë¹Ìá…PÐ6Ç×r­.E	/Ú†[)ú69<û°¬âW²j+¶âÒ2T›ë†qR%ËyY2«a~¦Öª³Ÿ—B1qUãd†K1^Òm“jÚ&ô³hx!
ü7ˆYñFØj)ó%«äb‰´F@oÎ¾ar	Ñ½@h]­†^Ç0ƒ¯cœ/nãÃ¬Ôùhþ¼ÖÆžwX<:jü¨•8;"¼¢TW´Æ¬-â$®DÞZÀenE“@!4ÍyŽ”šfÝL2æ~³ïšF7ÀšÅ‚ÿ¼´o­wƒHÐØÊ…2T™Eà~Œ$foá/UÌÖ’‡5ÊX·Ù} õ2öÃU×ž‡±L]Â0u½~­ÖÊ2ËHµk•‘ˆ]AmÄýì‹èL@$,:ü‰‘ÙÍ!ø¹’Ž~"û"8Ä>c’÷~6ðÙÖ!ô9˜]Þ7öñxìßù<œQÞ3ôIÑ¹S°PŸdìð8†ß?EYÖzn´"¯æ Ó%HšÓ¿áù¥ÄgHãë"@¥a
_huL'„¦­k‹_-tò–ü&J/Ô¢á@€‹ùé!ßšqàŽÒÆ×‚¬µ?k4tŒG‘D'`~à…4ˆÊ£³ÙîÉ ïsâ°É”m¦ý0Ùÿú®	Ï4€ÏÕ†©{Eö`+	Ï“…°f 
­‘mM”1ËÖz…VÛuñÖšè4˜—+¾6 É‚²L}É³lÌØ{aAìÛï¾óÓè`x§Ë˜,¡O<VöcÇãÏ-j©a¼w„{s½ñ¾ÇŒñtÜWdb .4Mdµmúhs°hâªbšéŠ½¬¹]¾ÃË@kðº6–4½XM3?éqx&ezû‹ægÑ’¤›[¾äj!ŠQ2½´g¡\Ç>Ü×6dbþg01Ç˜[ìL¨œ×2+ûˆë•ƒ²œd–YheøogcÀi¢ëE¡4´CÉV\Yän~ŽÍ¤¯RI¢tr«b—`™@¥øIƒ€—Va˜±ÅKœºÔk¼ÄQÉ:R¹8QÛèã¯qÊ[ƒÃíïÆ6|¶mäb!ð2i NJæÀ&ÝrßCÑ/ÇR]¤Yê¶,Ðé{çž„è0xr(3ú]©¿–ó"·‘@×èRyùèX€vJ×£–›ÌZå&—àè¼`GJ»•ãhÄÈcóC3h“ €6-)ChÐ¹,[#®ÝAûÑ˜1+5{ÖZ ÁK;§ôŠ×M	<Ào-˜»€¥ª4šÈqMj´î£0ÐJ#0¼ØÏX×V*Cê`Güê‚Þp,};ÿvÔüHƒ fôÆì?¡o¬íð¯i°p+>¢vÃµµSo­®¸•9Å¢Á­bÔã'8f?1oKäÁRJy‹l[Õl.KaÖÆŠÊkÄ¯ >&Séôõê ¬ºÅÎêÅù¬¼ú|%ð²ýÏµŸÝÃT`ª0½à¼À¨ózKÚ/8æ_o4)Ü”‚M,ê¸eoÏ`Š%ðä|-iwÌþËË8v%„b'Âðd2™ŒÙ¿D£Dñ¥›,=GFl …i“;Íëöü·V[>Àz'¬é.°!Íq'IÙ0¸ó…Á ðígÁæuŽQ¶wL0)ëÙ¢Ñ°ÂºùEºöãÂT²F
šfF—x,r‰t@IFt1Y"0’Uó†WÂŠfÌ¾ïÅÝõRµW,§à²ìøBÅ
BÜÌ—²’6Ý é8«µ‘ÄÎñ¸†N¶Õ‚PA%óFô/0M'–"x¾DÝI›®,{1ùâïð¬üáY êÆ™†§®Åéæûv¥ÝûÁ :Rû©-&`–àµÍ`‡pmÎI…ÁzÂH:9*BÚV8¥ŸO&§_1§ÿ1{«÷#u{<Ó-®¶Ò¶”y6¢Lë
¯mÖ1¬r[ˆ.e²M¦>/ ×D·(ºÝ,‡0 `Ö÷·N%tLÈóyåB	ÃhÂ8ºVÈÀ“Ó»Ù7ðngßû5ßÁü à0“x®Œ‡#Ä‹àQk Õ7ð7Æe1ºW8[Æâ©tƒÇ!lÊ&£“Ñ)þ09>í!E0ñ‡ÛÁ¬DuN¾t@t_Obäïéžã;2s³‹÷X ºY³gR%ç°A~îý¬µÇgóãwî½Øß‚ù.ðÅírÝùDÎzŠx£æÆ€*ƒ•wh;#ŸÐŸ-ìÐº:ÇF—G±Ú¼q	ìÏ&h®'Ï‘áŒÒàOþ@ÒñÐÇ A?Ù¥,» 	Ö¢¹—S€ôç#·¯rò¹…Ý˜»¾Â½][96F¢<‰žÝýDX7ðßññ/¤çeggï"Anž¼gïeaáƒƒfa­õ	PÙÚÏÌPÝH‡ö#3íÌm*¿ÆY„c[ia…l;‰Y±¸~²…ãfÅëZ*0˜â‡ 8ðrÖkÕ‡T´×ßÜÄ¨E-^±¯¶x¦«‡B¼ÉÑ)z|¯õ.úœÂ¾A	â¶5(¹¢’?DTò7´Ç‡MHRß³ß­·†ŽÙ4ë††·yÇ3Yð"—qˆƒµ-1ÎPóYÊcüIû­É/ûÇ51Ÿ¶&f'ìvH

a>g!Ìî(î”
:T¿|Êê—ÁÛ– :”¼|¾’— Ü9ís¨sù<u.[QÜ½Êe(nù´Å-»@·K3T´|ÆŠ–AÜ-ˆÊX>aËnØma†Ú•ÏV»²‚{„0Â ì˜cMyr”Œs!ëÔR$JjF©Õš1û™7NÿîZÉª.%æêc›6§¼Wú‚M(#0½¬ÂC<óû)b5ÖkÓŸNrË=zæ’·QçÜãùø®Æ¬–hTó\¹V˜NêNéçH£B: €•p„áy.À’›V©ô&Ž³£n>G'xÓ‹¡¾«Ïvé: IƒzÙÈâõ³—”¨ûz4ŸcýËÛ¹«îpw	º’sºàõ¶‹ëu[gŽdµN:q9Ü"B·"·¡VÄ¤äéHqG•Oç~t´™<dbõM7{ByDÈ´_ûÏdQ¤Ì{ïòR½¦¯j™°Ô€'*SFÍx~2C}Ï«NRŽeý½ÂœMoù
—O¼îÊ}ÐµÉ¤‹n>êQ—àOU	Éðy£ª««Ã
YÛ^’{¯ëã„WÇMÊše/¾t - ÎvfKn|}ÈÃÐ§¦Ëò”ü™O[ñNK¾î/R^OFéäã‘á+ä¸[ºçËo§ëy©ÜM}R«åÄœ›|)Š¶Y’Y8?ÀËH–LÌ>i›—û‰hÏ€^öØ=÷;/DÉaàFf62â)étIÈ˜aé[ÂÒõÞGÿ}^ù’cŠ(æŸZ™·%“*ºVŸ·79LÌçXé‘Ýl
û¼&À?uDrÿÇ¤
w©ÅNÉõù²6JÞ+tòŠM¶ø,§˜Ásmýèß·ÄVIEßý#µ%|é½K`åö2Ã«Í3ZŠVøeÀ˜©}ÿìçù{M“J'ì™ÒIB<ŒãyºCô.è)S³ŠÉªñ¾Ár(5óeCä¡Ë&·v¾½Àä{pà€:ô÷ÏkU5#WVs¹`³V@Úû7?ââvaº[òn$T¤ìÊ“Êkt\Z©»Êtê·Ÿ%	äXÚ4sŸY]lFr®…û¥k¼¥æm8Û§jÍŠ;×óo„YrŠR»ýª¢W¯{&QQžÂPÀ‰Eì¯­\I¼å—l$bÜ}G›v†a8G—DëzÀž4B›<j×5V”aÜrgÉcJFDk•‡æ”µ®´:ö¤"~O½
*m¾âéç,êVù
9Œ›°,nŽ™NØŽ>øP®qds,Pé3¬ˆü”}{»9ÅÔL˜óÔ/Úµ²¶è|q‹5€v©¹K%?†jJ†;v§¸HóÀ¤ÑõNœ¹)ñÇ?våÌML¹¶sÜäLD…»(C.ú/š>Å¹Pi,GÛã®Ó”S®[×î<¦¶|ñ6Á›N‘f‰i+DU-53iŽEðÿ]ó—ªÊëiI!f¿¯¤]Æ¸4œu%z¹RÍKªÏwÕÈË¤Xb&ðM .)6½©XþRË+Ñ×þã3´=ßðþ®¢ÞÜ)?Ï²PìþÊm-Ò¯Ö„^Ý™J†!EòtÝi†5_º£pšãíæþ˜oÀïõI|·uíš$Âz·ÛŽô¦ì®ûŽèÍ!,xÐ° Òôcˆ(o"yÿÆ&7Üe   0707010001f0aa000081a40000000000000000000000016a102a8a0000011d000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.stonith.conf.gz  ‹     ­Q=SÃ0Ýó+t—ÒfXÊ@˜8Ç~itø#X6´ÿ»)mïX£Å¶Þó“žÔ¶kFÓÒŠQå$ÏiZKnÝîÖ]ó~2ÛFöû¦vü‰ÃOˆæîRX;Sò·+FŒè0«Áâ\¨ˆøÊaþ£Ê6]:Û?)Piqe†æ‘5=Ò‘u¥éà¿¯þËd±?š.Ú¢ÐO(4ç”7”eA=^û—ísÿDŠf vô&YY{¨L1¸ðšýîjç9±åÄ…¢ƒµÐ‰ƒïŽ%dyÐ2wb¡T²^¹âb\îÁÔÒWj§]Ý*¾Ï3âÂvY eLWlÝ—ÅÑmö›TgØür…rp}     0707010001f110000081a40000000000000000000000016a102a8a00001110000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.sync.symsnap.conf.gz  ‹     í]QsÛ6~Ï¯À$ÓI2'+²ÓöAM2Í]Û¹Ì5ÍÍ%¹>t:&DBÆ$Á¤euîÇßî Ù–([²ª–y°‘Xû}» VØå“'»ü÷è	Ûá?§—y<`Õ²ðg¦s^ÜKÜn{·[Ý=úû¤”y"®~}ôúµö£GØÿ±\¨2·ÝˆU®¥®D^Áå“þCméX|’ŠæyŸÊZà…RüVËR´ù§š®¥º”Zª\æ³qp%S^§UÛw'LæsQÊŠç±}P*ø”½asÁ×Á`Ø¢4×u!b9•1Ü39´ñ6ÐÄ¥(=ù¥@RNŠƒ§ë.}Ö‚UQ£“Vstõ§* 'Ëxžè!4ü¦½	  ß‚CÁe©
»°?Áx÷‰@
ªÔÿó\VB<'Z¼ä•Hè.¦¦pÏ¥Œ…fQôJ—ñ›ñ«DWo¢ˆ>OŒ‚Y¥<+NJy)ØBVsVÍ¥•hU—± äÌý¯Ùhô÷¯GcüyJŸÑß/oÃ±q) [ç•Ì„ªÏª^ŽFû„4©4èH ë{~%³:c.+†ŠcSU&`LdR•‚±ñxn>"ý&LWð‹ ZÕ9 †£¸!]©²ho Y‡:_xR™<,Žÿp¾ÐSý»&¸.Jôq¨_ÐO&ªR^1^–|ÉæJWÐ?sƒù‡U¼œ‰ÊwwÖ?ÏV(™£M–l|±GôiDh €ÑT©Û0š¤*¾€‡žððóFMGgLûBí­[@h\Ê¢B#W"®+ÀgZ‰ÒAæá4ŽOxŒ& ¸‘DÌ|4dß—¥ø¼²¬Q,H±¼[ÙN°Û²ê¡¼+”>€¤ÊÈ÷n0’˜­!„y2YöHîÖ(}ÝF÷ÔÊØWUôxîÆ2U±ÃTÅVøÁý<){wm“V¯w¶GÛ~k,séÑÜš¤Ù{áI¶FÔîUzL÷€©Õí½Pµ2î‚+üêaÝ¬ðë¾¨Â¯­A­‹„W½­îT£Ú{jDlj÷±„=ê©ö® z"º‚ZŠ>:´-¢1Å	nÏá!™-ÐìƒC÷CrÇÑ!‡È¶ö±¡ý˜ä}ƒC+ølkÚ™]Þ76dñØ¾>2´'‹¼GhÈÃe[(ûÀÐÞÀ¼sd(Àf[@û¸Ð!½G`hŸ;ÀÚ‡…ö‡êãB!:ÛbÚG…ö‡éÝÃB!:[`Ú…ö‡é=£B!:ë1Å?Ð9ãÛOvÝÃ}à÷O‘šÕZ€žëZÑ†Ï ¦RÑœõ%Ú¢]H=`‹¹Œç R³o¨ü$æiÊt]ª¬X!Tòæü’ÎˆÁ…Às³p#ãN¾'
žºd¸“«
úWƒ¬%>ºÀ“Ì c<ùg6œ€ùm€'R#*Gg³Í•Aßæp%À&ÑÙéqÛMö¿öÎœ¿uŠl)À ž&g9µ0U(|PD=¯«D-Ðj›G¼«4Ì¬5Ò†§¾Ä£ÏHEùECÆ>Š
Ïµ÷ýo?ÿøi@t\ÌUê“Å=¹>ýþºë‰‚õ6j® ¿k"'u{u+n}²Os¡+Ò¾ 	š&²Vu	ú¨c°hâ²d©Œ½*x5ãºÖàv-*_†´ƒº;aPíñpíO¸’õÁØâ9Ïg"ÃŸ,róàFæƒë ›ìû1˜˜aÌ-v&ò˜2+»ÃIæ²œdUÐ:ŠðoccaŠ Z„ÙI‚äJ\UÈ]|Êý€N3Y¹Û	© QÚ«á>°L R|ð¤M–f†ƒ'¢HÕc;CC©\‚Nèy›è“ZYeO xkp¸N©×|vUÊÙ8Á‰:à¤daÒL÷-ít,óÒsU§	:}ëÜƒè%tžÊÄœ¿çèÌ‰Vä&¨]*OŽh§&ëXáC‡ë%¸:OØÓ\™™ãé€	Ç¦»fÐ*A mš"B†Pàá2­¡8w;í{}Æ%Xª¨ß“ºR¼Tà¯§ÁüVƒ¹˜ªR­ˆ×¤zó>
­”b†É4>áL[™kR{Ê/¡£¦!èûÒ¶³w{ÍŸ*Ð ¬mƒ!û{¶ævøk9,ÌŒoc0^ŽŽ§^W*ã•Œi-êÜ*®zì ‡˜—7­SäÁRRy“ll*S¡—º™&Öˆ+žÁú˜L¥Ñ×ë°ê¶<¿Ùù$½ê|!älþ'û"uS	Lä‰nÛ®b†SÚ/Øç_o4)Ü”‚,êï¸eï>ÀSà	&õ‘v‡ì¿<­¡ƒQ-„ÈÙéˆ0<FCö/Qæ"dì`é:%†h1¾­A6.êóßjUñÖµ°zÐáê6¤1î$)ñw¾&¶¤ý,Ø¼Šq•m
—õlV*˜aÞü"ÜÛ~Á:ÜX#-š&Z¥¹D: $-š5Y Ð“…é¥™¨D9d?´Žb€îúG™×W,¦à¢èä–Š,p3ŸÊLVá> IÇY¡´$v®ôÇ4¤ådML¢W&ãRiôOV²V…PÂèN*ØtEÑW£/¾…kMGàÿ(c¨Kèg¸<5-ÎVï¯ÊÜï !5±Ÿž±Áô¼¶îm`®Í8)×YKI‘£ÄÆš(||::û’ýÙ»œq?RÔ'Uãl+«š’<ÁFr]Û@áµÍ:<V¤tV—2Ø&Ó3/ çD›9MöÓ!tÈ™µÄý­Q	…)9|>/ÌRBA7J×€€¦2ðôl=ûzÞuö½oQóìÎ‰qeŽ/œGE¬Vÿ€Ÿ¸.óÙÐÜÂµÍÇ/Áç3ÁÆl48œá£“³RÊ­?Yf&²sò¥=¢Ûz-÷ß“™ë.vÞb	 ¨rÉžÉ<ˆCÀù¹1ôuuòazòÞÜwû[0ßFPÌ.×Ä'b®ÑSø{t×Té¬¼AÛùˆþm`‡RÙ9F<J•öÙÅló–MS>cÏFh®§Ï‘.F©ñ#4<ôqÐ÷@ÆÝ·‹$˜‹¦VNÒŸÌ¾ÊÈçìÆÌ×W¸·«3ÃFO”%"Ñ³ù~ÂÍø·þ¥Ž$.^öáÃ{Oé¤%ï‡÷Ž²0qºÎA37×‚ú¨liÇ f¨†ß”¸žézb6•ßà(\ØVV0C¶‚ú,÷X\?ÝÀq½àE!s0˜žâ» 8x9kµj—T®žE°ðQóZ¼f_nðL=Vû[„X“£(ºÿ½Ö{ïsr
Û.J·‹’Ë~Uò‡X•üíqßK’Ô>ÙîÖkMaöº¤î­~è¯g"çE.ý%nÔ6¬qúÊ*©¬b#í·~Ù¦®J_NåaË©tÂ®C¾L_Cå5Tº£Ø)K¦/œò…S:‚·)7¦¯–rˆj)ÝÀë”Ó—H9T‰”î vÈƒéë¢².Jw(;e¿ôÅPWe+(7æ¼ôPW¥;’]2]ú²'‡+{Ò	ÉÎù-}­“ÃÔ:Ùˆb÷J'}“‡-pÒº.áš¾ªÉ«št±[´¦/eò€¥Lºa·1XÓ×/9@ý’NØu‹ÕôEKT´¤3†]B5}¥’V*éŒd·HM_žä`åI¶Ars ¦¯Ir°š$ì§é‘¬I ·Ó+„»~áá.æ)é‘2«.dîF $JhFáÎDÙÏ¼4ÊøwÓJfE*±ðÌeS•©c‚M(½3<yŒé¢x€ë³Ç
j¬–º=jÆ+nùÓ2gíKÝ}çÏã[+ïÇ,Ö½—sØ¤
ø‘	CÇ,¹¬ó<<VÍÙÓf<OOð¶CÇµ¯
<¨Gg;ƒœ¶W¥LÞ<{EY×oÃáð93y75¥:ÌÁP•É
ôàöºè
cuE Ø“UéÄew$+âæåƒ:$OCŠ5%qlnþÑÑf¯¯¼é˜ö'WëÂ•MXš—šh o½Ë«ü½‘P»©<Q2jÂã—>êÊ!´¼j$9áXƒ£=$:eã0ÃÝ¯Õ³RS»]›ŠëÐ1ÖbÐTk ’ÉðŽi©~y{WSTÇ¥à[Iæ¾æ1˜êg/nR
¬«abë@ ø©ëlÎµ-ò0¼SPæCHþ’Ì†‡­x£%ûnOÅ¾ÂÁû=Ã[È%p3uOKS¬€r-dî½…ÐÞ1bÎu<IŠ(H¦MŒà©'ë_L^i«™hË€VöÐ\·;ODÊ—‘í™^)o@KN“QŽé²¶%L]m„³-0ç˜ï‹ÉÄ•Œë”û2M6²MÂœ€&¦S,Ûå‘]¯
8û¼&À?3D2ÿÇsB™‘ë³5ŠPüYófN_mðYF1½çÚø²Ôïˆ9,“9½3•Ôð¥õ.Ž9”¨Í4Ïnt4Ïh*ZÌ¡¹ÏÔ«nûy>Ä§†Â#ö,ÞvŒýxîƒn]½³‘?ínÇMìÍÐi?è{ëÅ­‡ç«å¢Pì¼„žÿÎMµûpB˜µ._Pºó‹DÅ/àá¹¾Œ_4Þ*jR•ÜG(¼âWs¦@¯Ü¾Éº¹ï5ûåñße}2:…Ÿß~}ŠÓáã{<:£O_ÚO«Öquþø×[yýKúŠQû*9vßM”«'ekÑÌ–0¸¥™ó¬°S
LL'úÏk¥s¦vÎåŒMj	†Ò>¾ý	=ºI…izB•M¢ôÆB<&w+Qšò“ôÜv”$cý"Š©¨³Õ¾iaxmo(lÕ%¼Mi*SÒ,	|þ06§úv5âV§¦(#ì¥EF[³v¿j¨‹Ø_[Ñëp»”ÓÁË«]C›z‚ËO˜4Í×"´ÞsØ“FhóB«ee£p={â—ÂÀ¼/\)ƒ[~
šRiŠ\å'–TÄï±UA¦*·Xºû‰ÉªÎm,\Ocí«)¦3b;ªêš.±gS¬B9²…lŒÙw°·­V‡ƒvcÛîù]»V»Êûn}ƒ5À¼ßG/ö½ Cä"$Ãš¨n{Òì™4ªèÄ™Û˜âWøíÊ™›˜r-¢°Ê
ë(C.ú/š#íEk9ËÃµ…MšvFSF¹f^[ûõmP&°–»è-r¿Ù(ú(|ùuZ¬³N{,¨§Ðþ)j>ù”ØDŸŒ_=\Hp·/t8M^Î",xžÉ¼^Ùb}Î%éÍ%Ú=™=±ÊDäøDaS*îG‘<fb#ëaµŠÕÚá´qö*äRñØÞ»ÒÑ8½ caHæmž¬%Ha¼(RØ0ÙŠÂæ{ˆ¶~)ã³R|áƒ­ŠaƒJ¦B®ÿåk’×ÁS] ”"˜†@2wÁö°2¥w‹ãXÔr´ù«h ëHJûÃÞËíßË‘¢ÆÏ…´Øäéì‰½žFû§‘UõÑi•¨d~õLÚ?“0¸wDDò‰±‰GæÄbÏ£ýóÈhúhx´BŒ5<ªøì/Ì››ÊÓa¼*à“£ª
/'²*9žD‚ÿ»¯) ’)ìæ^ÆA]–×mX!«¹ i°DW™håÊ|šÒÛ<è¥¼J«N¾ÎÄ¥éozµÆ¥’	¾·bi_UEç?Vˆn_GRªÌ{š9fd]F‘{5ÆkóeøŽ+÷T³C¡/¨cWÕÏlZðû‘æ¬%ñvVóëã=Ÿëž¾v×ÃºÛqÚð(öºµÞý\¶×¹ÌÓô1Ìe7ƒ¼Æÿêù;¸q¢  0707010001f0cb000081a40000000000000000000000016a102a8900001236000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.container.zone.conf.gz    ‹     í][Ü¶~÷¯ k£³“Ýmò²±u›ÚÔEí¤E1Ã‘¨b%Q¥™ ?¾çBJä\vfëYÛ[kakI<"Ïwn$Ï¡¾øâ”?Ï¾'üAr‰)©KUD³®”øÍ”ê=È¶w§åÝ³uƒý¢Öeªîþýì[ô+ö³g8‚[µ^™:½î;’ª\5jbÊ‰mLœŸðyfSÉY®ºw¾«[…7jõk«kÕwæ™[ºSÕf©­6¥.ç×ÑTe²Í›¾ÿÝ].T­Y&îM¹’™¸%Sß%`ìªæû¶R‰ÎtÏÌpM'øX"ËT§²Qöºï­øOÿàòRÕAfÆÀ«Jb/ôÏ&pëÇLXÕˆÆˆé´Ó)àÂÉPÈô¼­e#Ú:R!³FÕB‚V+kÚ:Q±‹w@BBgQ Ð¶²ê®2u#LFïˆÈ´2SÓýRÝ5@OÖ„×»æð[­ª\'û]Ç‡üS‹ÒÀ¸àMi@.­+wÝÒ£kdhä-4õÃUK£Ì„¦W$¦¨p´ù: µ0ß	ÜÂ.$È„Hµ½O¿å„™‘Ø#ÍóVÁcöÉ‰ñßPCQŠÛRß¯À0˜•d…
ˆ¢¼”sa× Eý  —ºnZ™ƒ¸%°,ˆc1 D÷–²PŸ7›AúKÅª5Sdt¶‹ËïÈ6è,S5¨|¨Éµ)¨¡o€6]ËW îf6O—wlÚØÃùŸ¦·=,ýÛvÒÃ°Õ­gß:vuz(fRÕJU3‘3°Çƒ'~4Oü¦RåÛ_þÖ0Ï…c:ôÊjô·ª^²c^éf9cÿ$¸°úãS)Sô»²»‰7è/b8oÈ‘‡;W¡kt„½Ë€@?ÚŠÜqƒö"—|=xYñ\Ù»ã~8¡é4C–L§ÀS&¡š‘¸¾]]O7…jJN¬L½”Þé†ƒTõyuÌ¤Õ–åyƒÐ!?lì2™ÔÆ4“J6‹Á	ôÒˆÃ Çr€3Š’Ìs€c©A–dr·¼'1 !X²“‚AcÑ‚  öìYÆâ{;™ƒÚ8Ø*àõ¬Ís‡Mè³1hº0³ï;–ˆa‡¯LÕÀ_UÂ}b¢¹zõ!D¢Ã÷s¶oM½Æ»Žó„-¨µ‚öKXÆ¦¤†!œ¯`p
íOì·CŠ¶Ž­è€ð‡EX&ÄðE8ë?ÿF<' tÕ]’·V/Õø5×¶©%è¸)óõÂí÷hýÁâ7¨ú¡+„?0‹«×‘é¥‡·Òuš­Y`OžŠÙš]—w"`Šî4Û•€$Ì&kµí[9:&;–›äVàóš¦¿ ‰çm5öÔ_+iñù€TCkt!°ÿ%¾š°»9NoÑf
;†Œ‚AóÄ¯äf0æ‰î8‚b¯Ïí5À{Û€Zìw÷yÛžÄAuC>L`¸Ê´O/d¼úúâ”ê¶©Mi·¨Ó?¥nh-ä¥¿}Á4‰I.kïû¸A#¸2©ó¶æ?â<À„cÙ’©ž,F—WO£žñ eDRO ¦×™²Í§ê·<ªpÅB‰×mc
²W?ºPbMÇ(qWä"Ó¹"¨:("«Þ¬gg)Ý zZäÉ Bm­J	» ‘ûW3f5/L~Æ í5¬A5±m¸ß./à÷”i©¶P¦uZâcˆRÝ‰B€øê+O<\LL­çºÔ+\w%–„+Mrém[­h)	­ÆY¤r'lòö~Eq“¹‰¬“IÁ D„ãp¼Á¯ÅIRK®õ#-º‡ÎÎ¥xéž¼9C•ÙŒ·€ñëEÿ9(Ec8kÃøu«­¦W¬A#;f©t´ý ÓÐLèºX÷åFnH†6{u¯á|?³ƒäô‹Å–uø"2\®ara`Fd6Ö‘ÕÌ½û9l“	ŒU|àr Ÿn[*Þ:uŒzŸ8Dî!¹­J’#!€²¬½b¹Ùš‡ì ´¥¬PPyIœøb¦ù*É1Á…I^^pPz‡XªÕNt7œ"’;„É(Býrøˆd/;¢’û¡ Cx/è[?á=‚‚ÅãÄ}¶Ýò-÷+ö*ÄeÏÁÓ$\PƒQM*c›IGoØšq|Ýíë±I­+ZèSw*iårsXx½.ûëäœ×®§ÝH¦nIb,¾¯kSãP¨[$»Pþ&M¤v#ó8i1j€ò…2X9ÝX²}8ŒDæÁÂ?e:[HžV)CÞNßPGãa¸>Á¬ÊOT3Mu
Å4ÕCðkËÁW>’Z¬ý_µ2 q,¨µ¢Ÿ‡"ê6_9ü	‘y šCðó~Hž8úñˆ<Á!öy•|ßàgŸÁ:„>'ÓË÷}€oˆ|O)ß3ô‰Ñ¹Sì0×<±t¼ÇÀïÏ*¯,®ªŸÛZ‘Us{8¹òÛ6>ïïy íH¬:	fqg	0å9îtéÛ•2Ðóû¹Æ•Ø(WäÄôRTA…;‚¸5ÑÚ–vàÕ•)­žñÞ†Ûó%8ó½}Ú"*ÿg<ïòµgd/]„ž—¦æ$¾(E±­Hdì¢mR³*ÇÀ?6ŸiZ‹b#ó•\SÁšNË¯ät:v(¥øîû^ÿü×w\6¸Z˜<ÿN*Gá>ŽÃþúû©Q¼w¼0Ðß{Âƒ)u{Óß8Ûc©ðÐzSd£†B¥Zb9Œ¥ikàG›€®@ W¤×SÜxÆÕéß½)eœâö[H‰R'Ý ÇþITŸHjƒñ5¨In_<YÈr®ÒQ4¼øÍªäwüàÛ ó¶…»|(÷R•‰
fO£loãgüu,N&ö%\xŠÛ XÌ¦&h1¡ÓTpÊRQ®¥íÉâ6>h&ˆRš«¥3»r¬Ô­r³V©'ì­Ûa¢÷ŸÐjH+:‰µƒë™ºe³›ZÏç˜&J¢FJ' #¾Ò¯‡¢wÇº¼µ(BvaZLkWÂ÷(DÇoWv×Ð”º¤ Gò`T…&UæON
PO¹¤\Ú[ìºLl/Á-ày*ÎJÃžãl$–%e§– M´ÉEÄÂÂ.?Ø¢ïöÜúŒ!Xn¨ß³¶éŠòpæÀoÍÀÀüÚâv8¸ªÜºšùMªßGbÀ•ZÍ1}$8nKÙX¸»~&—ÐQnxF)bA;÷tÐüK ftÆâþÝðíðVrÇÙã#j;Ž`6ld¼8³ŠQàXülUÖæ(;©äúsèŠŠs(‡Ç’Ô¨;‰å\IæùõêRµ¯ðl>™å·ÚLVJÏÿg«…Pp`ªLíõF†à¤š£Kûöùß;U
'¥sJcë5êT£óãbN‰Å‚¹;¿`Ñá5XÅf¥T)./ÃË‹‹‹±ø‹ªK•‡I#~°tŸÊÍb´@0¾¹¸Ù¤j'¿¶¦‘¬÷Âº‘eÒ„*½0õg¾|8IMóYÐy“P&†õb^ðƒà7¿ŒçÁ®_˜—ª|é©œY“ã²× %«º˜,"¸«¦u,~èÅÍõ_uÙÞ‰„Z€q˜NÏ/!T, ÀÉ|®ÝÄó *Ä•±š¤s£?ÜÂÉ¶˜Ap ,(tR«@üÓÒÈŽ!J&ä60éšN¿¹øòp¯ëüŸ*Â!è@^B?ãð”[\m>ß¬?ï j’~zÇ “Sì §0ml¤º
M+G©[êViàòåÅÕ×|rV	Æõ ÍùÌ´èmuÓRš)èHi[·P¸5YÇÄó‚§>]êhšLï@yªè}å;^)ÄSDœZc:ªc	-SxáåyÅ¡òãú	 ·B	¼¼º_ú¹;Úö¾FÎw°{;ˆ§Âh\WÆÅ’oQk«?Á¿U³ý#ƒ-‹é›`ÎË¹×âbt9ºÂçW=¤&^Øf¡Š	ÙÒÑ‡Z«‹çß“šÛcô¼Ç À
‚çºŒÖ!`‚ü‚ýMÛœ¿ÉÎâçn5Uåa&µ›åòúD‚Uþ&,MiÐ`TÒZLƒw"Ñ¡ÍJ~A?¤Ã˜b‚+µÉ9…·y-²\ÎÅóT×Ë(~Òâ%· Érè£ˆÐß8æMº ‰j˜N
Ô_Œx^ÅôeÓÐ‰5¡¹][°4¤œ ’xvûÞoàïáò/u$õëeoÞüâN:á}ó“Y<{Îušy_«Ýan &€j¼Sâ{fÛO*¿ÅQøe[ÝŒŸaqJy µX|@ÆíJV•.Aa?…ˆã¯=W]Hå%Œf!jA‹Wâë–iÀêñ‚§r|F Ž?×¹¤ñA	âv0(YQÉ'•üõñ±C®HêÞìfëxÂÕL…ÀQ×Ô½ÍýÃ0ž™z+²Cœ¨ˆq†ò˜RãVÚ÷&¿<¤8f¨‰ù°51GawDRèPó1aŽGñ¨TÐ¡úåCV¿	Þ¡Ð¡äåã•¼áÑiŸCËÇ©s9ˆâñU.CqË‡-n9ºc¢˜¡¢å#V´âqAÌPÆòËXŽÃî`3Ô®|´Ú•c|@óT!ôÄ>ÅkÊ“£dœ[]ÅšÊ`9hPPb5ŠµÖŽÅ?eÍÌø{×
¿j 1W_Ù¦nÊ»q_—ÁSŸ1#0Þ¬*è»3cñs ÔØ¬m¿:)é"ï$‡˜|Ht&Ï§·5ð8&`µÐ F•LÔ¹U˜k…é¤¼JŸ¡¥šù(ŒLš\·eïÄIqÖç¬³¯{2‚¿.…k»´¥A½¬uzóü%%êÞŒÆãñ¬Á#¶ñ
ï%˜æN1×«­F¶AÝVÓ©ˆ>ƒÐ2u>îÛí"Âkù˜o¦O'÷TQ¹tî''6zZó®½þ£dáiôŽ} #uyYÞ<ŒÙzW–(%j&“[Ÿqè3è{¹ê(yâX¶Ñï+dâ:NŠË;Ð+uå>tgTE;Õ¨Kð§*HÈð‰¬6¿©²ª«ÃòYÛŽ?×½óè~”MÊšôe/®t 5 Ìv>Þ8¦JÊB¯h³<þšÔFÆ­dÇ%÷é6Rn.FñàÃžá#d$»î¬æüvÚž×%ïÔGµZLfb“…JÛ\M£üË”í€ÌBþ4i
Ô67÷#ÒNzÚc¾ï6IU.aÀ=³ñ‚Àpº$dÌ°t-Áu½uÑŸW¾˜"Šù§NÚ\†I]«ËÛ›á©Ô*ËèC=½°ÛM¢ÝA×[´ ü+$þ?&Uð¦–¸"ÓçÊÚüY/ÿ’Wââ€ÍbÆ–ëà9óß‘äˆB—ºhAl‹ä¥·.^røkO–¿ ¶ehž“+Z- y(©SúÈ#éÏ‹1¾5N*½ÏK%ÄC?^„ 3¢÷AÏŸMŠÉ«ñ}ƒe_jæÊ†ÈBçø¹5Ûö”Ž`_Ó7AÐ ¿õxnUÕŒ¸¬f9³VçøýñöõßÜ'Xü.ypP4<ÀåIùÎN+À"õðó›Ý(‰ ÄÒ& ŠYjóÍHŽ[ðyŸÜøð7K†e±£«Ö¸PœM(#þ°*¦‰¼ŽB¸^æLª <†=<P€ÉÒg?7=Woõ_qwMXlÚ†&s‰@ä×=öÄšäÑf]aEÆ-ça–<¦„`D´.“3<±œ²ÖKSž„¥fÐ§¬²NÜÃœEÓ–Ág„°,.ÃL'lG>äkìY†êã±X¹ìµøæ0Íæce°~Ì×®{a×¶ÊÚ‚õÅÃ_ðf©9K%?…j,÷ÌNéË6ƒÐ<®Ð˜ê(™Ù')ááÇÊÌ.IÙš9nÊL 
÷‰™èÏ4}2kõ¼Œc9šwí˜SÌ\ök÷.S7rþ«à®„S³H5½"«@—ê™nj‰Eð]ø£Î9UÓ—×S6¾Qé½1¡·ïPs_Üíèê2Ë©>ŸªÖË¨Xb¦ð€& ›î*–_b%úÚ>CÓó›áðŸ…æ·ñ*[ŸN}±û+÷)Çx‹Å½•×T¦R$>O—Wk0¬ùŠ—ÂiŒûÕý)ï€=Ýí“pokk›$Àú¸ÝŽx§ì¾ýŽàÉ!,xÔ° àôSˆ(w	YÿåKu¢»‡    0707010001f0f6000081a40000000000000000000000016a102a89000012a8000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.ip.gce.conf.gz    ‹     í]msÛ6þž_™ÎM’9Yµ=Í}PO}}™Ë\Ótš´ýÐéˆ	J“ KR”é¿Ý@²õâFNê+}WO,`Ÿ}°}öÙ1}ÆŽøƒäd5bÍºlžŠ#;îèŽË»G¿Êê³ZªL¼ûíÑsšôœö£G8òk±^é:›ôà…äžœñYdR]ñY!º®ÞÖ­Àµø½•µèÇð-/=©j½”Fj%Õ|=ÉDÎÛ¢é‡í‰Iµµl¸J]G…à9»dÁ3?
Á`Æ¢¶ÏM%R™ËÞ™mS|-Õj)ê€þLk ¤ˆgÐ»IáÑOF€ Y†ÁÇì•²
?3O¯™i«J×+a¨²*ðíÏKn®Y%ê@j¤jDóTŒ˜Ñlr½š$D3af¡Û"c3ÁŒhX£YŽ`«…P¬ÒÆHàçÆôœÞ\‰[€M"½ž¦¼®%u? ¼à«´‘KÞÖ,+¤ºfŽ{–—cö&DEæ€o&–LÆÙ@¨,€yVËlŽCcf%›t¸E˜ìÁ/Sfªx)¦¦ÍsùîÁ!ø½VÇC0D)Ë %/ZÁ82ßr¡Á_ÿfE†ü³·ð¡û ­ Âè5‹Ö°\×%oà’L’lzyr±Âåø‚Wü.¹âóˆÐ{˜ïe’~n´¤s­wÝVŒëÁaÜ=9–šr•Iä„™ô£eôý¤Ç ¦Œpp+^`qYSËù\Ô(0("–å·IÈD€ë­‚±!ükU!Œ!L-Q’ƒXÞÜÜEÖÉŠˆ‰èAïÀ	<<!ùõ·û4ä…4MlÀ_J 
<ì‰ˆ?‚‡ï1#‚è¤/?¿€)4:ÕÅå¯“‹…†§ôùoI …4k9Ci2,|ž¹^ÊT ÕààêA`9ÎÞüø³H6À•1ûE6‹€õ0¥Ð¹ÈRÉjœ*É²Z.ÑÉh•Ëy[mF€ˆAÇÐ“Ö$)àè@oúÞÞÁ[8[@¬ÆÀµZ·€–ï¶Ð³¬zôÜ³x+Úz8Y	ÓœÌ¶ñ™<ÿ'e²ðà1ZÐ.¨%ŸË4
4èaº`¯+¡Þüü[É¢ ¼F7mãeÄÅ©Sh «1ûŠ+Œ‚3™ç¢ªay­K† )ˆ¹½£×ð«A×®§”ŠÝbIò%¶À1%	º
®;ûË^æA,¦TôÏ±È<¾€ùå—“
¼/­öÒ$Ð[tS§U’Ÿ‘Â§k…ÿ¦F£-¢8S£¨Á…e"x¨ƒ‡e}Ñ—vp#0B%O—¯6ÐÈÜ‹HÉ»8Åªàß8Ä¦vÄDŽow/²rþm07^0¸°JP³—?0žeˆÇ0ºÙ"ç.IüÈ½Å€þ_¢·kÈÛ‚´‘Þ]ƒüôaÒ²óvÑ¯ƒãøúÇ€\kYx½†wÛ™ðˆ×¸„ºé{4Gpœ* Õ,Ð©9î „Y›¢eàWA€Q­3¥ç¼äïñyÌÑ†²Ó<x.ž…NAÐ!&sZÔˆ¢0¾+&jÐ(#ç
&¯Ä* VÕv-èàsú@¸îT˜=®ùp¦ŒÓ	.“°>ÙšUE[ÎÀÚh+_µÖM`²žì2èŽ½H- *@…|Åú²z:f¯($Óàh¤Ó¸Þ>›(´ËdJ¶Ð)—sö$kËr4»!ËRQ1éS»–°ë²ÚÕ•ešV`tÚ€xæd–_ŒÀ¬OçÏžíçãS|ýü|„v:nö/jÆþõI¤gÇ‹ õ³ñéá„OáÍµã	YU•ÒG4²²e-›3Þõ¨³7fÌ®
£Ñ¸fl¶¦F.jlë}¢LÂm4õ˜ø¤‚ôÒÞ ¼:…w¦ôiBëG»•$ðØí88* g§cúß6 :ƒÓÞ²FpbÏ£ç¯€¯us"«³m\]qÙLa)ÿà˜zzŸ‹ó¬­I‘"vÿ¬"k†<O‹ÖàæL¿igÐ#kpäiöü*]U	^3ž#Þ¿DÑ	YÎ™€~¼šÁš¯{ÅFI›®(FrAi1Žz2h‘a‚—
7‘hùïp¶!ÜHßIr¬ËÀž‹%Âˆtøà‹âHQè*\xÖÅšôÙËÞVMž‹V|ý9ßäãmÓr>²Ñ¸oÓo œÒÅ´k#³4Ÿûí=<Ð‰"åÐ‘gÐo¹é¹c½á©R7|¶ƒmw»™¿/¾·ì	§º(D
ûÃõµáÑ_þpõ*ðµ­ÙÐ9·õ†X„k=åm¨ÌÀË_¦íæùÆƒ'íT0ÕeIšP3Ü9­(RñÎZêÐ z2IO8­ü&I7“„ÙÆì›ºÖµ±AyÝVÖa»‡ädv+YÜˆ\À åŸ…2Xî£ÿ9­#¿+„ðKe³õ€äq•2ämò!€:wÃUWžÇÑL]C1uuüZ5øÊ{RË€µV+‡‚Z‹!ú¹+¢ÁbôÃŸ™; 9?†ä‘£È]bŸûQÉ~6ð¹¬Cès4½üÐØÇáqø†Èçþ”òCŸÝ˜â€…ú(²Ãã(Þ~ÿEep[øÜ*hEVÍåÝY¾?OÅgH3²G»qB¤’´µÏ´:IyQt‰ô•Ð˜F¿àKJç„*Ù|xpÌoIK€^×tZ«4æ7´@k]WZQî<îÌ’88ó­I¹Ò *ÿg¹=÷Ê3²›†§èszNY ^TÈG¶‰ŒY´M¦W*Ü–Ù|§iM'XñµÂ(,@(IÔç<IÆöD“S¾ùöê§ïÞÚt­ÕBÑ‰ë÷÷ÝÇáxýóLCµÐ*ÚØ¿ñÑ°7ý³=†’Œ7Eaî€YÌð¸‹„µikàG›¦˜t‚”Ù$Ñ%»¨x³¸ôÃK€kðºMH‰Î‹Ý¤ÇþM˜”éõ/˜_ƒš$íÜÒWs‘¢éÅ=e;îøa?¸	°Í•qïÍiV)¯†¼÷cå½‡yøo«c6;¦S@w†Ic†Ž4ñh4NLÑbf˜µÐø×	©¨Ï÷d]%ˆRVD¥.‚ÃŒ<M«
½Žâœ¢õ‡«6ÛûÛ'>% Õèz cYk0¸ž©7l¶+ ÀT9ÌI4­LAF|šUEïŽ¥º6(B}a›3îQˆŽ§íhPì1¹;ˆGp$÷	®Ð¤òâÁIê©Í„àæB#³V©‰ã%x4Ç“ÇJ[ÏñxÄ–ŸåÇ– M´ÉEÜ(‘sYP¡f08îc¦„9MãžµÁK+§8›ÏN	,Àï-¨»pÉS”éºI5ðûH¸R‹ùF-—ËÁkd{Ì—0PÛø†céÛ¹·Ã,oÌZ‡˜Ñ5³»´øvø×$ñXX¨…Ù[xÛè’72¥XÔ›UŒzÜÇXWŠ‰¸qÖ@O3YÖ–Ëe!ÌÚ4¢¤sX0ñ²r¡¿^Aª¶å$Ì§³âZêéJÈùâÿl·ðªL¨Ìô„mRý´š£KûÇüÛ­*åòû#ú7®GÙË×0Åäd	¶–¸;f?cÞò¬b³B±³SÂðìôôtÌþ+j%Š(›ÕM–žSjCŒÆ³ÓÈ¦U;ý½Õ`Ý	ëF®	,H1IQž®|1)¥˜KwY¡Þ0Á¤0¬góZƒ¿ù-…N)hš]à¶ˆKc×”þéB§ˆ`˜¼îsžÆìÛÞPŒÐ\'UûŽ¥ÔŒC’œœA¨XB(€‹ùB–²‰×(tœÙÂ¶åñØ†NbNw,(eZk#@ü3³‘±ç"xº@ÞI‹®$yvú/áY7ø	á^ òÒŒ6ÂSÛâ|óýf¥íû^:¡&é§>ö¨€Y€Õ6ƒÃ´Y#åÛW3ažÛêviàã³Óó/˜åÿ˜½TŒ‡ë‘ª=™é½­lZ—œ-”iÝFáÅ:&}•véƒåÑ2™ú@yªè}5Î[»Sòj-q}kYBÛ^8By^ÙP‚JsºêÐP m+”À³óÝÒ7ÈÝÁ¶÷
9ßÁîí æÚJÜWÆÍ’oQk«¯à7Æe›UCô
7®J¸s®æ‚MØéèltŽœžœ÷RVîÉ.0KQNÉ–ˆÞÕ’ù>^s|CjnÑóK  K€žHíCÀÙ•é¼n›“×ùÉ+ûÞ5¬oA}ç¸ƒbW¹v‚*t®1Ð`TX4FÃ$ÚVÉOégth]NqÇ£ÖÅ #Çð6W,/øœ=¡Ò©³§6AßîQRî¾Û´rè£ˆÐï|€˜ÇITkéd@ýéÈ®«,}¼¢´ÇW¸¶kK+)'ˆ$žÝù„÷øïpû—Ò½~ý* dé„÷õ+/²XgæÍ¼¯ö	`ÙÚÍÔPOJüÈL;³‹ÊçT˜ä¶me°ì,”ò@j±ÔhŒ›]a 
3ˆø1D7x9ë¹êB*_K­BÔ‚/Ø{,Ó€Õý!Nåh=<×z|NFá®A	â¶7(YQÉ_"*ù'êã}‡&D©ïÙ­Ö[CÛìsšu½YŠOç‡a<“x+²C\¨í‰q†ò˜ORãvÚ·&¿Ü¥8f¨‰ù¸51aw@RèPó)aGñ TÐ¡úåcV¿Þ¾Ð¡äåÓ•¼áÁiŸCË§©sÙ‹âáU.CqËÇ-n9ºC¢˜¡¢åV´âaAÌPÆòËXÃno3Ô®|²Ú•C¼CóP!<öW 3ÇÚ~ &ã\Ë*ÖT ¤‡%V£XkÍ˜ýÂkËŒºV²¬
‰¹úÂ45^‡¹®ô›PFàÊiÏï§@*¨±^›~w’7<¾ôfÏ7tïMžïhà~LÀÎÆ1íIZ @>Ja†§© M®[¥â“8ÎwóyÜY‚«žÌŽû“ä¢–Ùå“JÔ½Çã§Xÿò2·Õö,A—o5Ä\¯¶™ýu[%I|fk©ÛÝ)"t+ÒÆ×Š˜Xx:¡ØQEåÒ¹‡Kñöì½õåÑ5ÙŽ}áÝvê’5²®;W–¨ˆ%jFwkë0)?«Ž’'Žeý¹BÎ&qRtXÞ^©+÷AÓ&£z,:ù¨F]‚?U	DB†oäµ~/TÿVW‡å³¶%û^×&†	¯V6£ë0]é j@˜íÌÜ¸ú ‡¡OE‡å±ðÛ[·yÜŠw\rpwry:Š'Ž_±—l[××6¿Žç¥²'õQ­–%35xw[ˆ$Ê¿Ì¬À‹å:Bî‚BxÔ6÷#ÒNzÚcûÜMlš‰‚ÃÀŽÌldÄSÓé’1ÃÒµ×õÆEÿ}^ù‚cŠ(æŸ62m&Ut	¬.oo†÷ž‹<ÇJÏ@ØÍ&Ñ‘×Ï´ |wE®ý“*ì¡;'ÓçÊÚü9Y/ßÉŽ[#Æ–kïuž_“ä°R*Y¶%#¶EòÒ[/9”ÛËLüÅ(â=!WD·‡’šÐEŒ¤?OÇØkœTzÊž(%ÄÃ8ž† [DwAO™šÙPLv_Õˆ,ûR3W6DÚ])N¶=Ãäû^d_	4èo<ž7ªjF¶¬f9g³V‚µ7Wß£s»6Ý)y9$&sØò¤âÖ›V€Eê¶2úígI9–6UÌR›oFr¶I«k¼§æmØ»KÕš-·¦?”¿fÉ)Jív^ÇG!¶^ÖL¢¤<†=¼PÀ’¥oÚô\Q¼å¿˜A4ñµÃÔÄŠM;Ã0Œ£M"¿î±'ŽÐ"ˆ6ë
+Ê0n9Ù¸…ž"¢µJãÕÅ”µ®´:qBEò=q,(u#ð'îaÎ¢n•«Ã¸	ËârÌtÂvtáC±Æ‘åX >³•[ÁNØ×°†i6§+ƒñsž¸á…C»QÖì/îÑ°ïÃ*õ>W©Äã‡°B…aÇê—ƒÐÜ³Ðèê ™Ù&)áå‡ÊÌm’rcå¸)3(ì2ÑÓôÉPPì—ælÜÍ>Ì·³œ²Ìµ~mç6uÃçc¼-áTl~­¡×BdèR=“MÍq£þŽ¾± €°©š¾¼ž†böÞx…_­àÔ pÖ¥èéJ•TŸoŠ.V÷›^Ðä¢bÓÛŠå—Zâ×;¨µ»|†–ç6Ã]0`¿ÓÇ÷fwÜ®x’øb÷vißZã{µ{*‰ý.6ã¿±wk0¬ùÜn…Ó·«ûC>{¸Ç'áÙÖc’ ëÃN;â“²]çÁ›CXp¯aAÀé‡QÞ&d5þÌWø€  0707010001f111000081a40000000000000000000000016a102a8a00001152000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.sync.symsnapvx.conf.gz    ‹     í]ßsÛ6~Ï_I¦“dNVd÷Çƒšdš»¶s™k››kr}ètLˆ„$ŒI€HËêÜ»€e[¢l)ªSú!vDrì÷íXa—OžìóçÑ¶Çg—*°jY
ø³°Š——W÷·ßÞíWw~ÅÁ>1Reâê·G_Ó _­†ýèŽàB,ÚdãUGøÄê¼®\<ÙãjË¦ºä“\4­½75¶ÃŒø½–F¬ºñ=Ï-])¾”Vj%ÕlÜº’‰)¯ójÕóŸ´¢RÍ…‘W©o(|Ê^³¹àYè…`0haÜu[ŠTNe
÷Ì„‚gSR´`S¸ãƒl¬ËjœœÝ$ÐHD TW™Âs_‡{@×S­oÓr&òŠ÷*¾®bRÌ&ýÒÛ”{iÿÚºe 3u)L$?—¶j)ý—¹¬„-y*N¬(¹á•Èè.¦§¨B™
Ë’ä¥5éë$¡O2Vi–y)ØBVóÈVsiAV×&Cö~.°™J*^ÁèqÜ@×U¾dJˆš[Ì…‚gE$	1·s]Ñ-4(¬P hæÑ¿´ þhô÷¯Fôïç›Hp>5ºè™°3LÄ„ *“â0•ÂX°¿#ÆnY¢2òÊ?Ã€ ¹Ö º.ƒÀ3[Ov´dáÅÐ6Rk*Œ Md0|#.Â@ˆLÚ‹'#÷ëô6J(^ôÓÚ>õ²ÉåâõÍ×Š´6O·AØ¡,m¢5HR·éÝim“æÝ {êémÊ_2;ªîÃ…?­ß6y˜Æ)qcø’Í5ÎK3º<O©¥Âéo²Œ<Ðøb1NÐÉ$ aIão‡ª}³YLr^@[ç%´yÞh§÷A¬7íð<ldYá
C\íaÍ§ÀO1:é	Oqq1Nš‘ PôÑ}gŒÆ9P5¦F± Å_DðnAf8mÅMÕCyW(c I•IîÝ`$1;Cÿ¨Œ¿GrFë6¹ ^Æn¸ê²Çs?–©Ë}¦.wÂo©ÒóÌôîÛ&½^ïlþù±TÒ£y 4I³÷Â“$ìŒ(t­Ò¦ßNS¯Û{¡êeÜWøÕÃzXá×}Q…_;ƒZ—ï¿Ñ9¨Nµ÷Õ‰ØÔZõ±„©ö® F"º‚jDÚÑ‰˜âwàðPŒÌhöÁ¡û!¹çèP@dWûØÐaLò¾Á¡5|v‚µíÍ.ïòxì_:EÞ#4á²+”}`è``Þ92ÔÂfW@û¸Ð!½G`hŸ;ÀÚ‡…‡êãBmtvÅ´
Ó»‡…Úèì€i:¦÷Œ
µÑÙŒ)vX¨;hðØ†‡ÀïŸ"/-«-ž­<E>˜ÎEsÄK:nŠ×š3£¶˜Ë4>¤,-Sx0@«“”ç9³uYjS±RèäÍù%ƒJ'¢áFÆƒüöéÔ%fVº‚þÕ k‰M—ZY	:„‘ÀÃ	˜ßzPYZDåÁÙlsåFÐw96	°Itvv¼ê&ûßªNç*ßE®(À ž&gJã™f 3P…ÂuI”±óºÊô­¶iâmeñžª¦ƒË<_ð%žBF²€ $Q/x’ûYTx~óÛï¾óá‡÷¢ãb®óÖ‘vß&
}Æý×3ëm$Ô\C7DNÜÄõ­¸÷=ît¶®ÈÆ \Ì$hšÈZÕôQ§`+ðˆ+²q¢ö²äÕüuè^âì[QÙõ³‘~ÐÃp'ÊFg¶Wã«Ð’¤[:çj&²Akxí–…r7úp\Øg„¸ÁÄcn±3¡R^~bVv‡3Ê{16d=8É$©àé$Á¿µAÐ2Ð ÜN$WâªBîFà§è1¡ÓLVávBªõŒK8X‰µpX&P)>DÒ&K7ÃˆÁ€3Qæz	„ñ‚ƒ¡¡T.f2 ¨½mô) ­Š¬²'Ð^¼58Ü Ôk>»2r6Np¢8)™G˜tÓý
Š(ýH]X¤ë:ÏÐé{çÞŠ^BçÉ¡Lè3t¥àŠ‘^ä6è]*ÏÐNqMÂ*n/`i„[Û^/Á%ÐyÆž*ífŽ§&@›î›Aë´iŠh3„ú Ë¼6˜¤íG}Æ%X®©ß“ºR¼Tn§•¹!ø½Æ4"˜ªr«‰×¤Fó>
­1Ãì¸˜pîY©,©ƒ=å—ÐQ÷ èû²zÎß=þTƒ`Íè²ÿ„¶=0·Ã_ã$`áf|ƒ	?m5ðºÒ¯dJkÑàVqÕã8ÄÜ“i#n”’Ë˜dë¢dS™»´•(,±F\ñÖÇd*¾^íU·ØY9;ŸäRŸ/„œÍ?±/Rw0˜À„ÊìJpj0ê¼œá”ö+öù·M
7¥à@[õwÜ²·ï`ˆ9ð“8I»Cö_ž×ÐÁ‰¨B(v:"OG£ÑýK%òˆ/Í`é:2b- Æ—£È¦e}þ{­?µüç½ÃA‡«Ø¦¸“4˜R‹;_è oh?6¯S\e{ÇƒÂe=›ó Ì›Ÿµ÷Á¾_°wÖH‹¦¹~‰t@IV4k²–ÀH¦‹¢fÈ¾_9Šºë¤ª¯XJO€sH’“SX*°ÀÍ|.Yµ÷H:ÎJm%±s­?îAZNÖÅÄåw25Ú
 —t,+(DðtŽº“6]Iòåè³oàZÓø?
ÂX êúÙ^žº'ÎÖï¯ÚÝ !5±ŸÚØbv^Ûö6°×æœTè¬'Œ¤ÈQæã@M”>>}Áœþ‡ì­b<Þ”õÉD×8ÛÊª¦ôu°ek(¼¶Y‡6`EJWau)[ÛdjùRqNŒóãa:„³–¸¿u*¡0E GÌç…[Jhè†	ýhÐ=…<=ÛÌ¾žw}ïÔ|{ðƒ€ÃDb\ƒ#Ä‹àQk Õ?à_\—ÅlhnáÖgÏpçj&Ø˜§ƒ3ü`tr¶‚”Šœl³Å9ùÒÑ]=‰•´÷ß‘™Û.v¾Â ÐfÉžIÕŠCÀù¹3ôwuuònzò£»ïö·`¾3Œ ¸]®‹O¤ÜŠµ
è0Jnm(~£íŒ|D?[Ø¡uqŽ£óž#û˜mÞ°iÎgìÙÍõô9"Ä(-~ä’ŽG€>N -ô#q÷Äý"	æ¢©—“ôç·¯ròy»1÷õîíêÂ±1å‰Hôl¾Ÿóþ‡©#Yˆ—½{÷c$ÈuÒ“÷Ý²0q†ÎÁca®õ	PÙÒÌP]«ãá{fë‰ÛT~Mud|ØVV0C¶‚Æ,X\?ÝÂq»àe)LOñ}P¼œ­´ê—T´×_ßÄ¨EO¼b_lñL=V‡[„x“£(zü½ÖÑçäv]” n[%—ýªäO±*ùÚã¡—&$iÕ²ß­×–Âì3µ¡î­¯g’àE.ã%nÔ¶¬qúÊ*G©¬â#í·~Ù¥®J_Nåã–Sé„]‡|™¾†Ê1k¨tG±S–L_8åcNéÞ¶Ü˜¾ZÊ1ª¥t¯SFL_"åX%RºƒØ!¦¯‹rÌº(Ý¡ì”ýÒC9^1” ÜšóÒW@9^”îHvÉtéËž¯ìI'$;ç·ôµNŽSëd+ŠÝ+ôN>n“.Ðu	×ôUMŽXÕ¤3ˆÝ¢5})“XÊ¤v[ƒ5}ý’#Ô/é„]·XM_´äHEK:cØ%TÓW*9b¥’ÎHv‹ÔôåIŽVžd$·júš$G«IÒÈNqš¾ÉÑ
‘tr‡0ÍC…pß/õÚgÂ<%=RfÕ…,Û» d¥mFí‰²_¸qÊøwó”,Ê\bá˜;MR•«c‚Pzgûä1¦‹â®+èa½´«£f¼âž?+ælzYsß¹Çóá­•ã›Þ¸‰9lÒü(„#OS–lj¥ÚÇª9{ÚŒçiã	Þ¬ÄÐqí+|}¦¥³­œ¶—Ff¯Ÿ½¤¬ë×ƒápø‹™¼ºRþ=…¬0An¯Ë­0&Q—I‚#Yµ“îß¦æŽ„C³"­BáÛ&OCŠ%q|nþƒ£ÍèYò7Ó~j]„²	K$Wp¤ñ./ÕkVÉ´î§ðDy›Qž^„ôÑPaÅ«FRŽ58V‡D§lÜÎpkuà¬ÔÔnA×&[Åuèk9hª5PÉ‡ÉðŽ©Ñµº«)ªRð½$w_ÓfÆÙËŽ›”j˜ø:hqê:›së‹E€<ï”ëoÄÂTd6¼ýo´äáþTìëÑ =ø¸gx¹î¦î©qÅ
(×B*—vÑ*¼ãÄœÛt.²:I+™6s~€ç‘ ¬1x_Ãº–©Ñí°’=t×ýÀÎ3‘óeâ{f×ÊÐ†Ód”cº¬¦®Ÿ}„sU$`Î1ß“‰+™Ö93dšldŸ„99LL§X¶+"»]:öyM€æˆäþ2î„2;#×çk¡ø3ò^¡‘Wl´Åg9Åôž+ö\Yí– -×õ-1‡RÉ¢.©­Å—•w	Ì¡DmfÝ[|¯9šg4Ñ¥c¦&XuËÙÏó!¶ÚÎ±gJ·ª@?žÇ ;D7Alä“Ýí„‰½:í£aï¼¢õð|½\Šèùîµá¡pB´0/jk^Pºó‹L§/ qe/Ó·JšT¥ð
¯øU‹9þÒîu®á¾Wì×Ç£Ñx4:Â¿ß|uŠÓáã{<:£O?÷ŸV5¬ãjõø·[yýËúŠQ‡*9vßMT¨'åkÑÌÜÒÍùVØ€)&&ýÏÏk¥s®vÎåŒMj	†Ò~~ó½ŽÜ6©0MO¨¡«A”ßXˆÇåa%JW~’Ú]’r¬_R1u¶¾ÂwO8^»‡·¶ê¿Þ¥4•«é–1ÿ˜
«¨~ƒ_„Õ©+Ê{iQÐÖ¬{\5Ô‰Eì¯­hZëp¿”ƒfŒ»ÄÑ¦žàò&M÷µ­÷ö¤ÚüƒÐjYbÙ(\ÏžÄ¥00ïWÊà–Ÿ‡¦TšBiuâIEü{ºx‹§{œ˜¬kåË`ázk_M1Ÿ£ª®ù{6Å*”Ã![øÈÆ˜}{Ûj}ˆmc°aÌcß½¸k×jWEß­o±˜÷ûèÅ!£¤ã‡¹h“aCÔ·ˆ=iL]vâÌmL‰+üvåÌML¹QXçLD…M”!ýÍ‘Ž‰b­œ©öZŽÂ&ÍsNSN¹n^ÛøõmP&°–»è-ò°Ù(úAøòë´ØdþXPO¡ÃSÔü`Sb}
~õñB‚û}¡Ãiöùüc„Ï©êµ-Ö%)Hï.ÑîÉ…ì‰U."Ç'k˜Rq?Šôà1YoW«X¯Nç¨B.ÿ‡í}(Ó0†äÞÉZ‚ÆË2‡“¯(ì¾‡Èaë—3>3Bà|UTrrã/_›¼mµ Át’*ÛÛ•)£[Ç’G›Ï°Š¹‰¤´?ì½Üá½)úÁø¹6-¶y:b¯§ÑáiäUý`ˆ´NTr¿z&žIÜ{@DŠ‰±GîÄbÏ£ÃóÈiúÁðhxTñÙ_˜77•§ÃxU‹O:¨*@ÞLde8žD‚ÿ‡¯) ’+ì^ÆA]h-¯Waý…¬æq ¤Á]b%WªiNoóp yÙ*­:ø:7×*MÓ«5.µÌð½Kÿª*:ÿ±Ftÿ:£‹¨5wÌÈ»L’ðjŒWî;Êö;®B«n‡B_P§¡ªŸÛ´à÷#/ÜYKãí¬|ÈG¬îùÜøðôµs¸ÖÝŽÓ¶bo:PÝÙÏeË"M?„¹ì&b×ø?{ƒÂˆ¥    0707010001f0dc000081a40000000000000000000000016a102a8900000f66000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.vdisk.conf.gz    ‹     í]moÜ6þž_A (bãv·k_ûÅM‚úú‚ÚÔÅ%í}(
‹+Q»„%Q©]oq?þf†¤D®_v·Y;u+£½ŒÈyžÉæùóCþ<{Îøƒâ2©¯FÌ¬kÁ–øûŠ;lë«»g¿`Ÿ7²ÊÄõ¯Ï¾ N¿²Ý~ö[%Ö+Õdg}#*uY7B”µ¹ä3Õxh|ÀÔ˜NUÍg…è¾ú¾iÞhÄo­lDßœoy¡éNÝ¨¥ÔRU²šŸEw2‘ó¶0}º;²ZˆF^¥îK…à9{Í‚g¾‚AïEcïëZ¤2—)<3¼›âc)¯2™q#ôYßZö¿þ;©ª–¢	Z0S
>U‘‚¡}:…[or¦…aF±$1  IFì¢Õ»Ÿ¿b+YÌ)Z¥%(B‹fÉtîšãÓü“©*KhôSèS9ãÝM¼Aÿ#†öN•ÂŠ6¼™ˆ“e]ˆRT†>§Y¦€ÐŽ¶®áUfR³‚kÃJ•	v$ôõñ¤ïN (IrTI’€N­aFììju–l’*A­²™`<5ú) õUÜIÑŒAªmRÁf\K=}~±)Ølq¸ƒÍ57‹Ê`cÿ ªÃñ7 à $‰Y¸´”)¾ÌrYÖj@€kàÏR6¦å+yº• o9aï"Ä}Y*¬rH’ 7ü‡Ìhkx5ÏEÄ¢`B¾HXÒÕW,WêNô¸öäÐCá÷µÈ©{¬,Ã ŽC—Zµ6„´7¢I à{~rêF*hßš)p_ô,J ZŒ±v¾—YÈ™˜ˆ$8¿Ü·Ç?kÑ¥·¶À«JeÄe³zŒ;ÀþÎ£ŒiÖx×iž@çêýŽz]¯ sWÃÈa´·mîGKÌáÇE˜§¤pô?gGd€®¸N‹VË¥¾æàEnDUÅú˜pûç]Þ94t½1"àWé:ÔSdl¶¶Dç…Ø7× ´ŠŸÖˆ›ŽP0’˜*½òîÉ2qÜÖ†ßrØ
Æ0x>U@×Œ,¹#;èŸãÇàtSüPœ†a†Š‚N[·ˆ×q3eì¦'wF>½ø˜'G?wÅ<½ˆmæFj‚.\ÖJ›ËŽëÃêŒç¼‹™'6²&øÄµH[ ÏˆÍ «³tŒp©ê,éz’0{	XÝ4ªÑÐ`Ð´(v!üMÄðdîã—ˆä”Ê@Re²aˆûÃHbö†þ¨²ÙÖØ(CÝ&¨“±®ªð<Œeªú†©ê}ðk«a¬| ³TûG­2±+¨¢Ÿ}‰\5â¡ÃŸ™=Ð‚ŸCòÀÑGd_‡ØçaLòCƒŸ|ö‚u}f—û8<ö€oˆ|Î(?0ô‰Ñ¹Sl°¨ÌÓ[d}üþ-ŠZã~è¹­à-òj0U¿æWñžRØj!ÓE¸=ªY…T5NyQt[£µP5È[ð%.âµh¸]-ç^~ 
¾º¦ÅG\"lu²ÖøéZUZ‚ŽqÏ†HààÌï<“Qù‹mÎ?újú¹WdO»Š>LÎ+°sZšžÛš(£­ÉÔª
÷ÕÞÏ˜V#mx±âk\úF²€ $©>åI2aì0  öõ7ßžÿôýûÑqµPEHÿMäÛ8	ÛëïgJÐš7[(hï=áABÍÞoœïÑ´Ç«½+ÒQ~@)2	š&²š¶}´)Ø
¼âÊì,Q%{‰[ ¯}óÚGÀeðP-ˆ»NOü“Ð©~{@ý3hIÒö-]ðj.²QÔ½øË¢²îôa/ÜØ.ª»ËÛVÔE•òzH9ˆ±½‹÷­ðwkcñZí² d#®)Ÿ% ?E	fÒøÇ	©è¢t VÃs`™@¥ønê¬í(=ÎD]¨µÈ¼`oh(•KÐ‰%}o}J@ËU:ˆ·‡ë•zÃg›FÎçÀ	NÔ'%Sà“v¸ï¡è‡cY]i¤^¨7+sÎ=
ÑqÛŽ6!éºRpÅH'r	T.•OŽh§6„ë+ôºJu/Á-ÐyÆ^TÊŽ/FL€<–šA›´iˆˆBm€Ë¢…6âØíµ´C°BQ»g­éÞpæg¸Ø.ø­s0TZ9nHÆ}ZiÄ·vCÂÙw1_ÕÁ^ð%4Ô¾zÃ¶ôï¹§ƒ×_à†4ÄŒî…	ûÿ¶Ævø³ílÃíˆ¨EI¡xkTÉL)õn£×Á	ûI‹¼-·J)ä²mYS&™^k#JM¬×SÉT:}½: «îJ'š_ÎŠ+©.WBÎ±ÕÂ=L0Qeºœ6uYÏqHûÛüë­&…“Rp ‘Eý‹2/Þ\@àÉ|-iwÂ~æEœ	³¢b'SÂðd:NØw¢©Dð¥ë,Ý§$¢- ÆçÓ{Mëöò·V>Àz/¬týÀ„4¥üÌ³Á™/4€oh>6¯RÊŒ±Ž	:…a=›7
ÆA7?‰çÁ]šŸ³F
šfZ¸,²D:øìO:E£t¡†—ÂˆfÂ¾íÅÝõ÷²j¯YJo€sH’ñ	„Š%„8™/d)M< $gV+-‰í±/R8Ù–3@¥L¥Ð?ÛHxëB‰ª ;©`Ò•$ŸO?ùîu¿S¶5¨KhgžÚ7N7Ÿ7+eŸ÷Ð‘šØOßØbz^[6p×f”o¬#Œ¤•£Ì­u«4pùdzú³úŸ°7q¶:@3ž©G[iZÊi©të
oLÖá‘Ò]ˆ.e4M¦o _@*Ž‰>sî½])ÄìlgÖ˜ºíTBËž!ŸW6”PÐŒÆ·#" }xrz?ûÞíì{ÏQóìÞ3‰ëÊ¸8B¼è§Q½öü¹‘Ù?Â1ØÒW¥\agl::â…éø´‡ÁÄwƒYŠò’|é€è¾žDËßã9Ç7dæz;ï± T³fG²ŠÖ!`‚|lý¢5ã‹|üÖ>wó[0ß9® ØY®]ŸH1w[åáFÍµUz+ïÐ¶F>¥Ÿ-ìPª¼ÄFG1Úœ³¼àsv4Es=9FBø5J—Ü‚¤å @„~ 2Îž¸’(ÙÊÉ@úñÈÎ«¬|nUƒEs»¶´lD9"=»ý	?nàïáò/5$óëeoA¶‘Ž¼o=eaàôƒ×üX+]
¹ë˜	 ï”ø–évf'•_`/ü²­4#f+NB–¬®Ÿlá¸^ñºÆ’ša`;Åq—³^«.¤ò•HÑl D-xãûl‹g°z¸ Ä™œ­oÌñmpÝVOì” n[ƒ’å•ü)¢’ =>thB’ú/»Ùz«i™}N½n¨y›û‡a<“x/²Cœ¨m‰q†ò˜RãVÚïL~Ù§8f¨‰yÜš˜°Û!)t(„ù˜…0»£¸S*èPýò˜Õ/;‚·-t(yùx%/;A¸sÚçPçòqê\¶¢¸{•ËPÜò¸Å-»@·K3T´|ÄŠ–AÜ-ˆÊX±Œe7ì¶Æ0CíÊG«]ÙÁ=B˜§
¡ögÌ±¦<9JÆ¹’ul© H%6£Øjõ„ý—7V?voá‰sõ…6M›RÞ-}¡³|0#0Þ¬ÂC\óû)`½¬Öº_ä†ûã¥:æ’·QçÒáùô¶Æ¬hTóTŒµÀ\+L'µ«ô9Ò(“(àG),axš
°ä¦­ªx'Ž³]^tžà¼C;|×5®íÒv@”õ²‘Ùë£—”¨ûz4™LŽ±þNÂ+v/A•xÜR†¹^m=ÒÇë¶NÈò'ÚÃí."|ÖÞd¥ÅäéHqO•Kç~r´™>dbõm;{ï}yDtÆ˜Sp¤ó./«×ÌÈ´î†ðDEÌ¨O¯|Æ¡Ï ïyÕIòÂ±l£ßWÈÙYœ–wà¨Ô•ûÐÉ¨Q=í|Ô£.ÁŸª"’áy£~UÿTW‡å³¶$û\÷L,¢ÓÕ›”5éË^\é Z@˜íÌöÐ2Ð˜¨(½¦Íò˜ü™ßâ–ÜG¸ÛHy=Å[†KàvèÎ›ß¾îN|Œjµ¬˜K.DÖ"‰ò/3ëxò§»fHÛÜÜD;ô²'ö¾ëØe&
€Ì}ƒO!t§KBÆK÷&]ï\ôßç•/8¦ˆbþ©‘i[ð0©¢K`uy{3ÃDžÓ!¸=Ùõ¦Ð‘·Ï² üSK$ûwLª°›Zì”\Ÿ+kCñ§ä½üG^±éŸe3x®Ðse­An95••²’e[2R[Ä—Þ»xæØ“”5/ou4G4­ðzÈÔ5­ýOð«qRé”U*Jˆ‡v‡ [Dïƒž25³¡˜ì¡ª?4Xö¥f®lˆ<t‡¨®­oÏ0ù\žôˆýÇóFUÍÈ–Õ,çlÖÊOådïÎpkú]ò®%T¤lË“Š[ktlZ©ÛÊtúnßKÈ±´	¤b–Ú|3’³oØÓ(íËÛ~–Åö¨Z³…âÖõ‡üa–\åß¥QÇG!¶^æL¢¤<†=<PÀŠEìoŒ\Q¼å†lï_±´igf€s´‰@4®{ìI#4É¡f]cEÆ-ã0KSB0"ZWé<s•²Ö+U©ˆßgNt@qÞÑ=ÌYTm‹eq9f:á{tàC±Æ–åX >™°•›Áž±¯ac6»ƒö}>sÍ›v£¬-X_Übàß‡YêCÎRIÇOa†“ážÙ)NÒ<0iT½gîbJxøÇ®œ¹)7fŽ›œ	¨peÈEÿMÓ'C¢h-çUËÑô¸{ÏjÊ*×Žk÷.S>ÿ›àm	§H³È4½¢ªÀ–š™4Ç…"øûæ?bS5}y=5aã_ð£±ý×oúV4÷¯Ùtre•TŸoª‘Ë¨Xb&ð€&›ÞV,¿T2ÃJôµ;|†¦ç>Ã0€§Å÷_³«@nU<I|±û+w@¼Åâ¾j×T)RŸ§kWk0¬ùÔ.…Sï6÷§¼öt·OÂ½­Û$Ö»ívÄ;e÷íwOaÁƒ†¦ŸBDy1Èkü…{º´‹o    0707010001f0d7000081a40000000000000000000000016a102a8900001005000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.disk.lvm.conf.gz  ‹     í]mo7þž_A (ã$Åöµ_Ü$¸\_Ðàšº8'½ŠÂKíRáÝåfÉ•¬â~üÍÉ]R²,©‘ã¸Ý âÝåp8óÌpHÎÐOŸòÏ“§ì€\&õõ€™e%X>/>šÜa¹;¬ìžüŠƒ}ZË27¿=ùšý‡ýä	ò~-–Ugç™žÀóáÿ ˆtª*>ÎEÛÑ÷<×ßÔâC#k‘­¿©j5—ZªR–ÓóèM&&¼ÉMÇõ»º¡²œ‰Z^¦®§\ð	{Åf‚gžÁ`À¢¶ïu%R9‘)|3%´Mñ³”—™Ì¸ú¼£Ïþ×1ªr.ê€±RÐUI2öt
¯~øyøþ¿L•ùrÄŽ@¬G¬h´acÁ´0Ì(6Arl¢jöã/oñA?–(X0RYq3K’ç™˜?ÏôõóÑh”$ÐÕ\¦BØ¼¯òw1`<ÎT¶¨y
rðþ5p	è in€GÉñ)ðá5ºÿbtü¤ÊÃ¡#Pð»™ y05a f6Wy?MkÕT(Mz÷¬6JS]UµEe®øXÕæA%ûG¯}óYYÞ›‰7±$1@ Iì¢åå/ß°…Ìsæ„\i	‚Ð¢žs†·fÆx`/þËTðãÔ†„*çíK|Aÿ£GìRÞ‰´áõT˜€œ,ª\¢4Ôf™ MUAS@‘Ô,çà%
•	öLè›ãQ7œÈŽÉ{€ý‚û ÂØùõâ<YU‚RE·ÃS#aœàG`¤Ñ Áã€TS§‚¹–Ö¬,[=l@sUÃ³GáûòßŠ‰,…˜´‚œA¶Ô¤F³ ú¯†Q žwüèTµTÀß’)0 ú)€ëæ0eä80è/³Z 8Â7•5ó™x~ü·¤kÛêNg2-”WõâñÔO™z‰oäIé`žÂÏÙ±K¨aÃN }kð=Æ©Öëm›£Ï²Àì5üi5ÌS8ŒÐ‡_±g¤È@»â&Í-çàÕb
^¤æàF0º<&½ý}“wÿ0¤\†þ¢Bì•žÓrHƒxòŒ—v
j½ûîH‚ôC’àÓj±>G‚?1v:ç*½öîÉ"qØT4‘‘ßrØ‚kü> •ÃÐŒ,¸;ÈŸcgÐÄNÛ€ã;š ¦aJCÆPP0hëñy@n¬` èøô(RÅÆ¹³³ ?kÔâùsÓ¬Ù‘Øfnª"i~¦3¦o²fi¿þvH3[µ"=ËÅÍZ˜=Ÿ¦àÞÐ³Y™ùÕRS*æÓˆ{Ò‚ÿî%²»iB›ë‡õs×2$ûl}MôøÃ]t$w4Ìj¶Ô2å¹[óèÛ–@vò™ß­r0ì«JisÕ
¢"^·+ðU:­eE.LÜˆ´sà#âÀ1ðWçé]–*Ï“v$	³À³×µª50êÉÎ„‰šÛ ™»cÄ¸9ú^•T•¡I”ÉÊd´¿‰ÌÞ*„¿ÊlÜ/íl”¡l“Q¨£±Ÿ^UÕëó0–©ªC¦ªöÑ_Söså=™e Ú?j•‰]•Z‹>úÙW£c1Qµ¸ïð'ÔÌÚìƒŸÓä£¯‘}5ØÇ>÷c’ü¬èg/µö¡ÏÁìòcc§=Ô×G>÷g”úÄÚ¹[§È°(Íã;h¸ýý òJã†&È¹)¡y5«0•·	~GßyEê[Ìd:™5+ñ3@€*‡)Ïóö€¹ªz3>Ç­sü 5·'FÜÓHA¯KÚ€ÇmòF7@k‰]WªÔdŒç–§NÐùÆÄ"©Q+²‡O~¢ôÚ²ƒ€=I&§%Ø9OD‡/MEÑ³ÆdjQ†gËoŒÆoL£6<_ð%ÿ X(ß¨|Î“dÄØ¥0  öíwß¿~ÿã»Áq1Syß'ò<ŽB~ýûL	:÷a3üÞ$Äöê|ã|áö´ö®HGY…È$HšÀjšäÑ¤`+ÐÈÙy¢
öw´_yö:KÃ£ 
¹Aü—0¨îˆLã3hIÒŽ-ñr*²A4¼¸gQÚŽ[yØë
vÉYöñ¶S%Q¦¼ê‰bl—ñÙ-þÛÚX|LŠ–A'HÙˆÊ

”Ÿ¢Ç¦™4þsÒTÔ† ÕðX&@)Ë£4>:¨èÃˆÁ€3Qåj)2OØRådbDýmƒOÚ2d•=€â­Ááz¡®ùlSËé0Á	:à¤d
aÒN÷*ºéX–×!¤gªÁ{<em¬à‰ãÑ5ÄÓ3t¥àŠŽän'Ó<t(@;µ¹T\_Ch¤—eªãx	^Ì3vT*;s˜ DÜC#h  mš"b„Ð¹Ì›RËVúÏ‚åŠø7¦MÄ•Sœåe‡àCæ.`ªÊµ=²]£ÌûH¤R‹)¦7„€³m1ëÅÁŽøµAnÈK×Î}4?Â¤ˆ]ƒû·ïÛ)ævøæ,ZÆíŒZ‹›B1ðÆ¨‚<}†ùÚ»UŒzÜ Gì½“&GÜJ%—×0É6EÅ&2z©(4¡FÜpLÐ2@^/€ªMÓ«q~-ÕÕBÈéìO¶[¸‡©À&ÊLw„m¶ÇU5Å)íWäù·[M
¥à@#‹ú'e½¹€!æ€“9øZ’îˆýÂó³¢d§'¤ÃÓ“““û—¨K‘xiKï)—!Ö ã«“;4›VÍÕ‡FÞ«õNµ®ä›À‚4¥6Ì5Ã•/0Š¯i=6¯RÊsy(µÂ°Þ&œà¼ùE¼nS]5RÐ4Ö*Çm‘9Â)iÑÆdÁ(e®æ…0¢±ï;G1@wý£,›–RpI2<…P±€P ó¹,¤‰×”*Î*¥%¡s…ÛÂÉ¦Cp "(dZ+- þÙJÒg+ÁÓÊN*Xt%ÉW'_üÞµŒÀÏ”³AÊøŒÃSÛâlõ{³Pö{o -¨	ýÔÇÐ3ðÚº·C¸6ë¤<³0’vŽ2·ÔîÒÀãÓ“³/™•ÿˆ½‰sþA5Ã±jp¶•¦¡¼N°‘R7n£pm±Žév…]ú@t)£e2õxª8'úìÑwv§òf-q}kEBÛ!ž6”PXÒäùˆ h[!OÏîF_»}ïk”|«vïAc‰ûÊ¸9B¸h‹P¼#öü½’Ü}ÂµK¬Á—SÁÎÙÉàtp†N†gJQ™ø`³2Q\‘/í5º¯'Ñò÷xÍñ™¹ÞÅÎ;]‚T½dÏdíCÀùØúEc†“á[ûÝ5¬oÁ|§¸ƒbW¹v"Åú5	×è0*®µÏÝµmü„þlA‡RÅîxÔ*ï1rˆÙæ5›ä|Êž ¹ž# ü¥ÆGnCÒâ´@¤ý@É¸zâ.H¢\|K'êÇ»®²ô¹1TSg­í0›ÑÄÚ.žíù„Ÿ7ðßáö/1’ùý²‹‹·!Ë¤ïÅ[Y˜8=sÐÌÏµÒ•Q¸1€™€Vã“Ï™nÆvQù5ŽÂoÛJ3`¶:ç4Dy€ZÀúéŒë¯*,+ë'¶ƒ@7x9ë¤êB*_­B­-^²/·x¦^W÷„8“³5>9¾žÛ
¢=ƒÔÛÖ dÞG%ŸETò7´ÇûMˆR×³[­»+¦4êšØ[=?ã™Ä{‘yâàBmKŒÓ—Ç<HyŒÛiß˜ü²OqL_óikbvÒÝI¡}!ÌCÂì®ÅRAûê—OYý²£ò¶%€ö%/Wò²“
wNûìë\¦Îe«w¯ré‹[>mqË.ªÛ%Šé+Z°¢eg%îÄôe,Ÿ°Œe7ÝmaúÚ•«]ÙEƒ{„0U…Ÿå5©—áe¨˜Œs-«ØRA!j(±ÅV«Gì?¼¶Âø¹m…÷.JÌÕÚÔMJy7¶ô…n	ÂŒÀø°
3qÏï}€
j¬–ºÛä†û+ÖZäÜu{jûÝ•Óçã;¸°˜I€QÅS1Ôs­0Ôß<„iO²½ö©0<MXrÝ”e|ÇÙQ;ž£Ö¼îÈÐ	ßM…{»t¥A½¨eöêÙJÔ}5FÇXÿ‚—‡á{– 
¼r,Ã\¯¦hƒóuS%	h5–º½ÈÌ"B·ö3K-OŠ;ª¨\:÷£ƒÍÉ}&Vßv²÷Î—GD÷ì9ñFZïò¢|ÅŒÄ;¬ÜTž(5æéµÏ8ôô®ZJž8–mtç
v'E‡å8+µå>t¿lTE'Õ Mð§*døÅ¤V¿‹²ûª­ÃòYÛŽ’ý®íóè†AÄ&eMú²W:€f;³™½¸$&JÊB¯è°<MfÃãV¼•’ë„»ƒ”W'ƒxð!gø	¹n§îImóÛ—í­§`Q­–%s¥Ó™Èš\$Qþefý ÏBþŽÜ,j«‡ûi‡€ŽöÈ¾w»ÊDÎ—‰ãL¯dÄSÃi“1ÃÒµ„©ëÒEÿ]^ùŒcŠ(æŸ™69“*ÚV—·7:LL&t•pv½Jtàís(ÿÌÉþŒIöP‹‘ësemHþŒ¼—ïä%;Ùâ³¬`zÏz®¬±!È-7³B–²h
Fb‹ðÒy{µ¶—³¯9šg4-fÐ<Dj‚…šÖ~ŽGØkœTzÂž•*Jˆ>ŽC¥[Þ¥zÊÔÌúb²ûªFüØ`Ù—š¹²!òÐ9^$¼´¾=Ãä{pxÛ):ôK¯Ïµªš-«™OÙ¸‘9ÞLË._ÿä.—õ§ä-'T¤lË“ò[ktlZ©ÛÊtê·%äXÚT1KmºÉÙöFVÛxÛåÇý¶ØUk¶PÜºþÌ’+ÝÔ4ëø(ÄÖkÃšI‚Çj/°dQ÷k3Wo¹)[G7ìû&6ÍÃpŽ6ˆæu¯{’-ò€¨YVtŸ)xÊð—oPJFDË2=Â{‡)k½TåÐŠð}îD@—tOZ¸‡9‹ª)ƒ’±,n‚™NØŽ.|È—ÈÙÔG#¶p+Øsö-¬aÌêccÐ~ÌçŽ½µµ²¶`q‹5€ïW©÷¹J%?†j†;V§¸èAsÏ QÕN˜Ù„”ðò]1sRÖVŽ«˜	 pdÈEÿEÓ'C h-§eËÑò¸mg%e…kçµ;·©Ÿþ…Mð¶„SaVLÓ[!Š
l©KSsÜ(‚Ÿ}tAÈEØTM_^O,¬üö?Ûß!ÔqÔÜïjéÊr’S}¾5¨ZÎ£b‰±Àš€\Tlz[±ü\É+Ñ—îòZž¯øwÁ þÆ„®7»ävÅ“Ä»¿t¿¤">bq½Ú=•CŠÔçéÚÝkžÛ­pãfsÌ'`÷ø$<ÛZ;&	t½ÛiG|Rv×yGðeÜkXHú1D”·ƒ¼ÆÿÍz9ã<t     0707010001f0b7000081a40000000000000000000000016a102a8900000fac000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.app.winservice.conf.gz    ‹     í\mÛ6þž_A (6Áy]ï¢îfq{}ÁmšÃ%½~(Š5-Ñ6±©ŠÒz]Ü¿™!)‘~wÖNº­ò!H,qDÎóÌpHÎðùócþyöœñŠãEÑcÕ¢l.•å½LÄ#Ä·wÇÕÝ³_`°ÏK©Rñðë³¯hÐ¯‚a?{†#¸‹¹.ÓaÛ‘D«‰œš[¡îe©U.Tïñ*Î$ºàãL4~_Ö”â·Z–¢íÑw<3ô¤(õ½4R+©¦ÃèI*&¼Îªv¿üŠ?K5¥¬¸JÜg2Á'ìšÍO}ƒÑ‹Ò>7…HäD&ðÎT(h›àk {QÒÍ,¤=ø²IàÁûj&+a
„2¢à%¯DÊ2i*¦'l4ººçåõ«+«]¦x.®?¿í³‚W³ëÑ¨Ðé>!²Œ‰‡¢‡]ËdU‰’Iƒ$Î¤H{Ìh–ê´È~«5|œ%\±±$ñR×*ÅÏoü0Ó*[€` $-õ4µ¦Ïì¿¼”#tª¬.
Q&ÜˆFóì«5L6˜×1®cÜã·/ÓPð“£ØZ‰£‘l‰J¨dMÿþ¦=7ÌÍ¤XzþŠM´Þ¤ÒRT‰‰ON­ƒa]°‹!YJ»»Õéîzxe*^ÕÆÙ~“e=€a‰‰TÐØk›Œ ó„ß*MðÍy©Î˜•Dà5/¿Š»H#hÐÍÿ‡xã›=}±ÕíZ¿vàü(_ì<p >¿ñÃ›|±mð_¼†G[}2¶¬n+™]Wmç¼†si”‚žD´û™Ë
|rÉ®üãk  ü à$tÔ”|p†e¼V	tÎ*šñßg<€xÂeV—¢ÏÞó;ÀØ—ˆTÀH˜†Î°áÝ|8rø ø¯'L		ÒË@DôSÚµŠ°!‹¨z¶kS$ÆFb9Òz:°)`'Žpeàí€^u©Àt˜“¼$º.2°Ä9tÔvE8nž½ª þŠQ´1Ö¨?çÁ÷a%©m> 4÷%•)Iq"ö‡#Þ_ü}°™øºèxÿ1x¯‹MûÙ'ËzPÚþ¤oÇ»óÝON÷mLÿÚF
ÈïR¦@v6^X¢\Ùf×·1ãu€XÃÖõ|>ˆÅÑ4æó¡,I»Äg¯‹}y¼…ëœ›»n,sH#ˆ
Ð">“	±û™@DK:¶/ÃŠäòr“†Ç™Nî`H·…6Õm3ÈNçÍª#ÑyÎUŠñ»IJYT¨}ñ ’ºÍOpÅ€À2B×eä×‡É¹5Šá¨ÉÈÙIŸ}[–ºDK	ebgÞˆ¼Èlß!ˆQ€ÔAù¡P† ’*GáÜÁhÜC!D›Ž’Ç5ÊP·£Ç êd†«.:<c™º8†aB~ ~µêæÊ™e ÚµÊ@Ä¾ –¢‹~EÔ-Nþ„È€fü<É#G?‘CìbŸÓ˜äcƒŸ%|‚µ}Žf—}À×E>§3ÊG†>1:Û1Å¤cç£`x
üþ%²Â°ÚÐ3& ”äÕ,`xdë²6üž'>ó@šžK'³xÓVákÀ ­ÎžeÌÔE;¨…ÐÈ›ñ{ÚO†
çØw‘½ü@|uA¿¸{Z›d-ðÓ…VFâ±4Ëååîôwà©4ˆÊ“³ÙæÉZÐ9 Ød
Ê6Ã¶›ìíVN
ÆÀçjÉÔ"[
°¹ˆÇ‚É©;OLOš#ë‚(cfu…Y!áŽüëÊ¸4Ê;Èæ|a@0’E`†—úœc {'*P ûæÛïn~úá½= t‚u[ò¦éc?ì¯žjaˆP3ýÝŒ¨ÛËóó=¦ÒÆ»¢èŒ¸˜JN‡vË ¡miüHžG:gW”ãà»ç²$ŒˆNëèHÂºïß„A™Öþ‚ñUhIÒŽ-™q5i/^üe¡ì‡}ØV¶»éîg01Ë˜–	/þdVö§nG16d=8ÉÑˆ£è4ÊÚð_¶@Ë@ƒP”’+ñP!w£óZð˜)¦÷TþuB*jC”Äx,¨”ið6GrÀ@Qdz„q‚½¡¡T.A'ÆeNÂ÷vÑ'´*²ÊŽ@GñÖàp½RW|vUÊéÏq‰:à¤daÒN÷-ít,ÕA
™™®³¾sîQˆŽ§«èPÆôºR—°åDî"?„}r,@;µÇ’ÜÜAhd*1q¼@ç);SÚÎg=&@››AË´iŠˆb­Ý±½Á¹Ûk?è3†`™¦~ë
HEðÒÊ	€_Yø­ÆôÌ.56gtEj0ïÓûtZŠ)æ4†„³m›Cö3~µAoØ—¶{;h~†Ù3º}öÿmÌíð¯áÈcag|D-«×•Îy%ŠE½[Å¨Ç°Ï~2bRgÈƒµR2y“ll"3a¦¹!ÖˆžC|L¦ÒèëÕXµÁÎŠéí8»“úv.ätö'Û-<ÀT`*5­à¤À¨ÛbŠSÚ/Øç_×š.J§˜¦XÔ?q=Ê^¿…!fÀ“{ðµ¤]ÌeÍjèàXTs!»†ƒÁ Ï¾¥a"v3XzŽŒXBˆñå`²IQßbÖ.ï`Ý
ëR†	&ãJ’² påàKZÏ‚Íë£lç˜\¶	›–æA˜7?[J^²ýÂTj²F
šÆFg¸-rtð¹,.tŠ²0Ý;•(ûì»ÖQôÐ]ÿ UýÀjÎa4:¿€P1‡P ó™Ìe¯tœÚHbçRlC
'ë|Á¨ —I© ú§ðH‡²¼BOf¨;©aÑ5}9øìð¬éüá^ êúÉ—òÇ±ÅåòûÕ\Û÷½4¤&öÓ7v˜€™×6ÃµY'å;ë#iç(uû@Í.ü|1¸ü‚Yý÷Ùë8O 9SÐ°¦Ü-°ej·Q¸²X‡o@DJO!ºŒ“ZéÈŠs¢íj¦Cè7k‰ë[«Ú¦ðäù<·¡„®(£×ö#" m…¼¸ÜÎ¾Žw{ûÞÔ|»÷ƒ€ÃXâ¾2nŽ/¼GE¬V_Ãß—…lh^áÆÕÇ”àÎÕT°!ô.z—øÃàü²…”ê–Î·™‹ü–|i‡è¡žÄÈßã5Ç·dæf;o± t¹`/¤Šö!`üÒúÛº:;9cß»ƒõ-˜ïwPì*×îO`ñxŠp£àÆ€*½•7h[#ÐŸìÐ:¿ÅRgGŽ1ÛÜ°IÆ§ìÅ Íõâ%ÂïQüÉmHZú8Dè‡iéw9lDéæVN
Ò_öìºÊÊç¬Æìñ®íêÜ²1åˆHôlÎ'ü¼ÿ·©#©ß/{ûöM ÈvÒ‘÷íOY˜8}ç ™ŸkA}T¶pc 3Tã“ß3Sí¢ò+…ß¶•ÌÐ€­`!ËÖb.úŽ›9/
©À`:Šƒâ¸ÁËY«URùÄöh5¢´xÅ¾Øá™:¬N„8“£]ôð\ëMð;9…CƒÄmgPrßE%ˆ¨äoh§MHRûe·Z¯m³OiÔ%uoùü0ŒgFÞ‹Ü‡!.ÔvÄ8]yÌ')q;í“_)Žéjb>nMÌ^Øí‘ÚÂ|ÊB˜ýQÜ+´«~ù˜Õ/{‚·+´+yùt%/{A¸wÚgWçòiê\v¢¸•KWÜòq‹[önŸ(¦«hù„-{ƒ¸_Ó•±|Ä2–ý°ÛÃtµ+Ÿ¬veaž*„^Ø1Çšòä(çN±¥Ú[e4H”ØŒb«5}ö3/­2þÝ´’9^¨ˆ™Ò¦*ë„ònlé6¡ŒÀø°
3qÏï§€ÔX/L»;É+îøÓ2‡”¼‹:·Ï§w4pÐ^­y¾zµ&¦=Ið#–0<IXrY+ŸÄqvÖŒç¬ñ7­:á³oÒq@|f)ÓëtI«¸îõûý—#{?ýbÏtŽ7u¦˜ëU=Sá|]#¼þ-¼ÉÕJ'.ûSDø¬H*_+bbò4¤ØREåÒ¹ŸÞ¼§L¬^w²÷¾½±ÌfÚ/ì•dîfÌÖ»\©kº•Ìø©<Q3jÌ“;Ÿqè3è[^5’¼p,ÛhÏ&l'E‡å8+5å>èÚdTE'E¯Ið§*ˆdøÆ¤Ô¿Õ¾µr5š“dßk>ƒ‰ƒaÂ«å¦»òÍ–½¸Ò´€0Û™Í¸qõ CŸ‚Ëcò—d6<nÅ-¹pwr=èÅƒ{†¯Kàvêž”6¿Žçé²d·¯Õ²bnM2i‰Q”™Z?À³@¿X7MAÚòá~$Ú1 •Ý·ÏÝÀnS‘qX Øž™¥Œx
A`8M2fXº–0u½sÑ›W>ã˜"Šù§•LêŒ‡IM«ËÛƒ&&¬ôÈn–…ö¼}®Èð/-‘ìÿ1©Âj±Kr}®¬Å_º‹§ýÚs°ÃgYÅtžkç]ßsX.•Ìëœ‘Ú"¾´ÞÅ3‡r{™±WÞ¯8š4ÑÍˆ!SGX¨iíçe¿'•Ø¥£„xèÇËt‹è6è)S3íŠÉNUøØ`Ù—š¹²!òÐY	ƒ[Xßžbò=¸p@ú;çJUMÏ–ÕÜOÙ¸–@#öîæGœÜîLsJÞô„Š”myR¶¶FÇ¦`‘º­L§ï¶£$K›@*f©M—#9ÛÂ^Ykï¨yë¶Å©Z³…âÖõ‡üëa–œ¢Ôn7ëø(ÄÖkÃšIä‚Ç°‡
X±ˆýÊÌÅ[nÊ6Ñö¾‰¥M=Æ0CO\"Íë{Ò-òð^ÚEe·œ‡Yò˜‚ÑB%gÀ¡	e­+­Î©ˆßC§‚\W_qtsu­\…ÆMX7ÁL'lG>dìÙÔû}6w+Ø!ûÖ0Õòcc0~ÌC×½°k+emÁþâk ÿÞ­RO¹J%?…jL†-«Sº¼#ÍiI£‹½8³‰)áåûrfSVVŽËœ	¨°2ä¢ÿ¢é“!QŒ‘SÇr´<nÚYMYåÚymë6uÅ§a\—pŠ4‹LÓ[!ª
l©Ëªä¸Qÿ÷Ñ1€°©š¾¼žºb¶³1ÝeàÒ pÖ¹håJ5É¨>ßT)ï£b‰±Àš@\Tlº®Xþ^Ë+ÑîòZž/ùwÁ@©óàkvÈíŠ–nÝÅ·Öø¯Ú=•†‰ÏÓµ»5Ö|n·ÂiŒ›Íý)Ÿ€=Ýã“ðlkå˜$Àz¿ÓŽø¤lÛyGðfœ4,4ý"ÊuÄ ¯ñ‚bÁÚ]x  0707010001f082000081a40000000000000000000000016a102a8a000000dc000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.eva.conf.gz  ‹     ­Q=OÄ0Ýó+,u¾ë^##ËéŒóJ#JRì¸OÊq=$Öxô³Þ—»®å¸ŽÎJ‡nI×Ö]ÛîÜcûäV›¯8}&õÃUÍd‚/3*¸k8k%&iáç›Ú=Ï¶
‘â½…ÿxŒ\æ|5ø".d êzm„1ÝÑ"4ÈOÂÊ`R/¿¹haå7d(I)O Av,9¤8—bS-çH1yÐy»§ðç¥Å´·‰½OÒ_úÚØ¶…bæ¯}uq³ínëYrîµ€èì  0707010001f0f0000081a40000000000000000000000016a102a890000136b000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.fs.zfs.conf.gz    ‹     í]msÜ6’þî_ªTJrÝhv¤‹+uJìZ_âÔ¦n½¾:;»R)†gp"	† g4ÙÛÿ~ýÀŒF3ŠG²µKWbY$ÑºŸn4Ýà_óÏ³/Äÿ ¹ÌŽD³®”ø-³G wÜÞ—wÏ~Îìµ.SuóË³ohÐ/qØÏžaÏ¯Õzeêô²ï@²PÉõU­d
·ÏŽøùdSÉY®º÷ý s«ðN­~mu­Òí;Um–ÚjSêr~ÝIU&Û¼é;ßÝÑåBÕº‘eâ^•+™‰WbÁÃ¢~(W5ß·•Jt¦xf®Jh›àc‰,SÊFY÷Žu«ÄÿõïIL¹TuÐƒ™1ðª’xý³	Üú1V5¢1b:m€Àt*š…*EÚ‚Xæ0rkÚ:QÂ6²i- Ÿ€Ç3« _vmU?¤´­U©X!Il…mü“p³0mÙÀ}
Þ¨1yÿè
ø¥Bª©¶(ªt,>,àWà°/¯`TJ‡0Y@­M‚äQ,6VoÚèþ¥8it¡LÛÔþŽÌ¸r×ÿA¬g™ø{Q6ÿ8¡±8ÀX‘™<7+äd"\:×Íú2fZk’{N3;îaŽƒœNoë(±$ ts˜íÐÌ^¡jñMS¶2ñó	ðäd$ðÇW'£€Ð‰-f|@G?ÿ£âŸs÷Sº–‰¿ž·Ð¥	é$ªZðMOþ˜À£+S5ÎFêÒB÷j3>*¿éÇf€q¼Ã¤jù UêVàoÜCÿÿœ?šúŒÐ˜åÔ˜¢q`5þôªVÈ¹»±0¶A<Æê<¦€Ôr,¾×Y¦jU6ž2ªÙL‘Áh+@ Í=êšŽÚ»— ÄÕòxµ”‚Ð®Aô7(o¸‚ÎŒÙ%æymÚê“
ú÷Xú‡”4qø
&Ç.L›§(³‚‡Q«‘ßÊÆ4¦^ÅkèK(¢¶@â».f&‡g¦.H	ù/ÑÎìKA´ÏÉ„ÔETFÃß`rkÕéP7ç±nQ>w‚Þ™Èö[,®@O¬g#M$w²²4WU­TQ5Wrfê§ÇÓ§â4ŽÄ»J•ïÿúXé<ŽéÐ+«Ñ‡TõR’W¶k$d`Šü“‰)
ôœÀ%hpþc&»›xƒþGŽÅ{S(&ÝÈz®ÂÉGU®
˜èuà
À ô£­*h
êP.m€J•8Uöæù¸Nì"K`º#I$T3—×«Ëé&¨¦~^”àyÂ8Áñ‚‘FƒTõYçIÏ¤ÕáMBûœ SV!°
Èm-IzkêóóãB.ÕX¼7Ý­h*d‰NP "lnX"„DIJø;Hæüë¯¿Þ)˜®’q’ù^eºPöôº’¡5 ®¡ßAÌïÖ?P>Èk SÕÚ@ÿÖÂ€e¢g‘ø£g¹ZªïKY
·,%Ñõƒ]Ë|ü³,^juç,‚¦†»=Dii@á®Lˆ %\Àž½§›Ë^u“Àê–Ð#à×0VKtÊ|ýœäöï»°ÿzêuh¦%LNðVºŽ«èk‹Qp±×<ston4/£’€øZmOm€¶†g!^¥9ðbØ nÎÚŠæBµ» VÒâó©†ÖÀ"®Qô$ð_âË 	Ï¶£ð¢¬6ÌDØ1dš•¯äfJë´q$ŠS^¯~²¨ÅÓÞ®É®'±oš³¥¬®¬þMµWÂ_¸¢·bÖêÜñY†Ã¹™ë&±¥Éa‡‚Ô64… T0™ë¾Áéÿb¸yyƒÇãçü"<¡ÁSk\ÏÇ&Õ\œO¾ôËÎ o(;øtUë ‹/„	¹¡›Ï
C‡þ&†òjEXšq'ÏTÉ±¬Ú®ËäŒBÄø¯Ní&±¶ÔDè­šËÙÃÏƒj©"{O~/Žiç7ÍxÚÖdå¶—fòFmVøŽ¬£)]rX°˜#@Z•)Ó±ø†$ÕM…ƒm j3ØŒ¡L5¯e
â‡™•¬Ë¬±u²…ž/ð­ÅùáÃ»ï„Î6ºŽ …šÞÊpäˆx±@¼ÌP mdÈƒ†Ÿè/Em`Ü!”ßx|ƒP¼P!£«l‰ÑQÉÀëñóûÈß	cUÑÔ ë"‰‘\ÔµT­qtÉ)BidlHé]wÇ¯®3û°|“-	ýüËCÚO»ÈÕM$¹7K`w“52LÈ4Õh_á÷(Ìå˜	,‡þíàø¯­iäSc÷C*:ÈèÕžgN°Wà–Âr)ó–|¼®S×]Þb£Ûá¦vNJèëÉHð¼†NÌ`0¹`Cwµ€±é*×½!t¬ÿã¤æzpªn.ÅÍo{Q'ïÖ)Pè')ä›ó±gÍç.eßO4pe·œƒå Ò·Ë;`ÑÁR„Ä~„ÔÃ¥¶¸[êaÔa’’¿[òOHìÊü3Žs<Ž°os•ˆ'
~²ðaiÝòÎYTg‹»ÀÓ4› çG]ËQäÌH_I´v;æåÀ‚©Kv*€ƒÄÍ¨K~çKƒ8ŸLŠ]˜ 8!Háª2¶¹êF5,y^w»…¸‚Lj]QüRÝ¨ÃM2kT¼§î29“” v9íF2|	V«um`Ý U×mÅû÷î&
o‡dîVñ¸EyQþ^Q†$VN7"Ñ÷#‘¹·á¯2»~GVÊ·Ó¨£q?¹š!ÙìHšiªc(¦©î#¿¶æÊRË€µ¿W+‡
µVƒ÷s_‰ÎTfjõÐîO(™{Hsp~>N’Gö~¼Dî+ÁÁ÷y•üXçgC>÷ëàúM/?Ö÷qò¸‡øÏçá”ò#]ŸX:wË;¬§¶â¸Y†!¿?©¼¢òAàs[B+²j,0“«nÆ¥ÓqðÎå¹‘01ÜŠ˜òŒÒR|Rx¥L……QrI%Kð@¥jLµãÔ*¦g–¬)ûsäZÛ­µKp±sª X\ÎÆâ™ï,cãJ½²²„GO'}Ý—<v™ÒHgJèy‰Y?(Ì(ó²­2vÑ6©Y•aÚñRd¾’kL©@° ¡é´üƒÄšLñ^5À ñý›^ÿôçœb±Z˜¸*Ö½3ªYúëï§FQÒ§XèïîÁ”º½9ß8ÛÃÉpÖ›"UF*ÕÀikÓÖÀ6]6@®H/§¦ßbºÇ+ß½)%ÒâÆJH‰2BÝ ÇþITŸ” "iô8¶d!Ë¹JGÑðâ7sºß´ç_Ø0§G¸ËûRJU™Èj(þ9Š²½ß,7NÇâiÔT.År_uC•<áÆ;ZÌÓÃÿ8I*jÃ{öa*_³ Í(¥€‡0«yÍ³0Œ¸ÁÌÁ*7kÊfŠæ	WxŒ%× zß>ø¸ÜÁ@Ç²Ö`pû„Ì›ÝÔz>W¸‹0 #¥ÀˆÐ.½°E?ëòÚ"„úŒDgÜ#÷ð(Ÿ®¡)u¹ Žä>pV–ÌŸ
PO¹ÌFÚkp0}ÚÆþÜž§¸+Î3ÇÉH( '²c#h  mš"b„ð™Ôy‹§(`¹†ã~ÐgtÁrCýžµMWê‡+§x³ž‡à×Ô‹ÁrË	Ž[Tƒy‰Wj5Ç|ôpÜ+‘âD.¡£Üø†}éÛ¹§Ãs0©|F×`,þÇ¿Û	ævøÖrÇyÆG©Ý’+Ílmc
Ù`ê?Ì×Þ¬¢×ã8Æ=ô¬Í·RÉõ5L²mQ‰ü–Pãö±IU:~½<ªvÓÍ¯fùµ6W+…Þÿ\ëÙ{¨
L`ªLmO˜+#®ª9Ni?cŸ¹U¥pQ:Ç´õ@£þ“J~|CÌ©D0wÇâ¯˜áp	V±Y)UŠó	Éð|2™ŒÅ©ºTùvÝÇ%ß§ò¹XZ˜K?¹C²IÕ^}Î‰ŒŸ‹X·]*`Ã¬\ùbvbø¤Ðy“Pi˜ñi0èÖ»£"`Þü2^wUNÉišY“cX$Ê¦r®SD0ª—«e¡UÅ½¡¡¹þ³.Û‘P0ÓéÙ9¸Š¸¸˜Ïu¡›x@åÝ¢2V:7úÃÉl‹gÙ:©U ÿÔÆµÛC”LÈ;m`Ñ5¾˜|ùG¸×uäW/a, y	ýŒÝSnq±ù|³2ü¼W€Ô„~zÇ°°ÚvÐc˜66R]nÆ<åk…|”.ŸO.¾Ìÿ±ø1®ÓÑœÍL‹³­nZÊ )më…[‹u¬=*xéÞ¥Ž–ÉôÄPÅ9Ñ—Ž~àHaË¥æ¨Öx,c	…)<8B<¯Ø•0TMãú[!Ï/îFß€»ƒmïkä|'voA3qeŽ.ººrdïX|o÷Ht¶lƒQéÃ!âRLFç£¼09»èEŠÂÄ»…Y¨âŠlé ÑûZ’­Ó7¤æö=ïe	À“&N£ò6*:}ÎŠþ®mÎÞegoù¹kXß‚úÎ1‚Â«\ŽO$ÒªøÀºF%-ž¢åµ¼“6+ù„þìA‡1ÅF<j“9ÆlóZd¹œ‹Ó	ªëùs.…ä¥ÅK. É8éãI?<tÒÌÙI¢B=_ë|:y>âuÓ—MCçàPM5à´`4¤	žÝþ„Ÿ7ðßaø—:’úxÙ»woÃê¤ï»·²0qúÎyØÚ¡àÆÀ‡¾mÔàºžÙvÆ‹Êop>l«›‘;Ûñ<Dy€ZL•Þƒq»’U…'ŽÛQ Ž^)z®:—ÊÔ­B©-^Š¯öX¦AVç„8•ã>u|\çãCîé” Üö:%ËÁ+ù,¼’C}|h×„(õov«õÖR˜}N£¦ãÊÍýÃÐŸ™z+²]\¨íñq†ò˜ORã"í;“_îS3ÔÄ<nMÌA²; )t(„ù”…0‡Kñ TÐ¡úå1«_Þ¾Ð¡äåÓ•¼$ÂƒÓ>‡:—OSç²WŠ‡W¹Å-[Ürˆèñb†Š–OXÑr°sb†2–G,c9Lv{}˜¡vå“Õ®"Á{¸0OU„žØç˜cMyr”Œs­«XSÃƒŽ(y&V£XkíXüMÖÌŒÿîZá·4æê+ÛÔmBy7\úB‡YcF`¼Y…†óû)@56kÛG'e#~zä“÷AçÊ5ëÉaèaLÀj¡F•LÔ™ÅÃ9)”£ôÂˆÏ‹¤€B1`d’(Ðäº-Ëx'NŠ“n<'%xÝ“¡¾àP£(êÛZ§¯N¿¥DÝW#<&ë_ðdQ¼Â{	¦ÀóÆSÌõj«‘mp¾n+úæXx8³;2‰N1w»ˆðZ>½œ©Åàé@qG•Kç~r°™<dbõm;{|yDtÈ¾c`¤³.ß–¯è(gë§°DyŒ¨™Äš0)?ÀUGÉÇ²~_!—;Ž~v‰×]¹}&ªÇ¢jÔ%øS•@|Ê9<‘Õæ7UöOuuX>kÛQâçº×`â`ŸóØ|$:—½¸ÒÔ€0ÛY,øÔ~àÐC×§¢Íòü5©Œ[ÉŽKî%Òm¤¼šŒâÁ‡=ÃGÈ$H÷™©šóÛ×Ý1@£Z-&se“…JÛ\M£üËþøÙžÿ®MŠ'ponîG¤zÚc¾ïv•ª\Â uŸ‰…O.§KBÆK×¦®÷ÎûïóÊñ“.˜-
÷uÒæ²Þ:å%ÎÛ›¡²Œ>ÿÓƒÝnyýÜ¢Â¿` ñï˜TÁ›Zâ‚LŸ+kCòþô?·öœì±YÌ˜Árí=iž?*#
]ÒaóÄ¶/½uñÈáoHYYÜjhNû“†HÒ¡ó¤?ÏÇøÖ8©t"NK%ÄC?ž‡Bg‰Þ%zÊÔL‡b²‡ªFüXgÙ—š¹²!²Ð9~”tÍ¶=Åä{0ø©4èï½<·ªjF\V³œÓ·:ètÒ÷¯ÿâ¾,ãwÉÃS++OÊo­Ñá´ú.U¦Ó{ûQA‰¥M@³Ôæ›ž·àc(¹ñ¾O±a±{T­q¡8›þ#Ì’£/ùYÇ{!îÄ©T¹à±ØÃ˜,Ê~kæŠü­þ­¡Ü]†M;£“r³èK­þ,[ä-ò€h³®èc2`)Ï¢ã²-ÙB¬ø:ÁQÖziÊ3*Â÷¥cAaþð Ã=ÌYtv_GÂ²¸3°ø¯±g¨Çôí”ü¥øÖ0Íæce°~Ì—®{a×¶ÊÚ‚øâm û>¬Rr•J<~
+Ôw¬Nq)0€æAcªƒ0³)áá‡bæ6¤l­71@á.È‰þMŸb­ž—±/GËã®sŠ™ËóÚaêFÎÿ…Uð¶„S„Y¤š^‘U KõL7µÄ@üî½B>‚S5}y=u!*Äìgcþîoß+ æ¾ãÛÑÕe–S}>+T­—Q±ÄLáM@.*6½­X~itŠ•èkwø-Ï7l†;` ?—Ø¿£@.*>úb÷—î•ñ‹{+ÇT¦èR$>O—£5èÖüCá4ÆÝêþ”wÀžîöI¸·µµMÈú°ÝŽx§ì®ýŽàÉÁ-xP· àôSð(oYÿJwü†±Š   0707010001f119000081a40000000000000000000000016a102a8a0000019d000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.vhost.envoy.conf.gz   ‹     Õ”ANÃ0E÷9ÅHÝ¶€
–ˆ°C,Œ3Á®ÇÌØ)âîŒ]Ò–+"^UùÎŸ?ï«Y­–<Ý
<Õnt$yyJGš~f·lºeÙu÷mÙûØãëC·mK_Öîºÿ§=qyJÑÓÎø(ªm<•”XJæ1àqØ¬ãKñŒ§7&HSÓèÅSôñéò‹Òã`JÈ§àoÑìð½J>:dŸM´Ÿ£š®Á¡éçº5òA—„ÖÞê'Œú®­×,ÅùlBð’4.VŸß9lp9§™&€ì¼ ¾&¼Ð—¶³t5ý?SÉøÿèß?üùFL·*l|1«'²À@|(¢ýZ¾Zƒ&þ¦K»züFG ‹”°Xg¤o1$"gÑÏ‹z÷JU‘SÀZ@ý­úè•~Õæ*d{ç­;ûJj±^Ë7Ö„ RR"Î’ú93ê¢íBB6Ú,E0³ÿ™•NÀ0B¤¬ùŠzMut¢(^×ò[ãŸujå]÷zÔ™û     0707010001f0a0000081a40000000000000000000000016a102a8a00000209000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.pool.share.conf.gz   ‹     å•MoÛ0†ïþríšûŠî¸ã.ë­(E¢j-²©éÃ7ì¿’'kàbk"Àt1b2$ß‡4µZ-yª,xr8Gd¯ ö!ÔÂãÃ-[Ý²ìªû,våM«pÿPÝÑ·ƒìªÊÕï°"¯>‹p"Ölø°àÉ”‚$'¶§LŸ…˜-¿'ãQ[j‘l<÷³þ×:kZÆÀ¬ýàJ£„Oðˆ-z#‹RŽ${ÜÕ:Y›uiˆõ8
—!#ùj
Ñ´Å˜ÓAG65Nú}p68;X"ÚX×œî¦D¿=/xº›Ü›Kå¾×áÕ¨3’>Dl hòˆG¬
;#à“‰uá6‚`t9ç­f·µ»¹x©¼îþ€¤¶CòïP[ÜŸüÑåµe»+­b„¶–än„Ò£ˆ†ZØ4ÅÇåŸ!sOC¡ãáÎ£Ë»ïy'J&¾Ü–2ÛnÖÖ„¬y†ïã=Bp½Ì´ï…ôÎ§%ú…ÚW¯Ü†R'dãÞÍ0óÔòî%Æ?n`ÂÜ4Ñ¸ELad*Ù‹]¤o!Ø	›Ê”ŒùQ.®?eÀø·ÖÉ—OÛo|i–7Gá®=5÷køŠÈÃì‡Ûj­H®§Ø<ÜÙñ$Úd
}Å¾tëyEc×~$„µë
     0707010001f0b1000081a40000000000000000000000016a102a8a000007de000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.nscfg.DEFAULT.conf.gz ‹     íY]o7}Ï¯ m°’*¹é>¨I°®ãéÆuÖ±ûR5CÏS~HQ±?~Ï%‡%Åu‚xñ<Œåáðòòžs¿8ßåõè1»Ã‹Ä½<}u|õæò®ÄÝ­vwk»G¿u›ýý©z-Ö+mÊñvÅÂîÄ¤­0Ú¿Ã‹ìbÝòY-6Ë]/hÀˆ?¼4b«Ç+^Û0RŠ9÷µÛ*˜¦,Á »0qÐ¶¢sY°¬JYÐkW¥,±#;ÞÎgÿÝ.Phµ&[`¦u-¸
æÁò¶ÀÐ•¬5ºÖÒÇ%°lµŠµÚZ‰MØÉv„×µ^añ¶•ªÊ(ÑˆF›uÙoTcEë™·¼‚|a˜f)ˆzó¾ÈâF0Å7»ÎÄm^‘ŠR5³×²Ís'µbzÎÜBH,à¸qÐj€íý°š=f½¥´„Ú=%ÄfäoeÄq²J	]­ö¦l%ëšÍ“•ÒÐÁð Ñî>BnÞ•z¥¯¥wœ·L{V|m!¸ÕÆAÐtª¾åÓé€±wÂ1©RÀêª`¡à¤+­	AIÇA®o/µ°LiÇúFIÁ\”ÓÐ¹ Ù»\øA÷/“M#J	;Ökh
nb·¾ žã¢)ÇSÝd²žµÜ-^$5¦°&Z¯Wãi·¥A‡ÊvcZ›ÉÙîö
»¤=®*ò´rZO¨¸\Úm&)iƒwtá‘79‡i•.ƒ†÷Ñ;>Å1öy_Kë&ˆcÂìR¿vÂ(²ýŒ× uØ¢!Â121¬Ð¾.É#ˆ3KNTyyÁË‹·ÌJçc˜’!L±¯;›~ÀåK.kblä`X‘¢b&
&Ð~Í™]«ba´JÑÄ®ˆgs°|\ôóxJïL÷ï ä7¡-Ôòï zƒç' ýÌò–¶Á8¤è‹Ï	Š'§—Š/O¥;PB€<¿Àýõ/ôüìêîoÏOè~qî/Ãýgº¿½ˆwzôŸ«ã7øsqBó.NiÆ»ËŸp¿<#©—ïèùÕñå^xUZõIÀ†K\.€M3£}µpÔº<,M›î±™wñÿ„C0ÛFâ03-Löqs—‘Oáòà­Î–¼ö!ö_“Á‰ðüù 70J–÷–P¤@ õ^–Ï¥/^°§££#–‰Š¯†`0[û¡úQ–Ö¨„‚ÑŒ˜#”¨"·<ÀÙx+é*•ìò²Nð²«q6p+ *¢ìa^9E6`¼W"u»…&òÌç±JUaÚnx€êÏ÷Œr¦µ.®'N6"ï+ºÿ~Nj(QŠ’ù÷k¢î1ï[ÏâgiäZ „5ß-1ð(Öð÷²ñ[qéY*g·ãk±ÐÆÄï°±89¦ø~Ÿ&Ó(j‰6–¹PÒHŠàn!óš¡å0#m sx ),qŽ÷9±?[hëˆ¼‰ø…7`³J¿ølÃ®'VÔðmÂrIîÇ2˜;áëä:T;fEíážfcÊt‘öôî¦X…s•\-¥á[6ylm5™Õ×ROVB"OÜS ÑêSÚ–R´B•v|Ø¨ShüøýƒpJåDµWÂýœêõ9ô­á°K4ÁTö+%¶1B£[	4·£ahYFÃápÀþ²OÔ‡ÙpÇ	¸=ÓÂï‡ºßÉ^;þ%a”á@Ñ}A”‘“xŽ5"•Ò:œ#Tj»˜ÈÒ!D…¨¥zù«<EÚ¤5B›Šéufuí!#-dcy ðƒ“½
ŽÎ›¶FB…§¿‘Ê¿gE˜²:í¦SÖ C¶Ì«Z6¹½—r0ˆÓ9‰TÛÓ'Nu”of0A#£­ —Ké*¿3ˆàÅ‚l'5ºÛéôûáWÿÂØFüO‚ð(Øzîv¶qÆÑþûn¥ãû‰Í†*‡5ná³] /±Aç èÄðqXB£è+©{” Ô[OG\ôx4<zÊ¢1ìõnÛ;÷gÚÓaJ*ÕAxeý^º9â Â²	>µ@á']žŠÂ!WµžJ¾XóÅR5J>*é´ š$î$¤sr®PMÂÅ4ÔØœçí°)Î":ŽþšJöŠÇáð4a˜"Œ:“ªŒçFäë8pä÷½3í+è*bIb¨©PD²aoÔ;¢ÃþÑB†ÜŒL#šIˆr_¸[ù§ØÁí48 ýÜOÄ©ùÎp›­°±ùfçÞõÏçý³øÞµ¬k8V…æŒ¬câ¡RÁ-ùp&Ã‘+·Õ©ª’ÿm ‹î7×-PkÝLè@Ýèú!¨õc6¯yÅ¾’#žº®;Xµô¨;E¤ ”gw Ì£w…Bþ¼“SBú“sÜ^Û(Ÿ;'šÖQH€’Ö7‘ZßY"×°ò:|AIá™~ç§×A‘ÍiÄùùY&(*Ù1ñü,ñù))‡i)¥Á|&[w{ çw¿þøN3ëgvÎ¨ùv‘Îš¥C"4¨ŸØ(§lFAwtaéÛR+ØÿÀ×¾ÒÎ¶&êÊîôa·Î!Èf<gOo‰‰û#wç¡ÝÏ?‡eÏƒ»~j"'nMäË‡L~÷™üáÓöÿ9IÛ•»FÒÛðy 
»6A½L@øÊ˜× ÓäßË¼, ¶#ÕÿÃfòÑ¸"    0707010001f104000081a40000000000000000000000016a102a8a00001057000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.service.sync.hp3parsnap.conf.gz   ‹     í]msÛ6þž_I¦“dNVd·½nœ¹Üµf®i:—¤÷¡Ó1!’0&	– %«s?þv È/¢l)ªSúCóe	ìóìb±–OžìòçÑ¶Ã§—e:`fY	6«¾¬x­K^Ý]Ün[·[Ý=ú;û¤–e&.{ôuú,èö£GØ…±\¨:;]µ„×5_Â™£þ ®tª*>ÎEû¨u#ðD-~od-²+'ªZÍ¥–ª”å”N~ÏsMg21áMnV­þI•tB–3QKÃËÔ='|Â^±™à™o„`ÐaQÛóº©œÈ®™ŠîMI-ðÂ?ñ.Ÿ03ì‡ŸêÎ*ˆÅ´(3–ª¢àe¦áÀnýÆž=c¥nÒð|~^‚\ý—V2Í•sQòs©M¤ý uÒB€œÕ3eØ/¿€lP¾¡Ãð@ÑT7‚ ðÚ½ƒq®Òèòy¥´9o•pPHZÍÿ	ˆÿÚ›tÖ²2¨gq)ÒÆÆ'FÔdµÐª©S¸ÃÓôˆ§šš´=I˜=4dßÕµª54$ÔŠ)î$âw2ÛÀ©¯Må]¡$U&¸wƒ‘Äl!üSfãeän2Ômr@ŒípUUçn,SU»0LUm…•çYÝc¸k›tz½³=ºû·Æ²TÙ#ÑÏMÒì½ð$	[#
M3ª=¦{ÀÔéö^¨:wÁ~õ°îVøu_Tá×Ö Ú‰kê@µª½¨VÄ6 6eŸKØ¨jï
j ¢+¨µè³CÛ":àöœ
‘ÙÍ>9t?$wœòˆl‹`ŸÚIÞ79´†ÏV°ö©¡Ùå}sCmàë3C{²È{¤†\¶…²OíÌ;g†"l¶´ÏíÒ{$†Öð¹¬}Zh¨Þ1/£³-¦}Vh˜Þ=-£³¦}Rh˜Þ3+£s;¦Ø`Q~’ÜA‹ÇN0Ü~?ˆ¼Ò¬ÑôÜ”pMø,`*oÍÁù¹„É	žó@ê[Ìd: •š•x0@•G)Ïs¦›ªRµa•PÈ›ñ9t”.¨Döpq/?O]2Ü)•ö5 k‰®T©%è®‰NÀü&À3©•g³í™kAßf‰À&ÑÙéÓU3ÙÿV¸²†o¬ |^®™ºSäŠl!â±`rZ‚g¦§
¥šŠ(£gÉÔ­¶}Ä£ñÓh¤Ï|©A0’%Iù‚'É±÷Â€Ø·ß}ÿúãDÇÅLå!Yü3Aoã0l¯?Ÿ)ˆ·‘P3í½%s’P³×§âÎ÷è!.iÔÞéÐ €‹™MYMSƒ>šlîqEvš¨‚½¬¸™½òÍK@kp9®„[t±8M\§‡þJè”^Ù_Ð?ƒ–$mßÒ/§"DÝ‹Ÿ,JûàVöÀU€5™˜;&fsƒ‰2åÕgfewX»cCÖƒ“Lw'	þßÚð_®8€–ag’ ÙˆKƒÜÀOÑcB£™4þrB*º‡(ˆÕpX&P)>ÒÆK;
CÁ€3Qåj	„q‚½¡¡T.A'–@ô¼Mô) -CVÙh'Þ®WêŸmj9'8Qœ”L#LÚá~Åj8–å…F
é™jò¾sîQöOeLÇÐ•‚+F
8‘›H *t©<p,@;Å˜„®/ 4Â)†Žã%8:ÏØÓRÙ‘ãé€	Ç&»fÐ:A m"b†Pàá2oj\œ_¶ÚÚŒ!X®¨ÝãÆ ©^J*ðW’Óà~oÀÜU¹VDŽ+Rƒq…Vj1…æ…~ÆØ{e©Iì)ŸCCí 7lËê>wupûS€˜ÑÝ0dÿñÏv ÀØÿ;M<vÄw9ÿ«7FÜÈ”bQïV1êq²ZLšyp­”\^À Û›È\è¥6¢ÐÄqÉˆÉTZ}í€U7ØY5=çR/„œÎ>³©[˜
`¢ÌôJpZ`Ôy5Å!íWlóo×šNJÁFõOœ²7ï ‹9ðd¾–´;d¿ð¼Ž…YQ²ãax<†ìß¢.Eð¥í,GF¬¡Äøzt²iÕœÿÞ(Ã{Xo…umLHSœIÂ´T*œùBc øšæ³`ó*Å(Û9&è†õlZ+aÜü"ž»vAn­‘‚¦±V9¦EæH»÷¬É"¬Š×¼FÔCöýÊQÐ]ÿ(Ëæ’¥t8‡$9:†P±€P 'ó¹,¤‰çH:Î*¥%±s­=öF
'›bÁ¨ i­´ úÓÞ¸P–Wˆàéu'Lº’äëÑÿ€smCào„¹ Ô%´3Oí'ë×›…²×{hIMì§gl0=¯­{Ø…k³NÊ7ÖFRæ(sy 6K‡G'_1«ÿ!{S2ÎGªæh¬m¥iÐÖJ°‘R7.Qxe²Ï€ˆ”ÎBt)£i2=ùRqL´ƒ¢ÍÀpòf-q~kUBi
OŽÏJ(hFíÛÐÞ…<>¹}=ï:ûÞ×¨ùvï‡±Ä¼2&GˆÞ£"Ö@«Á¿—…lh/ášö¡bVºÆt;e£ÁñàŒŽNV"˜xàf0Qœ“/íÝÖ“hùG<çøŽÌ\w±ó– €ª—ì™,£<LŸ[C×˜£w“£·öº˜ß‚ùN1ƒbg¹6?‘rž"œc Ã¨¸Ö Joå-ÚÖÈGô³Jç˜ñ¨UÞsd£Ík6Éù”=¡¹?GBø¥ÆC.!iyèã ¡€Œ³'î‚$‹&NNÒŸì¼ÊÊçfcöõÎíšÂ²1åˆHôlßOøqÿ¦©!™Ï—½{÷6déÈûî­§,œ¾qp›kA}T¶t} 3Tã7%¾eºÛIå7ØŸ¶•FhÀV°ãåkëÇ8®¼ªd	ÓS|Ç/g+­ºŠæúë³µàŽ3öÕÏÔcµ¿ Ä™eÑÃ÷Zoƒãä¶J·AÉ¼JþQÉßÐ÷š¤Õ“Ýl½Ñ”fŸR¯kjÞúûÃ0žI¼™‡!NÔ6Ä8}e•ƒTVq™ö¿lSW¥/§òiË©tÂ®Ã~™¾†Ê!k¨tG±Ó.™¾pÊ§,œÒ¼M{cúj)‡¨–Ò¼N;bú)‡*‘ÒÄû`úº(‡¬‹ÒÊN»_úb(‡+†²”÷¼ôPW¥;’]vºôeOWö¤’÷·ôµNSëd#ŠÝ+ôN>m“.ÐuI×ôUMXÕ¤3ˆÝ²5})“OXÊ¤v“5}ý’Ô/é„]·\M_´ä@EK:cØ%UÓW*9`¥’ÎHvËÔôåIVžd$7'júš$«IÒÈNyš¾ÉÁ
‘tr‹4ÍC…Ðû3n˜§M´³êBVñl YAƒD‰Í(ž™è!û/¯­2~nï’E•K,¼ cgÝ¤´‰ÊÖ1Á[h{g¼ò·‹â®+èfµÔ«¥fÜpÇŸsHÉ›¨sîð|x±ò~\Àb&FOÅ‘¸q÷Û%—¤Q&-PÀBXÂð4`ÉuS–ñ²jÎž¶ýyÚz‚×+1´\û²Â…z´¶3ÚÓö²–Ù«g/i×õ«Áp8|ŽÅLÞLl©»0TÒà=¸¼©Ú`N¢©’²+¸ì—„ÃcEj|á“§%Å-%qÜÞüG›Ñ>wÉ_·Lûƒ¯uáË&Ð'ú€#­wyY¾bFâëÜPž(5æé…ß>êË!¬xÕJòÂ±Çj‘è„Æ;ÜÃZ8*µµ[ÐµÉ¨¸-c­mµ*ù‘¯˜ÔêQ®®j‹êø-øN’½®}îw/[nÒX_ÃÄÕ@·®³×®XÈÃôNE;bò×d6<¾‹·ZránUì«Ñ î|Ø2¼„\·C÷¤¶Å
h¯…,í¶‹¨ðŽs®Ó™Èš\$ÑfÚÌúž‚°þÅXà9¶¾S#í°’=´ç]ÇÎ3‘óeâZ¦×ÊPÝiw”ãvYw']ï]†sU$`Æq¿/n&62mrîiw#»M˜cÃÄd‚e»²ëu¡oŸWdø'–HöoÜ!cW(³r}®FŠ?!ïårÆF|–ULï¹BÏ•56‰\×·ÄVÈRMÁHm_VÞÅ3‡6j3Í‹kÍ3Š3¸=dj‚U·¬ý<âSãÂ#ö¬TQuhÇót‹èmÐ{ùlg;~`o»NóÁ Û[CÏÖËE¡ØY-ÿƒÛj-î	à2„ˆ˜®_Ðvç™J_ÀÃK=O_´Þ*i·*ùC(ÜðËˆ9 ƒA´ÛëÎØ¯G£ÓÑèhtÿþãïÇ8>°Ç£:ú¥;jˆãšòño7òÛ—õ£öUrì¾“(_OÊÕ¢‘;¯¡sK;ægXa†˜ôï=žWJçlíœù”	†ÒÞ¿þ	ƒžÝn…i[B•m¢üÚB<vïV¢´å'é¹«^’@Žõ‹@*nE®GøöËk{ó†ÂVý+ámJSÙj6$ù7À­°%ÕopÑˆNmQF˜K‹‚¦f1ìaÕP+±¿ÑDq¸å Àqw·XÚ4c?aÐ´¯E(ÞóØ“FhòBÍ²Â²QÏ…¥0pßFÊà–Ÿ‡&Tš¢Tå‘#ñûÔ© PFà%ŽîáÆdÕ”®ÆÓXûj‚Ûñ>ªêš/±e¬B9²…Ëlœ²oankÖ»ƒö}>uÍ›v¥vUðn}ƒ5À¸ßg/ö™½ ?„ÌEL†[²8EìI³gÒ¨ªgnbJXá·+g®cÊ•ŒÂ:g*ÜFrÑÑ=Ò!Q´–Ó2Žå(mÒÞg5e•kÇµ[__Ðe±ÜEo‘ûµÈVÑÂ—_¥ÅmÖé–õÚ?…@Í†@!%6Ñ§à—Ÿ.%¸Û:g_Î>EZð¼e³6ÅúXJJÒÛS4{²){b•ÍÈñ±Â¦TÜ2=¸ÌÄeÖãjëµÃiâTÈ¥âÿ0½÷¥£qxÆB—ì× YKÂxUå0ar…í{ˆ¦~9ãÓZüàƒ«Šá’J¶BnøòµMÉëè©>JLK Yúd{\™2¸Äs,Yq´=†U4ÈÛHJóÃÞËíßË‘¢ŒŸ‹i±ÉÓ¹{=öO#§êC¤ujt ’ýÕ3iÿLÂäÞ"RHŒM<²+{íŸGVÓ†GkÄ¸…G†OÿÂ¼¹®<æ«">yê ª ùz,MÍq%üí_S:@$[ØÍŒƒš…×«´þBšY˜ i¢«B¬äÊr’Ó×<,èµœG¥UÇ?çâ¢Òô×}Zc®d†ß­XºOUÑú5¢»Ï‘Ôªžf—¹e—Iâ?qfßQÆß¸òOµ3zAúª~vÒ‚ïG^Øµ–ÔÇ›Yù—X?Üõ¹áâé+ëp¬»-§—bß¶ 6¸²Ëö:–š~cÙuÄ ¯ñ^­&ä#   0707010001f051000081a40000000000000000000000016a102a8a0000030c000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.pool.drbd.conf.gz ‹     å—ÁnÛ0†ïy
¹¶i±c‹ØeÇ^ÖŠ"P$ÚÖ"‹ž$;IŸ~¤ì8^[]EæK)¢Èï'Ey>?æ3›‹#>l®B´g"î*Ú¯ô{Í×»ã²›Ýs°soœ†íÃì:}“ÂžÍØù5ì6èõÕÁ©µ§‰ó#>)(¬äÊB¿Ó¯'<ü®‡ƒß¥iFC&k¾Ý LÅÒª€¶-2ô"Ò¸CN–°Øo‚âß®(“%¾Šx£Rô´EPW­ád6¢¨ð‡Bç@E!Eàâ'³Yãrž&ßŒR´µ;äAçv2!C³ëdüæ•ŒSÊí’}	¡N/Â?Èsùjô‚ñ6à‹‹ƒ&‹«ËxY[« ¡
H™ÉkOT]‚XB”ZF¹àµ/hÒH[“ÔÖâ&ˆÜã†åÜ~Iõ|I¨Ë1DYV	nÑÁ[€“93Ç•Tkpzî¾.8W“ÑK2¶? ÒÿM î˜	“‰€e›×Év’„êóžLÞ~»£/]@[)ûoh3S¡’±8E	²ÚZŽm/ƒ¦=UD¿†˜Žå#]4hkfj+‘ÛáK«'Ù5ù©'o‹†Ë»®8Í¸Ü•Œð¼Ü(iÙ»Ÿ¡ãÐ&ŸÆùÈOh
rå#}9PkL‚ËÂ’o#ŸÝ6o®b®Æ]ˆP22:3KÀ44F°‰EbÕ Z¼çXó_¯ìz‰Uü¬¼îÞÓýCaaû$çÔ™°Í™Ô‹z±¢\w…ò £AGË2ý§âŸ¡ëWƒº¦ÊCÅè©móßó%(”QH¬ÿLŽy„ïDzwP[\ÓL]ü(¤oxåxÏ	Zbíb¬;J&g-]|ð€k™L !ÊX‡ePèÚÂ)ö öºNYøž‰	Ö“°ûûæ_ÍPù¸úEw¨Ð·.zãòXöÜù”Ìþ"t:\hT½íîÝl`­Ÿ
;å6©õÔ£Nµ?#Æ4Ñ  0707010001f039000081a40000000000000000000000016a102a8a000000e8000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.cluster.asset.conf.gz ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:ßzB,Bvmq×ðvœ—Ñz
»£Ù™q]—DUSA,rV±œ\Ywe»«ž×°/ÕbôçÏ ®»ÝSàÒˆ¼l
b)E9Ìö8b»ödG]‘à#yû¿qèmãÍà—11¹ËïU”3@.$Á¾÷LôŠ	âyMš•”3cÿ“f+öB}Š¨ãÆrôaêsÒa­é@Sp Ë|G{à×÷&•6ó­Ü^›Ûô¶ž§hO»ìã~›=üMR}•="  0707010001f020000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002800000000root/usr/share/doc/opensvc/provisioning   0707010001f021000081a40000000000000000000000016a100daf00000a2a000000e600010003ffffffffffffffff0000004200000000root/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()
  0707010001f022000081a40000000000000000000000016a100daf0000016a000000e600010003ffffffffffffffff0000003d00000000root/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
  0707010001f02b000081a40000000000000000000000016a102a8a000001a8000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.array.eva.conf.gz ‹     í”?OÃ0Å÷|Š“º–F¬@‘`dªXR¯Îµhlãs
ùöœ¦Tª˜BBç%ñ¿çw¿gy2³±%9»)ÄÎÐ*7®»qÙÏ¹ØI¨mE/Åu.zžÊ.Šdþ•ºwª«/«ÚÊøÅˆ-1bã<®vt<èwLi&Ð[[ªÎg*Zc»‹_Þ¥AŒ@lSè'Ù“©×µ[Ø¥P›\œ(°‘‹-ÁýÓ4hqCèƒL“ˆZ¦™,¿–Â…Lé|,™¹-¥Ÿ¾uPû[X‹Ðþ.+dœµdb†4ç+tèÌšÎ¸à¿ãä‘9ýCPCi‡k”>;·ÚNåŽù@ÌT2 ÜxŒÛ[±µ¦@Öä¥xòx0™@q–UûhZŽ°"Qƒ(£Ë%w©Y.ÁbCìQTÐVýº-îéD®ß0Ø“-KÎïèx½\)§•ù‘¸Ô 5@PÔ 5@PÔ 5@P)@©,¤#ÿa€CigfXÇÙ9ç¢òQ>ÊGù(å£|”òQ>ÊGùŒÇçÝ‘Ø  0707010001f03b000081a40000000000000000000000016a102a8a000004fd000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.cluster.conf.gz   ‹     íXQoÛ6~Ï¯ —ˆ•%ØŠ%M‚Øìe/Íž†¡b¨“ÍU’’ëvýïûŽ¢:®²$3
o˜_d‹äñø}÷Ý}x¸ËÏÁ¡Øá‡Í©ªsžì®ÌíÖ»Ýbwð[<ìïìê;Z--ÎïvT¯g;ü0 N™VÞT´Þç'Y9âK·¶TlTÊ®òw®¹ü®©c%gñ¬éjÜƒCvXãZRºÔJ\‰95dµâiÊ4=ÙÄl¥X`G§ðþµX.´'×JE3G­´ÒSæ	SŠVú…ã/~AÎXòN,Œóº™‡wÁ5œØ‹€	‡QéÃ¶Å.‰Î‘ðFô²Ò¦ 55°—.ËàÝ+%ÅåƒLÑ	CÿBáæ3)M˜»´LÏ&;›äÜûy:îJc…¾rÂõŽü@LÜ#cÛû	:"okÓ¼…ùZ~r®m÷Tn~1=EU²)B»hB™úñâO3Zý~‹'Kls„?«`©Á`Ñ¨cu‹îHèR4ÆzßVZA•Õ
¬zq³Jxb
g³NaZ¯M“…=F'Ö{iÆký‚fz+iç)å‘ÃAsÛdöhs’àÆí)£;Í‘×Q5¬´ÆœÛL F:†@èfP›i¼Ô°ëà©3UÏÐŠŸKžztAGÒªâ ªaÄ‹0|®f0:K¦†Ýóü"¨-°¯2×«lC‹ùÑß˜\)„-Þ˜<Ä)&£Ã¶­}ÍÈOUý#â’‚gI^-XxÖÔò?â$j‰{UYÀ]EºO¦%ÀóŒ0†ƒÂÙ^«h¥žw¨Ô¿Èó‘™ðOyK1/ÏNF²¸w4P×‚ÏÒ˜)Ju±§z¿7³À÷/Ï,¨šµ<$?FöÉ,¦å4]0²È@ëo¹;7kmO/žÛ¹Œ·>!ªXp>eÈ>@˜q fÖ8y¨7wQ­°'—ÍaÖÇò¶h>ÝS‡¥Å°Q´ž&äC¯”ØÂ»
ÅTÔä%Š»\/ˆ'<Úl¶êwl¶cë+í¿Àø“ýJ|õ@gûbÜ5MÀÚb×&ÊÖêžß"Ð6½Øjé^dÛ,¹…éªÔjV×èÛChVfK]„ÇZfõ|æ… (tª¦í*:Óx@iL@Èj)WÈ fIVIÄ‘•Ñû¡sb£:ÔHy7iÈax™Ø’½Ì8:û£'J;m*UoI; t¹VÆ”¦ÿ¯‰_°&>» >¢ÞvÆvõ¾R¹y.—7ÆT$›:ß„ ý9\Qh^ã
?f¥1]0£<ÚáÃDý3ØÁ¿±?f"òÜ£åè`[ìë†$‚kÿðâTH{£=6–o¨beºô.#‘ëMUq‚éÚ¹•—pô­®($áf3\uÞÀí Ñ»ë±—ýŽ…b î#Öm„@ä—Ú,TÓ«Z™ÝBÂ—C&~uƒ\b¥<.(<—Êá>ûúÇ7§ß¾ÌR‚øc©Â7Â,¾ç³ÝLr#)¾¢…ËGz‚Í^·ÿ…?&¸êµõ¬„nƒ ÆdÈ4t0!øPÿÌ¥ÙåùØôê !S–RW¦çˆ¹¬ƒ9§!Wbf“AÉÉÙivòò»ìì,;9=>ýæ{ò‹¯þX…vP     0707010001f03c000081a40000000000000000000000016a102a8a000000e9000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.cluster.cni.conf.gz   ‹     Í=Â0†÷þŠƒÎ¶»¢‹ ¸8¹‰C›\ëa¸Ô$Eý÷^lý@qËàM!w¼Ož§œ,‡„åSJ¹´éÒ²ËvR6wÄ/û,¦=âõlž¾L•å†ZYMNäâ•íªÚàÓkUqãðÔ“Cý½ÑØT½	¯x¥íB)%JÆPè‡,‚t@7œù5¤`-2:R÷¦¢å•\lZÜT°î
ëqA~—›5ˆ¬ð8ŽzW²ô…ÈÌ†˜ù³3}KìÿžfMœžåXþÎm|¿ƒ‹žÙÈ°M`     0707010001f053000081a40000000000000000000000016a102a8a00000311000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.cluster.pool.hcs.conf.gz  ‹     íÙKSÛ0 à{~…fråu†ÂL/=öRnci¨‘%W+™¤¿¾+ùÓÔ™¶	!Î0°EûIÞµÍtzÈ×dÊøŠÃ•Öê3æ×%°…À½‡;ììk7yˆÁN2V“›ôm{2‰s_ÂúÙ:yý2î_SËù_	…-ùLC÷U÷.@lpð#(r«ABÎƒö/sûj´C£iƒ«±¡r%Ø›ƒ§D
ŽF@A=îÀ/è#9óô{
ñŒ-}6Œ#»çéÐõSz›~Šïž˜2©»±zËl: z·&Wóà¸WÖ\ÐwÞ¤8·vGàx>=`a‹Ò"}äÌ_¸Æ?;w-[Ð]Ë_IÇL®7ÀŒ
?p³±
Ÿ…W÷Ð`ô#ÐQÉt0˜ûí·Í\FËW·” Cù1%÷¼Ž_‹t.ÿÑ2u¢¨p9wö­%S‰R´,…Ë¼e\k+¢geu( YîlQÓµ*»Ìˆ7â½™i	U¦ä©¦À}ü(¼–Ïq3O–1\¦$nøun=ÝršÏ@gTªsµztud	IÊøSÐtŽjn ;ÚVŒ¸çê’ÑÇÀHWŒ68Ù	dÂaÈ«}®d”ñ0·%Ü†ÞËŠ	6èñ,O”›@„y5D‰ž;ÿž“b
ðÓâÊî­˜cŸŸªÜ*Çÿ…Ë•\£‡""åÖÜ'ÄøÔœ:VJP	~V~‘ÌÒŠß9 U,gz™ÙÒŸª×Ãã>'-.4¬¶ Vñ_*º:c<h"tlF[nÙ Ö©0ÞÐÝp‘ú”ñÏ´9öŸíÐÊ%w°µi:_Z
eph±>Ø"Ä˜|wlïµæÚmjü±HL¾N-l0¾#kiÄl
¸%þ¸Údw¥\ëf(tã­á=– Šëv‚o/~»…i}ë;œþSÝÙw»b%SµêÜ/Ø7 ÚÌî”.¥—ÝØ´¹cÇÞh]®ç«¶èmÌ¨Yµ_½{;±     0707010001f080000081a40000000000000000000000016a102a8a000000e4000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.dorado.conf.gz   ‹     ­Q1nÃ0Üõ
žï.Ò1c—ŒE°Ò9êŠ.%¡Éï#×‰ «näÇ»cÓÔ„i¨"f9'ÊNªÉÕuW·;ó¾„mÔ‡ó‡™áò+êºÇÝh¸<¢›Š˜Ë‰V&þ±^ÛóçC¤øÉ^áþ3=ç1=¾IÀ]T¼C2N°¾÷–^é„ õö/aQˆ¶ln¹hbåo$(õ¢”Pg7l“—Ð§‡¥¦#q …ØÒxúoŽÚÆ­ÛÞ+[×A¼„Äçm1ò²ÎveMŒ¹î¡ý  0707010001f0c6000081a40000000000000000000000016a102a890000105d000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.srp.conf.gz ‹     í]mo7þž_A (’àdÕöµ_œ\®/¸àš¦¨ÓöCQh©]J"¼»Ü’\É*îÇßÌÜ%eË’Ù‰›5Ú Ù—Yržg†Cr†þì³Cþ<úŒðÅåª¶\ÖB˜]7‚Ý¼¸Ã¶î°º{ô[×ÙÏ´¬qùû£gÔéØíG°b½Rº8ëÛ1o…±ÊÀ½£þ ®L®>-E÷±wºxC‹?Z©EßŠïxièN£ÕR©jYÏÏ’;…˜ñ¶´}ÃP5ÝõBhiyû•‚ÏØK¶¼­ú,´»o‘Ë™Ìá™¹ eÉËy]È‚[aü'ÚZ^²ÿ±hR­)ar¸ýn¡¹…f2³6VTÐfáúRjÛò’U<_ cxñ™W1à€B·±PÆÖ¼Ÿ6‰š¥aµ³ŠMÁv…erv–;Ý1x£³™Ð¢¶‘½Î´ªèÅð>LØto¾`3¥¶aó qAšv.!ôøð0÷°ô_»†+ÍzôÌƒp]£·¢&¢jì„O•¶ îÎ]ù0l-ø°î; Ø¥ÐQ¦JÁ§êÏ×3²4°¹,³  ËFìm#êó_¾oX–Ì+Ze$(Â½O¨j¸kŒGfžÌUUAÛ Ÿà*yÁÔŒñî&Þ ÿÃ1;WÀm¹ž‹ØŠeÕ”¢"Ñç€X
8 íh›^bÙJn,«T!Øa.ŸŽûîD‚²l†*É2Ð©!ìˆ]¬Î²MRe¨Uô><·ú	ÞzštRè#Ð‚ju.Ø”iŸ7·[Ø¬Ì2Ÿh¥ì¤áv1=Ñaò9úÀ©ÄËàXJàÏ/àV„ã¨HƒÅ5¸®¥j(ˆ½YÆì[èš\x	fãa÷¦€×gmYzlâ1c ¼í›Öµ‹œ£…cDŠ0ðáÕXø_Ôpc- ‚JY1Ñ«û D‡ï§ìß¬^ã]¯yÂÌZÀûK	X¦®DCŽVÐ9~°†`÷¸í2|ôuÎ‹ß/Â<'ÀX„Ó¤£¯Ø2BW\æekäRŒ@_si¬æ`ãª.×O	·¢÷oÑôã¡þÐÐuâzÀèá«t‚"sa@=eÁ¦k7t…A\Ñ¥t~%i×âêØê¢còc¥Ê/>,uL<j›qð á[qƒÏG¢Jèš•÷dýsü¼â†{àqnƒG›
l*
:Ít†®Gâ¦
:ZÃlÆ	[ÇÜÞÂhIKÇÝm£m/b§¹¡&Ð]¡Ú‡2ž~y|HsÛ´¦¢ÕD¶Äœ~åBw@íy¸ý ‚èó’ë0öq;‚FpÍ¸,[í"üDó öe+Hªy°œ>dŒzÅDØ•-Éæ#&¼rëx64WEßÆ‹B³Ö8ÇœC|àÝêùO?öÚ'eÃK7®:4ºšä ¡ùXƒ­ª>¹Kµs¡¯`P·Õx“KT-I˜W^Ñ=Üñ!šW/}4ªB'„ú“NÃüÌ+þU7¹ê›\Ë†F{q)òˆÏg8×BÂ ígù‘sBgY×“Ìû%‚´VÚÚZ·(v!ÂM„o27›TúH”Ê@Re¶·ÝFskáº˜®$k”±n³÷ÔË¸®ªð<Œeªæ†©šÛà×ÖÃXyGf©ö¯Ze$b_Pµ¢ŸÛ"êg`wþÄÈÜÍ!øy?$ýDn‹àûÜI¾oð³Ï­`BŸƒÙåûÆ>[À7D>wg”ïú¤èÜŒ)6Ø%=°=¹»Àï?¢l.j‚žÛÞ"¯æ S¥À57Úcò›Ox/ iFlµy¼kŽi]ø0@ÕG9/Ë.‡£ªy¾¤kåóì„Ûüñò#QðÕ5íUáŽRkZµÆO7ª6tŒkâD'`¾ðBDåo–Etï›¯¯‚"{
¸M×©`r^ƒÓN^²OÙ6D³hm¡Võ8øµ5øŒmÒ†—+¾ÆR$Ê²úžecŸ…V³o¾ýîÕÏß¿WUÆd	ß¤œ4×ÆqÜÞp¿P‚¶HÙBA{o2jöæxã}ãª´	®È$‰L•($hšÈj[úhs°xÄUÅY¦*öwŒ_†æe´íŒ»¦±$Ú?õ‡'¡Sýn²‰úgÑ’¤ë[¾àõ\£¤{é—Eí>ÜéÃ]¸
°Ûƒõ—wmÀŠ:çÍ«wc;OÓðïÎÆÒŒ‚E›ò´".)ñ.?G	fÒ†Ç	©ä¢t$ÖÀs`™@¥øç ¬ûœ(` hJµE­Û’q¢ïí¢OhY²Ê@ñÖàpƒR¯øl«å|Ž{ÅDpR2Ž„tßŠ~8–õ…A
™…j1·E0ïÜ“³<|î\CW
®)àEîL–lÐ¥òòÁ± í”öé-7™u›4^‚[ ó‚=®•9˜ÀÜÄÙ¡´I@›†ˆ”!.MÀ'	»ƒö£6cV*j÷´µ]f.Îœ ø+30ð ´`î†ªÒ¸ôÊ+R£q…V´˜c&PL8÷.e]‚8ö˜/¡¡îEÐ¶¥Ï?½þó— fô/ŒÙOáÛÛáo˜ìîF|D-ÉŒÕÀ[«*neN±hp«õøŽÙÏFÌÚyp­”R^À ÛV›ÉR¸úC¬—s ]:iÐ×‹°j[Â|2-/¤š¬„œ/þf«…·0ÀD]˜^°Ëë˜4sÒ~Ã6ÿ¾w®Â¿)Qïõ[èb	<Y‚¯%íŽÙ/˜y|^Ñ®„¨ÙÉ1axr||<fÿºeÄ—®³tŸR´€_ß€lÞ´“?Zeù ë°n¤™À„4§tOL³Â™/4¦Ë8›W9eü8ÇÂ°žÍµ‚qÆÍÏÓy°oÄáÎ)hšUâ²ˆKDIFt1Y"ðºÄö1û®w#t×ßËº½d9½Î!ËŽN T¬ ÀÉ|)+iÓy Uc°FIìÜh{Ñ&	7•Ìµ2è_läGw
<_ î¤‚IW–}uüù¿à^×ø7•…@ÐA99£ðÔ½qºù¼])÷|0€ŽÔÄ~úÆ0~O)V×æœTh¬'Œ¤•£Â¯u«4pùäøôKæô?f¯Ó²€æhªZm¥m)¥l¤6­_(¼2Y‡o@DJw!º”É4™¾A™aM‹cbH´~çV
±”Ð›µÄù­S	-SrÄ|^¹PBA3thGB@÷2ðäôfö¼ÛÛ÷¾BÍw°?²ühq„x<*b´úþÜHïál‹«Ò—CØ;ŒNñÂñÑi)‚‰¶ƒY‰jB¾t@ô¶žÄÈ?Ó9Ç·dæf;ï± ”^³'²NÖ!`‚üÔúÛÖ½½qÏ]ÀüÌwŽ+(n–ëÖ'r,õQ³xŽ£áÆ€*ƒ•wh;#?¦ŸìP
óMk«U9pä£Í+6+ùœ=9Fs=yŠ„k”/ùIÇ#@€ý¸B3·}De+NNÒŸŽÜ¼ÊÉçÖRÙ*&wÃÜ®­#QžˆDÏn"Œø÷xù—R„õ²·oßD‚\#=yß¾	”…34^c­ôG¾`&€jºSZfÚ©›T>Ã^„e[iGÌ²Ä,X{Sž´Ú¬xÓÈf ø!(Ž¼œõZõ!Íõ7g1jÑ/Ø—;<Ó€ÕÝ!Þä\9\dŽo¢ë®Øî–A	â¶3(YQÉG•üíñ®C’ÔÙÏÖ±Ì}*ÀC`¯55osÿ0Žg²àE–qˆƒµ1ÎPóAÊcüJûÖä—ÛÇ51÷[³v{$……0²f÷Jª_î³úeOðv%€%/®äe/÷Nûê\>LËN÷¯rŠ[î·¸eèö‰b†Š–XÑ²7ˆû1CË=–±ì‡ÝÎf¨]ù`µ+û x‹æ¡B„}Œ9Ö”'GÉ8²I- é¡A¢¤f”Z­³_¹vÊø±{6•˜«/ŒÕmNy7þˆi<ú3ÓÍªŠŸ³Ÿ#VÐËjmúÕIny8°c)yu&Ï‡·5p7.`µ@£†çâÈÌµÂtR·JÇÏPÀJ8Âð<`Éº­ët'Ž³Ç]wžàU/†vø.\Û¥í€$ê¹–ÅË'Ï)Q÷åh<?Åú<g¯¸½Uáé|æzµÍÈX¯Û&ËXrjë¤»3ÿü."|Öõç¤¥äéHqC•Oç~p´9þ'V¹#cã#)½ú€#wy^¿dx"›	Cx¢2eÔ”ç!ã0dÐ÷¼ê$áX¶Ñï+ÌØYš—wà¨Ô•ûÐÎI=í|4£.ÁŸª’á3­þuÿTW‡²¶½$÷\÷L,“Ã8‘›”5Ê^|é Z@œíÌîŒKÐ˜¨)½¡Íò”üšÌ†§oñNKþ#Üo¤¼<¥[†Kànèži—ßNÛó²v;õI­–31ùBm)²$ÿ²p~€—‘ puQ€´ÍÍýD´g@/{ìîûŽM
Qr˜ ¸–™Œx
A ;]2fXú7aè:÷ÑŸW¾à˜"Šù§VæmÉã¤Š.ÕçíMA³ÖÝ“Ýl
û¼"À?uDrÿÆ¤
·©ÅNÉõù²6JÞ+|ä;Þá³œbÏµó°Éoˆ9¬’µ¬ÚŠ‘Ú¾ôÞ%0ÇùnÜ¯Q¸âhžÐP´ZÀë1S3,ÔtöótŒ_M“JÙ“Z%	ñÐŽ§1èÑ› §LÍb(&»«jÄ÷–C©™/"]â™ÛkçÛL¾×£C?x^©ª¹²šåœM[Yâ!ÎìüÕþæ°KÞµ„Š”]yRymŽK+À"uW™Nßí{I9–6TÌR›oFrîwèª{y÷ÁÅÃ²ØÞUk®PÜ¹þ˜#Ì’«ýYí4ê„(ÄÕkÃœIT‚§°Ç
8±ô»6G®$Þê…@Œ»ÅÑ¦b˜ÎÑ%Ñ¸°'Ð$„Úuƒe·ÅYò˜‚ÑºÎãÝ”µ^«úÈ“Šø}æU@çÙÏ:ºÇ9‹ª­£³Ä±,n†™NøøP®±e3,PÙÊÏ`ÏØ70‡±›]LÁ„>ŸùæÅM»RÖ­/î>Æ{˜¥Þå,•tüf¨)n˜ÒñÖiî–4ªÙ‹3Û˜þ±/g®cÊ•™ã&g"*ÜDrÑŸhúdLcä¼Nc9šwï9M9åºqíÆejËçŸ°	^—pŠ4KL3X!ª
lIO¥ÕŠàß!º'Í»TÍP^OMØøE5a4v¿¦«oHó¿v«“+ëYIõùÎ ´\&ÅS4¸¤Øôºbù¥’V¢¯ýá34=ßðþ€ð»áÜ×Ü*_Ï²PìþÂÿ>—t‹ÅÕ­©dRä!O×­Ö`Xó…[
§>n7÷‡¼öp·Oâ½­+Û$Öûív¤;e7íwDOaÁ†‘¦BDy1ÈküÊä ñx     0707010001f040000081a40000000000000000000000016a102a8a000001c1000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.hb.disk.conf.gz   ‹     Í”1oÛ0…wýŠ<¦5!KèR C;e+:Pä“uMª<ÊŽûë{¤jG€k @T$$ƒäÞ}ï|«Õ’«YÑ‚«¤ëÛw”È±<¾<Ý²ê–e×|ïÛUâàðô£¹¯EojÙMS¤?âxˆÉ}xVà°×ý÷®BHlLëqþÐCQ~Žœà.:3úü¬ì[8¥©h¤éPXîØÒGÚ" ±­¥i±zã¡G)Š-(G:$ÎúC÷z˜ÔÂd)Û&8•bu)îÖô%Ón”L-fö:8¶&Ã•€’Áìb QP"„„¤£wI·_é¦<$²^Ó!Í²…è°V¡÷ªM-éb¼æÜÿª¶|6^þîËÝ?›BdcØ#Í‚Ý˜Læ.;­ ó”Ÿ”y±H”{6$N*ÀóÕj¹Â°Ð–7
ð“÷“¾õKHz–|A±¦¥Á¤Ì–%¶§æ-íŸKÿW†ÓÅÍ\Ë’™wˆc~£,oÿO7:xsÔÿw°ÓìðF‡C‚ïuœQÖéA¦ÔÆíbšÏýœ°Jª&ô,Ó5}ou²Uþ°U´Žæ7Wa*F     0707010001f091000081a40000000000000000000000016a102a8a000000e4000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.netapp.conf.gz   ‹     ­Q1nÃ0Üõ
žï.Ò±c—ŒE¨Ô¹êJ*)¡Éï#×S «näÇ»c×µ„é¨!¹€lSj&×Ö]ÛîÌË¶Î¯f1üËw7Üï*OpeF%w±”£“}›±]{²³.‡HðU¼ÀýgF[æ|7ønb ê²’šÀ~ôLôŽ ñü“°*(×ão.JVì'2„Æ(”'ÐÀ;ËÙÇ0œRÑi­éD!:ÐJìéüùoQéu²‚ÞEîo•m‚Û@/!Ûó¾yØf‡º¹«  0707010001f0ad000081a40000000000000000000000016a102a8a000001ac000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.syslog.conf.gz   ‹     ÝSÁJ1½÷+z­A/J=
žõ&b2»Ì&53[-øñN²»íB)î¡X’M&ïÍ¼7™Ï§³9L82ïØÇz2¸i³›V»ÙKWìë,gúŽ»Ï˜Üí°2–<ÉN/&Y¶qcÞ<îÙŒgÌ'	?ZJèŽOV¦õrHÐlbà4{LÝ1oÐREî¡Æ€‰l©Q1ØjÄó£‡"A"ä‰—z·ß^4'DZG–sÈGk|N¨‚ML:30Ê%+ˆ*cpY3Î¢ÁcIÖ˜º€SÇiŽBOƒŒÞÖ¼–·Ë‹_ÄÂPhÙÔÈù²¢°h¼ÄÌp«˜UøV§Ë=aÇ-úsõŽBí€U5ÉAî!l"!Õ¾SR/¾áÓ¤@¡ÖU×Éá[[¹ÜP ¦mz¤þ‰TXœè[ážP$cêÖÀ8rÉXi÷;pÄY.æöm¤_­W`ºmå
.jTˆØŒ †lŒ¶ÞZ}-æ÷ËJí/Š°:wÃ¹:}suýßd¡Y•Êf?*¢0Zñ  0707010001f0f2000081a40000000000000000000000016a102a8900001335000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.ip.amazon.conf.gz ‹     í]ïsÛ6Òþž¿3›$s²*ûš¾sªã©¯iç2ï¥é4iû!Ó!”0&– ¥¨óþñïî$Ù–äFNê+Ý6Ebñãyv± v¡Ï>;æÏ£ÏØPœ¬FÌn*ÁxÉ×êCÅ·uÇ»GïdõY-U&Þÿúè+êôsßíG°ñWb³Öu6íÛÀÉ<99âŽ’IuÅç…èªz[7Ôâ·FÖ¢oÃw¼0ô¤ªõJ©•T‹iô$9o
Û7»&ÕRÔÒr•úŠ
ÁsvÁ–‚gm+ƒ‹Ú=7•He.Sxg!”MñµT«•¨ùs­A’¢1ƒÚM
~2¸äZ8f¯4ˆUø™±<½b¦©*][VBSeUàÛŸ—Ü\±JÔq¤²¢Îy*FÌh6½ZO’™0³ÔM‘±¹`FXf5Ëq Øz)«´1ÆsmúŠÞhin6]Šôj–òº–Tý ð>€/S+WÜ
f—‚R]1?zn,ÇìMˆŠÌßL¬˜4Œ³9@¨,€y^ËlMcf-mºDÜ"Löà—)3S¼3Óä¹|ÿàü^«ã!¢”eÐŠØt|7B>xñýèA
Žß˜½…ýi- à&@Ï.Ãr]—ÜÂ3™$çXôâä|…‹ñ9¯*ø³äŠ/"A`cÅE’~n•¤s­wÝT´ëÁaÜ=9–šr•I	3í[Ëþ¯¯ç =5µÐvÀÀ­x@€Åe¶–‹…¨‘0H7ä7±8àz#1¶hÐ‚?b*„1„©J<ˆùæû.²Ž+®-@×Ñ[ˆ"d5X1¢V5ó^7þó0X nŒN%Yq?¹Ê
!HÒ$– 	Ì¾…N¡D’x£bEQ²%]O‘?$\.H‰µ¯>Ä³¬ð	UÀ
à|v6>ûŸñ?'ã/ÿq+¨ïafxšÿî×ûœillôÁ	’`*p›NŒ@¥FÄ÷˜ÎQ-Ñóºøüº`uª‹‹wÓó¥†§ôù¯Ià„2k9G\¹‚IÝˆz%S˜sðßÀ
!8{óãÏ iaTÆìi— ªaF5 Ç6"ÊÈjœ*É²Z®ÐsÐ*—‹xÁœov*†š´vD!ü+0¢·P„ÜŒOÊöÁŸDñ;š&x¦q é’½®„zóó7l-‹þ•Ö9ç‡9´ÆÕ˜}ÃºÜ™ÌsQeY^ë’Aó™¿õ*4üQ j_SÊ9Š‘%ùKµIp.#O¾3öìe8.0 ôh/hf	Ä<>‡þåÓsòò/«¨85u]§UŠ	*…'ÐN_"Zk:BN­^ƒna:<¸YnâûÚ5nÊQòteyµ…Fæ_DIí|Ê#Ç&SŽ°k1I#EpÎôNOÉYòaT=§5{ùC;µxÃ$¿êrÏ\büŽâÞ¢÷ÿ–h…#ò¦ m¤w7ÀŸ¾!ÐLZãÞ¬£@ýÚlÇ‹ q,œáõÞmæJÀ#^ãzíJ¤ïQÁ «@–]¢±õ£fMŠ–½#‘‘§ç~¿ÈYòÑ–²{G§žÖ¸¸›«j·ð=GöÝ
½Ç†A#¼F€)ã´!ƒkò¬O¶aUÑ”s°6Úñ«ÖÚ&ëÉ.ƒî‡¥e¨Rhn ……¬žŽÙ+r4L4Òk\oŸMärd2%[è•ËÓœ=Éš²ÜÅ®qY*ª/†-}êÜY·8!«-Q]Y¦i©Ö@§è™“]X}1³>dœ={6vÿ'øúÙÙít\ìK*Æ¾ü‚ÙÇó ô³ñd9á“œ¡¹öcBVÕC¥´åhƒFŽ[Î²yãXpõöÆŒÙea4×ŒÍ7T(C‡¶Æ²Þe¢~W«Ç¤ý©uci3‹ÖJðêÞ™Ñ§	-VÝžW’Xc¿½á¥ N'cúç6¬¹´3Xá>8Lîsy“55AÑã*Ò;Ä)-ƒ{ý^–éW¶içvø=¤GU	^3ž£ Þ¿Dó(éø\@=-!ÞÛþ7ŸoÏ½è»Òlî§{â¶j2h; ƒ–K…{+´€òx‡.1“æ5gÜ°æb…0â>ø¢x/R¤&|X‰ºØóZ>õnå˜Ç”gbæÕ„ÆùOÉ¾¶È§^\ã¶6nh´v%£cÓú2Úy‚¡3i¢•Ð‘iwÛünËˆñÞí÷xvm-¢Îk™]Œìw_ŸS7qO¸îÎ¥³3‡³Œ‹R«ÙÇÙ?ë ?Ž:íÝ`=˜´í>Õ•&''ðà=×‹¼—ES’K{ÝÂæÅ·ß]þôŸ·ã(d’€:´+ºÜžÃ’´pÍ7MÑïíŒ„6¤#w»ú½p2áÒ¯¸6Ffi¾hwºñl3ZÇ…nfõ–Û~eìMnù‘¨k¥‡m÷"'v‚þºøÞ ½©.
‘ÂúTØÕµåo¾üáòUà	6fkžõ–ˆE¸R}ÞK¾•9˜…+€YE{ máÁÛïT0ÕeIšP3Üo®Èn½ç…N\ 'Óô„Ó¾Ä4éz¶5u›ÐßÖµ®[2ÖŠÅ½
÷¼™ÝJ"·o€òBHCºHFç¼ßBøCeóÍ€äq•2ÛäC õ2î†«N¤™º:†bêê.ø5j˜+ïI-ƒ¡ý£Zˆ8ÔZÞÏ]6 îÑý	‘¹šƒóóaHÙûi¹+‚ƒïs?*ù¡ÎÏ>w‚up}Ž¦—êûx<î ßàùÜŸR~ ë£³Sl°PöÏº“~;†÷ß¿EQÜVƒqn”"«æŠ.Ò¤=íÇgÝÊÈÄ±ÁJ¦îÈE¤¼(ºœ’JhÌ(YòE6Ã•À#kà7Í@­Š%P£oµÁª+­(wfÝŠƒ0¿õEDå¿,8ý£G¥_¶ÙSÀ…)bŒÇBžSŒJKš#›Š(c–ÍôZ…Ûò/­Áwlc\°ËšoF²€ $QŸó$»sr'/.˜p½ÔEtÊëëÄý}ßÆqØÞöy¦…!B-µŠ6ö¯y|ÔìíùÆÛCgƒ¦5Ead‹.fxÄMdµMãÑ¤)†Da%e6MtÉÎ+n—mó5xÝJŠ›ü›Ð)œaöý³¨IÒõ-]rµÙ(ê^\³P®ân<Ü×v‘\þã½áý*åÕr¬0jÿîtÌÅnu
èÀ0¤ÑP†CÄáª)ZÌÏ¿mû:!•iÓHZ±>©¨”Q–ÓáÐc‹§iU¡7ÁQœW´> ÂÅˆc}ûèSZV×Že­Áà¶ƒzÍfû\"äÄˆYÓÈ8ÒöPôÓ±TW)Ôçxzã¹èaƒÅ…Æøà¤€¹ºB“Jù-‹¨§.ú‰›+pÌF¥&ö—àÑs­+ífŽÇ#&03?6ƒ¶	hÓq-[æëœË‚Ò;0jÉ~Ðf
çÔÔîycT/­œâXS×%° ¿5 îÂ‡öQö¶Ô`ÞGa0*µXl¥5ú1ŒÿÇá`ù
ê
Â¸a[úrþí0s*ÀgôÆìÇ.ÔÌ s;ümš´X¸Qc£aàÕ%·2%_´5«èõøŽ1ÅÃÄã¨^
ÆÅ²¬)+–ËB˜±¢¤üX0ñ²òÉÑÝx=?«n‹IXÌæÅ•Ô³µ‹åÙnáT&0¡2Óv)³jSÚ;ló¯7ª”Ï>‰4ê_¸e/_CàÉ
l-î˜ýŒAGS°Šv-„b§Âðt2™ŒÙÿŠZ‰"Šµö¥çÚ£…‰y“È¦U3û­Ñ–°î„u;SÔ˜BÁ(¶W¾‚”’Ï¥»0²Ö0A§Ð­g‹ZÃ<óæßnÉ‘õÚHNÓÜè·EºÀD#:Ÿ,xsDâw½¡¡¹þTÍ{–R	0Irr
®b	® .æYJ¯tœ¹tÀÕµö¸‚äNbÆACPÊ´ÖF ý3³¥ÛˆàéÇNjXt%É³Éß¾†g]Càw„{8–f´åžºgÛïÛµvï·
Ð‘šØOuìQ³«m8†isFªmlŸk‡qx~¨Û¥O'g_07þcöR1®Gªæd®œm¥m|ê€P¦ñ…×ëôUº¥&ÇDËdªùRqNlsÅÞºBhP«Ö×·nHh›¢%GÈçµs%\¸p›SÐ•BžžífßÀ»ƒmï%Ž|{k1¾^â¾2nŽØe¤‡X­¾?Ñ/ÛÎi£W¸ñ¡Ù5˜sµlÊ&£ÓÑ~099ë!¥Hü“]`–¢œ‘-½«%1ò÷xÍñ-©¹9DÏ{, LP{…dÏ7°@öId¯{ò:?yåÞ»‚õ-¨ïwPÜ*×íOP~\ªoÑ`T˜ÒHÃD‰m§äúÙÃ­ËîxÔº8rŒÙæ’å_°'”ØwúÔ%å¸=JÊ×ñ’ŽG€>N úñõ'àó8'‰rÅœ¤?¹u•“w¤”îø
×vMéØˆòD$zvçí¼·©!]ZÛë×¯A®‘ž¼¯_µ”Å,Hß8(ÖÎµ0|†lãû j¨n%'ø–™fî•_QÚœß¶•fhÀV°Óåk1nÇÍš.~ …(~Šã/gý¨z—ªÍŸ‹V!jA‰çì‹=–iÀêþœ¯r´‹žk½
>'£pW§qÛë”¬¯äOá•üõñ¾]“þâ'ªÙ¯ÖCÛìêu½}Q†þLÒZ‘UèâàBm3¤Ç|’ô¿Ó~kðË]’c†œ˜›sv…‰0Ÿ2æp
²_>föËàí R^>]ÊËAö9ä¹|š<—½(žå2$·|Üä–C ;Ä‹2Z>aFËÁ æÄi,1å0ìöú0CîÊ'Ë]9Á;¸0ÂcÆ1c¬Ý·a`0Î•¬bM@zh(±ÅZkÆì^»Áø¡+%Ëª«/Œ­ñ²Ö•ð©/X„"¯ÝÕO{~?¬ ÂzcúÝIny|éÍžû¥º÷fÏ‡w4p?&`çµìö$PÀR8Âð4 Éu£T|ÇÙã®?;KpÙ‹Ùq;µ¿6îÉ9ê^ŒÆãñSÌy™»ìw– K‰wnb¬WSŒÅùº©’$¾¬µqÒÝ}šþª©msELLžŽ;²¨|8÷pæ¾“½·mzDt‰»¾ð>ËsuÁ¬,aÔýT–¨ˆ5§›ßu”ðª“Ô
Ç´þ\!gÓ8(:LïÀY©K÷AÓ&£|,:ù¨F]€?e	D$Ã7òZÿ.TÿV—‡ÕFm{Iî½®^7£ËZ}ê j@íÌ–Üøü‡®OE‡å1ùÝð<.Å»Qò•pr1Å[†¯¸+àÝÔ×.¾Žç¥r'õQ®–33xC|SˆdÁ†ô±Ý½ ))<iÛ‡û‘hÏ€^öØ=÷›e¢à° p-3[ñä‚@wº dŒ°ô%aêzã½ÿ>®|É1DãO­L›‚‡A] «Û›ã­ü"Ï1Ó3 »Ù:jõóš, ß_àì~Ç 
w¨ÅÎÈôù´6FÖ«­dÇMªÑÀ–kï¾/ˆ9¬”J–MÉhØ"¾ôÖ¥eÅö2GP§xOh*¢»ñC¦&t#éÏÓ1Ö•NØ¥£€xhÇÓt‡è.è)R3’Éî+ñCå6ÕÌ§‘…öÞ“mÏ0ø~Ž_³P	4èoZ<¯eÕŒ\ZÍjÁæ´7—ßãäveºSòþrHæpéIÅ9:.¬ “Ô]f:ÕÛ÷’rLm©¥¶Øöä\	b«/¼'çmØ»KÖšKw¦?äß£ä…vûY§õB\¾6sWñÆóûÖ…N,}¯ÒöÌù[í×†_5NEmš9º`] Íë-ö4"´È¡vSaFú-'[ß‘@ÑF¥ñºrŠZWZxR¿§~Jm¾âéÆ,êFù9ô›0-.ÇH',G>lYŽ	êã1[ûì”½€5ŒÝîb¬¦íóÔ7/lÚµ´¶`q6€}V©÷¹J¥1~+Ô˜;V§¸HsÏ¤ÑÕAœ¹)áå‡ræ&¦\[9ns& Â.Ê‰þ‹†O†Dq_é´õ}8‡µåÜH¹ÁuóÚÎmjËa¼)àTld«…8T Kõ\ÚšãFü}K	 áB5ÛôzjB”ˆÙÏÆküâ¯ 7Ž³.E/Wª¼ ü|§Pt±z¸ß$ð‚&%›Þ”,¿Ò¿ÒEmüå3´<ß²þ‚÷SmmnÈïŠ'I›ìþÜ--â[kÚZÝžJâ¾)Ð´ß'ˆ»5èÖ|î¶Â©·«ûC>{¸Ç'áÙÖµc’ ëÃN;â“²]çÁ›ƒ[p¯nA0ÒÁ£¼‰d5þ­¸1„     0707010001f0f4000081a40000000000000000000000016a102a8900001289000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.ip.crossbow.conf.gz   ‹     í]_sÛ6Ï§ÀL§“dNVlOÒÕñÔ×´s™kšN“¶Ž‘ „1	°)E~øÛ] $ Û’ÜÈN}¥ïê‰Ebìþö€]è³Ïùóè3vÀ$'«kÖ•`i­™éÕÇ‘;ìèË»G¿Êê³ZªL|øíÑ—4é—Ý´=Âá_ŠõJ×Ù¤/$7ðäè€?È'“êŠÏ
Ñuõ¾n>¨Åï­¬E?†oyaèIUë¥4R+©æ“èI&rÞM?lOLª…¨eÃUê:*ÏÙ9[žùQ3µ}n*‘Ê\¦ðÎ\(h›âk©VKQôgZ%E<ƒÞM
~2Ðd#³7È*üÌ4<½d¦­*]7¬„¡ÊªÀ·Ÿ•Ü\²JÔt¤jDóTŒ˜Ñlr¹š$D3af¡Û"c3ÁŒhX£YŽ`«…P¬1JàçÆô%½Â%6Ü Øt!ÒËiÊëZR÷ƒ€w	ø"mä’7‚5Á
©.™ãžåå˜½¥"so&–LÆÙ  TˆyVËlŽCcf%›tr‹d²C~™2SÅK15mžËN‚ßku8	†RÊ2Ð’­`™o9„¢Á¯¾3H"CþÙ{øÐ}ÖŠ	¤×,ZÃr]—¼gH2IÎ°éùÑÙ†ÎÇg¼ªàwÉŸG„þ€ùž'	éçF+t®õ6A·Uãzp2îžJM¹Ê$rÂLúÑ²?û~öÒcPÓÆráV¼A€ÅeM-çsQ#`"–å×¡0ÈõZ`lÀÀÄZUcH¦–(á Æ››»È:¬Ø± LìDo Šø Nàáä×ßîÒÒ4±} )AxØ##Pþ(<|é%ˆNúüÙL¡Ñ©.Îœ-4<¥ÏK’@ZH³–3D“Ñ`áðÌõR¦­W€EÉqöîÇŸ­€d\³_d³QSêûˆ,•¬Æ©’,«åŒV¹œ·5Ð¶a@:†ž´&¤XùJ€£7@„<Ò'Eˆð7ð¨Ù]°E¾€id4à#]°·•Pï~þš­dQ€mX£û°qŠÆ‰9TÜjÌ¾æ
£³Læ¹¨…jX^ë’Áð™‚XÐ; ¿êtízJ¹¡˜" –$_aS’ Ù£ ¯³ìuø80 zÀ…!#y|óËÏ'gž[TÑ$ÐŠuS§%’[–Â§k…¥¦F£ŽPü£WÂà…e"XÎ½‡emäWvp#PŽ’§Ë†WÒÈÜ‹HÉ›^ÅP`w9ÄLvÄDÁÆ][*½">4ÎZ.Ÿß“* s„Â1!*úÕ±¬xVv¶ˆ“1ëxm€í0Ä¹î¼Ýªz®c¨aMOÍ^ÿÀx–¡Æx-ƒµ#ü©[PË¬K {Ù™–% ÷Ã#ø‰¾¯5"o²ôî´¶“¡×[F08ucp¯~üÈe°²…×kx·)xªKQ•ænT´šº8ÇaÖ¦A^‹¶®•žó’ÿÏÈŽ6L,ÍƒèðyQèÌDhÎv5¢(ŒïÃÂ#DvÌÈ¹‚É+±
ˆUµ]:ñ9 ¹n5C0{Ü4Âi8N;&¸h.ÀægkVm9¯-¾j­›ÀV=ÙæF{‘Z& *@…<2Eþ²z:fo(@ÓàÞ¥Ó¸Þ+š(ÐËdJÈ)—ƒ9{’µe¹š]Á²TÔ_@GúÔ®,ìê|¥Due™¦µX6 ÏœìÂòùò$ qúâÅØþw:>Æ×OOGèãf_P3öÅsB¤gÇË õ‹ññpÂ'8CsíxBVÕ‰Jé†ÜàÈbËZ6g¼ë†-ÎÞ˜1»(ŒFãš±Ùše¸Œ¨±­ûD™(„ÛvêeâjÝ6´ÛD;…ðêÞ™Ò§	­&í¦T’4Àc·ÿà¨ NŽÇô¿›0°â²™ÂôÁàø.•™‹|"xü¬"½C9¥EkpS¡ßl2è;4¸œÀˆxß‡ð¨*ÁkÆs$Àû—È’ŽÏôã1W÷Šõç›¾WäÍ»'¼áH¡'ƒ¶&Øp©póƒ–­NÅaˆAÈ$¿fö\,QŒøA'|Q|)B>¬D]¬	yO ½17­øúo
9ßäþ64i÷(²&¸ßÐo\‘8¥‹¾ÖFfi>÷ÛRxÅt¡ËÉ ßrÓÇÄžeÃ§¤®x'¶íOlÿ¹ò½f/3ÕE!RˆEØŸ®¯ßóú‡‹7WhÍ†Î¹-#”Eµ¾ì)ß$•ø£K`À´¢](ßxðü
¦º,Ij†;~ùToá¬¥z '“ôˆÓe’t3I˜ýhÌ¾©k]>Ö-’ÅuKÚ-«oÌv%‹‘DùWE
Xîÿþ51ZG~[Â/•ÍÖƒ$«”!o“¨£q;¹êjça4SW‡PL]ÝF~­|å©eÀÚ¿ª•‰}…Z‹!ú¹­DƒÅè†?¡dn!Í!øù8I8úñ¹­‡ØçnTòcƒŸùÜJ¬Cès0½üØØÇÉãâ"Ÿ»SÊ}bél—)X¨{q<"Ã»ßDQÜV>·
Z‘Us‡ŽEwêìOþð™¤ÙCÈ8‘OIÚÚgZ¥¼(ºðJhLÿ^ð%¥!Â•À,4<âä× C¯k:WTOâ[ µÆ®+­(çwf	Nœ ó“I¥A©üŸe’Þ{
é…gd›(†ç½szNçÕ*ä#ÛŠ cm“é•
·å_7ßiZc¾W|m€0‚%‰zÆ“dL™«”FñÍ·?}÷Þ¦s­ºˆN|\Ÿ¸¿ïÆ8ÇëŸgZÔB«hcÿJÄGÃÞô7Îö:Æ6Þ…§Ü°˜áqµikàG›¦˜”Ù$Ñ%;«x³8÷ÃK€kðºMH‰N6Ý¤ÇþM˜”éõ/˜_ƒš$íÜÒWs‘¢éÅ=e;îøa?¸*`›Õá>Þ™‹«R^ùÚ‡Ê×O°ñßVÇlG§€îÓ›iâÑhœ0˜¢ÅÌð|½ñ¯“¤¢6>çÛ“u  ¥¬ˆRúÝ‘=‡7xšVzÅ9EëWm–.ö·>%H«Ñõ  CYk0¸ž©Wl¶KüÇ¤.Ìž3­L#>!¨EïŽ¥º4¡¾ Ë÷(DÇÓv4(ö˜ÜÄ#É] ÐšT^<8 žÚLn.!42k•š8^‚Gs,Œx¬´õGL`ÙT~hm¤M.âJifºrYP‚=f08îc¦Ô.Mãžµ€ŠÄK+§8ïÌN	,Àï-¨»pi>”“¹I5ðûH¸R‹ùF’ËÁld{Ì—0PÛø†céÛ¹·Ã,pÌj‡˜Ñ5³»´+ðíð¯Iâea=>J-Ì3ŠØÀÛF—¼‘)Å¢Þ¬bÔã&8ÆzHL³z*˜#Ç²¶¬X.aÖ¦%e Ã‚‰—•«dìøõò ¨º)'a>—ROWBÎÿg»…·Pp`Be¦'l“î§Õ]Ú¯8æß®U)—ÿiÔ¿q=Ê^¿…)€“%ØZâî˜ýŒ¶°ŠÍJÅNŽI†'ÇÇÇcö_Q+QDy—n²ôœRbi0^o‘lZµÓß[ÝðA¬[Åº‘kRL§f”ç†+_LAJ)æÒ]þ¢7L0)ëÙ¼ÖàÁo~~CA›ÓF
šfF¸-â®5%*ºÐ)"¦Yûœ§1û¶7#4×ßIÕ~`)µ ã$G'*–
àb¾¥lâu ‚Ž3[µ¼2ÛÂIÌ>®‘¥Ä*ðÏÌFÆžgˆàéy'5,º’äÅñç_Á³n ð7Â½ ä¥m„§¶ÅéæûÍJÛ÷½t &ôS;TÀ,Àj›AaÚ¬‘òƒí«0Ïíu»4ðñÉñésfù?f¯ãáz¤jfºEo+›Ö¥eZ·Qxe±ŽI_¥]ú`¢|´L¦>/@}¢¯yow
a@^­%®o-Kh›Âƒ#ÄóÊ†TDÒU5† ´­'§ÛÑ7ànoÛ{œïÄîí æÚJÜWÆÍÂ…·¨(k€Õ×ðã²Íúz…WÝZƒ9WsÁ&ìxt2:ÅŽN{‘RVîÑ6a–¢œ’-$z[KbäñšãRs³ž÷²`±Ê©¢}X »‚’·msô6?zcß»„õ-¨ïwPì*×îOP­ŒÎÃ5Œ
Ë›(c˜ ÑIÛ*ù1ýì@‡Öåw<j]9„·¹`yÁçì	ùœ<µ	úv’r÷Ý†¤ÅH@$ýø®ˆylDÕº–NÔŸŽìºÊÒÇJ{|…k»¶´hH9 <»ó	ï7ðßáö/¤+qyûöM@ÈÒ÷íY¬ˆrƒƒfÞ×û°líæ jROJüÈL;³‹Ê/©„ÆmÛÊ<4ÈV°“åj±(fÆÍŠJïAaˆâ¸ÁËYÏURùZšh5J-hñ’=ßa™YÝ]âTŽvÑÃs­7Áçdn” Üv%Ë!*ù[D%ÿB}¼ëÐ„(õ=»Õzkh›}N³®7‹Æéü0ŒgoE–aˆƒµ1ÎPóIÊcÜNûÉ/·)Žjbî·&f/Ùí‘:Â|ÊB˜ý¥¸W*èPýrŸÕ/{
oWèPòòéJ^öáÞiŸCË§©sÙ)Åý«\†â–û-nÙGtûD1CEË'¬hÙ[ˆû1CË=–±ì'»1ÌP»òÉjWö‘à-B˜‡*ÂC_]Èk{u=&ã\Ê*ÖTH/J¬F±Öš1û…×–?t­dYsõ…ij¼¸q)\é6¡ŒÀ+kÓžßO*¨±^›~w’7<¾ôfÇÍùÝ{S'Ï‡w4p7&`ëÅØ˜ö$­  ¥°€ái*@“ëV©ø$Ž³ÇÝ|w–à¢'³å~à$9«evþäŒuÏGãñø)Ö¿¼Îmu‡=KÐ¥Äû÷0×«­F¦AÝVI_ÜØZêön=wŠÝŠ´ñµ"&OŠ-UT.{¸o×ÉÞ{_]£íØÞmw¦ÎY#Kàºs5`‰ŠQ3º{[‡Iù®:Jž8–môç
9›ÄIÑayz¥®ÜM›Œê±èä£u	þT%ßÈký‡Pý[]–ÏÚv”ì{]7˜8&¼ZlF7ºÒÔ€0Û™-¸qõ@CŸŠËcðÛ[¹yÜŠw\rpwr~<Š'Ž_±—p[××6¿Žç¥²'õQ­–%35xGw[ˆ$Ê¿Ì¬À‹å:Bî‚BxÔ6÷#Ò=í±}î&6ÍDÁa`Gf62â)étIÈ˜aéZ‚ëzç¢ÿ>¯|Á1EóO™¶“*ºV—·7Ã{ÑEžc¥g v³Itäõó
-¾»ÌÕþIöP‹’ésemHþ”¬—ïdË­ŠcËµó:ÏW„VJ%Ë¶dÄ¶/½uñÈ¡Ü^fâ/ôèï	¹"º';DjB1’þ<c¯qRé1{¢t”ãx
ÝJt›è)S3ŠÉîªñcƒe_jæÊ†ÈB»Ë¯É¶g˜|?Ã+×+ý—ç•ªš‘-«YÎÙ¬• # öîâ{tn—¦;%ï/‡Äd[žT\[£cÓ
°HÝV¦S¿ý,‰ ÇÒ& ŠYjóÍHÎ¶ ´ºÆ;jÞ†m±ÛT­ÙBqkúCü0KNQj·ó:>
±õÚ°f%…à±ØÃ,Yúf›MÏÅ[þ+D_;LM,lÚ†`m"ùu/{â-ò€h³®°¢ã–£ûÒ)"Z«ô1^]LYëJ«#*Â÷Ä± ÔÀWÜÃœEÝ*W!‡q–Åå˜é„íèÂ‡b#Ë±@}<f+·‚°W°†i6§+ƒñsž¸á…C»RÖì/îÐ°ïÃ*õ.W©Äã‡°BÁ°euŠK4w]í…™›^þ±/f®CÊ••ã&f(lƒ™èhúdûõ.wó£óí,§,s­_ÛºMÝðù?X¯K8›_ÇçµYºTÏdSsÜ(‚¿£o, AØTM_^OCˆ
1{o¼Â/
äÔ pÖ¥èéJ•TŸoŠ.V÷›^Ðä¢bÓëŠå—Zâ×;¨µ»|†–ç6Ã]0`¿}Æ÷fwÜ®x’øb÷—vißZã{µ{*‰ý®6ã¿Ñwk0¬yf·ÂiŽ7«ûC>{¸Ç'áÙÖ•c’@ÖûvÄ'eÛÎ;‚7‡°àNÃ‚€Ó!¢¼d5þñÒÇ„µ     0707010001f0ef000081a40000000000000000000000016a102a8900001332000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.fs.xfs.conf.gz    ‹     í]msÛ6¶þž_™NÇö\I+ç6ÓY7Él¶M÷vn³ÞY§ÝŽ‘ „5I°)Y}ùï{^@,Knä$Þ2ÓÆIç<çÀ9à'ŸóÏ“OÄÿ ¹ÄDµ.”¸IìÈ·wÇåÝ“ûI©óXÝüøäôö“'Øókµ^™2¾è:-Tt=)•ŒáöðˆO62…œ¥ª}ß×2µ
ï”ê§Z—*Þ¾S”f©­6¹ÎçÁX%²N«®óí/T©+™GîU©’‰x)<,ê‡0pUò}[¨H':‚gæ*‡¶>É<Ö±¬”uïx[ÖJüÚ½'2ùR•^fÆÀ«râ-ôÏFpë›DXU‰Êˆé´Ó©¨*qb™ÃÈ­©ËH	[Éª¶, |Otª<|Ùµ­T&P8üÒVÔVÅb…$±¶iž„›™©ó
îÃPð¶GÉ7®€_Ê§k‹¢ŠGâíþ	¼ ö¥ŒªRQò&ñ¨å I0‚4è€ÅÆjãMÝ¿'•Î”©+Ú/ÈŒ‰»þ±FñK–W¿ÐX`¬HLššr2’ .êj}3.5HÉŽN;ê`ŽƒœNoë(±Ä£ts˜íÐÌNPµø¦É[‰øáxr2øã³“GèÄf3¾ £Ÿ.øçÜý”®eÔ\OkèR‰ÿðéDªXðU†Oþ˜À£‰)*g%un¡{¥™NÏF€Ê/ºqƒ`ï0±Z¾€*u«hnÜCÿÿœ?šú{ŒÐ˜¥Ô˜¢#q`5þlT-“swcal…xÕÍ{L©åH|¥“D•*¯Ê¨f3E£. I€4÷„¯k:jï^‚WË¿àÕ\fB»Ñß ¼á:1f—˜ç¥©‹*èßcéRÒÄà+˜»0u£@Ì
F­F~g(/CT™r=¯5\,¡/¾ˆê‰#Hì:›™~OL™‘ò^ Ù%–ŒÞkŸ“	©‹(Œ†¿Áä–ªÕ¡Öç±nQ>w‚Þ™Èö[,®@OlÃFr$w²27“¢T*+ª‰œ™òññô±qY¨üêû/ÅJ§©pL‡^Y1¤*—’¢²X#!=SÔ<™,ÃÈ	B‚
ý?3ÙÞÄô?Êp$®L¦˜t%Ë¹òÎŠTeàèu
À ô£.
h
êP*m€Š•8UöælÔ'Œ?‘%à®ÀH	UÄÅõêbº	ªiã%Dž0N¼`¤Á U9l#é™´š!¼Ih_Ðd²Þ*xV¢­%Io¹¾Æ?.äRÄ+ÓÝŒ¦@@æy"Âæ&‚)‚O”¤„ÿÉœþùç;SÂµ^2N2_©DçÊîAà!€¾ÂPÒ·À5Œ;ˆùíüÇÊ[ytŠRèßZ°Lô,R€xt˜ª¥Jq`ð¾˜¥pËTCÏØµ¤éOó,‹—ZÝéEÐÔp·ûU‡÷ä@*Ð_¸+#b@	'°ÃgâtsÚ«n"˜}Âz üšÆJ‰Bž®ÏHnÿ»{ð §\ûfZ‚s‚·ÒuœE_[\U€{Íž£Å¨x}£yå‘Ä—jÛµÚ*öB<KsàÅeƒ²ÖùBµ» VÒâó©†VÁ$®Rô$ð_âË 	{[ƒ«ð¢¤4x"ì2
ÍJƒ×=r3¥yÚ(ÅN—×i@ãì<j¡ÛÛåì:ûÜœÍe1±úgÕ[ÔN]þŒÞŠY­SÇsdK¤f®#pbK“Â©­o
¨`2×]ƒÓãróòÿFgü"<¡ÁSjœÏ‡&Õ\œ?m¦^ßPv(ˆéŠÚA_¹¢›´žå/67q)¯T„¥wr¨r^Ë*í:†´DŒ¿µºh7‰Õ¹&BoÔ\ÎÖ`¨~¨ö˜zoeïÑÁïÙ1íü¦ë’¬ÜöÔLÞè¬ÎÀjß‘uäÒ%/‹S„H«0y<ÿÂ%IuSà`¨Ú\lÆ¥L5/eâ‡ßÌJ–ù æØ:ZˆLÏø‚Ú¢x{ù¥ÐÉF@×¤¥fÀ…·2<9"žíÂ/{3äIò ËOô‚¢4à wåg_/”F(È««l‰1PI êiüû ¹ã¯U®æEWrQ×rPµÊÑ¥ ¥‘°!¥wÝiCÙ‚OÜbÍG*¨¦É–œ~øñ!­¨]¤ê&ß«8ÖÈ(pÚŽchè
ié'JaÃ§3{›ºt…îç?ŒeJ›)  Þ_	$R„ÁîZ„¼NìG¼
ùÑˆïõt¦é Ã„Ü&¯U:fÞÉñ8âÝÉí£µÛøM<Ù°vÅ9o³¡¨9MçÝÊB6Ä¥ïˆR×7šÑÀØú2M jÀ=k˜„=Ï_~úË×ÿ|ýú×|õë÷ûcŽ"ÏÇãl—,—ó?´$7FæÈ	gÕí&V0kp{ŒmXÌ¤Ý0Ü—Ä°œßí†h¶ãžÆV“–D84Ž§]sÇ8,*uA« êFE8i“I¥Â•9O ÑPRšÅÅ´ÉTð%ˆùÊÒ@ôaÌë‚wÁÜM”ÜÉÜGœ´VÒ‹ò÷ŠÒ ±rº±žs1™{‹þÊãY¿v~d¥ôy;}:÷“«éS6Ž¤™¦8†bšâ>ò«óÞW>Zz¬ý½Zé‘8T¨¥ê£ŸûJt¦`ö :üñ%siöÁÏ»IòÈÑO#‘ûJ°}F%ß5øÙÏ½ÄÚ‡>GÓËw}œ<î!¾>òy8¥|ÇÐ'”Îžýè°z?ÊÇÍÕyùýŸJ*Â>×9´"«Æ3i»,×$¥pñŠÛYw;­~z¥9>0ù6w›ÔÊB™Ëä’ÿáB•¸QÁ	
L?ÜŸ]ÓfšÔ¶Zk·Ml5f& °xÓ‚Å	2ßYÂõ.ÿeÉ½ï=)ëUW8ÔZdJÆš)¡ç9î£0ƒü¥º ÈØE]Åf•û»QßTÖÛÈ—éJ®qcÁ„¦ÓüO+›Ä•ª€â«×_¿úîÛ·¼Q¹Z˜°¶Ì½3¨üòúÛÜ¢Ô)±0Ðß;Âƒ)u{Óß8ÛÃ)%¶1E6È/ÎT¬ÓÖª.uºm€\_LM&žã¦éË¦{SJGÃM8ŸåU¹Aš'aP]–™WÈ…|¤ÑãØ¢…Ìç*ÃßÌI3ÓŽ|a[À¼?å.ïKÌRy$‹>…þ(ÊvµYtiœŽ…™†¨¨\T…Esê†òáýÊ?´˜1&YTÍã$© —ú	1Õ4 üÜÀ5{aq…ù7EjÖ”ø	W¾‡…‹ zß>ø¸œ@Ç²Ö`p»´¦›]•z>W˜‡…0 #¥#ÀˆÐ.I§EçŽu~mB]^3îAˆŽ{¥”ËJ×Ð”ºŒ*Grx[\¦¨§¼¥,í5„F˜„hÃx	nÏcq’ö'¡€žHŽ M€€´ÉE„áBèDê´ÆZdLzvÜ÷úŒ!Xj¨ß³ºjfpæfçñÀüTƒºcIn™SÝÄ&UÏï#1àJ©æ˜ÕéŽÛb½²CœÈ%t”ß°/];÷´_]Œ©©3º#ñÏæÝNàÛá7¬ÖáŽ³ÇG©Ý’qÈlue2YaBøëÆ¬bÔã8ßY•Ô)âàV*©¾'[g…·±m	5êFbi©JË¯G@Õ®’”ùd–^k3Y)Ì“üïšÏÞCUÀ©<¶›ÙiÅ]ÚØçoU)œ”Î1ùÓÓ¨¿Rÿ7—0Ä”2©sw$¾—iœ©j¥T.ÎÇ$Ãóñx<ÿ¯Ê\¥ÛÙÓ|ŸŠPBiaFêøÉFE=ù©6•ìÅz§X·ËÅ"*ÁDfœùb*cd¸^tÞDT`Á†	…a½ËU¿ùi8nk‰œ6RÐ4³&Åe‘%Â)YÕÆdÁ ê¤”™ªT9_w†b€æú[ÌOµ ã0Ï!TÌ ÀÉ|ª3]…ó *’…±šÐ¹ÑnHádÍ8e7ÓQi¬øÇ6¬€l¢d´@Þi“®éôÙøÓ¿À½¶#Ï¸ ×—ÐÏ0<åO7Ÿ¯V†Ÿo 5¡ŸÞ±Gì¬¶íuà¦T[ØÁ€qÇ·4÷Í*\>?ýL0ÿGâ›°ÚD3œ™½­®j* Émí
·&ë˜ÁŸñÔ¢KL“éˆ Š>±)ÀzË+…5l¢ZãáŽ%´LÑ€ÃÇóŠC	C9é® ¹"ðüéÝèëqw°í}…œoÅÞØAÃLãº2.Ž.ÚêLdïH|	o”ÔuH¶l…«Ò%.‡ˆ1œžâ…ñði'R&^Ø-ÌLe²¥½DïkI¶òs_“šÛCô¼“% ëµOƒ"*Ý:cE¿¬«áe2|ÃÏ]ÃüÔwŽ+(<Ëåõ‰HZûT¡ÁÀTz`e£å­´YÉÇôg:ŒÉ&¸âQš´ÇÈ1¼Í+‘¤r.NÇ¨®çg\PÄk”/¹IÆH@ }ÿè"ª6ã ‰Ê]šŠÁÓñÙ€çUL_V&A•‰€ÓŒÑè‘r@$x¶ûßÀßýå_êHÜ¬—]^¾ñq'x/ß4ÇÙtš5¾V»Jd7>:i£’ÍõÌÖ3žT~£h–mu5p'¤û(÷P‹éê{0nW²(°n¿wlG8.ðJÑqÕ…TÍqÁlÀ—š×â…øleêeõpAˆS9.“÷Ôñw‹ðï” Üö%Ë>*ù(¢’ÿA}|èÐ„ËšÚ7»Ùzmi™}N£¦¢Û|sÿÐg¦Yú!NÔöÄ8}yÌ)q+í;“_îSÓ×Ä¼ßš˜ƒdw@Rh_ó!a—âA© }õËû¬~9Pxû@û’—WòrNûìë\>LË^)^åÒ·¼ßâ–CDwHÓW´|ÀŠ–ƒ…xXÓ—±¼Ç2–Ãd·7†ékW>XíÊ!¼GóXEØûs¬)OŽ’q®uj*¤%T£PkíHüK–ÌŒ´­ðÄq¹úÊVeQÞ—¾Ð[˜nVa†!®ù}ç¡‚›µíV'e%~:ä“÷AgÒ|{æÑaèaLÀj¡F…ŒÔÐâw”NÊ«ô	Âˆì¢€L1`d)Ðä²Îóp'NŠ“v<'­%xÕ‘¡>ï© êy©ã—§Ï)Q÷å [Åú<Ÿ¯ð^‚ÉðÔÞs½êb`+ô×uA_îñ8uçSÑJn^Ëg 3µ<-(î¨¢réÜ6ã‡L¬¾mgïmSUíØi­Ëóü%ˆjW–(5“ø9-ã'å{¸j)5Ä±l£ÛWHÄÅŽT]âu[îC_Vê±hç£´	þT%žO$¥ùYåÝSmV“µí(ñsík0q0éFl
>X˜Ë^\é j€Ÿí,|ö5pèaèSÐfyþ’ÔF†­dË%÷é6R^Žáàýžá#d¤ûXKÉùíëöXyÐÀ V‹ÉLl´PqªiÙÿ×j¾ã9¶››ûi‡€Žöˆï»Mb•J˜ Äî€ýPø‚ÀpÚ$dÌ°t-Áu]¹è¿Ë+Ç#`¶(Ü×QJ?©¢M`uy{3 #T’ÐG4:°ÛM¢ƒF?·hðŸ2øß˜TÁ›Zâ)™>WÖ†äŸ’õj^òBŒ÷Ø,fLo¹öž×ÌŸf™ÎéÈfb[€—Îº4Èá/±X™ÝjhN»ÏûùHÒÑÍ¤?g#|k˜T:§¹	â¡g¾ÐY¢w‰ž25ã¾˜ì¡ªß5XnJÍ\ÙYh:vÍ¶=Æä{0øÁ 4èW<·ªj\V³œÓ‰÷øqqõêïîûÍ.¹D¤qåIé­5:œV@§ÛSe:½·%lNÅ,µùf$Ç-ø(Pn¼ïƒý²Ø=ªÖ¸PœM¿¿fÉÑw;¯ÓD!îCž±T…à¡Øý˜,Ê~ËsñV÷¡C_î®	Ã¦ža˜ÆÑÿÞ¡“=q„&y@´ZtD-XÊ¡Ÿ%)!­óèÏ5¥¬õÜäC*Â÷…cAf*>¾›áîç,ºO¨ºoŒ`Y\‚™NØŽ|H×Ø³ÔG#:A%!¾‚9Lµ9ÄPl3æ×=¿k[emÞúâm ûÞÏRr–J<~3ÔwÌNq*ÐƒæAcŠƒ0³)þá‡bæ6¤lÍ71ãAá.È‰þƒ¦Oú@±VÏó0–£éqÛŽ9ÅÌe¿vç2u%ç`¼-áa¨f£…È*Ð¥r¦«RâBü»‰.ù NÕlÊë©A!fçùë™]¯€šûfKWçIJõù¬Pôqq½IáM@.(6½­X~itŒ•èkwøMÏ7l†;` ?:Ö½WÜªøtÚ»¿pßy·XÜ[yMeŠ!EÔäéòj†5â¥pãnuÌ;`wûÄßÛÚÚ&ñd}ØnG¸Sv×~‡÷d<hXàqú1D”·ƒ¬Æ @ç–÷…    0707010001f090000081a40000000000000000000000016a102a8a000000e0000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.necism.conf.gz   ‹     ­Q1nÃ0Üõ
žï.Ò1c—ŒE(Ô9êH.)!Éï+×S «näÇ»cÓÔ„i¨"f¹ öz©&W×]ÝîÌûöÃÌN?q¿FqÝó ò —GrSs+Êq²§ëµ½u>D‚¯ìî?ãÐÛ<¦§Á·ðï…Ô©äê=Ó+ žå²qøÍE“{A‚P…Ò êxc9ùºã”uXú9Rˆ´[: ›UZ¬ u‘ÛGe«à:Ð{Hö¶-F^ÖÙ®¬Ec¾F‚‹ð  0707010001f035000081a40000000000000000000000016a102a8a000002a1000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.cluster.array.symmetrix.conf.gz   ‹     íVMk1½ûWøêz“~\š:P‚†Æ-I„RlYÙ"ZI•´®ýï;ÒîÚJ÷âZ/úx3ï½ÑÈýþ!G¯	ŽyÏ¶ˆ[‡¶e‰Ñ«ÍßÃ6»Ãj×ûžÉö½27?z™ôhO»×KžpûËzñqŸ‰a%ÒÂ›Ž$UàÖ±…Æ]¤k¦C
VÊ£øsE d•Žûä¦Ö`†@y£¯ƒC®¤âp	K4èÏì!pÚq¿ÂÌ¬„HßYš!L$Áy»VÅ $ÓzÁøD›÷äQYÓjÒŽPI©6C
q‘QGpvvvþöÝû/‰êXiê…m¹%Ñª€éGÛ%(3 %[#Õ²òI^Ü8!  €Á'Çâê²£«G‰Ï,©ï1súÊ*DX gæó°Ëù¼ƒ’,	Ž
3¢>±bkl´ÙÎçÉ¦ìáŽ@º	® hE®‘ó“¡ÿ‰¡Ô¹V3bgèÒ›­w$u-×ÝãÍÕ—Éìêët:¾º'ÑÐ¬•·¦DaÍ¼J9ÖýOšÐ&Û»Ý/YØ´NúÔ’E¥Q–•Qœå¦ÉWŒ´ÔTS‰ÚøùnW¹¦Ýb_¥T7Ì©¢®¼Â`äF.)u©4fÃŸ›G¶ß<Îˆèço“ÙÝøöa|ûŠë©nÍòkëyÖ==z¬ašÞº¥ge …C¤×.[5 g•‰Ê,³êI:ÔñHÅ +d%Í£õôx~ÞÍ…()eå4¶aÖègï»8uÐÔ/è&§Ó]ªAXMi¥™¼©Aë ¹ÜYR¥È&úIïCf1‚ÂºXÔS/iäõ_OËíµ^ž•Ûm·6žô:éõoõú7í60     0707010001f09d000081a40000000000000000000000016a102a8a00000311000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.pool.hcs.conf.gz ‹     íÙKSÛ0 à{~…fråu†ÂL/=öRnci¨‘%W+™¤¿¾+ùÓÔ™¶	!Î0°EûIÞµÍtzÈ×dÊøŠÃ•Öê3æ×%°…À½‡;ììk7yˆÁN2V“›ôm{2‰s_ÂúÙ:yý2î_SËù_	…-ùLC÷U÷.@lpð#(r«ABÎƒö/sûj´C£iƒ«±¡r%Ø›ƒ§D
ŽF@A=îÀ/è#9óô{
ñŒ-}6Œ#»çéÐõSz›~Šïž˜2©»±zËl: z·&Wóà¸WÖ\ÐwÞ¤8·vGàx>=`a‹Ò"}äÌ_¸Æ?;w-[Ð]Ë_IÇL®7ÀŒ
?p³±
Ÿ…W÷Ð`ô#ÐQÉt0˜ûí·Í\FËW·” Cù1%÷¼Ž_‹t.ÿÑ2u¢¨p9wö­%S‰R´,…Ë¼e\k+¢geu( YîlQÓµ*»Ìˆ7â½™i	U¦ä©¦À}ü(¼–Ïq3O–1\¦$nøun=ÝršÏ@gTªsµztud	IÊøSÐtŽjn ;ÚVŒ¸çê’ÑÇÀHWŒ68Ù	dÂaÈ«}®d”ñ0·%Ü†ÞËŠ	6èñ,O”›@„y5D‰ž;ÿž“b
ðÓâÊî­˜cŸŸªÜ*Çÿ…Ë•\£‡""åÖÜ'ÄøÔœ:VJP	~V~‘ÌÒŠß9 U,gz™ÙÒŸª×Ãã>'-.4¬¶ Vñ_*º:c<h"tlF[nÙ Ö©0ÞÐÝp‘ú”ñÏ´9öŸíÐÊ%w°µi:_Z
eph±>Ø"Ä˜|wlïµæÚmjü±HL¾N-l0¾#kiÄl
¸%þ¸Údw¥\ëf(tã­á=– Šëv‚o/~»…i}ë;œþSÝÙw»b%SµêÜ/Ø7 ÚÌî”.¥—ÝØ´¹cÇÞh]®ç«¶èmÌ¨Yµ_½{;±     0707010001f10e000081a40000000000000000000000016a102a8a000011ad000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.sync.s3.conf.gz   ‹     í]msÛ6þž_iæ&IO–e'½n’iîÚÞe®in.ÉÝ‡NÇ‚HHÂ˜$X€”¬ÎýøÛ] $ ¿ˆ²¥¨né±#’KpŸgñ²Â>|üx—?³þ 9³*’«V¥`æùýÍí¶u»õÝ£ŸðakY¤âòçG_ÓC¿‚Ç~ô›~!VK¥Ó³¶“:¹:Úá:É$ªä“L4÷ú¨k´ø¥–Z¤W”Z-¤‘ªÅŒ~Ï3CGR1åuVµÍþQt@s¡eÅ‹ÄÝ'|Ê^³¹à©o„`ðÄBÛã¦‰œÊÎ™‰®MÈ/p“Àç‚<‡K¦¬‚¿?<wb•bu™)žÒçž\Ô%|8„Ë¿v§¼bp³Â,’£|ÅËò&Oë,;7É\¤u&êøÆ¿¿Ï{§ ÷ÑKÎÍfÈÎ’#žTÐÀ³1òû¼.S^‰1³Æµ³Òr6ð¤p<&ÀS!~œFÕ:ììb	¶ÜÇ¬ä`¯„0^¤,U96†IËŒ¤ÖZÃv°œW`ÉÐ2a<l5÷×ÀÀ‰9âÍXÑKiq+²ûæäÅ‹fêâ&z©’\òÇ&c‰*BöÍ<—åÞ[O&Æ W€!ÿñ«¸fSG's¹€vÎ« ÀÑ‘¸L²:ìËa¹Jn‚Á\÷Á·÷-:†M%™¢ªäÕ|h£¿9`*…Á[µWÑÃ!è8+„H-l©Z%¸dÂ³lÈÞÚh5B/$Dý”ËÌÆp˜Áñv©46„ƒ–ÌU¥l¶I‹3Ô›†Ð0+0#æ +YÔ‚,†mKVIfc›îóŠ½8ÎW0p/¸>Ænx2$ÝD.ü¡GìkÂ;“¦Š8÷Áöó³LM`|h¢œþªæycC\ÐŸÄÍDå9tÿÄØTJe&ü ÆxóåóªL%à–óR™ê¼qTß%8xÞxgã@h-Kšc‰K‘Ô0¢ò)Ž‘áˆ[0hžÄÏ†ì;­•6Ð`° k4VÜAš­]€=UªœÈRõPÞÊh:‡®gHwƒ‘Ìl!üS¤“Uänƒ2ôíø>€:ÛáªÊÏÝD¦*w˜ªÜ
?\Þ¥ºÇp×1éüzçxt×o%NˆMæÐ$ÏÞO²°5¢Ð4\õ˜îSçÛ{¡êlÜWøÕÃºXá×}Q…_[ƒj3¥=¨{ 5NBß	TkbPë¢Ï%ì	ÔÀµw50ÑT-úìÐ¶ˆNÄ¸=§‡Bd¶@³OÝÉg‡<"Û"Øç†ö’÷M­á³¬}jhgqyßÜÃcøúÌÐž"ò©¡ —m¡ìC{óÎ™¡›míóB{„ô‰¡5|î kŸÚªwÌÅèl‹iŸÚ¦wOÅèliŸÚ¦÷Ì
ÅèÜŽ)6XŸ%wÐà±÷ß?DVV~®¸Š|0•5;´ýÎ><æ4¶œËd *+ð4`€*ŽžeÌÔe©tÅJ¡J°7ç·OÚ©1þ .îí‡»‹çb…ŽY¡*h_¶VxëRFNp_£ÒD'`~à©4ˆÊƒ‹ÙæÈµ o³`“ØÙ™³¶™ìí®ìó›( Ÿk¡îÙR€-%nÙLÎ
Ú¸	`zªPú .‰2f^W©ZbÔ6·x[<§ªÒ†gK¾2`É†Æãâ˜ÇCÆ>ˆ
À¾ýîû7Ÿ~ø8 :.ç*Éâï	†|‡a{ýñTÁ|	5WÐÞ[2'cjöúRÜo„§´ÆwEáNW	\L%xšÈZÕüQ'+p˜ËÓ³±ÊÙKÜùÚ7o^ƒÓ¨BK´ÁÝ=ôÐŸ	eÚøž¯ÂH’öÙ’9/f"DßYöÆ?ìW6bîc1Ë˜âL	/gQv‡=³;	¶¶f<®àêñÿ¶1ü—-020 ìJ7C‹Ë
¹€Ÿ`	f²ò§RÑ5vsxk·hCd•RàC`m²²£0<1p*ÊL­€0Î°4´Ê%øÄˆî·‰>9 U)ÝhW½5t¸Þ©WúlW¼C/m¥7µL€#¾ì¦…¢ŽeqaBíf}×¹GÙKÜ“Ê„>Ã®TØÞÎä&Ø
ž=8`œREKÅÍLp‰aâùŸ§ìI¡ìÈñdÀØcÓ]3h €61C¨TSQCqìöÞÚŒS°LQ»'u…E /%•ãÚûHÐüRC¸ª2[¿qÕj0î£1ðŠ3h^ØÏ¸J2Yr{ÂXUB‚ß°-íuîìàò'X&sFwÁýÛßÛ c;üu6öXØßå`üOì^W*ç•Lh.ê»Uœõ¸²OFLëyp­•L^À [ç%Õ½˜•©Dnˆ5â’ç¥+Xiüõj¬º!ÎÊÙù$»ê|)älþ;û"u‹PL©i'Z £ÎËi?a›¾6¤pQ
hQÅõ({û1ž, ¯%ïÙxVC'¢Z
Q°“ax2†ìŸB"øÒ<,GF¬¡Äøjt²IYŸÿR«Š÷°Þ
ëZí,H\IÂ²T*\ùBc xmK—2 gÙ®c‚‡Âi=›ieËUÿ¯ƒ]»`n£‘&M£2L‹,hÉˆfN¼®–uÈ¾o;Šv×?È¢¾d	]Ãx|tSÅ¦¸˜Ïd.«x€¤ã¬TF;×Úc/¤édO°xvÊr™heÐ?Åb®Ð–wˆàÉ}',ºÆã¯FúŽ5ÿ£!Ì /Í`mzj¯8]?¿Z*{¾€†ÔÄ~ºÇ† ŠCÓÇÀ.º6ÛIùÆ:ÂHÊ¥.Ôdiàã“Ñé[ñ	ß·ãáz¤¬&ªÆÑVV5ÆZ1R˜Ú%
¯,Öá0#¥£X€-“éÈ°Šc¢íj†Chk,4u.¡4…'GÈç¥JPm¸oGD@{2ðäôvöõ¼ëÜ÷¾AÏ7°û~p˜HÌ+cr„xá{TÄhõ7øwM’ =…ªUÅ¬´Æt;c£ÁÉà?¶"˜øÁÍ`æ"?§¾´GtÛžÄÈ_ã5Çwæ¦Kœ·X J¯ØSYDyX ?³þ¾®ŽÞOÞÙó.`}á;ÃJ#QíL¸Áž"R° ¢aÅ²×Ñ¶A>¢ŸìP*?ÇŒ‡VYÏ‘]Œ6oØ4ã3öt„ázò	ás”?r	IË#@€ýP3 U&Ü$‰”
¬¬?Øu•µÏ+XÙ¯¯pmWç–‘(Ý‚èÙ|?áÇü;LÿRCRŸ/{ÿþ]`È6Ò‘÷ý;OY8}ãà2?Ö‚û¸låžÂP¿)ñ-3õÄ.*¿&Á—¶•ŒÐ€­`'!ËÖ×O6pÜ,yYÊ¦§ø.(Ž	^ÎZ¯º)­õ×W!jÁ¯Ø‹=SÕþ&!.ä(‹~¯õ.øœ:…m'%ˆÛÆIÉ¢Ÿ•ü&f%ÆxÜ÷ÔÄ
÷4wv«õÚPš}FO­©yëß†ó™±ïEájæ8½²ÊA”U\¦ýÆÍ/Ûèªôr*ŸWN¥vêez•Cj¨tG±S•L/œò9…S:‚·©6¦WK9„ZJ7ð:UÄô)‡’Héb‡:˜^åº(Ý¡ìTýÒ‹¡Ne+(7Ö¼ô
(‡S@éŽd—J—^öäp²'ì\ßÒkFëd#ŠÝ•Nz“Ï+pÒº.éš^Õä€ª&Aì–­é¥L>£”I7ì6&kzý’è—tÂ®[®¦-9hIg»¤jz¥’*•tF²[¦¦—'9˜<É6HnNÔôš$Ó$éd§<M/Dr0!’.@n‘¦y¨zc¿Å‚y*z¤ÊªYÆ« ¤…‰‡Q¼21Cö_®­3þÕ\%ó2“(¼ c§®*¢²:&ôÚ:,ïŒwc¹(nàú°‚.V+Ón5ãwüi™CNÞDs‡çÃ›+ï§XÎ%Ð¨ä‰82ç°6Øn¹œ"Ri~äÂ†'‰€HÖuQÄÛª9{Ò<Ï“¦'xÓš¡íÚ—%nÔ£½QMÛK-Ó×O_RÕõëÁp8|†b&o§VªÃnU¹¬°@N¯Ë©0'Q—ã1lÕî}jÈe¿%n+’Ê˜˜<)n‘ÄqµùŽ6£}VÉ_·Mû£×ºð²	+$sp¤é]^¯Y%sðºj 'ÊbFá›G}ù¨—ChyÕXòÆQƒ£Ý$:egq…{¨Õ£R£Ý‚]›ŒÄuhk9hÔHò!"ž1ÕêWQ´g5¢:¾ßY²ç5·Á*Ð°zÙr“J`½†‰ÓÀK×Ùœ'ö0½SRåCL~MaÃã«xã%wîvÅ¾â‡[†§P—ÀíÐ=ÕV¬€j-daË."ák¦}ÇlTL›Ú~€g!Ô¿˜<ÖÖ+5"ÓŽ­í¡=îì<_]ËÌš¼MAø6[WQŽå²îJº>¸g+0çXï‹ÅÄ•Lê,z×gSìŠ0'`‡‰ée»²›u£ŸWlø§–HöÿX!cw(³SêúœFš?¥ÞËßämè³¬cúž+ì¹ÒÚNA¢®ë[bËe!ó:gä¶ˆ/mïâ™C…ÚÌðüÚŽæ)EË9\2uŒª[6~žñ®q…ðˆ=-T¤n íx‚n½úßý{¼ýÀÞ<:­ƒÇÞz2Dóáùº\škhù¯Üªµ¸;@—!D49®>¦rçãT%ÇîëÇMo5nJ•š×fƒñŠ_FÌ™½P†^õÚ¾^û§/F£³Ñèhtÿ~ó—¿°/F§ôés÷iUÃ<®.¾øùF^Ð;…{Å¨}IŽÝwåõ¤œ6Ü™†‡[Ù1?E…R``8Ððx^‘ÎXíœÅŒMj	Ö>¼ùÑ½MÚ—Â4-!%B«A”]+Äck‡P‰ÒÊOÚwS7OI9êU,E­Ïðí–×öâÂVýWÂÛHSY5H;%ù7ÀRØ‚ôÜlÄÏN­(#¬¥ENK³öP5ÔšEì¯Ìh¢y¸›ÊÁ3ÄÝ]biSOpú	ƒ¦ýZ„æ{{ò-þÁhµ*Q6
ç³G¡Ö}áLºå'À¡)ISª8
ß–~æ\«Jà)Žîaa²ª'ƒ…óiÔ¾šb9#^Gª®Ù
[6EÊá-]fãŒ}kÛjýã`0þ™Ï\ó¢¹¯kWß­oˆ÷ûìÅ>³äã‡¹ˆÉpKÖ—ˆ=iöLUvâÌML	~»ræ:¦\É(¬s& Âm”¡.úZ#Å9+â¹¥Mšë¬§¬sí¸vë×´@™À\î¢ÈýFdãèÑ—_¥ÅmÑé¶õÚ?…ÀÍ†@!%6Ñ'ç—Ÿ/%¸Û:œ¤ÏçŸ#-xžË¢^[b}*$%éí!Z=Ù”=±ÊfäøD¡†)‰ûQ¦·™¸Ìz¬V±®Nç@!—Äÿayï¥£qxÆÂ#Ù·A¶V`…ñ²Ì`Áä…í÷,ý2ÆgZ|áƒSÅpI%«~ùÚ¤äMtWŸ ¥¦%,|²=V¦Nñ·m>Cò6’Òú°ïåößË‘£L?ÓbSOçvìõ4Ú?œ«‘Ö©ÑJöWÏ¤ý3	“{ˆH!16ñÈîXìy´YO?­ãU|öæÍuòt˜¯Šøä©ƒ®äõDVšãN$ø¿ÿš‚Ð"Ya7ÿ2jB4½nÓúKYÍÃXƒ)ºÊEkWÓŒÞæaA×rI«N¾ÎÌEÒô×½Zc¡dŠï­X¹WUÑþ5¢»×‘h•w³ÛŒÜ¶ËñØ¿ã•ýŽ2~Ç•¿«]¡ÐÔ‰Wõ³‹ü~äØîµ¤g¼™•y‹õÃÝŸnž¾²7ÀºÛvÚx+ömjƒ3û±l¯cYàé‡0–]Gê5þüTIÇ£     0707010001f0a2000081a40000000000000000000000016a102a8a000002ac000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.node.pool.symmetrix.conf.gz   ‹     í—MoÛ0†ïùríÇ½Ý
ºË°CÛŠ¢Ud:Ñ"‹%¹É~ý(Ùq³¦I÷‘uA_›2%>|%2Ãá6¯Á¶x%w5‘=€0¯ü¼ª0°™ý»í®n»ì×)Ø!Wàìfpšƒ~ûö`"˜âüž¸8yXˆbVs±nñJ¨¼¦Z,öS]qÄd`üc±b(°TÑ†‡µ}"‡W²läÖèkÔ¦4Î`ŒÙèœxðZF\Mœªä“‚üÎ!ÀÔÑ½åáDæW'wù6|“ŸÝqy¸£—’MÚFPîäJ3Ž¬‚!w$sžf‚¹$ÚÞÞÞÞÞ=À\”ã—à{®¬poÙá¬­Ñ|`Š5‚èQ80\^¼?ÏpRä›ÙxK¯Ícñ]"7F#|Ä-¼3b….,Ì-ê€\b5^^›A
¦g8
ê]Ù[~•ägÑSÒ’LÖ«­Å!Ñ
ÖñZ"õ¿ò2§šÌë)²è+uài*kIX!4dc…J¦ªÃVoVQéoSç¾«Bš•þw¹•Æ¢ŸË^%DrHU*dˆ™Zi‡z¸7a’Iu „Všs­j:²Ó[ªÃ®òº¾ùi\J[ƒ¼ôµŸXœ­€üÖ¤?µ¶9 å
 AÈ0ÁM;ˆ Û² ©ªò˜:=ú®@,oò¢–­W2‘“Ðó•4H(k³ ÉúÏ’b^Ãwƒ¼;¨-®ÍL]x)¤k›Â¿S˜+Š©ôvÈºƒ4ÁLª•"L‚¸–É3e8¨ý­×,¢Å×ØÚ4ÊÆ¬2!ÝrócØ0¾m!Zî¨G_¤çñ}©*rê¹IÏ„"f>ö9ŽÒÇ½ïÔ`ÊÀ%o½ÉÏ]P³¶Ô=ZQ—µïÚûäz3  0707010001f099000081a40000000000000000000000016a102a8a00000204000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.node.pool.directory.conf.gz   ‹     å•Moœ0†ïüŠ‘öš†{¢ôØc/Í-ŠV^3îë²¤êï¬²%j­T_xÏû¼£ñf³æ*6°âÊé‘¹€Ø9„J{”‘|÷ŽtëV·.»â.‹Ýxm+<Ü×½è›YvQd{ìÉWWs!NÄš7>­¸2© É‰Áé¤/ÂÌ;$.ª:Ý©P‰dâ\ÜÏVø_eÖUÎ:Æä¬ýJ­´„Ïð€½–½ZÎ$GÜÖ*“µ)ˆõq?Ô¢¶ý×|´dRƒa
ÑŽÌ'†ÈÒÃ%ŸsÝ§½ùsµÔUØfƒÎüA…7sÎXº±H È7"Îh+lµd´:Ö=»ãËg.Ðjö;³ß’‹çÊëîþ¯qH²-ú£¿Cmðpò©Í³Ë´ lÄ=ìÉý¤G5YNØ4}ŒË¯!sOC¡ãâÎ£OœèM˜ø²,eÑ6ë?3!k^àûJ{P\¯3µñ£Þúô¯D¿’Å·Î†’²qèf˜¹k‘a2þpæ¦ˆ–À…(b
Û k¬’9ÛAú‚­0©ï²€1?2¬²ŸÁ<ón®ãËj÷/¢ Âðäè¹+OÍÄý¾!r3û2Ô<ÊŠd9åææÎGÙ¦­ÐÙ(½[/+]û	JàN
  0707010001f044000081a40000000000000000000000016a102a8a00000131000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.cluster.hook.conf.gz  ‹     ÍS1nÃ0Üõ
ž“´HÇŽº¬JT¬FJrÝßW²ãÄ@Ð©ÊÑGÝïà¦©9¢ŠSè:¢SEººîêf'ÞÊ±[¯qzÅî	¿¿ˆõÃMUQßK¯3¶«8%™ h¯bÏÒ,ã9YF}h42¹xó÷BW2„lyÃ€Ê«à	Žè‘­*kŠüˆ¼y:‡Ó|zæ*¯®GC$À	UŠ™ÚC@‡*¢ÑÇ°ŸW¦#Ël “¡Øá¦õÏ@~gˆ{¯ï@Ë(óS:v¢¶~Ÿ<®š0Dâ—6åÿZÆ_Šp6Ä»ÊG s9{[GÎyùU×ØÈ/}ÌrÒÁ(]ÚVÑ¶Ò¹¶-=eÿ!qneÿ"p !~ Ç†eÛ¡     0707010001f062000081a40000000000000000000000016a102a8a000001ac000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.cluster.syslog.conf.gz    ‹     ÝSÁJ1½÷+z­A/J=
žõ&b2»Ì&53[-øñN²»íB)î¡X’M&ïÍ¼7™Ï§³9L82ïØÇz2¸i³›V»ÙKWìë,gúŽ»Ï˜Üí°2–<ÉN/&Y¶qcÞ<îÙŒgÌ'	?ZJèŽOV¦õrHÐlbà4{LÝ1oÐREî¡Æ€‰l©Q1ØjÄó£‡"A"ä‰—z·ß^4'DZG–sÈGk|N¨‚ML:30Ê%+ˆ*cpY3Î¢ÁcIÖ˜º€SÇiŽBOƒŒÞÖ¼–·Ë‹_ÄÂPhÙÔÈù²¢°h¼ÄÌp«˜UøV§Ë=aÇ-úsõŽBí€U5ÉAî!l"!Õ¾SR/¾áÓ¤@¡ÖU×Éá[[¹ÜP ¦mz¤þ‰TXœè[ážP$cêÖÀ8rÉXi÷;pÄY.æöm¤_­W`ºmå
.jTˆØŒ †lŒ¶ÞZ}-æ÷ËJí/Š°:wÃ¹:}suýßd¡Y•Êf?*¢0Zñ  0707010001f05f000081a40000000000000000000000016a102a8a000000e5000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.cluster.stats_collection.conf.gz  ‹     ­RËNÃ0¼û+VÊ¹®A Nü@¢f=!&»ŽÚþ=NÒ¦Žx;ö¼ìª*9¦¢‚3Ñi²I_9† N>öÿ¤+ë®lwf÷;ì‹™<àtˆâš«´r7dpSp¦~”ã`ßVµgt"Á×èî/âÐÚ1¤«Á§»Û([‡,˜`ßz¦GzGñ<ÌÊùÄö‹+ö	BmJ¨á+iöçz–Ÿ±§>:Ð‚Ýüxà-@£J­Ô.r})må\zê“=æÛæ~Ý=Ì!Ì7ó–_º     0707010001f115000081a40000000000000000000000016a102a8a000018ab000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.task.docker.conf.gz   ‹     í=k“Û6’ßó+PqmÙ®“4šÉæ6;ñLí\Ö¹ÛÚd}µv6R)	"!	7$Áàh”Ëý÷ën $ ÷ØÒ8“ÐU{HûÝîÆ³gÇüóÉ3vÄ?8áú¶ÇÌ²,UÉ­¨>lºã®î¸°ûäüØg•,Rqÿã'_ÒG_¹Ïþä\þ­X.T•^¶«˜dp[³Q©´UuOõøa¦UòI&š×¾«j7*ñS-+Ñ®çkžiºSVêNj©
XÚet'S^g¦ý„¨‚nÈb.*ix‘¸e‚OÙ5›žúU_/*{_—"‘S™À33QÀØ„`oÐ	<qÃ•ç¼Ha®di˜QLÜ‹¤6‚ñ©b2s_¡U]%‚]&}žXôå 9ÈÅ^°×U¥*K…±UÂxw^þå6 S¥ö#¯îÇÝDLU%N‡<‹Ý¸Kæ"¹ý}#€ (’)7B_¶ó³_XÆù·ÿ-xUDHþÛ”iAx}îzÞ#¤à¯‡WÂ$*LjVk‘â³•(Ue'Í`¹!m¸©õ`Óô´†Õw$‹h¢æ©ÔeÆ—ðb®áÍ+p€5½£P†ÞóÂÁúe/˜_d×ÃÔÔ­'æÙ‚/5+Î8QÑag½Y†øÝSœ*îDÌ¯ç™¸èê Ý‹@hÈ‚Ðá¬˜Çp	Ó2U X<¸QWìL•æÌ¨<áæl"‹3ø—g²à=ßŽ b*gz$Š;Y©"…yrÈúáÇÇGÕb.A~”YW”¼i’²LwËŒÇ¯îxu}õÊB—<×g¯ ú¬äf~=v»Wˆ,”ƒ×øÉ°´L¢¢G¦+áÿ"í1­€
j "û©Vðnc SB•Q©è Þ¾õ½@7ÙÕÓb®`¦•…smX2û¯$bz¬.KQ%œ£Â5BŠÔì"¼*ç¨ÈžÅ5wN¥‘pµ †š÷¬QåDþø6Õ„Ã{Å6˜è„Ì´îDyÒMµ•1(æCº|7ò›f|æ(Í*3œ0åÅL ±i£îoAD°é0rZŠðï¹rß²igF:4~5GðJ JhÛJ¯D€]"¾H=Û)Pæ¨ÄBXÚÀAþ¬¨r·pdvde“—V †éns1†'ó'‡?Ù©TÂ&æû'pQ«žAmOxr;#‰<`o-[ŽÇS\³ÃÈJ²&ÀW3ƒöÍT1ÓŒºËÛÅå,²ÊŒŒÌ…ªÍ˜‘0'ÂðÙå¸PÖd}¸Ax¶hT`¶âúN&BwÚÿÚÈ@€’ê¼|Õ`Uþ,Ò ?þ‰ñ¸çÖ³DnG´ú%t¡¥-=Wu–²9¿‹”OÂ£ÊaÜŽÜ¥ACWËRÉ'hñJô"nã¼	&4‘ëLñÔÛâ&ì¦ši–×€ª‰ Ý+‹ %Ä¶Î:··9Zëh¢o·Ì;‹üùÏjAÖ¬3»|½ŸîÍnšYÓm1µÛì´§µ£QÅ”·&0²¨ã\ÀžZ@“¥»)eÐŠ­zdôJƒ’Èœ
ÐG€¬*M¨±wvN³aÝEÚVüšº÷à×,d‘ª…^ãa˜øu ^Üs/rLÍ3–ƒ’×bÁ‚ð€“nCêQôe;q`&S4²'<ˆÂÀ XŸÖŸÀ•©dØýÓ»é_æ|öqaîoüJ¬
À#¸°²Î²YðÞ÷n±±fN€¶^±/>›^ðÏÒôâÏ_QÔ“º0õe†\evÂ„ï•*“ÉòÉÑ¿èŸRáü ‰lL{?º˜ã ô¸N]¯K0ºW%BN[áëüiÙŒg|Ã’®ŽjBñ-öC%H¶!¼ðtÁö{ÑT}caùïB”€ž4Ì
(¼ƒ3ýexìBÉ‡×#.ÆÖ¤GcÑ•…AÌ„å®ð—,“Bw*ªUQÚÅ6òôrìxEe¿ƒ¹<«Ñ0Ž5V»OºØ\äªx®›(™ßFÎ$Ø_ð-ÀŸ©kgÌÁ¦3gàQƒ³–·#ÊJÞQÁ øýnñÈ¬$Ø«±ŽLÊ…9˜üí\ô+"*š­fËlÛgjž{6„¹Ä=Óï³¡]ª¥SZôUxwfjÖ~£fMH©Ú”µÑÞ–÷c3K?ü»'2—óû:´ &²'þóSÂEìù=ÌÂÈ2µ@iQøXa!‡UÉqŒ jœ*©«
„H¶$4„@|œoCÆ“t&^ñÚ($FE®Oeä’_`xÕZÎ
¿WmZ·ÂÜ	ï…q(ÏšŽ1œ’‚:×ƒýr=hão¯*tNÒ{ÜØ¤wG3á7ãîS8]º59'Æ90«`Ù†r”Ãv>@;¤Bíü.Ø};[-à«KUšË±+­öm‰ „&…Q1&ÖãËAÚ:ÖÞ´sb•FÇ‚œ’Þ¡J½½	·	nd^‚JÐÀ1ú]ÁÅ¤ã6¯'±»ß5èYÜl-C*LS*Zé/u=^qû@øºX‰síPç4XÊ¿…ƒÕ
X¥S…{°Œ‡ß×‚¼}×%±ºÂy½#Th¢ ë(sÁ¯cÀ‚´2Ýn_Ø5¿ø21
…™l"/ÍˆOTeºÜ€#çŒÇ&À-§7à÷¼ý×W–¬ÐaUZ"mƒ•b÷•‰2CêñOú¼£ DÊ››xƒþ"ì­Ê…â™Ma™ƒVFyA¯C]YP—”2GäLio9æ·½úþe&Qc³»Zµ^IU¢7±{tÏh§ ¾4úHQõŸp-!¯L´/¯A#Ÿ©×i8G6HrØ¯$ÂºWÒá.ZœñXWÜç.’äŽûkºº;û_jÿw†É)gvgOŸ¡=8òîÈSú.aÔ„»´
ã=/=¬–‘Úê
eÝI<¹ÅàÙê¦i8:ä40ºs›m´‘ŠŒk7Sì5|šÿ“ÓîxÖÉ1¼>­³Ìá&ø{Ãß-­Y×´R9³<mÙ9Æ°Ï ÄxÜØF¥L;sð s µ+X³)œ¶Éx#€ï4Þ¾Óµ5òCº¢Qô€³	qQœÍ”šeâ¬äØ£ /%hó’ý¬ò‰ÒˆŒÁ5&>8Ð²º¢6Zc™Á£,³M«Úh£@D³RgÁG6\á—çÚ­yÂÂí [­«Þy¼êgm­9¬v§+Ùy¼’½;¤`e ÌÄLt5mëü'î¶ˆ{#Š~=½Œr(ðÐ=Pà¡ mñØh@û{öKMzv·Ù=¨8›ÜHù<í/àãÈ§ ÍW‡g·}í¸º$Û'Çqù)ù-­+W­àï{.É|a¯üík/5S‘d¼ò¶xkF9×Ž³)—YíŠB¸ŽàK¶ Q<ÂÔºN"¿Å}Ì`íQ"Ü$ŸK³âšàJ)‹„ŽÊûv®+1ƒ·VËQàé¼¢µÝ
ŠiÐ1Í ^oö5ÐÙAá.†<øÌ€¢¹LæñÎ5ÊÄLÍä†]ëÓxÄx·kÓ‹uÃ
•Š~&îDÖÂMèÉÁ«Á‚ž(,Æ9jNü·ï/÷ÿÜÓj+Ùä]\÷}Õ#OÓÆO¬ò±'€ÀóÆŠ¾æü6ôõtíŒYï*"Z+‘«; ª‘UiQïS˜~c¦Ç+è÷%G#t´ž®þúÞT¼	ŒmZr­7`Òy'=[bw§2ÁlAuë„W]–ðÉÖƒ÷(ÔõïlHËN9pœ9€iÏ`²óKûÌ¢~É>ûÓàó?þt>¸øüò‹áCú±íOõ§ñ$Û„þzG*í`9–©±h§4>Y!t%Ùsl,Ð¸”7íÈ³®RÁ–øÇ¸~ýâiÿëÞ`0xW£16©€Ê¥1(üÇãºìi“N–¬.ÇãFµ¸¤x¶¸@B$øUv¶†Ø<! Ám£ùtuöÛMNôûÜÍ§#`‚¯~ïDmŠR

?#ÖŠhóë¬ÖÕEøÛ}ûÌ ü¢™Nˆ»Äôl†ûÈšŠJÀ'j›ç®ØŸ‡—Ãax?ÿòïç,WÅ§=öéð‚®~æ®šh¿.>ýq;hi7;úqhž°Ç²ýÏÙò“#îN²ZË;Ñóv ×€—„·ÏpSTÃuŒX‡;ÄŠ²g«e´©cæHÃëØ‘äVx2W‰CV‡55 ¥Ý±	¦$Aµ¾ålÍ^Ú!Â®7M*9úýºø½)-¸^1~3ø4ð3¹‹%àÆ IEXí‚+cEíM.ÍÀLªèz0ÝDÁ‡¢õnÙ±ÝrÀxC‡h;zÛ&t;Å>ãÌ:]w‡ÔÞXIèœºM2n¢üÜ÷ïíáJ#1¿ý½Û
í€éí±ŒvÖ$êB©ŸŸžª>zôì-*JvÁ³ÕüU¢‘¦Á%Þ„Ûè·…ZP0 ¡M
Gìä”ŠYÅS×ôcNÍ<*"³–ŸŠ8ŽjÈxÆ«\7vƒÿ’r X) a	Ä>Å¶úèQ>Ù¸êùÅðÉVCÀc^óÅV=UìœÚ‘ÿ0ô4–ûì„Z¡<P3496?`oÃøvl"\ó?”dWI»ûÑbÿó­9cº€úAu,/HÐzóL‹:U} ³Ù-¯s‹­.HîƒäÊM¦¡i²‹\[#X'66!Û‡AË\\l…°]²e`Ôó _™£÷¦
$ÝÅ\ë2NtDiûd¡‰¤nÛ Ð…+ÆÓ\»ÐQt±áUOŸÒãq¯90™Î‚rG>ÝÁÅv¦ƒ«!ÖàìxŸW¶’úæòÉì3PFÞÚJ(Fwt²­1-î`:1úI—„5*t'Äþj5jHMs	¢¹§c¬ÝãQým×Œì(ì®bá´]B|¥²ZíOfKŸ™k]fQ%Eå(Rð=*òX).Vr`Ñ2½çíEÍ£‰®I,ÖæZ©ßcÁÊ÷Ífø-LEA;;j­5Q`D];cqSô†:Ûâ}S]ãú:ŸBÙ|Éø=ÛÕ7Ú´Q|¸Cå1²–	”~ê Mó`bC:Yv˜<.S†°BÝÃ«êÚÙ‰3UyÆTåCðW®<[ }_®¦xÀ!/F?¼näæOˆ™`³3~ŽWtëÇcä¡ìlŸÓ°ä‡?+øyZ;Óçh|ù¡¶ÃÇÐ×Y>§cÊ4}bìì9"üHyRÇÍO<þþKd¥æÃÏF‘TsÍ´3*o	“W¨¥­C¤îÙº•8@Xàc@ªèc_®¦ÍK)6gsÅ0ø µ‚6ÎÍl²¤T¥‚ZzÛòxu©
-1‘jê‚8tîHMN¥F¬üÆ
R=õÆ2ŽœÃ…‰`rV ŸSVc”³Y—D2z^›T-Š(%ÂhŸºÔ=gÑxÆUqÆ1MiÒ²`}ýõÍwß¼³abJ€w%Ü;íáx´ÆA¸^¿éþ9WEtºÞšÅGË^Õ7NöhÚAÕ^é¨×Q.RÉ)Ãö¯°ÑiæörTÎè˜žk¿<—×§…Ñ«gK¸ø'á£ÚÌZ|f1øÞj	/”ö¢Ï‹ß,
ûâöÂ:‚mPÝ]Þ—Œ*Š„—]Ù×Q˜ímœòMüÄcqvµ/9¤eÚÉ÷f¥~(A‰™âÞ~S¡H˜ŠÆØ¤€vZÜ}ÎRJ³èÔwÒ¥-aÒa™©e{tŠg´vÓÙuïÛG>9`Ë<ÁNZ¿Né‡ù¢¨k2ÛTr6Ã”B"R’’D]j‹ŠVËâV#	¹Ó‰ð¸¸²9¤*ê•è:<áQq…¹m¿j§ÜÛOvüøÓkƒ‹|jó›ðÄ¼–Þ$:¶—ð¸.ìDñ¼PVs<ï1°¦§®<Ýœèfó]¶¢FÝí¡¬M°LÑº'µiš÷¡ç§¤¸ôGÀ¤#v“i[î¸6k ÷©ûlV‰îj‡§âBÖçüjÜp-í8÷t0ü9Ör€ÍèØ?ý»ÚcÕìÂ­ÆG¬m8½×‚!î7ëÅ*Z=î©-Ó´Î6ÎbÏQ¬ó2¨Í·é×âžc›DÛ´ÌÃëêTµ­Æl4Én¥-„œÍcÑÂ°
(0Q¤ºØvg•3Ti?àš<¸3õPÑÒßÞÀ'fœŽ°ÐÅê‹¬†N„YQ°ó!áð|8ØßEUˆ(gÃ,Ý§æ1¶0Ñw¸³IY°Î„whÝ‰Ö•<,‰¡Ò7LLEÏ“³²¹8(.-Ø	&—¶êO‘š²?Ä~pÐƒAø.‡|¢U†aÛéÁ%Å:Ó)špSûÄûº=×ßÈ¢¾g	ÐXîÛ?SÛ'£3ŸÉ\šØ †­ØÕMu®¬Ç$s²Î'ØÎ{Êr™TJ,N^iÅÓ „ÎFØIEÇ-|>üÃ_à^³øšd€Ñ°Ô½óÔŽ¸X}Þ,”}Þ3@CÔDýôŽ=,`Ûw<pÑf…ÔæC6\¨‰ÒÀåóáÅ]»hê‡ömÔô't¢aMIàÀ#EÓ»bÍYÇr§Üº>Ø|;r“m>"Öß—5êD_túÎF
ñL1ÇÖý[
SxâéyaM	›8ëÖ ë€x~±›ú:º;XöÚ<Ú½<Lda[­‰Œà°¯àçJqp¶€v’ˆs<mù’{ç½¼0ì_´(Edâ…íÈÌE>"YÚaô¡’DËŸcŸã5±¹>„Ï[\ðdìÑÙ«“%8È/-£¿©MÿÍ´ÿ­}îü[`ßFP¬—kãÔŽ5><6fPz.o°m™|HöP‡Rù#•Ê:9†¶¹±ÇÒ¿ óðÎ_"Aø¥ÆK. ié°
 Â~€dôž¸3’([ÞÎ“Âì/{î|{šŸCí±Ä|;LLFj¦r„HäÙìOx½ÿÃ¿´ædÒ7o¾kyi‘Žxß|ëI¿¸ÅÁ°æ`×}Á}°IÜJ†6veºžX§òKü
¶•¦ÇlÏÄóÊªÝuŽ{Z/¨0LGâÇ qðrÖBÕ™T¾B.òB¬#®Ø÷H¦W§3BËÙÖ ;~\·Gh” Þö%wUò«°JþùñÔ¦‰=¶¾y³óÖÝ©ô3úêŠ–·ºÚ3c/EîBµ=6NWóQÊcê`ÊKWó¸51áî€¤Ð®æcÂŽÅƒRA»ê—Ç¬~9yû@»’—Wòr
Nûìê\>NË!§¤< …óxÅ-‡ î+¦«hùˆ-#ñ0#¦+cyÄ2–Ãp·×†éjW>ZíÊ!|€	óTQè'û5æXSž%ãÜÊ2æTÛžÎ¡	%f£˜kõ€}Ï+ŒÿnFáéÇsõ…6UPÞ-}¡6Ø˜oVa†!Æü¾¨‚«¥n£“ÜpðUC9ä}¤ÓêpÒCšï9èh‡¸ñó¦C?Ú!îä·zÈÃÆ£Ö‰b×˜ú’vÙù§*ïøPéãs÷]öœcQóµM&@†`6#vÇ½ç·ŸkiÊþ0š›Ô2ÃØÛ›¸&ÿ~Û¡Y	U}Ù|ïlcÒ³Ý§Áª?[êGïm¿’&ä˜+³â¶ÿl•5ìÛìØÞ×¿ó3Òï–*ï¬Ò_Ó
wÎ"y>_»¢\ä$Ób´‡švZÄ½Ÿ£­,Á•Ø“¿C¼»!–lê	&Ëª©ÛY¥,{‚iM˜Ô,KLÑG)ØÓqs—EòÏ 4ÀB}GTDß—tå´!÷0	[ËµU`Á·Ž©ç:VÐfK\Ù+þ¶p&Á%û+(³ú‰13hÿÍ—nyáÒÖê‡m7T¦Sû'Uûã§ òcbØ¥î±­|G4§%U>…À"RØE2$¢§ù(!¡h-gElË!)´ã,¤,p­^Ûé÷>û³à¦aVXÓs!‚
x©šHSqÌÕ„ß½uÑ«J¹/¾^‘–°rÈ´×ÆÔ9ÀÌ†³ÊE;¯,¦<Z†ªÀ¿³O';^ÀtQõÎ¦êÃ;%S,í[ºj~*ã_‘®b›à¶o#×À‡Æc_=xå‹cVî­¶,’NŒK|â“­ÉD³æÌÆè·³ûS)>ÝxT,\‹;¸>,|‡w‚';³à¤fA é§`Qn"’ÿY†©Ä»   0707010001f109000081a40000000000000000000000016a102a8a0000102f000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.sync.oci.conf.gz  ‹     í]ßsÛ6~Ï_I¦“dN–e·½7É\îÚÎe®in.ÉÝC§cB$$aL,ZV§üí. 
”ˆ²%«já‡Ø‰%°ß·‹ÅX={¶ÍŸ'ÏØPœ^”é€™E%˜JåƒÅm·wÛÕÝ“Ÿp°ÏjYfâêç'ßÐ _ã°Ÿ<Á¾_ˆÅ\ÕÙÙ²†×SaàÒÑPK:Uç¢}Ö§ºx¡¿4²ÙµU­.¥–ª”å”.~ÏsMW21áMn–ÝþQ•tA–3QKÃËÔ='|ÂÞ°™à™ï ^g¢¶×u%R9‘)Ü3%´Mñ¶”—™Ì¸Ú=¢T™Ðì7–ÕýI7©òRÔA'r©iú§Søü[¡,¹!8	jÂÌL‡pë7VÛ€ˆ•z&ã\¥ „óJisÞªe¯ µXì¥@Ïo‹¢ Ì 4®ee˜QL\‰´1‚ñ‰5é½Z5u*«=KxŠ%íHf?²ïêZÕ:êÅ‚wñ» t¢T?85pÀD(ïe ©2	À½Œ$fcáŸ2/"’Û5ÊP·ÉC u26ÃUUÏíX¦ª¶a˜ªÚ?˜fÏaÊŽnÙ&^ïm®ýÆXúØ+¢¹m4I³ÂÓ¸›"
]3ªÓ`êtû TŒûà
¿"¬»~=Uøµ1¨M…kâê@µª}¨VÄ& 6eÌ%ìÔ@µ÷5ÑÔZÄìÐ¦ˆŽÅ'¸§‡Bd6@3&‡†ä–³C‘MŒ¹¡Ý˜äC“C+ølkLmÍ.šrxl_ÌíÈ"
pÙÊ˜Ú˜÷Îu°ÙÐ˜Ú!¤H­àsXcZhw¨Þ3/ÔEgSLcVhw˜Þ?-ÔEgLcRhw˜>0+ÔEçnL±Ã¢|”ÜA‹ÇV0Ü~ÿy¥Y£è¹)¡-ø,`*í¶,Q_JXœà5¤°ùL¦³ P©Y‰·Ty”ò<gº©*UV	U¼¿„Ò•¨Ñþ .îå¢à©Æ;¥2Ð¿d-ðÑ•*µÂ5‘ÀÁ	˜ßx&5¢rp6Û^¹ô‡lÚÃn²ß–¸¶ao¬ |^®˜ºSä’l.â±`rZ‚g¦§
¥šŠ(£gÉÔ­¶}Ä;£ñÓh¤Ïç|¡A0’%IyÌ“dÈØGa@ìÛï¾ûù‡O¢ã|¦ò,þ™ È÷qö×_ÏÄÛH¨™‚þÞ‘9I¨Û«Kqç{ô}š	í]‘ ¸˜IÐ4‘Õ45è£IÁV ˆ+²³DìUÅÍìï^ZƒÛµ0¡¤³‹ùYâ=ôwÂ ôÒþ‚ñ´$iÇ–Îx9Ù 3¼î“EiÜêÃ~p`M&æ>³Œ¹ÅÎD™òêfe[Ø!{/cCÖƒ“L­“ÿ¶6ü—K e AØ•$H6âÊ wðSô˜Ði&¿ê´!Jb5Ü–	TÊ€´ñÂÎÂ0b0àLT¹Z aœ`oh(•KÐ‰%=o}
@ËUFmÅ[ƒÃõJ½æ³M-§Sà'ê€“’)p„I;Ý/¡XNÇ²¼ÐH!=SMž¡ÓwÎ½“½„Î“CÓgèJÁ#œÈu$PºTžÐN1&a†ëp‰¡»ñ\gìy©ìÌñ|ÀÈc“m3h• €6M]†Pàá2ojÜ_¶ÚúŒ!X®¨ßãÆ ©^J*ð×’Óà~iÀÜLU¹VDŽkRƒy…Vj1…î…~ÆØ¶²Ô¤öœ_BGmCÐöeÙÎÝ4®@3ºCöÿlÌíð×Yâ±°3¾ËÁøŸ®xcTÁL)õn£7À!û¬Å¤É‘7JÉåL²MQ±‰Ì…^h#
M¬W¼€ø˜L¥Õ×ë-°ê;«¦çãüBªó¹ÓÙìEê¦˜(3½œÖu^MqJû	ûüó&…‹Rp ‹ú;®GÙ»0Äxr	¾–´;dÿåy3¢d'#Âðd4Ù¿D]Š<àK;XºŽŒXAˆñõèdÓª9ÿ¥Q†GXï„5€£X¦¸’¬ñ\®|¡3 |MëY°y•b”í
Ãz6­Ìƒ0o~Ñ]»~An­‘‚¦±V9¦E.‘(I‹6&ëdU¼æ…0¢²ï—Žb€îúY6W,¥à’äèBÅB\Ìç²¦»@ÒqV)-‰+ý±)œlŠ1 ‚B¦µÒèŸÁ%Êò
<¡î¤‚EW’|=úâop­íüa. u	ýì†§¶Åéêýf®ìýÞ ZRûékL@ÏÀkëhÛpmÖIùÎ:ÂHÊe.Ôfiàã“ÑéWÌêÈÞ•Œ‡ë‘ª9«g[i:6RêÆ%
¯-Öá‘ÒUˆ.eg™LÏ@¾€Tœí¤hW30B‡¼YK\ßZ•PšÂ“#äóÜ†
ºQû~th[!ONïf_ä]oßû5ßÂîý à0–˜WÆäñÂ{TÄhõøã²í-\Ó¡SÌJ×˜agl48œâ££Ó%¤&~p;˜…(ÎÉ—FD7õ$ZþÚ]s|Gf®ûØùK @ÕöB–<,_ZCÿÐ˜£“£÷ö¾Xß‚ùâéa·Êµù‰”kôáFÅµUz+oÑ¶F>¢Ÿ5ìPª8ÇŒG­òÈ‘mÌ6oÙ$çSöb„æzò	ás”?r	IË#@'€úÈ¸zâ.H‚¹hâäd ýåÀ®«¬|n`5f__áÚ®),QŽˆDÏöý„Ÿ7ðï0ýKÉ|¾ìÃ‡÷ ÛIGÞï=eaâôƒf~®õ	PÙÂÌPí¾)ñ=ÓÍØ.*¿ÁQø´­40C¶‚„,X\?YÃq=çU%K0˜HñmP¼œ-µêB*Zë¯®BÔ‚¯ÙWk<SÄjwAˆ39Ê¢‡ïµÞŸ“SØ4(AÜÖ%—1*ù]D%A{ÜuhB’–Ov«õFSš}J£®©{«ïÃx&ñ^ä2qp¡¶&Æ‰•UöRYÅeÚoÝü²I]•XNåqË©ôÂ®Çy™XCeŸ5Tú£Øë”L,œò˜…Sz‚·îlL¬–²j)ýÀëu"&–HÙW‰”þ ö8ë¢ì³.J({~‰ÅPöWe#(×žy‰PöW¥?’}NºÄ²'û+{ÒÉÞç[b­“ýÔ:Y‹bÿJ'±ÀÉã8é]ŸtM¬j²Çª&½Aì—­‰¥L±”I?ìÖ&kbý’=Ô/é…]¿\M,Z²§¢%½1ì“ª‰•JöX©¤7’ý25±<ÉÞÊ“l‚äúDM¬I²·š$½ì•§‰…HöVˆ¤¤iB/ì÷x`ž=ÒÉªYuW# È$J×Œº+=dÿãµUÆ¿ÛV²¨r‰…`î¬›”QÙ:&Ø„ŽwvwãqQÜÀõ9`5V½ÜjÆwüY2‡”¼Ž:çÏÃ‹•wãæ3	4ªx*Ž´Àƒsx6Øn¹œ 2i~Â†§© K®›²ìn«æìy;žç­'x»CÛµ¯*Ü¨G{;;gÚ^Õ2{óâº~3‡/±˜É»‰-Õa7†ªB< ·7Õ@ÌI4U’€à@Vc¥—ý–px¬H/ü¡»äiIqGIw6ÿàh3Úå)ù›¶iòµ.|Ù„’Æ©8Òz—WåfdZwSx¢¼Ë¨1O/üñQ_aÉ«V’Ž58–›D'ì¬{Â=¬Õ³R[»]›ì×¡m¬Õ ­Ö@%:$Ã;&µúU”Ë»Ú¢:þ¾“dïkƒ§@ÃÓË–›tÖ×0qu ÐÂ£ëlÆµ+ò0½SÑÉ‡.ùk2ÞmÅ[-¹‡p·+öÍhÐ|Ø3¼…\·S÷¤¶Å
è¬…,í±‹Ná+æ\§3‘5¹H:‡i3ëxÂúc×@ÚêIŽhÇ€¥ì¡½îvž‰œ/×3½RÞ€BN{¢Ëº–0u}tÎe‘€Çó¾x˜ØÈ´ÉyxB¦=ìaŽA“	–í
È®W…¼}^“àŸZ"Ùÿã	»C™’ës5ŠPü)y/ÿ×l´ÆgYÅDÏz®¬±!ÈÊwl"sX!KY4#µuø²ô.ž9tP›i^Üèh^ÐT4ŸAó©	VÝ²öórˆOíž±¥êT7€~¼A·ˆÞ½·‘?ìjÇOìíÐi={ã`ˆâáÙj¹(;«¡ç¿Úï^õO —!D'€9nt}LÇ3•ÃÃK}™·Þ*i*ùP¸áWæL€^XÑnï{Í~z:FG£ø÷o=Áéðé€=Ò§_ºOMq\S>ýùV^`ÿ²X1jW%Çºˆòõ¤\m š¹ó·°s~†6`J‰IàDÿÑãy­tÎÀÖÎ¹œ²q#ÁP@ÚÇ·?bÐs¡Û£0mO¨¡­A”ßXˆÇžÂJ”¶ü$=w9JÈ±~HÅ£¨ÓÕß¶°¼¶×¶Š¯„7)Me«AÚ äß Â–T¿ÁE#>:µEa--
Zšua«†Z±ˆýµˆ¦‡»PN‹0¾ñM,mš1†Ÿ0iÚ×"ïyìI#´ø¡fQaÙ(ŒgÂRxî#epËÏC*MQªòÈ‘Šø}æTP(#ðG÷ð`²jJWãi¬}5ÁãŒØŽªºæìÙ«P‡lî2gì[XÛšÕ!vAû1Ÿ¹î…]»V»*x·¾Æ`ÞÙ‹]f/HÇ‡¹è’áŽ¬.#ivLUõâÌmL	+üöåÌML¹–QXåL@…»(C.úOzF:$ŠÖrZvc9J›´í¬¦¬rí¼vçëZ Œ!–»ˆ¹[‹l}¾ü:-î²N·-(Rh÷5BJ¬£OÁ¯/%¸Ý/t8É¾œ=FZð¼e³²Äú\JJÒÛK´z²){b•ÍÈñ±Â¦TÜ2=¸ÍÄeÖ»Õ*Vk‡ÓÂ9¨KÅÿayïKGãôŒ…!Ùoƒd-@
ãU•Ã‚ÉU¶ï!rXúåŒOk!ð\U—T²rÃ—¯mJ^wžê ”Á´’¥O¶w+S·xŽ%KŽ¶Ÿaò.’Òú0z¹Ý{9RôÁø¹.-Öy:·c/Òh÷4rª>"­R£•ì¯È¤Ý3	“{D¤ëxdw,FížGVÓÃ£bÜÁ#Ã§bÞÜTžóU>yê ª ùz,MÍq'üß¿¦ t€H¶°›ÿ2êB'¼^¦õçÒÌÂHƒ]b)W–“œ¾ÍÃ‚^ËËNiÕ±À¯sqÒô7}µÆ¥’~oÅÂ}UíÿX!ºû:’ZÁÓì6#·í2IüWc¼¶ï(»ßqåŸjW(ô‚:õUýì¢ßÛ½–4ÆÛYyÈ[¬wn¸yúÚ>Ü ë~Ûi»[±ïÚPÜç²Îe¦a.»‰ä5þ½^SÆÊ›   0707010001f02d000081a40000000000000000000000016a102a8a00000337000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.array.hcs.conf.gz ‹     í™ßoÚ0Çßù+NâµãGkKG=lªÔ®/Ó4Lr¨IœÙiüÎ‰™U§Òµb—ûr¹û|íØIšÍC&ð°á„Rb}f#,ýÜp‡Íî°ìß\±Mg!®¾7.]ÑC[v£a“¿ÇõƒTáÀç ò˜Úßð°Œt s1K°¾ÐH$mÂŸE¬Ð§p«
×b$ŠÄøÔ¾È«X”5ª²SçÄQÀÌ1C®6Š ò¸]`©9]K[ *i‘Ï¥µ‰†1¹´ÛÎ©EÁ3½ZLÛÔß^v[†Å°Ã:X!&býº¸êž^ÝÎ“i2[¢Ú:;,”0±ÌvPºša†æ1sù8ªÆ`šk¶É¨µÍP)©´#]ž5´IíaiUø‘+¹z³@Ÿ3 ]a Qe0
Iåª® jÛaìhÍcÍ#ÙŒR¤îo+]Råƒ^·þOýÕÕ ÕÃ4•4ŽáöH^da
ƒzâîæÆ½³ü*Í³Nm^ÔæÈ;Œ¼ÃÈ;Œ{}ï[›ãsïëÍñ{o~ð¾uä[Ç§Þìv¶ì¾÷®ìX™B$pc¤s„ëD)uM¾ÞÀÝç¿[ŸjÍIeû+YsâkÖ®>?kNö,;›QÓ¸t»
õ¾a–‰qÎÚº@FåŒt‹-|Š “ÆÎæebx‘H’™î«™«1°ËPÅ¤:tEñÊÍiu¢sÁ<ÿÏ\hm›Žp£X•V-)ô“È9ÄÙ	à*§¤Æ„saW”V„
³À¹Šm¢ÐN¨å¢–6¤í”fHÑœÓ©^kÚ6M§NŠB·åÒo!–Û•'TéÑ)$‹“«Îxe¸6]­ÜËvY@dY@dY@d$ {ÓûV¬{Ïzýgç¨v¹‹t†Ê>t?õÍ·k$ª½½¯{Lœ¢,Ì›ýŽpú2ìÇ—
â¶±íÇ‚SfÆÌ^•Ý¼Õë¿>|‘5ª*mgrtêÞ!()óa>Ì‡ù0æÃ|˜óa>Ì‡ùŽÏow#+?ë0   0707010001f02f000081a40000000000000000000000016a102a8a0000018e000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.array.hp3par.conf.gz  ‹     í”=OÃ0†÷üŠ“²:°Q¥##b0ö%±Hmc;´‘øñÜ9I‹(‘*R…ê!‰î|ï“³ó|Î•å0ãâtÂ{Ñ- v¡v7Nø_¥›·»yÙeIlîµQ¸{ÊVIt1ÈÎ2îÿ»­õêöÐ†l4Ù¯f\Œ)HëÄsƒûBw¢	È¯­ö¨Ž=
KÑ6ñÐ·Ý·—ò!Pçè{p(u©%¬¡Bƒ^Ë¤’I;j'bÑB¤ïôãÕC©dOÒ«±hsM	VTŽˆ…'‘å\‘Ý[ƒsðr^¿‰ˆ,õ{Pì(`É1Ëh—Œd‚ÖFQ¡?Wb[–kÄ†h•Iz:DëŸBLÑ£f¨mˆ)yb6Z‹É¢S1ÖV+Áê“iÑ}#ŒÒŠf+œ·»Þù"¢gJö…©´Æ ŒÚšÄ0’=ÔÞR¤È	~n[þ÷{êG(>Ÿ[&3b=è$7j;b´ ]ò¼•ºj©„l¿¹ om¼ðºðú[^À³¶T    0707010001f049000081a40000000000000000000000016a102a8a0000010d000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.cluster.network.bridge.conf.gz    ‹     í’1S„0…{~ÅÎÐjä®°àæ,ývŽÃAòà2`‚»Áóþ½ÁÓ±”ÂÂmßÛÍûv“¦kV’ÒŠ5Žs'Ïí…sªØš¿·nºuw—<Î°)[gðö”ì&èýŒ$#A‹sô˜üdîŠÚõŠ5.K´ïËªÃçc÷e'ÆË`æ§bP—C.ù6™ÚnU¦²›Íí2À&é¡mm5ÝQ¶z“DGÇÃ¤»A˜ªR·pfAV“È~0Åü7Û×è¬¹lžá‚P8Zùrù¹•¬žòö”l/E.œ/¨ê¼n…d¨¢UTÌ²[zößaþïñÇîñ&{ÌéÍ     0707010001f0ec000081a40000000000000000000000016a102a8900000fe7000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.none.conf.gz   ‹     í][sÛ¶~Ï¯ÀL¦ãdŽ¬Êžöá¨I¦9Mrš9MÝiÒö¡Ó± ”0&– -«—ÿ~v 	Ê¶.‰×-óÇ¼,ý¾],€]äáÃ}þyðíñŠËÌ€Ùe)˜ÒJ|¸¸ý¶n¿º{ðsfVR¥âê—_P§ŸR·<À¦_ˆåBWé¸mA2ÉÅy%x
·÷øe]òi.šï½â¹x§¿Ö²éõ;e¥/¥‘ZI5wî¤"ãunÛÆ7w¤š‹JZ®ÿ©\ðŒ=cs×-j‡`ÐqQ¹û¦‰ÌdÏÌ„‚w|,á*•)·Âøo¼«jÁþh¿“hu)ª¨S­áSŠtí3	Üz1#,³šM&L&ÌÎ…bi¸Ì çF×U"˜±ÜÖÆ€OÀã™ÌED0³4VÁq(iXmDÊ(ßÂwÂ“p³Ðµ²pº‚·#iN|xtú±ÔT„*²wsøtêËKè•‰<˜Î"i*3Øƒ¼Ó ƒ/‹•/­4ÌŽ¬,„®m$íwTÆ¹¿þ'©†gì÷BÙ?¨/ž0†e:Ïõ5™p —Ì¥]Ž;ÝL+	(™aÐô03Ã–æØÉÉä¦†’J"I7(Ç©^3çh[î&:±Ÿ@'G†?>;D‚ŽL1u·€tôóß¥û9ó?¹3	×óšTá/±œD”s÷€-ðÉ_€Ótt®K‹ä´\*Í«ôdòx¬ü¢í7øÇã[A*.?†@“ºÑ„;Øÿ·è×öeþ‘#5¦¹³¥ÈÄST?ƒ©|æoÌµ±ÈÇ®¹EÏ‚+ ³²2ËD%”’ÑÌ¦‚F]“€iþ‰ØÖ44ÌÞ)..¿Ä«ŠHB³è¯o¸@gZßsA’{˜=Ì„+µ„¿Á¶+Ñ€Õ8W"Y(o£vÑïTÃï3’^Å h‰	j$µV•ñˆpïôùù>£’Õ 	ŽÊ¼®i~%‹2.¾ :ÐoÅ¸NžçHnK­`˜ÿ	‡2qUbg+Îk5HÁ!PÌ*žÂÿ1^©˜ŒLæ¬³9~ øÚ»³¯˜ŒC4¤F …(JZ_%2Ä8#>¿¿9ŠövÐF…0 œ§2Ðb9œ¡AáNìå¤éà\rlV¨Z/·ävÎ*­mæ,–¾µÞ\åoUxÂ+‡EçCu:Y/b¸ˆªÎõL‚]²K×=MãÉt-FâÑÄÎT^.U}5 ú®1Ž sKÃù7ì‰zöÉï¯¾ùòï~|ûÇÿýÓÙ~ÿ);ŠÛ°¤Ø´w^BdsÞ4©·>ãs€º(pV™¤’%M÷Ä•Hj˜TñB÷Ž×‹@'Çœ¦SãIÓ“	s—†ìeUi0a°EQUué‚Á»™õ¦Ù}	ümÕ(ïe ©rû~0’˜!„¿T:]öHî×(cÝN>P/c7\uÙã¹ËÔå>S—»àW«~¬<YFª}_«ŒDlj%úègWD§"Wqèð'Ff4ûàçÃÜsôÙÁ>ö9ŒI~hð³‚ÏN°ö¡ÏÞìòCcÇðõ‘ÏáŒòCŸ.:ë1Å‹³A´ßL€Cà÷µÈKÚl=×
Þ"¯æ Óy³†÷i'ÏmR; _ •†)| Õ1-Æ›º,5~¥Ð%n#òKÚàƒJQq ÀÅƒüîzú’ñ
ÓY,´¯YK¿¬oä4§U·ùëàÌoÝôuûÚ÷Îf×g~|ô”çm‚@ã‘ ž
&g
÷:Ì@#ë’(cæµMõ­¶ùÄkk¢ž/øW÷‘, h2QŸrÌ``o…°/_=ÿá›wnµ1×ÝÿÍN†GÔÞp?ÕÂ¡æÚ»&<˜P³WÇï{\ˆ	®(ÞjÀÅT‚¦‰¬¶®@u¶ï€¸"OtÁžàÎÃ³Ð¼	h7ÂÆ’Æ‹ñÄwzž„N™Öþ¢þY´$éú–Ì¹š‰tÐé^÷ËB¹7úp®ì6FüåMÉB%¼ü›YÙ]åW½]M®ÒÞÆÜvI»Óèò£\ò&Çˆ+‹Ü3|ÐcB£™´áqBªóŽKŠ70í,¨”"iðÒ(=¶¸_ZæzIkqÂ§é`‚ˆ¾·‰>~Ç´'Ð¾¼58ÜvzÅgÛJÎf÷Í‘à¤daÒ÷-íp,Õ…A
™¹®ó¾wî÷éÐ¡LéºR¿îEn"Ë àù½cÚ©ÛÎäæB#³T‰éÆKptž²#¥ÝÈq4`ä±lßZ% MCD—!.á1ã2¯1ç“½ö£6c–kj÷´¶@*‚—fNÝl
×%ð ¿Ö`î†*Ü®Er\“û(´R‰4/ö3~Ÿ^*Cê`Güê^½a[Ú÷üÓq¡@Ìè_²ïÃ·=0¶Ã¿Æ“€…ñµ2DœxmuÁ-nfÃxÜ*F=¾ƒCöƒY#n”’Ëdë¢Œö»±F\ñâc2•F_O÷Àª[ì¬œOó©ÏóZþ^óÙL0¡RÓ
N*Œ:/g8¤ýŒmþåF“ÂI)8ÐŽEý‡Ò)_ŸAsàÉ%øZÒîýÈó8v!„b'#Âðd4ÙÿD¥Dñ¥é,ÝGF¬ …D£5È&e}þk­-ïa]ëJÒ
LHœIRâÎ|1(Ñ.]l^'e{ÇÂ°žÍ*ã Œ›ŸtçÁ¾]˜uFÖHAÓÔè—E.‘(Éˆ&&ëŒd•¼â…°¢²W­£ »þscXBo€s˜LŽO T, ÀÉ|.i»ó $g¥6’Ø¹Ò÷"…“u1…à TPÈ¤ÒF ýSLºŠe…žÌQwRÃ¤k2ù|ôÉ—p¯iüŽ‚p- u	íì†§îÓÕçíB»çƒ4¤&öÓ76˜€™ƒ×6½ìÃµ9'ë	ãË4B†dX¥Ë'£ÓÏ˜Óÿ½VŒÇó‘²>žêG[ikJòQ¦ö…×&ë˜qY¸©D—²3M¦o _@*Ž‰¾‚ì†ChP0kLb÷*¡eŠ@Ž˜ÏJhhFÚÑ! {xrºž}=ï¶ö½ÏQóìÁS‰ëÊ¸8B¼±Z}c\³¡y„c°e,®JW¸ÂÆl48œâ…Ññi)‚‰n³Å9ùÒÑ]=ÉµÜÐ—dæf;o± tµd¤ê¬CÀù±3ô³ÚŸeÇoÜs0¿óá
Š›åºõ‰„Ñ-ï²è0Jn°æ$Xyƒ¶3òýÙÀ­‹s\ñ¨tÞsd£Ís–å|ÆÐ\O#!Â¥ÁK~AÒñÐÇ ƒ~\¢DÕ.H¢œq''én^åäs³1·}…s;ÌUF6F¢<‰žÍþD7ðßñò/5$ëeggo"A®‘ž¼goeaàƒ×ÂXê ²¥ïƒ+‘Z©<ð-3õÔM*¿À^„e[i¾ò$fyÄZL•ÞÀq³àe)LOñ}Px9kµêC*—¿2ˆQ‹ÞxÊ>Ûà™z¬„x“£Uôx_ëMtœÂ®A	â¶1(¹ì£’¿DTò/´ÇC‡&®¤¦ù²Ÿ­×†–ÙgÔëŠš·ºÇ3“àE.ã'jbœ¾<æNÊcüJû­É/»Çô51·&f+ì¶H
íaî²f{·Jí«_>fõË–àmJ íK^î®äe+·Nûìë\î¦Îe#ŠÛW¹ôÅ-·¸eè¶‰búŠ–;¬hÙÄí‚˜¾Œå#–±l‡ÝÆ¦¯]¹³Ú•mÜ!„¹¯aÅkÊ“£dœYv- i¡A¢tÍ¨kµfÈ~â•SÆwÍ[²(s‰¹úÂØªN(ïÆ•¾à+”ØÝ¬ÂC\óû!b½¬—¦]ä–{þ´Ì!%o¢Îy8còÞqè0.`1—@£’'âØà9Q”NêVé3¤Q*Pxbžp„áI"À’«Z©îNgGMŽOð¼C;|ÑéE4¨'•LŸ=zB‰ºÏÃáð1Ö¿à!WxÅí%èBZÌé‚Çër`,Ž×uI'tF²j6r9ì"ÂgEbC­ˆé’§!Åš**ŸÎ}ïh3:dbõM;{ïByDÈ´_úé(Ræ­wy¢žÑv&5à‰ò.£¦ÍÕqR~Ä«FRŽeí¾BÆÆ·xç¯›rtm²SE;å Ið§*Éð‰¬Ò¿	Õ>ÕÔa…¬m/É=×|ã„WÇMÊše/¾t - Îvfsn|}ÈÃÐ§¤Íò.ù+2Þ}‹7Zòá~#åÙhÐí|Ü2|„\÷geV.¿¶ç¥r;õZ-'æÜ$s‘Ö¹˜tò/Sçx	ò§£Â=¶º¹ßíÐÊºû¾cç©È9L \ËÌJF<… Ð&	3,ý›0t½õÑ›W>ç˜"Šù§V&uÎã¤Š&ÕçíMAY†•žÙÍªÐA°Ïk² üSG$÷;&U¸M-vJ®Ï—µ¡øSò^á#OÙhƒÏrŠé=×Æó5_sX!±Ijëð¥õ.9”ÛË/nt4Úc¼c¦Nè¨M²ŸÇCüj7©tÄ)ÝIˆ‡v<ŽAwˆ®ƒž25Ó¾˜ìPÕˆ,‡R3_6D:Ç#¼—Î·§˜|®@‡þ6ày­ªfàÊj.glZK H{ûü[Ü.L³KÞ´„Š”]yR~cŽK+À"uW™Nßm{IÃI”˜¥6[äÜîJ÷ò†š·~Yl—ª5W(î\Ì¿fÉ)Jíö£NˆBüý)…à]ØãœXÄþÚÈÕ‰·ÚÍcÜý+Ž6õÃpŽñ¹æ{ÒMò@¨]–t<*xÊã8KSB0"Zªä8”QÖºÒêØ“Šø=ö*(´ugà:ºÇ9‹þ¿J°n!Ëâ2ÌtÂ÷èÀ‡|‰-Ë°@}8¤ù1{s»ÚÅ®1˜Ðç±o^Ü´kemÑúâk ÿÞÏR9K%ß‡j—kf§8èIs`Òèr+ÎÜÆ”øðm9sS®ÍW9QaeÈEÿCÓ'c¢#gªËÑô¸yÏiÊ)×kk—©-ŸýƒMð¦„S¤YÇ4ƒ¢ªÀ–ª©´Ç…"ø=DÄ| Â¥j†òzjB§³ÒÎcÜ@Îº­\©²œêóAÑ"¯7	< 	ÄuŠMo*–¿Ô2ÅJô¥?|†¦ç+>Ã0Pé"úš[ò«â“I(vê¦ÝSkÂWÝšÊCŠ$äéºÕk>uKáÔÇÛÍý>ï€Ýßí“xoëÚ6I„õv»Ý²uûÑ“}XpÐ° Òô}ˆ(o"yÿÑc»Qàq   0707010001f084000081a40000000000000000000000016a102a8a000000e5000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.gcedisks.conf.gz ‹     ­Q1nÃ0Üõ
žï.Ú±c—ŒE(Ô9âJ.)!Éï+7µ «näÇ»cÓÔ„i¨"f¹#Ãy=i%¹ºîêvg>—°øàpù2³å®ç(®»_VàòˆBn*b®G9Nö0b½önG‘à'{{fz›Çt7ø1Pñ¹‘:}ï™Þèˆ ñü—°((—Ý.š¬Øo$õQ( Ž7–“¡ÛOY‡¥¨=…è@7jK;àáÇY¥ÕÁ
Z¹]J[%×^C²—m±ò²Î^ËZ4æÕ°x     0707010001f03f000081a40000000000000000000000016a102a8a000000e7000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.cluster.disks.conf.gz ‹     ­Q=OÃ0Ýó+NÊœÆCŒüŽ©æüB¬†8½³]øí8)MA¬~ƒ‡»Óûr]—DUSA,tÎëQËÑ•uW¶»êyûR-F8qÝMOy€K#ò²)ˆ¥å0Û×›Ú“u"Á)yû¿qèmãÍà—11¹Ëï•”3@.G:ƒ}ï™éÄóš43)ç‹ýO>š­ØwDõA( ŽËÑ‡©;ÌI‡µ¦MÁ.óí_ß›TZ¬ uÛksß6Ðóíç.û¸ßf“TßSÑ\˜"   0707010001f0ff000081a40000000000000000000000016a102a8900001118000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.sync.btrfssnap.conf.gz    ‹     í]QsÛ6~Ï¯À$ÓI2'+²Óv¦jâiîÚN3×47—æî¡Ó1!’0&– e«s?þv 	J±EÙ’UµôCb‹ÄØïÛ°Â.Ÿ<ÙåÏ£'l‡?(Î,U<`å2lRScÏï!n·½Û­îý‚ƒ}RH•ˆë_}Mƒ~ÝûÑ#Á¥X^é"7¹"‡';üAM™Xç|’ŠúI?•À…ø­’…hºð=O]É½Fj%ÕlÜº’ˆ)¯Ò²éõKüTª¹(dÉUìž’
>eçl.xâ» ŒVöºÉE,§2†{fBAÛo‹µZˆ".U)f¢ }Á£M—~ž–ñk™USU6ÓS†z5s]VjXÉ¥B³¯Q§ û—7é\ñLÎÒJìLíjß:@lœV‰HàV‚¶½níUÐ/_h™ –…*¡ÃˆÛ4•qiÃœˆòJÅ2è¹ÌSgõ¬B]±0Cxì´JÓ•ÇÈPØT/Kè7,Š^™j²ÐéùðÞz>Ä–ÃW	/E)áï(²ï®yOBà:BŸ.Ó¥mq6:ýòdôòdôÕðt4}5þâ)CÍì7ñ¦qU¹8>òÔWve´\%uoÆMoÙÿšç¬YõDkx”jS/5h·9ÐD1p•	S“°ˆYYAV]kp²Ï¹&+ç ù‡²ïO JS®þÕ\‚9AÓ"çGcÃûÐ¹‚Å¥|"Òó±·¼(B?à-–0±W ³ˆO§fŒææOõì&„&©Ž/A%¹6åE­¤Þ'×ÐÄ:ËÀÆ 4.d^¢êÅ5˜ 
Ÿ–0¢x‡x¼q|ÂcôÐã¨IÄìGà"‹B†æØ¢¨P,HqÐ„§ZwƒÓ”¼({(ï
e ©2
À½Œ$fká•L–=’»5ÊP·Ñ} u2¶ÃUç=ž»±LïÂ0u¾~°Å¼HŠÃ]Û¤ÓëíÑµßK¥a =š{@“4{/<IÂÖˆB×J]ôá…}`êt{/TŒ»à
ÿõ°îVøï¾¨Â[ƒZåÃèAÝ¨Vµ÷ÕŠØÔJõ±„=¨ö® "º‚Zˆ>:´-¢1Å	nÏá¡™-ÐìƒC÷CrÇÑ!È¶ö±¡ý˜ä}ƒC+ølkÚ™]Þ76äðØ¾>2´'‹¼Gh(Àe[(ûÀÐÞÀ¼sd¨…Í¶€öq¡=BzÀÐ
>w€µíÕ;Æ…Úèl‹iÚ¦wµÑÙÓ>(´?Lïj£s;¦Øa¡$vPã±÷ß"Í«Œ =W
ZÑ†Ï¦Sç·èüœ(6'x­>{9Às_ñ< T¦ð6`€V'1Çs™Užë¢d¹ÐxŽsÎ0Pº!xNndÜËDÁS—Œw”.¡ÈZâ£s­Œã©N"ƒ0¿	ðDDåOvÖòáYzE6`W ž&g
ì<A0=U(|PåD3¯ÊD_¡ÕÖx[¼§¬Ò†§W|ið'E‘zÁñ(.û J<Åùíwß¿ùøãÏ¢ãÕ\§!Yü3Aïã0ì¯¿žhXo#¡æú{Kä$¢n¯nÅï1C<Fn¼+
Kàb"AÓDÖ²*@U¶m@\–Œ#±W9/çç¾{hn7¢uz|y5ŽÜ ‡þN”iì/_‰–$íØâ9W3‘ZÃk?Y(ûàZöƒu€™˜ûxÓqY¡bž÷'šwblÈzp’QTBk{^ÖÚð_6ðç›íN$—âºDîàÇè1¡ÓL–þvBªÕ†(ˆ5pX&P)>„'ó—v†ƒ'"Oõ28ìï¥r	:±¢çm¢Oh•d•=vâ­Ááz¥®ùì²³p‚uÀIÉ8Â¤î(šéXªKƒ2s]¥	:}çÜ[ÑKè<9”	}†®\1RÀ‰ÜD£KåéÑ± í×$¬äæ–F¸Å0íõ\'ì©Òvæx:`ä±é®´J@›¦ˆ6C¨ðp™VÐGœ»½öƒ>ã,ÕÔïIU©^
*ðkÁið ¿U`îbh“2kRƒy…V
1Ãœp¶­T†ÔÁžòtÔ6½a_švîî ùS€5£k0dÿöÏv ÀÜ¿#…ñ]¦ÉÓ	ÕÀ«Rg¼”1­E½[ÅUà}4bZ¥ÈƒOJIå%L²U–³©L…YšRd†X#lÆ™J­¯×;`Õv–Ï.&é¥ÔWBÎæ²/R·0˜À„JL#8.0ê"Ÿá”ööù×ÎÉ…Çý({û†˜OàkI»CöžVÐAŸÖv:"OG£ÑýSJ¤aª™,]GF¬ Äøbt²q^]üVé’÷°Þ
ëJ^(lHcÜIÂ¶TjÜùBg øÂf-¦ .®²c‚Aá²žÍ
ó Ì›Ÿµ÷Á®_°·ÖH‹¦‰Ñ)†EHÊõš¬%0…éT™(E1dß7Žb€îúG©ªkSpQtr
KÅ–¸™Oe&Ëö> IÇY®$v®ôÇ6¤åd›É¸ÐF ýLŽey…ÏQwRÃ¦+Š¾}ö\«;£ Œ .¡Ÿíå©mq¶zy¥íýÞ jRûéLÀÌÁk›ÞváÚ¬“òu„‘9J\¨ŽÒÀÇ§£³Ï™Õÿ½UŒ‡û‘¼:™è
g[YVhk
lD™Ê
×6ëðX‘ÒUX]ÊÖ6™ž|©8'ÚIÑîf`:„y³–¸¿µ*¡0…'GÈç+»”ÐÐÂ÷£E@Û
xzv;ûzÞuö½oPó5ìÞ‰qeŽ/¼GE¬Vÿ€q]²¡¾…—Z€;W3ÁÆl48œá£“³R?¸ÌLdäK{D·õ$FþÞÞs|GfnºØyƒ%  ‹%{&U+äçÖÐßWåÉûéÉ;{ß%ìoÁ|gA±»\Ÿˆ¹AOî1ÐaäÜP¥·òmkä#úÙÀ­³Œx:í9²‹Ùæ›¦|ÆžÐ\OŸ#!|ŒÒàG. iyèãÐB¿]ýÖ<v‘sÑÔÉI@úóÝWYùX""³__áÞ®Ê,QŽˆDÏúû	?oàïaø—:’øxÙû÷ïA¶“Ž¼ïßyÊÂÄé;Íü\ê ²¥˜	 Úþ¦Ä÷ÌT»©üGáÃ¶²„°ì4dyÀZàúéŽ›+žçRÁôßÅ1ÀËY£U·¤¢½þên D-hñš}¾Á3õXíoâLŽ¢èá÷Zï‚ÏÉ)l»(AÜ6.Jýªä±*ùÚã¾—&$©y²Û­W†Âì3uAÝ[ýþ0\ÏDÞ‹,Â%nÔ6¬qúÊ*©¬â"í7~Ù¦®J_NåaË©tÂ®C¾L_Cå5Tº£Ø)K¦/œò…S:‚·)7¦¯–rˆj)ÝÀë”Ó—H9T‰”î vÈƒéë¢².Jw(;e¿ôÅPWe+(7æ¼ôPW¥;’]2]ú²'‡+{Ò	ÉÎù-}­“ÃÔ:Ùˆb÷J'}“‡-pÒº.áš¾ªÉ«št±[´¦/eò€¥Lºa·1XÓ×/9@ý’NØu‹ÕôEKT´¤3†]B5}¥’V*éŒd·HM_žä`åI¶Ars ¦¯Ir°š$ì§é‘¬I ·Ó+„^Ø1až’)³êRæíÝ Ò@ƒDi›Q{gb†ì¿¼°ÊøWÝJfy*±ðÌES•­c‚M(½sí½st€ëcÀ
j¬—¦9j†¯–³üi˜CJÞD‡çñ­•÷ãš÷¬¿‡sØ¤
ø‘	KÇ,¹¨”j«æìi=ž§µ'xÓˆ¡ãÚ×9Ô£³­œ¶W…LÎŸ½¢¬ëóÁp8|ŽÅLÞNm©{0TgßJˆ‰{U>0%Æ$ª<ŠÚo/¬¬tâ²?qé˜6yjRÜRÇåæmF‡x_(Õºðe–ö¡6ÈïòJ3|¤ñSx¢´Í¨	/}ú¨/‡Ððª–ä…cŽæè”Ûîa­œ•êÚ-èÚd«¸cÍuµ*ùÐ"Þ1-ôïB5wÕEu|
¾“dï«ƒY aö²å&¥Àú&®Z@˜ºÎæÜ¸b Ã;9e>´É_Ùðv+^kÉ=„»S±ç£A{ðaÏðr	ÜNÝÓÂ+ \©lÚE«ðŽsaâ¹HªTD­dÚÄúž‚°þÅDà5¶š©ÑíÐÈÚën`‰Hù2r=3+åh	Ã©3Ê1]Öµ„©ëƒ‹p6Eæó}1™¸”q•ò0C¦ÎFvI˜ÃÄtŠe»²›U¡oŸk² ü3K$û7fÈØÊìŒ\Ÿ«Q„âÏÜ;9}|}´ÁgYÅôž+ô\Ie— -×õ-1‡eRÑÛŽIm-¾4ÞÅ3‡µ™±o5^s4Ïh*ºšCó©VÝ²öó|ˆOmgØ3¥[Õ ÏCÐ-¢·AïmäO»Ûñ{=tÚÃÞz1Dëáùj¹(;/ ç¿s[­Å=\†­Ì‹Ê/(ÝùE¢ãðpeñ‹Ú[Euª’ÿ…—üºÅœ)Ð‹ÁÐ+^ý}¯Ù/G£ñht2:…¿ùò§ÃÇöxtFŸ¾tŸ–¬ã*õø×yýKúŠQû*9vßM”¯'åjÑÌ0¸¥ó¬°S
LL'úÏµÒ9[;g1c“J‚¡€´o~ÂEÏ¥©SaêžP%B[ƒ(ýd!›;„•(mùIzn3JÈ±~HÅTÔÙê
ß¶°¼¶7½KºÿJx‹ÒT¶¤]„ü`*¬¢ún5âW§¶(#ì¥EF[³6ìaÕP+±_[Ñ´Öán)Ìw×ÄÒ¦šàò&Mûµ­÷<ö¤ÚüƒÐr™cÙ(\Ïž„¥00ïWÊà–Ÿ‡¦TšBiuâHEü;dºx‹£{˜˜¬+åÊ`ázk_M1ÛQU×t‰=›bÊá]¹ÈÆ˜}{Ûruˆmc0~Ìc×½°kkµ«‚ïÖ7XÌû}ôbŸÑÒñ1D.Úd¸%j[Äž4{&Î;qæ&¦„~»ræSLY‹(¬r& Âm”!ýÍ‘‰bŒœ©öZŽÂ&u;«)«\;¯ÝúõmP&°–»ì-r¿Y+ú(|ù:-n³Nw,¨§Ðþ)j>…”ØDŸŒ_?\Hp·/t8M^Î",x‘IU­l±>*IAz{‰vO6dO¬²9>ÑXÃ”ŠûQ¤™¸Èz»ZÅjípÚ8r©ø?lï}éhœ^€±0$û6ˆ@Ö¤0žç)l˜\Eaû=D
[¿”ñY!¾ðÁUÅpA%[!7üòµÉ›ÖS} ”"˜–@Rù`{»2ep‹çXÔp´þ«h ·‘”ö‡½—Û¿—#EŸkÓb“§s'özíŸFNÕGC¤Ujt ’ý¯gÒþ™„Á½#"RHŒM<²'{íŸGVÓGÃ£bÜÂ£’ÏþÂ¼ùTy:ŒWµøä©ƒªä‹‰,Ž'‘àoÿ5¡D²…ÝüË8¨­åuÖ¿’å< 4X¢ëL4r¥š¦ô6z!­Òª¯sq­ÒôŸzµÆBËß[±t¯ª¢ó+Dw¯#)t<Í3rÇ.£È¿ãµýŽ²ýŽ+ÿT»C¡/¨c_ÕÏnZðû‘ö¬%ñfVóëã=Ÿž^;‡`Ýí8mû(ömjƒ;û¹l¯sY éc˜Ë>EòÿC•k1¡  0707010001f087000081a40000000000000000000000016a102a8a000001cc000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.hb.relay.conf.gz ‹     Ý”M‹Û0†ïþ¾´ÐnI²ÞC—öÐ{éÞJ²ô&U$w$çãßWqÂºe©0xÆ£wžM]O¹ªš&\)\×¾£pèA#/7­ºiÙUß»¶fmö?ªÇœô²¤]UIûOvŽÕÇ³„b¬éý„+AòÒõ¢58õEdaü4ã,â™‡lPX‰Á„³¸¯ÎbŒŠºÁÅè{H½Ò’>Ñ¬eÎ.Fð2z<wc­Þ™mÒAÖ©øÜEßÇb>²I¦Ù-@’þCB%1
Ž´ÁJ>ôáƒBÞI‰ {Ù	»†¢…íÅExóôùÛ¼yx›Á£.i6_ŒÏýbÞ4÷‹æ¡¹YÛ Þ
ó/00¯(Ÿø_anþ˜1‘tv¾øY,‚vöª cÒÔ"ì Kaù2„ò…½‡U>S=¹.é&ÃÔÅþ•|2¦è»{	I£}¸¢˜ÃR/8h©ûHÚ®S3§Fí¿¶!3,ŽËK-7H½Â+e9û;Ý¨òDôÚJdxFøeIèm¼ü'”´b·!QÆi‹•c\Œ‚xœ×QR.B§}q‹û:ª\…#Û4 b'ÿ.3…;  0707010001f0be000081a40000000000000000000000016a102a890000113a000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.kvm.conf.gz ‹     í]m“Û¶þî_™LÇöT§Ü]“/ÛS7/SO“ºS;í‡NG„HPÂIÐ):ýñÝ€t§“®ÖÝåz’ŒÃ—%°Ï³‹°}ñÅ1ÿ<ûBñŠKMÕJ]©f"ÚM­ÄåªüqÇmÝqu÷ì_}g¿ht•©«?û†:ý»ýìvàRmÖ¦É.†v,:e[cáÞÉÿ ®ljj9/Tÿ±M§ðF£>uºQC+~…¥;ucVÚjSéjqÝÉT.»¢þWSÑ]-U£[Y¥îC…’¹x#–Jf¾J@ŸUÃ÷m­RëžY(P–Nñ±TV™Îd«¬ûDWé+ñ_±Mšµ%åA#l
·?.A`­ÙB3…ÝØV•ÐÑÂõ•nÚN¢”é ˜Â‹ß8(tKcÛJ–ê·D¤fmE¥T&Z#æJXÕ
ß¤å^wÞÈtž«FUm`¯ycJzÑ¿€6ý›¯EnÌ.lž$.HÓÞ%ø¦,Ã×nd€áZ³ž}ã@¸©Ñ»@1³ºQª¬Û™œ›¦}r õwîË‡akÁ‡õßÅ®T´`n|ªŠð|—“¥Í%I’d"Þ×ªúðoÁ…pJ‡VYŠ°ªY'4Üm—BfçŸLMYBÛ Ÿà*e&L.doÐ¿ˆáT|0ÀÝÊf¡B+Öe]¨ˆDŸbà ´£«kxˆd+¤mEi2%^({õr:t'”$9ª$I@§,Bµqq¹¾H¶I• VÑûÈ´ÕÐOðFÐÓ¨“ª9-˜®I•˜K«-óyKp›qØÁfcWé¬1¦Õ²]ŽƒÀÀFtž€r~pF*É¢ 8V¸$ÓK¸åá0êR„`É\J®¥ì€(ˆ=,Sñ=tY€Ù8Ø)àõ¼+
‡M8fc€·]Óúv‘³cZ0#b„_šº…U7vÑ (M«fÍú!(Ñãû[öom³Á»Nó„-˜µ‚÷W°Œ]I]8YCçúÀ|‚=à¶Ïð?-ä|ØŸ£•5é%zõK"D G¼SÏCT ÿÀ+ŸTÙÀRÐÎF=åP¬G9§@K/„¹0µ÷ áÈµå—ˆ" <<ˆËnjÌÜDÃT²˜eª›'G–³ÓcRe›	YÇê‰¨ðO©”ýü|x—‚ÝZôß`Ù€xg3ÖÙã…e_oÂA°·]MÑ€2ˆ +çi‰ÉÃU¥²ìÿFè nPÆ°1æáhjôôëéeJ* q¹ääkñ‚zÀ u•Õ+5}-´m	önªbó’pûFùµèÂþQÐÐM‚ 2|&GöÒ‚zŠLÌ7Âú`B’+ÍñE Ò
Ù¨ë16Ó‘â™] >£3ò¤«§>’òÄZZ|>U@×Z]J7èþ%~^á°Æ³´õ‘Í\aÃPQÐia 3t=77ÐÑ
Bq; Ø{à£î@ZïŠºû†]ÒÃº«L÷ô¦Žç_=’SÍµWþö€.`h”²ñ‘ð0¶"hW.uÑ5<Ó40a_v‚dê'‹ÑÙùSÆhP<ŽZç»!ªdýK°<œ¸d©Ü²éÅ.MûeZÀÇhòÈ®ßiÞQ©õ Fàïp`¹.ÜäÅÝ¾F‰O˜|„"€Âc ¯éÄz;Ah­P¦êP(L~;¸@Ç…Ã+ý‘Ð¸ÁiÙe¡®®át‘–ÙE‚:;q³§¤_3„¡Ã…˜ÇÁ\Û…&ñDÊ+ûvH(.ÌjcÛY¯’q¥Íñ¶W96mtMñšºRijçÙ‡]…\¤'<Œ\$}O7²@Û4¦Á%;Ðt(v©üMÄo2w“bŠÊÿÊ@Re²yßFsgá?U6ßŒH×(CÝ&Ÿ¨“q7\M=âyË4õ1ÓÔwÁ¯«Æ±òžÌ2Píÿk•ˆCAmÔýÜQ7‡¾çð'DæhŽÁÏç!yäèÇ#rWÇØç~LòsƒŸ-|îëúÍ.?7öqxÜ¾1ò¹?£üÌÐ'FçvL±ÁœúÄvUï¿?«¢¶¸¢zî*x‹¼f
…©y´Kè¶ñžÒNÄz©Óe¼3^ácÀ S¤²(úl¼Z™ä-åŠYË˜V¼}çä¢à«ÚmÄ=ÁÎv7ëáÓµ©¬ã®‘ÀÁ	˜ï<ÓQù•åƒ>øöù[¯È¼mŽI.‹
ìœöb£æ®&ÊØe×ff]M€ßµŸi;‹´‘ÅZnp¯É‚’¤úR&ÉÔåWâ»ïxûó'DÇõÒ!Yü7)»˜Û8ÛëïgFÑ&·Xhï-áABÍÞoœï±˜ ¤¬wEQjp1Ó i"kÛ5•Ï6™
·¬mJñ
÷Þøæ%”8€ûÞ¡$Úwžú'¡SC>€ú×¢%iî[º”ÕBe“¨{ñ—UÅîõÁ®Ì;îò¾-tÊy³®blâDü;ÛXœâ3–(­‚¶AÔ¥Pà§è1¡ÑB·þqB*z‡(ˆµðX&P)‹S×(+Åe·U]˜ê3§¼¡õ2L Î†ÚCŸÐjÉ*GÅ[ƒÃõJ½æ³ÛF/¸ÛOÔ'¥Sàˆ/Ü †c]]Z¤]š³“”pÎ=
Ñ1OÇeQÃ5NŒ#
8‘{ÓÞkN{r,@;¥L‹VÚKì¦Jm/Á-Ðy&žW†GŽç¡0Ë<?6ƒ¶	hÓ3„=\š‡Å±Ûk?h3†`…¡vÏ»¶¯±À™ màS‡;ç0T>yr[j0î£0ÐJ£˜ËŽß¥ý_Üˆ.WÐP~ô†mÞsO¯?Ç4ˆÝSñwÿmŒíð7,ðà†óˆ¨EÙÜ¡d×šR¶:¥XÔ»UŒz\§˜sœwòàF)…¾„A¶+kN JAK¬QW«Y¸0ÀëëõXµ«Ž`1›—ÚÌÖJ/–¿²ÕÂ;˜
`ªÊì ˜³
fõ‡´a›ÿ}£Iá¤ti!Eý‰R-ß½‡.À“øZÒîTükH.À+¶k¥*qvJžžžNÅ_TS©"Ì/ñ¥ûT=£ÄøúôdÓº›}êL+GXo…u+í&¤)%ìb¢Î|1Q+¥˜ËÀÀ¥|vLÐ)ëÅ¢10Â¸ù»­qnÄál4Í­)pY„KŠ@’U}L	¼©Di*~ÅÝõºê®DJo€sH’“3Kp2_èR·ñ<€êêDm¬&vnµ‡_¤p²+ç€
J6Æ* ¶UéÒ+DÉt‰ºÓ&]Iòõéïþˆ;¾!ðÿTàAêÚ‡§üÆùöóíÚðóÞ zRûé{LÀ.ÁkÛÑŽáÚØIùÆ:ÂhZ9ÊÜ:P¿J—ÏNÏ¿¬ÿ©xH4'sÓáh«ÛŽ’RÁF*Û¹…Âk“uÌ÷*yêÑ¥Ž¦ÉôäHÅ1Ñ§Êä•B,
wf­q~Ë*¡e
OŽÏk%4£ñíˆÈo!ÏÎogßÈ»ƒ}ï[Ô|»÷ƒX=¥q]GˆÞ£"Ö@«oá¿[ÅÃ#ƒ-‹™žàÎ«…âtr69Ç§'ç¤&^Øf©ÊùÒÑ»z«ÿÏ9¾'3·‡Øù€% `šx¡«h&È/ÙÐßwíÉûüä'~îæ·`¾˜tíf¹¼>‘bÑ&eDÓ" Z-­Uz+ïÑf#?¥?{ØaL9ÃÆ#GŽ1Ú¼y!âÅ)šëÙK$„_£´xÉ-H2 } "ôÃ³>®rpD…G,'é/'<¯bù²mé *ž–ÌÆ@”#"Ñ³ßŸðãþ=\þ¥†d~½ìýûŸAÜHGÞ÷?yÊÂÀé¯ù±V»š1×0@5Þ)ñ-³Ýœ'•ß`/ü²­n'‚K’ÏB–¬Å’Ž=·kY×ºƒ)~Šã¯ƒV]HEsýíÙ@ˆZðÆkñÕÏ4buAˆ39.hÌñ§à:—KÞ1(AÜö%«1*ùED%¿G{¼ïÐ„‹—ú/»Ù:X2Wà!°×5o{ÿ0ŒgïEVaˆƒµ=1ÎXó(å1n¥}gòË]ŠcÆš˜‡­‰9»’BÇB˜Ç,„9ÅƒRAÇê—‡¬~9¼}	 cÉËã•¼áÁiŸcËãÔ¹ìEñð*—±¸åa‹[î(f¬hyÄŠ–ƒA<,ˆËX°Œå0ìöÆ0cíÊ£Õ®‚àB˜§
¡öKÌ±¦<9JÆ¹Ôul© È %6£ØjíTüS6¬Œ¿õoá!Õsõ•m›.¥¼›þ HÎŒ7«Jú©ø9`½l6vX”­ôçÊöÌ¹íèÐþ¹™?¯ôÉqè~\Àz©FµLÕ‰U˜k…é¤¼JŸ#2Í@?JÅ„q‡|6]UÅ;qR<ïûó¼÷o1´ÃwUãÚ.mDiP¯½yñŠußL¦ÓéK¬Á“ñ
ï%˜ÏWÌ0×««'¶Åñº«“DD§Zw,Omt»ˆðY>­‘¥ÅäéIqK•Kç~r´¹×C÷nÚÙûØþ*êÔé½Ë«êÀ3õ¬jÀ1£æ2½ô‡>ƒ~àU/ÉÇ²a_!qRtXÞ£R_îC‡ñGõX´óQOúªˆH†Oäùª†§ú:,Ÿµí$ñsýg0q°ˆŽSEnRÖ¤/{q¥ha¶³Xò)¥ 1UQzM›å1ù2¿%{-¹H·‘òætw>l>B.AòÐ7œßNÛóºâú¨V‹ÅÌlºTYW¨$Ê¿ÌØÈ"äP Ë@Úöæ~$Ú1`=åû®c|¨pâZf·2â)îôIÈ˜aéÞ„¡ëƒ‹þ‡¼ò¥ÄQÌ?muÚ2LªèX]ÞÞO½VyN¿»0Ýnxû¼&À?g"ñÿcRoj‰sr}®¬ÅŸ“÷òy-N÷ø¬'zPöÃú1G”ºÒeW
>¤:äËà]<søÇ;,ÿ Î5Gó‚†¢õ^™š`¡&ÛÏË)~5N*=/*%ÄC;^† ÷çgï>‡35³±˜ì¾ª?7Xö¥f®lˆ<t¿ž°aßžaò=¸<Úúçµªš	—Õ¬bÞéáÞþÕ¤íwÉû–P‘2—'7ÖèpZ©se:}wè%	”XÚR1Km±Éñ|4(¿¼ÿèéqYìàª5.g×òo‚Yr•ûÕu|ÂõÚ0gR%…à1ìá,–~Åm{äŠâ­áÇ`BÜÝ+L›nŽa8GN¢qÝcO¡Im75V”aÜrfÉcJFD›*}Ž‡¬SÖzeªG*â÷…Sý2IÞÓ=ÌY4]œeq9f:á{tàC±Á–åX >Šµ›Á^ˆï`Ónw16ëû|áš6íZY[°¾¸ÿ öq–zŸ³TÒñS˜¡Æd¸evJ”¤¹_Ò˜ú ÎìbJxøÇ¡œ¹‰)×fŽÛœ	¨peÈEÿFÓ'C¢X«UËÑô¸5ÅÊåqíÖeêV.~Ã&xSÂ)Ò,2Mo…¨*°¥f®ÛFâBü¿.ˆù §júòzjÂÖOŽùÑ˜pqhHs? ØËÕU^P}>T£WQ±Ä\áM .*6½©X~et†•èwøMÏ·|†;`ÀÿÊ'WÜªx’øb÷×îyâ-÷U^SI0¤H}ž.¯Ö`Xó%/…Sw›ûSÞ{ºÛ'áÞÖµm’ ëÃv;â²Ûö;‚'Ç°à^Ã‚@ÓO!¢¼‰ä5þÔŸ¨»~    0707010001f055000081a40000000000000000000000016a102a8a000002c9000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.pool.pure.conf.gz ‹     í˜MoÛ0†ïùrM›{ºõ8`—]Ö[Q¤ŠD'ZdÑÓG>öëGÉŽc$‹±­YdñÅISâó"Ýáðœ×`g¼R¸ŠÈŒ l+„*:|k¸óîî¼ìÏ)Ù¡ÓVáæeð“þ˜ÓÒæ—¸]“S“ý„sbË–»3^‰’—T‰™Áv©'1~Ú¡:2(,D4a¿·/dq
·®6ú
¥.´„G˜£E§eNŽ#xÉO+J~¥€ÀçG°´´¶ <Lä]~4yÍ·á‡äüø
ÚfwK
;:“i¢Èw²…žG'‚&{Ïk>ä L¸ º¾¾¾¾¾<À
œòâïAù“0þ×˜[Ëç Êùs‚"ÅÎðò”³"Kô>Ñ Ë•Í¶Á¡€l‡q´•£•öŒuž@0Pch­íZ™¸³
Ñ(^R›,Áž/ëS9!„34ÓÊa¡7—*Å[J¾Î,É!”J·¤GNš‹Þë¹Åö©t(’Jû¥Ï»púëù†ñ,+RWI2šLsðÁ(ãIÖ^*õsG±ºF:ôz(u½zi~š¾².•Ô¦ðªÐý–›n™Û¹R„º¿ð7&;®´äþ²Öa‘‰5 ˜VZó­r93Ë)UáRy=¿ü6.H³È
]çm¿0¸9ùc•þaV#–›.#t03$—Äºë¶Z–Ù§J?}â}wâ|°U©©*‘Ehù²œÊIX¬ÿL„”ó	¾=åÝ@­qõ3µá½žœâÿÍYR´¡EÖÌó	fÓy‰ñŸ W3é?@}!ú©—TÑàU¶ab®²fTO°ÒÞÙñ­ûO÷höeàyžgr™¹ŽÊ–û=|Eäbvc¿àÓa¬HŽÛØ\ÜÉ±­5ù­b“Õ:ÜQ£ÚOØ?£_ß     0707010001f09e000081a40000000000000000000000016a102a8a000001eb000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.pool.loop.conf.gz    ‹     åÔ½nÛ0 à]OAÀkíÒ±c—fƒ¦ŽkŠÇðG±[ôÝ{G)²CA›p,RGÞwô­VKŽj%Î#Ú‘ö„Eôï·ìé–µ«î8ÙU0®Ý}u]’¾)iW~û'Í—Ã¼L-M\.8)*ôrcaÚé›´x&Àc6šÓ™´Ì6÷»—áOÍ)Õ%…1.JÂ°.zPF%¾ŠpŒ*‰R°¨hÅm%E‘P¨ 2HüŠb·AQwEŸ]—•7'ûÎÐé¸fàsÕÛéøV±‚²	:vÓ:™nôFÚ“Ima!HŽ÷œÑê¶»]£OçêuwÿÏ\B(t=„£¯ckawù«çÞcû!]#ƒØXTÛq¸“ìº²ÆóÏÈîy8è8è…àe€“J”"L¾TJe¶
T¬OVÎyÆ÷•ë=¢\¯›ºôQ¤·!ÿ¯èwtðÖ>Ðavi"C]³tRÂ$þ¸Á„Ü4â\L2å¸Žª…&Û³m¤ïì¥Íå–EHü`¬i?Ã<ûRËÈÝñ?7?A¥(¤¥ÎQÜuÀnr¿? è2‡:¶ÔêU=Å¦ËÍ¢MSqï’Ü•j½<ÑXµ¿W–vÚ	   0707010001f0c7000081a40000000000000000000000016a102a8900001033000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.container.vbox.conf.gz    ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²yÁåú‚®i—´ýP-Q6±’¨Š”½.îÇßÌ”Hïzím¼IÝhÑ‰^Fä<Ï‡ä÷³ÏŽùóè3vÄ—ªÊpY‰fÄÌ¦l5W×ï!î¸­;®îýÒuö³FV™¸þõÑSêôsêö£GØƒ+±Y«&»ì²h…6JÃ½ñPY:U5Ÿ¢ûØ»¦x£¿µ²}+¾ã…¦;u£VRKUÉjqÝÉDÎÛÂôÿAUtCVKÑHÃ«Ô}¨<g/ØRðÌ·B0è³hì}]‹Tæ2…g´%S|,åU&3n„vŸh+yÍþÇÖ JµÖ¤<h„Náö»%¬EÃ4“é6¢„v0×W²1-/XÉÓ%`1Ÿ:(tØàBèÓC¢»óPP`kŠî;@ô•h‚Ì•‚OUHßVØcöÓk¦LjDÈë˜•*Mwá¹¿¥MÅKñi›IdR³JˆŒÅæ‚ia˜Ìo3NwÞÈdž‹FT&ð¦y£JzÑ¿€“áto>g¹R»°9I\Ð‡tÛ÷øø0½íaé¿v+ =7šõè©á¶FïEÍêFˆ²63>W¼ÚQ¼Ú«œ,l.IH’{S‹êíO_ÃPUÌ)Z¥%(B‹fÃ”ªà®Y2˜2Ue	mƒ~Â8Æ3¦rÆ»›xƒþG'ì­hð©Z±,ëB”@$úK m]Ã«@, [Áµ!×Ëž}}6é»J’U’$ S+B˜»¼Z_&Û¤JP«è}xj$ô¼ô4ê¤hÆ Õ6©`s®¥¶|Þ´Ïý+½JgRfVs³žè0<ùýàŒTâEp¬$p‰§WpËGHaL¤Áâ¸®¥l(ˆ½Y&ì[èš\xfã`w¦€×ó¶(6á˜ÞvMëÚEÎÎÒÂ2"Føð…ªü/*¸±‹ A©Œ˜5ëA‰ßOÙ¿™fƒwæ	[0kï¯$`»’º0^CçúŠ	ì·}†¾ÎzÑá‹0OI0á$vü{B@èŠë´hµ\‰èk!µi8Ø¸ªŠÍáöwôþàñš~8Âº‰\=|•®CP¤¯4¨§ÈØ|c‡.?ˆ€+º–Ö¯"5ã¸9¶Úè˜üX¡Ò+†ÏK-Çm=ñÔ_`k®ñù@T]3²äŽì ŽƒWìp<N÷hsCEA§™‚ÎÐõ@Ü\AG+‚õ$‚bç˜Û[€miñ¸»k´íEì57ÔÃº+T{z!ãÅ—ÓcšÛ¶5emCd‹Ìég.!tÔžùÛ/ "¸€.1-xãGÀ>nGÐ®œË¢ml„i`Â¾ìIÕ'‹ÑùÅ)cÔ+ Â®ì€ˆÜHÕÍ:õ«Cäe7ëTtÚÈšÜ ¸iŽ–ç„""Þ›>ï2[t.“®'‰F‡¦QFÀ ¡iQìRø›ˆádî^áˆ_"S ü£P† ’*“­íþ0’˜{CTÙ|3 y\£u›¼ NÆýpUõ€çq,SÕÇ0LUß¿¶ÆÊ2Ë@µÔ*‡‚Úˆ!ú¹/¢.4}àð'DæhÁÏû!yäèÇ#r_‡ØçaLò}ƒŸ-|îëúÍ.ß7öqxÜ¾!òy8£|ÏÐ'FçnL±Á6»âÄ6+¿‰¢Ö¬ÕôÜVðy5˜*îtÓâ»[•Ç{H=bë¥LÃíDÌwÁÇ€ª§¼(ºÍíZ¨ä-ùŠVò”ËvUÜÉDÁW7´ˆKí­nAÖ?]«JKÌZb	œ€ù.À3©•¿XzÅß•zéÙSÀîFÍ“‹
ìœ¶8¢œ¶&Êèek2µ®&À¯ŒÆgL«‘6¼Xón!!Y@P’T_ð$™¸ôœŠ}óíw/üþÝˆè¸^ª"$‹ÿ&%ëØ6NÂöúû™´wÄ–
Ú{GxP³·Çç{ô³|´wE:Êð(E&AÓDVÓ6 6[w@\™]&ªdÏp+í…o^Bûq¸J¢%×é‰:Õo³é -IÚ¾¥K^-D6ŠºYTöÃ>ì…› ÛÍ)wyßÎ”¨R^ILG1¶·ñþ/þÝÚX¼ÕêFh·’öFÄ5e$à§è1¡ÑLÿ8!½C”Äjx,¨”ÂÍÑMŸ,u¡6"ó‚½¡u»4–@ô½}ô)-CV9è(Þ®WêŸm¹Xà&Qœ”L#>²‡¢Žeu¥‘Bz©ZÜôÌ9÷(DÇío—”×Ð•‚+F
8‘{³Èjt©¼89 Ò¦áú
B#½©RÇKptž±Ç•²#Çã˜´•›AÛ´iˆˆb÷OÝî©Æ±Ûk?h3†`…¢vÏ[Ó¥,âÌ	€¿1ð[æ.`¨*´Í;»!5÷Qh¥L‘	gß¥t4Çó4Ô¾zÃ¶ôï¹§ƒ×cbÄŒî…	û¯ÿ¶ÆvøæKÚ†ÛQ‹’£B5ðÖ¨’™R,êÝ*F=®ƒö£y[ n•RÈ+dÛ²f¹,„­ŠÐÄqÍ19ÔæÙy}=?«v¥å-fóâJªÙZÈÅò/¶ZxSLT™î§ FÍêi¿`›½Õ¤pR
4²¨RÓ«7ÐÅx²_KÚ°Ÿ0%ó¼¢YQ±ó)ax>N'ìß¢©Dð¥ë,Ý§d¼- ÆWÓ;Mëvö[«`½Ö­z%˜¦”‡ù'8ó…Æ ðÍgÁæUJfÖ1A§0¬g‹FaÝLÎ>çÁ®]‡[k¤ i®UË"6C$iÑÅd‘ÀÛ2~'ì»ÞQŒÐ]/«öš¥ô8‡$ŸC¨XB(€“ùB–ÒÄó JSgµÒ’Ø¹Õû"…“m9‡à TPÊ´QZ ý³­ÄÑN!‚§KÔT0éJ’¯¦ŸÿîuS¾<¨KhgžÚ7.¶Ÿ7keŸ÷Ð‘šØOßØcz	^[6p×f”o¬#Œ¤•£Ì­u«4pù|zñ%³úŸ°Wq½@3ž«G[iZÊõ©të
oLÖá‘Ò]ˆ.e4M¦o _@*Ž‰>õ])Ä+gÖç·V%´LáÉòymC	Íh|;"Ú·çw³oàÝÁ¾÷%j¾ƒÝûA¬™“¸®Œ‹#ÄïQk Õ×ðçVNqÿÇ`K\•np9„]²éè|t¦ã‹R/ì³åŒ|é€è}=‰–¿‹­ŠSMsì¼Ç PÍ†=‘U´ä3kèoZ3~“_Ûç®`~æ»À;Ëµë)Ö@¨<œc Ã¨¹Ö JoåÚÖÈ§ô³‡J•3\ñhT1pä£ÍK–|ÁžLÑ\ÏÏ~Rã%· iyèã ¡–Î*\å°Aåó[9H?Ùy••Ï¡z>£hn×––(GD¢g·?áÇü{¸üKÉüzÙ›7¯A¶‘Ž¼o^{ÊÂÀé¯ù±VºR×0@5Þ)ñ-ÓíÜN*Ÿb/ü²­4#f+|ÎC–¬ÅLé=×k^×²:Åâû?%Åq—³^«.¤¢¹þöl D-xã9ûrg°z¸ Ä™œ­
ÌñupÝV!Ý3(AÜö%«!*ùSD%C{|èÐ„$õ_v³u¬ÿðØë†š·½Æ3‰÷"«0ÄÁ‰Úžg(ù(å1n¥}gòË}Šc†š˜[sv$……0³æpJª_>dõËàíK J^>^ÉËAœö9Ô¹|œ:—½(^å2·|Øâ–C ;$Š*Z>bEËÁ Äe,°Œå0ìöÆ0CíÊG«]9Á{„0§
¡ögÌ±¦<9JÆ¹’ul© H%6£Øjõ„ýÌ«Œÿtoá™sõ…6M›RÞ;ÏÄÂŒÀx³ª¤#“'ìÇ€ô²Úè~u’îië˜CJÞG™Ãóô¶Æ¬—hTóTŒµÀ\+L'µ«ô9Ò(“(àG),axš
°ä¦­ªx'Ž³Ç]wžàe/†vø®k\Û¥í€(êY#³OžQ¢î‹Ñd29Ãú<€¯Ø½Uâ±eæzµõH¯Û:IXtHdk¥ÛÃÐÜ."|Ö‚f¥ÅäéHqG•Kç>9Ú<èYV·íì½ëÎÒÏêsêŽtÞåYõ‚áQUÚ5à‰Š˜Qsž^ùŒCŸAßóª“ä…cÙF¿¯³Ë8):,ïÀQ©+÷¡³m£z,Úù¨G]‚?U	D$Ã'òFý.ªþ©®Ëgm;Iö¹î3˜8XD§"7)kÒ—½¸Ò´€0Û™-íá 1QQzM›å1ù2¿Å;-¹p·‘òb:Š;¶!—ÀíÐ76¿¶çeewê£Z-+f¦Ó¥ÈÚB$Qþefý /Aþ|Þ,iÛ›û‘hÇ€^öÄÞw›e¢à0°-Ó[ñ‚@wº$dÌ°toÂÐõÖEÿ}^ù’cŠ(æŸ™¶“*ºV—·79Lä9cÜ“]oyû¼!À¿°D²ÿÆ¤
»©Å.Èõ¹²6AÞËä9›îñYV1ƒçÚ{
ß7ÄVÊJ–mÉHm_zïâ™cÏÂÖö|ùŽæ	Eë%¼25ÁBMk?güjœT:eO*%ÄC;ÎBÐ-¢wAO™šÙPLöPÕˆï,ûR3W6DºÀÃˆ7Ö·g˜|®OLE‡þÖãy£ªfdËjV6oe§Û²·/pÔú]ò®%T¤lË“Š[ktlZ©ÛÊtúnßKÈ±´	¤b–Úb;’³oØÓ(íËûOt–Å®Z³…âÖõ‡üa–\å±¦QÇG!¶^æL¢¤<†=<PÀŠ¥ßX³=rEñV¶zˆ»{ÅÒ¦c˜ÎÑ&Ñ¸î±'Ð$„šMe·ŒÃ,yL	ÁˆhS¥ñìbÊZ¯T5v¤"~_:ÐAßyG÷0gQµUpÈ2–Åå˜é„ïÑÅ[–cúdÂÖn{É¾9ŒÙîblÚ÷ùÒ5/lÚ²¶`}qÿùÆÃ,õ!g©¤ãS˜¡Æd¸cvJçþ¤yXÒ¨ú ÎìbJxøÇ¡œ¹)7fŽÛœ	¨peÈE¢é“!Q´–‹*ŽåhzÜ½g5e•kÇµ;—©_|Â&x[Â)Ò,2Mo…¨*°¥f.MÃq¡þí£b> aS5}y=5aë7xøÑØþþ¢¾U Íý>¢N®¬ò‚êó­A5rKÌÐâ¢bÓÛŠåWJfX‰¾q‡ÏÐô|Ëg¸ü/Í²_³«@nU<I|±ûs÷‹.â-÷U»¦’`H‘ú<]»ZƒaÍv)œú¸ÛÜOyìt·OÂ½­Û$Ö‡ívÄ;ewíwOaÁƒ†¦O!¢¼ä5þ#F•Õ¨w   0707010001f05d000081a40000000000000000000000016a102a8a000000e7000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.cluster.rotate_root_pw.conf.gz    ‹     ­Q1RÃ0ìõŠ›q¸wÊ”4)&Òk0:å$“ß#aâÀP¢-oO{»«¦©	ÕPE9á¤ŽÂœŽaþ·\]wu»SÏ¿Ã¾¨âø×™Åv÷ÃÑ°ÓˆLn*¢´ý:b½¶×c,‡HpžœÀþe,z=énð‰=nb ì²1À¸Þz¤7xˆ3_	³B4yãð‹‚ý¡ž…Ò êÌF›äØw§¥#*Q˜OäÙ‚rûãƒ MQÚ8hAkÙ´·ÚVÑu¯>éK~­vëì!¯±RŸoKYm   0707010001f0f8000081a40000000000000000000000016a102a89000013f3000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.ip.netns.conf.gz  ‹     í]íoÜ6Òÿž¿‚@pH‚[«ŽŸä>l£¾¾à‚§iŠ&m?…Å•¨]Â©ŠÒn¶È3CR"×öîºY'õE¾k`Kâ‹æ7oÎPòçÁCvÀìNÖÖ®kÁ”h•ùàî;»ÃÒîÁo²~ØH•‹w¿?ø’^ú…}ípî—b½ÒM>¦ÀKÉÜ9:àÉdºæ³RôC½m:7ñG'1Ìá;^ºS7z)ÔJªù4º“‹‚we;LÛw&ÕB4²å*s•‚ìŒ-Ïý,ƒ7½oj‘ÉBfðÌ\(h›ác™VKÑýÏ´†žÑF7ÜúÙ`%K0˜aÂ^ièVá5Óòì’™®®uÓ²
¦*ëŸþ¢âæ’Õ¢	øFªV4ÏÄ„Í¦—«iJ}¦Ì,tWæl&˜-k5+ lµŠÕÚ	ôL`N_Òó€,‘á`³…È./2Þ4’†ÞðyÖÊ%ok‚•R]2G=KË„½	Q‘à›‹%“†q6*`ž52ŸãÔ˜YÉ6[ n&;ðËEy’{Ñè®÷¿þÎA @z£+ÁrÔnPò ØBÎ»†·0-?uÃòF×5Ì’ *m/™§d¯úºVûö"·b&ÈrYú¦(ïy=š„½Tp	ÀÎ¸q
ÝÕ¼á• ÙFi˜ÆUú¤Ü>>L¥–Ä9‚ØÂï&¾PæBAãÓ…|wï8ã­î„1ÎóœH¼äe'G¡´"8àÆ7?¼7È G†ôKØ[¸è.d ÁÅ€na…n*ÞÂ=ì2MO±éÙÑé
gÉ)¯kø·âŠÏ£Žþ„÷=KStÜ
 .´ÞtWçü³—~Pß H)a¦ÃlÙûaœ½ô;¨ïÖ
6€Hk#çsÑ Ã ‹X’_Ç,÷kcƒ<øÖ©”aj;%>ˆùÍ½»È{^±sÙ¥Ä;pî“üöû]øRš6Ö à%pPž×‘ˆ?‚‡Ï1] ‚è¼}q
¯€v¡<ûmzºÐp—®ÿž¦ZØg#gÈMmQ¾YÊL ÖààÃ"rœ½ùéD•°_e»:¢.h4gu’LI0br‰Î‡7Q†Y÷’“iƒ‘´&N±ø— Eo`òT>)‡øK’Ý;ád˜FBdöºêÍ/_³•,KÐk4Ö¿GhÌ¡àÖ	ûš+ôÚsY¢ªeE£+Óg
ÖÞ iø§™ÀÐn$r$àZÐYš~…-pNiŠj½^`/‹ÀÆÊ èÑ9!%tóèÞ¯8›žÒBáÌr½j±þÕiˆÝ€.ËàÌÓµˆ–+¦B£Œ_¬WÂà
KDÐœ{OËêÈ¯ìäÀ;VÎ–-¯7ÐÈÝƒØ“W½<ò­Aïrð¥íŒ©7ëo5ª²vzwtš¼( Ñ³BÐ°—?z×ã0ü©;`ø<Àe/ŽßÁãAwoÑPÃÿ+ÔÂEW’4Ò³kàŸa"0Mòß¯—Q`ý¦58o~úºËÁ7‡Çx¶›ç¶¿Á%ß¥(IÞ£w…®‚¾Ú*[G€0ï2Z{€¾FF¶JžîóŠÿ‰÷H“O6„Þƒ—hz`É¡3`tðœµ¢,Ã²‰]Ypcä\ÁË+±
W]»úåŠ•Âu«@€b“Q"œD€*ëyq‚LgÝE S/Rùµœ²tv4ÜAhàºÏ›Ê×øñ.HòFÎeFšþ=Y…ÔÂ{jüå¨<	~ÿ?ø]/ÍU‹^Û˜Ò™,RqÁ4îG¬Âèp<;"!5@ÏO˜ÀxOxÃÍ0q“âœ=KNþtw)%JË%¨_¸Noâ`*Žâ8ˆ#§+FÙJpò5«Ë®š§h+‡Öm€éãmþ•#/ö×¡ÓC®É¸¬Ÿ$ìyîü>éàà.™ˆO<Ã:[ç¬{œwUµš]1-Rm†Žp¦Oì’Ó.+É‰’h=Y®i‘	ÆYg-X‹‚Ìôò°å»iÐÇÉóç‰ýï$9ÆÇONl *jö/jÆþõŒXÒ“ãEÐúyr¼…9•WƒÎñž“½«µ–Kg«‰S½¡=ÕFïûGn£8ÆGuÝÑ„ÜJ'J· žXi¶®#{à>…+çp™„c€¶ÃÅÊlM†è²[’¹(…Û0ñ?6nú‚6s¢hn 4Å¨°úHðöô8¡ÿÝÈæS¯Ýï¶±ž\4l2´‹¼yP¹²ñ+Ú4íâT¡Ž‹Otõ•…Ëì\Z¢'pq/½<Â³G¨£¹^ß•ÃÇ³]è÷~L;âðw8‡\ÔBåf¹Ðíuîß/ßŸÿàâcè "f= ¸¾Ñý6á‘ÏÝQ¿=éhÖ¢ÍN‹…ÐQòõ€>÷‚={Æž=¿	„—íE®î_VÂñ]´s·É‘ÿW ¹väz”Áa£Ë`´@g2ôS{Ÿ¥Õ¡=¬kÁÆì€Qä„ÜÈ™€q¼|×ØÎf´£•oív)xÈÀâLa$ƒîiïý¸¹CÀ™í0¨D¦˜ÔºõŸqär‰0’åñÁÅ;‘¡-†‹µhJËwžŸ€ïn4²s ÑŠ¯ÿ¦,ç›|¼ÍTÚ¹ŠÜ'Üë6ÍNgeÍÚÈ<+æ~K“c¢(^¸ªÉaÜjs/^6–-AWW0¶í†9ö ?_|¯1Ç™.K‘Ár,°kC·¿üñüUà“¿ÊœÛ®B,Â8å‹¡ç›P™~	¸¨iÌ7Ý^3]U$	ÃÝÆš^ÃYM*ô@N¦Ù§¨ô4íß$eöRÂ¾mÝ8—¹éj›™ân"7 ³]ÈâFdF(ÿ*”!€DÊpïù¯Áhùm!„T>[HV(CÚ¦¨ëãv¸êzÄó0’©ëC¦®oƒ_§F[yGbö¯JeÐÅ¾ 6bô~n‹h°½C÷'DæhŽÎÏ‡!y`ïÇ#r[GßçnDòCŸ|nëèúL.?Ô÷qxÜ¾Ñó¹;¡ü@×'Fg;¦8a¡>Šìñ8†wßDY«;­H«¹¼–²Ï3ôÉ%xÏi&6Ï%."P’BûL«£Œ—e_”X%‰¾¤x ˜Y4üš”IuM©+¸sÚ™úZãÐµVT‡ˆ‘Yb'`~c!‹4ˆÊÿXËG/_9÷„XÀî°aJÑ\éÆ¦DyV!ÙÕÄ2fÑµ¹^©0,ÿ²5øLÛ›[µâk#³@Giª¾àišPÕ%Î~ûÝùÏß¿µ©ä«….£7&Æ÷Ý“p¾þ~®…!†Zhö¯x|4íM{ãt¡L)ãUQ˜H%sÜî"fm»èÑÑ–s‚ƒTù4Õ;­y»8óÓKjð¸mØ¥r¸—Nü“ðRf¿àýZ”$iß-[p5ù$z½xd¡ìÀ==ì…« ÛüRwygÊx=ÖŠªV,LÙÁß­ŒÙt“!ñË%šhhG[š¸5+d¨1sL(jýã„TÔ&ÙÈ'sÕ‡ÀJy%¬¸%oÜânZ]êu°çmØ\µB8Þ.ö© ­V7#J[ƒÂõD½¢³]Ñ!¦ñcf…éd&sásN(s,Õ¥A	pÊ=rÑq·ŠÝ&wñ6û˜ºÜÅºF•ÊË{Ç(§6‚›KpÌZe&ö—˜K6z¤´µ\jwqhÚd@›LÄ•ãÀ^\–TÜ‡ŽúÁœ){XÓ¼g]LeSÙpå§6ÛWðGâ.\^#UálöØ}ì¨ÒˆùFý³Ë¡zxèŽ=âK˜¨mtÃ¹íÜÓaVÔÏè$ì§>íÄ‚ ¶~›¦kñµ0±2"ÖëW¼•ù¢^­¢×ã^0Á3:°H(ÎzÁ4l–wUÍ
Y
³6­¨¨úL¼ªÝé=½^€«nÊI˜_ÌÊK©/VBÎÿcÑÂI4³õMÚo8çß¯)W{IÔ¿q=Ê^¾¦œ3Ì÷c–º	ûkª¦ Û•Š==&Ÿ'ìÿ©Dãj	ÈÔÞ§Ô†-`ŒçÇ[ÍêîâN·|„u+¬¹&° Å:Fyn¸òÅ¤Œ|.Ý'l{Å/E™¤óFƒ»ùŠé4’Ó43ºÄ°ˆ+±³çr8×)êðºc=öÝ (&¨®¿—ª{Ç2jÊ!Mž‚«X+€‹ùRV²×t³ÅàË+ó±ÉÄ—IPÉ¬ÑF ûçf#cÏDpÌ·ÊkXt¥éóã|÷ú‰ÀßØÆ–f²ážÚ'›Ï·+mŸ÷Ð35q?±CÌ´¶eàªÍ*)?Ù¡Òóð\¨ÒÀå§Ç'Ï˜¥?\ÃÃõHÝÍt‡ÖV¶«›Êt.Pxe±ŽI_•]ú`-V´L¦1_ W´‰¾Rø­Â„¼XK\ßZ’P˜Â3GÈÏ+ëJPÙp¢BÈ€¶ràÓ“íÜ7òÝÞº÷)ßÃîõ æÚJŒ+cp„øÂkTÄØêkøý²ÍŠfz„w²Fê\Í›²ãÉÓÉ	^8>: ¥¬Ü£m`V¢º ]:"z[MbäŸñšã[s³œX XùXª(dW³øºk^G¯ìs—°¾ñcÅ®rm|‚Ê1u®1PaÔXÐNÃÄ=ÚVÈégwh]]`Ä£ÑåÈ#‡°6ç¬(ùœ=¦:Ò§Ol‚¾QRî¾HZ>ôÑ DèÇç$Ïc$*Å²ýäÐû“‰]WÙþñ0¥Ên_áÚ®«,7]9F$öì÷'¼ÝÀßÃð/M¤¯é{ýúUX•M“tÌûú•gY,ºu“ƒfÞÖùlíÞÄPÝ(!s33ÝÌ.*¿¤šA¶•-XhÀV°§!—\‹U€;xÜ¬èØ˜‘ÅÁâàål ªs©|-M´QZ¼`Ïvh¦«»sBœÈQ=Ü×z\'¥p[§qÛé”,G¯äoá•üåñ®]êiÙ­Ö;Caö9½u³yLí†þLêµÈ2tqp¡¶ÃÇËc>IyŒ‹´ß˜ür›â˜±&æãÖÄì…ÝI¡c!Ì§,„ÙÅ½RAÇê—Yý²'x»@Ç’—OWò²„{§}Žu.Ÿ¦Îe'ŠûW¹ŒÅ-·¸eèöñbÆŠ–OXÑ²7ˆû91cËG,cÙ»>ÌX»òÉjWöAð.Ì}…ðÐŸS:dŽµýœ&ã\Ê:–T d€%£XjMÂ~å%Æ}+YÕ¥Ä\}aÚê^
Wú‚M(#ðÊG=(æ÷sÀÔX¯Íä-½Ùñ5§þ¹Vð½ã¡»Q[?ÊiOÒ…§.ws–	ä¦S*Þ‰ãìQÿ>zMp>t³åÛizÚÈüìñ)%êžM’$y‚õ/xÞ$^±{	º’xà(æzuõÄ´h¯»šN‹úêlïö0Q·‹ÃŠ¬õµ"&fžž)¶TQ¹tîñP¼];{o}yDô	G¾ðl»SuÆZYÕ©)ì—¿Â­úî‡“ò¾ê{òcÙÆ°¯P°iœ–w UêË}PµÉ¨‹v>êIŸàOU“áE£ÿjxª¯ÃòYÛ®'û\?&†	¯–7£“j]é J@˜íÌÜ¸úè]Ÿš6Ëcæ·_áq+ÞS©?Ýn¤œOâ—g†Ø€XÓ]46¿¶ç¥²;õQ­–íæÂà÷AºR¤Qþenõ ,×wä(„{ÐÛææ~Ôµã€¡ïÄÞw/v‘ãQ—©›™ÙÈˆ'^§OBÆK×L×çýyåŽ)¢˜ÚÊ¬+y˜TÑ'°º¼½~“EVzÌn6;xù¼Ò€ïÎ·cR…ÝÔb'¤ú\YvBÚË²åTÅˆ0£æÚyœç7Ä9¬’JV]Åˆl¿ÚÅsåö2L¬¼ÇdŠèË(!§¦t#ÉÏ“G“JÙc¥£„x˜Ç“t‹è6è)S3‹ÉîªñCe_jæÊ†HC»ï+nÏ1ù~†Ù©*ô7Ï+U5[V³œ³Y' ·7ç? q»4ý.ùp8$&sØò¤òÚ›V€Eê¶2ÆÞ’:¤ojB¯˜¥6ßôälâV×xGÍÛ»MÕš-·ª?ä¿	fÉ)JívVÇ{!¶^ÖL¢"<†=<PÀvK_ÕÛ´\‘¿å?%ÚøØajbÙ¦›¡›¡—DvÝcO¡EtÚ®k¬(C¿åhã“ä­Uö.¦¬u¥Õ‘c*âï©#A¥[8vsu§\…úMXW`¦¶£Ê5Î¬Àõ$a+·‚²o`Ón¾b,Æ¿óÔM/œÚ•²¶ ¾¸C@¿«Ô»\¥ïÃ
5f†-«S\
ŒLsÇL£ë½xæ&N	ÿØ—g®ã”++ÇMž	XaËŠþLÓ'CF±ôÛ8›m˜og)e‰kíÚÖ0uËçŸ±^—p*6?ì¥I²ÔÌdÛpÁßÑ ›ªéËëi
Q!æ`WøÙÇ 7è-±¬ïû•ª(©>ß
¬Æ›ÐÝEÅ¦×Ë/µÄÏ;¨µ;|†–ç:Ã0`?pæG³Q OS_ìþÂ.-âSkü¨6¦’ÚïÄÿ5YŒÖ [ó……Ó;Þ,î÷yìþnŸ„{[W¶I¬÷ÛíˆwÊ¶íwOŽnÁº¥ïƒGycÖø/íHŸlFŠ   0707010001f0d3000081a40000000000000000000000016a102a8900000f56000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.hpvm.conf.gz ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²M‚Û»¶¸àšæpI{ŠbEK´M¬$ª$e¯‹ûñ73$%Òûb»ñ&ÝV‹6ØèeDÎóÌpHÎ0OŸóçÉSvÄWHs5bvÓ¶lVÕ‡Š;nëŽ«»'?agŸjYâúç'_Q§_R·Ÿ<ÁÆ_‰ÍZéâ¼oC®jËe-ôeÍ+Œøƒê2¹jø¬Ý7ßë¿Ã´ø¥•Z7n4Z­¤‘ª–õ‚n~ËKCw
1çmiûæ¯jº!ë¥ÐÒò:÷ß)Ÿ³Wl)x!ô\hwß4"—s™Ã3½—9é¾`rxâýR0ÔSsfá÷NKÌ*Vñ†.¢®|šIkð‰¹\´š[h8›ËRD\—ð-Œju.&ð­¯RÅJs¥î©V—¢jì%Ÿ)m?)LàÔÝ9
PÀZ^²àV˜ó¾µìýw@»+¡£Ì”‚OÕ	Ê¯çÌ‹èd™Y6boQ¿ûñl-Ë’y¥C«ŒDô„^9|×Ò.Oæªª mÐOc¡KÈÞÝÄô?b8aïàN¢-×a#q²jJQ	`~Î°B mÓÀ«@&iXÉe•*{.ÌõÉ¤ïN$(Ëæ¨’,:ÂŽØùÕú<Û&U†Ze3Áxn%ôS@ëë´“B‡ÙŒiˆÉÛ‚€Ë‡;ØÜh¸öè(üP®æk1/`€&Ýƒ gÐ­øÒÖÀIèmWÒµð=¿9–
Ú·aj%Ü³(Aæb\Š•(±cð½Â¡@t„g")50j‚Þ,´'<KX»·îuV ÓJYq©×ã°?³Ÿ²zƒw½æ	t0O¡oR— ¡ã5tN }ð=ÖCpÛeÀè³1„?.Â<'€³ÀÐnü%{N@FèŠë¼l\‰èk^Dsp#ª.7'„Û_ïò.ðø½‰ý=‡Q¾J×}¸³eÁf7u^ˆ}s"Aû±HðiZÜ#ÁŸX7œÍJ•_÷ä˜8nÈÈoùlÍ>‰*¡kVVÜ“ôÏñcðŠ¶Ç9~hœ†!†Š‚N;·ˆ×#q3EÇg&	wŽ½„Q3’–ŽŸwš½ˆ]æFj‚.\6ÊØËŽëÃêç¢‹º '“kÙ|âZä-„Ï­HÍ«ó|Œp©ú<ëz’1w	X­µÒâƒÝ¢Ø¥7Ã;¹|L_"’PþV(c I•Ù–!#‰9Bø£.fCX{d£Œu›} ^Æa¸ªfÀó8–©šc¦jÁ¯­‡±òÌ2RíoµÊHÄ¾ j1D?‡":s¥ÅC‡?12 9?†ä‘£Ÿ€È¡±ÏÃ˜ä‡?[øëúÍ.?4öñx ßù<œQ~`è“¢s?¦Ø`QÛÇ·ÈúøýS”a­ ç¶†·È«9ÀTÙíÔ†ÕD¼€4#¶^Ê|o°VãcÀ Us^–ÝæZ#Tò–|…Ë†ø@#4w«å<È7w—¸ÜÁ%ÂÖ´ kƒŸnTm$è÷lüŽ/Á	˜ßx!¢òÛÞýè«éA‘=Ü*úL0¹¨ÁÎii6Yxn¢ŒY¶¶Pë:ÞW{m>c[ƒ´áåšopéÉ‚²¬þœgÙ„±wÂb6À×ß|{ñÃwïGDÇõR%™ á› (´q·7Ü/” 5o¶TÐÞ{ÂƒŒš½=Þxßc&˜É`‚+2És%
	š&²ÚVƒ>ÚlÞqUqž©Š½À-€W¡yí#à2x,‰Ä}§'áIèT¿=`¢þY´$éú–/y½Å(é^úeQ»wúpnìÕýå]+ê¢Îy3$QÅØÞ¥ûVø»³±t‹-‚vY(ŸF\SFD~ŽÍ¤RÉ;DéH¬çÀ2Jð!ÞÔÙ¸Qz\ˆ¦TQÁÁÐº4G úÞ.úT€–%«to7(õ†Ï¶Z.À	NÔ'%sà“n¸ï¡è‡cYcZ–_®ZÜ¬Ì;÷$DÇm;Ú„¤kèJÁ#¼È]$PºT^>: º<n® 42›:7i¼·@ç{V+7r<1òØüØÚ& MCDÊÜ%ÕqY¶ÐF»ƒö£6cV*j÷¬µ]ÊÎœÒ×%ð ¿´`î†ªÒ¸½R£q…V´XàÖîÍä>ÌøBu°g|u/‚Þ°-ý{þéèõg¸!1£aÂþ¾íA€±~Ã|-×p7â#jIRG¬ÞZUq+sŠEƒ[Å¨ÇwpÂ~0bÞ–Èƒ[¥”ò
Ù¶j(•ÑlŒ•!ÖˆkŽÉid*¾^Uw¥-.gå•T—k!Ë?Øjá¦˜¨ÓÎµ F]6Ò~Â6ÿ|«Iá¤hbQ§Ì‹×o¡‹%ðd¾–´;a?ò²…Î„]Q³Ó)ax:N'ì_B×¢ŒøÒu–îSQŠãËé=ÈæM{ùK«,`½Ö­Ôd˜æ”¿ƒy68ó…Æ ðšæ³`ó*§Ìç˜ SÖ³…V0Â¸ùY:îÒü¼5RÐ43ªÄe‘Ò%ÑÅd‰À$]HóJX¡'ìÛÞQŒÐ]'ëöšåô8‡,ŸB¨XA(€“ùRVÒ¦ó J“e2’Ø¹Õ÷"…“m5ƒà TPÉ\+#€þÅVÂ[§Áó%êN*˜teÙ—ÓÏþ÷º†Àß)_‚Ô%´3OÝgÛÏÛµrÏèHMì§oì0³¯m8†ksN*4ÖFÒÊQá×ºU¸|:=û‚9ýOØë4ß ÏT‹£­´-å´Ô¦õ…7&ëðˆHé.§²€xæß@¾€TCæÜ{·R
f-q~ëTBË1Ÿ×.”PÐÚ‘Ð½…<=»Ÿ}ïöö½¨ùöà‡™Äue\!^t‰Ó¨Þ	ûü¹•Ù?Â1Ø2W¥5.‡°s6ŽÎðÂt|ÖCŠ`â…»Á¬DuI¾t@ôPObä¯éœã2s³÷X JoØsY'ë0A>q†þ¶µã·óñ÷ÜÌoÁ|¸‚âf¹n}"ÇÜm5çè0n¨2Xy‡¶3ò)ýì`‡RÕ%®xhU9ÆhsÁæ%_°çS4×Ó$DX£4xÉ/H:ú8 $èG ãì‰û ‰òœ¤ŸŒÜ¼ÊÉçÖR=‘U4·k+ÇÆH”'"Ñ³ÛŸãþ/ÿRCŠ°^ööí›Hk¤'ïÛ7²0p†ÆÁka¬•>…Ü÷ÌPMwJBËL;s“Ê¯°aÙVÚs•	§1Ë#Ö×OwpÜ¬yÓ`IÍ0°…â¸ÀËY¯UR…J¤d6£½ñ’}±Ã3X=\âMÎÕ7Dæø&ºîª'J·AÉjˆJ~QÉ_Ð:4!Iý—ýl½5´Ì¾ ^kjÞöþaÏdÁ‹¬â'j;bœ¡<æ“”Çø•ö;“_)Žjb>nMÌ^Øí‘:Â|ÊB˜ýQÜ+t¨~ù˜Õ/{‚·+t(yùt%/{A¸wÚçPçòiê\v¢¸•ËPÜòq‹[önŸ(f¨hù„-{ƒ¸_3”±|Ä2–ý°ÛÃµ+Ÿ¬vea+„AØï1Çšòä(çJ6©¥ =4H”ÔŒR«5ö_®2þÝ½…gÎIÌÕÆê6§¼WúBgù`F`ºY…†¸æ÷CÄ
zYmL¿:É-ÇKuÌ!%ï¢Î¥Çóñm<ŒX/%Ð¨á¹¹V˜NêVéçH£B: ð„JáÃó\€%ë¶®Ó8ÎžuýyÖy‚‹^íð]7¸¶KÛIÔ-‹WÏ_P¢î«Ñd29Áú<8	¯¸½UáqKæzµÍÈX¯Û&Ë@p$«uÒÝ!N~>ëorÒRòt¤¸§ŠÊ§s?:ÚL2±ú¶½÷¡<"9cÌ«8Òy—õ+feZ÷Cx¢2eÔŒçW!ã0dÐ÷¼ê$áX¶Ñï+ÌÙyš—wà¨Ô•ûÐÙšI=í|4£.ÁŸª’ás­~uÿTW‡²¶½$÷\÷L,“ÓÕ›”5Ê^|é Z@œíÌ–îÐ2Ð˜¨)½¡Íò”üšÌ†§oñNKþ#Üo¤¼šŽÒÎÇ-ÃGÈ%p7tÏµËoßt'>‚&µZNÌ¥É—¢hK‘%ù—…ó¼Œ…óA‹¤moî'¢=zÙwßwì²%‡	@áÏ¾LÁ§ºÓ%!c†¥†®w>úïóÊ—SD1ÿÔÊ¼-yœTÑ%°ú¼½Èab>§cT{²›m¡£`Ÿ7døgŽHîï˜Tá6µØ¹>_Ö†âÏÈ{…¼dÓ>Ë)fð\±ç*ü¡Ê·œšÊ*YËª­©-áKï]sÜY¼Ær}ÃÑ<§¡h½„×c¦fX¨éìçd‚_M“J§ìy­’„xhÇIºCô>è)S³ŠÉªñCƒåPjæË†ÈC—xˆêÆùö“ïÁuàIèÐß<oTÕŒ\YÍjÁf­,ñTNöîâ{°fØ%ïZBEÊ®<©¼µFÇ¥`‘º«L§ïö½$K›@*f©-¶#9÷†;Ò½¼ëà×aYì€ª5W(î\Ì¿fÉÕþð]uBâêµaÎ$*
ÁSØãœXÄþÆÈ•Ä[~È6ÉéâáG›v†a8G—DãzÀž4B“<j7V”aÜ2Ž³ä1%#¢M?Ã3W)k½VõØ“Šø}îU@Ï;ºÇ9‹ª­£Ãa±,nŽ™NøøPn°es,PŸLØÚÏ`ÏÙ×0‡±Û]LÁ„>ŸûæÅM»QÖ­/î°ðïÃ,õ!g©¤ãÇ0CMÉpÏì§i˜4ªÙ‹3w1%>üc_ÎÜÆ”3ÇmÎDT¸2ä¢ÿ¤é“1QŒ‘‹:åhzÜ½ç4å”ëÆµ{—©-_ü‰Mð¶„S¤YbšÁ
QU`Kz&­æ¸PÑ1€p©š¡¼žš°õ/„ÑØýû)}«@šÿ÷P:¹²ž—TŸïJËUR,1x@ˆKŠMo+–_)Y`%úÆ>CÓó-ŸáÀÓâû¯¹U ¿*že¡Øý¥? ?Ýbñ_uk*†yÈÓu«5Ö|î–Â©w›ûcÞ{¼Û'ñÞÖm’ëýv;Ò²ûö;¢'‡°àAÃ‚HÓ!¢¼ä5þE²Á+qo    0707010001f027000081a40000000000000000000000016a102a8a000001fd000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.cluster.array.centera.conf.gz ‹     í˜ÁnÛ0†ï~
¹v1v]—[†]ÚÛ04ŒÌ8êbI£ä¬~ûQrœzÙ|
¨‹%Q¢Èï§È‹Åœ­ZÀŒ-»Cfn ÀKÄø?îæn^vÕ÷’ì‚­kèåGu[’^MiWUNà'¿=7ïÎq<ãŸ6Ö‰ñÍŒ-ÃŠÆÜìétÚ'ÜGÊ¦_½e:ÇñÈ}14´Å~ŸÎñ}óŽ&_:ñhŒŒÝZwÐ’#¶¦$(¢‘;‚€iÉC’~Nè…LŸrHyºåÁ½++îåÑ¡Ã–:]Ø·ŒÝRÎ¸ˆ	ÛÚ‡Tçq-ãÒy³ÁŸË×ÊÙoEÉLò<ÀÎÇd][f¿Üxx0lCZ>#' ÆcÖ_?|b-yê*Ž©]æÞ·`ÝTm`Š‘Àï3î;	kKLÎ”¥xQª‘SZ¯cº>&Øx+j¬×qˆ‰ºõvŠtÍ¸n‡ºp7n˜Â“-"Kðñ
FwµœV—oÐ[PTU@PTU@PTUÀ™ŒÄºÆ›t”›3¶tL0Kb¼sr¡.¨Ž³§_:ÊçùÈ›Á¹d¯Ð”Ú_€Bèd]{Ÿ”òQ>ÊGù(å£|”òQ>Êg>> >p— "     0707010001f0ee000081a40000000000000000000000016a102a8900001333000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.vxfs.conf.gz   ‹     í]ísÛ6Òÿž¿3Žíy$œ§™Î¹IærmzOçiÎ7ç´÷¡Ó‘ ”p&	– %«/ÿûíH’eÉœÄWfÚØ!‰%°ûÛÅb±~òÉ1ÿ<ùDñ’Kì@TëB‰åMbßÜq{w\Þ=ù!±Ÿ”:ÕÍO¾ A¿ a?y‚]¿Vë•)ã‹®ÑBE×“RÉnøe#SÈYªÚ÷}-S«ðN©~ªu©âí;Ei–Új“ë|~Ü‰U"ë´ê:ßÞÑùB•º’yä^•*™ˆ—bÁÃ¢~(W%ß·…Št¢#xf®rhác‘ÌcËJY÷Ž·e­Ä¯Ý{"“/Uéõ`f¼*'ÞBÿl·¾I„U•¨Œ˜N+ 0Šj¡r× —9ŒÜšºŒ”°•¬jËÀ'àñD§Ê˜]ÛJe…Ã)mEmU,VH[a›æI¸™™:¯à>o{Ô˜|óè
ø¥|ª±¶(ªx$Þ.àŸÀ`_ZÀ¨*U aZš#HƒXl¬6Þ´ÑýqRéL™ºò¨ý‚Ì˜¸ë¿kÄ0¿dyõÛ	ÅÆŠÄ¤©Y!'#	àÒ©®ÖÁ0ãRƒ”ì¨áô(±£æ8Èéô¶ŽK<J·0‡ÙÍìu‹oš°•ˆN€''?>;x„Nl6ã[ :úùç‚ÎÝOéZFÍõ´†.•øŸN¤Š?Peøä€i<š˜¢BpVRçºWšéôl¨ü¢7ØÆñC«åû° ¨R·€æÆ=ôÿïÀù£©¿§ÁYj@-):rVãÏFÕ29w7ÆVˆÇPÝ¼gÁZŽÄW:IT©òª¡Œj6Sd0êHsOøºf c öî%qµü^Íe† ´kýÊn€ cv‰y^šºø ‚þ=–þ!%M¾‚É±S§1
Ä¬àaÔjäw†ò‚1D•)×#ñZÃÅúâ‹¨Î8‚Ä®³™Iá÷Ä”)!¿áÚ™]bÉHà½ö9™ºˆÂhøLn©Zjç<Ö-â/ÊçNÐ;ÙÃ~‹ÅÀè‰mØHÉ¬ÌÍ¤(•ÊŠj"g¦||<},Nã@\*¿úþK±Òi*Ó¡WV£©Ê¥$¯lÖHHÏ5OF&ËÐs— ÂùŒ™loâúe8W&SLº’å\ù“ÎŠTe0!ÑëÀ2€èG]ÐÔ< TÚ
 +qªìÍÙ¨Nè"K`º#I$T5×«‹é&¨¦Í¼(Áó„q‚ã#©ÊaëIÏ¤ÕáMBûœ “õVÁ³
Èm-IzkêkæÇ…\ª‘xnº[ÑÈ ODØÜD°Dð‰’”ðß ™óÏ?ÿ|§`J¸ÖKÆIæ+•è\@Ù=<ÐWèJúÖ ¸†~1¿]ÿxBy+¯NQjý[–‰žE
àSµT)Þ³nYJ¢ë9»–4ýižeñR«;g45Üí>êðž&
ôîÊˆX PÂìð™8Ý\öª›VŸ°„ ¿æ€±R¢£§ë3’ÛÿîÂüè)×¾™–09Á[é:®¢¯-FÀÅ^óÌÑbT¼¾Ñ¼ŒòHâKµ=µÚ*ž…x•æÀ‹aƒ²ÖÍ?„jwA¬¤Åç=R)­‚E\¥èIà¿Ä—AžmF=àEIi2˜‰°cÈ(4+^÷ÈÍ”Öi£@;§¼NšÉÎ£N{»&»ŽÄ¾iÎæ²˜Xý³ê-j§.„?EoÅ¬Ö©ã9²‡%R3×LbK“Â©­o
¨`2×]ƒÓc¼c®1ÎøE yB7‚§Ô¸žMª¹8Ú,;½¾¡ìPàÓµƒ,¾&äŠnR<Ë671”W*ÂÒŒ;9T9Ç²J»Î£!Åˆñ·Ví&±:×DèšËÙÃ¯ÕSïEöüžÓÎošñ¸.ÉÊm/ÍäÎê¬ðYGSºä°(`1E(€´
“Ç#ñ/Iª›;Ø@Õf°C™j^ÊÄ¿™•,ó¬±u´™ž/ðµÅùáíå—B']KBÍ€oe8xrD<Û… ^öfÈ“62äAÃOô‚¢40îÊÏ<¾^(P!£«l‰ÑQIÀëiæ÷AsÇUS¬‹$FrQ×rPµÊÑ%§¥‘°!¥wÝiCÙ‚O\°æ#TÓdKN?üøVÔ.RuÈïUkdLÚŽchè
ié'JacNgö6¹t…Ó®
èÊ”6S  ¼¿H¤ƒÝ„¼NìG…ühÄ÷z	:SƒtaBn	“c•Ž™wrü#öxwrû¨Fí6~O6¬yqn¶ÙÐÔœ¦ó.²¸Í#qéOD©Îë›­h`ìç2pMÀkÀ=kX„=Ï_~úË×ÿ|ýú×|õë÷û}ö"ÏÇãl—,—ó?´$7FæÈ	WÕí&V°jp{Œ­[¬¤Ý0Ü—Ä°œß=ÑjÆ=)Œ­&-‰Þqh&ž6æŽ~XTê‚¢ êFE¸h“I¥ÂÈœ'‹h()ÍâbÚŽd*øø|eiÀû 7VuÁ»`î&Jn‡dî#NŠ•ô¢ü½¢ôH¬œnÄsî/F"soÂ_y<ëcçGVJŸ·Ów¨£q?¹š>eãHšiŠc(¦)î#¿:ïçÊRKµ¿W+=‡
µT½÷s_‰Î¬ÔC»?¾dî!ÍÞùy7IÙûi$r_	ö¾ÏÃ¨ä»:?ò¹—X{×çhzù®¾“Ç=Ä×{>§”ïèú„ÒÙ³ÿ Vï'Cù¸¹:!¿ÿSiAE8Àç:‡VdÕX`&mÃrMR
¯¸u·Óê§WZ‘ãc€ “is·I­,”)°¼@.)ñ(T‰œ ÀôÃýÙ5íQ`¦Imk µvÛÄVcf ‹7-Xœ óÅ \ïò_–ÜûÞ“²^u…C­E¦d¬™zžãÞ9
3È_ª‚Œ]ÔUlV¹¿õMe½|™®ä7&,@h:Íÿ$±²I\©
 ¾zýõ«ï¾}Ë•«…	kËÜ;ƒÊ/¯¿ÍýØ(Jý½Ã=˜R·7çg{8¥Ä6¦ÈùÅ™Š5pšÀZÕ%ð£Ž@W Ëâ‹©ÉÄsÜ4}ÙtoJéh¸	çS¢¼*7èQó$ªË2ó
¹4z[´ù\Åƒ`xá›9ifÚñƒ/l˜÷§Üå}‰Y*dÑ§ÐEÙ®6‹.Ó±0Ó5‚‹ª°hNÝP>¼_ù‡3Æ$‹ªyœ$´áB?!¦Z€f”bÀƒŸ¸æYF\aþM‘š5åó„+ßÃÂE½o|\N cYk0¸]ZÓ†Í®J=Ÿ+ÌÃB€‘Ò`Dh—¤ÓŠ¢›Žu~mB]^3î‹Ž{¥”ËJ×Ð”ºŒ*Grx[\¦¨§¼¥,í5¸F˜„hC	nÏcq’ž9NB=‘A› iÓ"„¡©Ók‘1éÙqßë3º`©¡~Ïêª-˜Á•S˜ÇCðSêŽ%¸eNu›T½y‰WJ5Ç¬NpÜë}âD.¡£Üø†}éÚ¹§ýêbLMŸÑ5‰6ïvB€¹~Ãjî8Ïø(µ[2™²®L&+L(€ùº1«èõ¸ŽÄwV%uŠ8¸•Jª¯a’­³ÂÛØ¶„u#±4‰T¥å×‹# jWIÊ|2K¯µ™¬æIþw­gï¡*0©<¶›ÙiÅ§´°Ï?ÞªR¸(cò§§Q¥þo.aˆ)eRæîH|/Ó:8SÕJ©\œI†çãñx$þ_•¹J·³§/ø>¡„ÒÂŒÔñ’ŠzòSm*Ù‹õN±n—‹ET‚‰Ì¸òÅTÆÈp½.è¼‰¨À‚
Ýz—«óæ§á:¸­%rÚHNÓÌšÃ"K„R²ªõÉ‚AÕI)3U©r$¾îÅ Íõ·˜Ÿ$"jÆa:žƒ«˜+€‹ùTgº
×T$)
c5¡s£?ÜÜÉ:›qÊn¦£ÒXðmXÙ2DÉh¼Ó]Óé³ñ§{mGžq Æ—ÐÏÐ=åO7Ÿ¯V†Ÿo 5¡ŸÞ±Gì¬¶íuà¦T[ØÁ€qÇ·4÷M”.ŸŸ~&˜ÿ#ñMXí
¢ÎL³­®j* Émí…[‹uÌàÏxéÞ¥–ÉôÄPÅ9±)ÀzË‘Âš6Q­ñpÇ
S4àðñ¼bWÂPNºëG @n…<z7úzÜl{_!ç[±7vä0ÓWÆàá¢­ÎDöŽÄ—ð÷FI]÷ˆDgËV•.1".Äxp>xŠÆÃ§HQ˜xa·03•MÈ–ö½¯%ÙÊÏ}MjnÑóN–  ¬×>ŠD¨tëŒý²®†—Éð?wë[Pß9FPx•Ëñ‰HZûT¡ÁÀTz`e£å­´YÉÇôg:ŒÉ&ñ(MÚcä³Í+‘¤r.NÇ¨®çg\PÄ1J‹—\@’qÒÇ	 ¾tU›±“Då.MÅàéølÀë*¦/«ŠN“ ÊDÀiÆhôH9 <Ûý‰fÞÀßýð/u$nâe——o<BÜIÞË7daâl:Íš¹V»Jd7>:i£’ÍõÌÖ3^T~£hÂ¶º¸ÒÎ}”{¨Åtõ=·+YX·ßOlG8x¥è¸ê\ªæ¸ƒ`5àKÍkñB|¶Ç2õ²z8'Ä©—É{êøÆ»ÎEø÷tJPn{’eï•|^Éÿ >>´kÂeMí›Ýj½¶fŸÓ¨©è6ßÜ?ôý™icE–¾‹ƒµ=>N_óAÊc\¤}gòË}Šcúš˜÷[sìH
ía>d!ÌáR<(´¯~yŸÕ/
o_h_òòáJ^áÁiŸ}Ë‡©sÙ+ÅÃ«\úâ–÷[ÜrˆèñbúŠ–XÑr°sbú2–÷XÆr˜ìöú0}íÊ«]9D‚÷pa«bcŽ5åÉQ2Îµ.BMt¢A „jj­‰É’™ñ¶ž8®1W_Ùª¬#Ê»áÒ:c3ÃÍ*Ì0Ä˜ßw*¨±YÛ.:)+éðÓ!‡˜¼:“æÛ3CcV0*d¤†¸£tRŽÒ'#>°‹v2Å€‘Q¤@“Ë:ÏÃ8)NÚñœ´–àUG†vø¼¤‚4¨ç¥Ž_ž>§DÝ—<lë_ð|>¼Â{	&ÃS{cÌõª‹­p¾®úrÄ©;ŸŠTr»ˆðZ>˜©…àiAqG•Kç~t°?dbõm;{o›òˆà¨jÇ>ÀHk]žç/é@TÛL5`‰ÒQ3‰ŸÓ2~R¾‡«–RCË6º}…D\ì8@Õ%^·å>ôe… ‹v>ŠA›àOUáYÁðDRšŸUÞ=ÕÖa5YÛŽ?×¾ÓànÄ¦àƒ…¹ìÅ• øÙÎbÁg_Ç€º>m–‡à/ImdØJ¶\r/‘n#ååxÞï>B&Aºµ”œß¾n•jµ˜ÌÄF×©šù—Ýù¡æë1žc»¹¹vèhø¾Ø$V©„@ìØ…O.§MBÆK×¦®+çýwyåøaÌ…û:ªSé'U´	¬.oot„JúˆFv»ItÐèç-þSÿ“*xSK<%ÓçÊÚüS²^ÍK^ˆñ›ÅŒé-×ÞóšùÓ"Ó9ÙLlðÒY—9ü%+³[Íi÷y?©S:º™ôçl„o“JÇâ47AB<ôãÌ:Kô.ÑS¦fÜ“=T5â»:ËM©™+"M§Ñ®Ù¶Ç˜|¦?€ýª‘çVUÍ€Ëj–s:ñ?î ®^ýÝ}Ÿ¡Ù%÷ˆ4®<)½µF‡Ó
èt{ªL§÷v£$‚Íi ˜¥6ßôä¸Ê÷}Ð ‹Ý£jÅÙôûø`–}·£™u/Ä}È3–*#<»  “EÙoÍ\¿Õ}èÐ—»kÂ°©gèf€qô¿wèdO¡E­ÖQ–règÉcJzDë<:ÁsM)k=7ùÐŠð}áX™Šïf¸û9‹îªî#X—`¦¶£Ò5ö,ÁõÑˆNÐGÉ_ˆ¯`Sm1TÛŒùÂuÏïÚVY›_Ü£`ßûUêC®R‰Ça…‚áŽÕ).zÐ<0hLqfv!Å?üãPÌÜ†”­•ã&f<(Ü2ÑÐôI(Öêyúr´<nÛ1§˜¹<¯Ý¦®äü¬‚·%œ"ÌÕl´YºTÎtUJÁ¿ï‚‚àTÍ¦¼žºbv³1=³ëPs_Ãléê<I©>ŸŠ>.îÇ›Ðä‚bÓÛŠå—FÇX‰¾v‡ÏÐò|Ãf¸ð£cÝÛ8
ä¢âÓiSìþÂ}ç-Übqoå˜Ê]Š¨ÉÓåhº5âP8q·º?æ°Ç»}âïmmm“x²>l·#Ü)»k¿Ã{²wÔ-ð8ý<ÊÛ€AVã?r‰Zªø…   0707010001f042000081a40000000000000000000000016a102a8a000001cc000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.cluster.hb.relay.conf.gz  ‹     Ý”M‹Û0†ïþ¾´ÐnI²ÞC—öÐ{éÞJ²ô&U$w$çãßWqÂºe©0xÆ£wžM]O¹ªš&\)\×¾£pèA#/7­ºiÙUß»¶fmö?ªÇœô²¤]UIûOvŽÕÇ³„b¬éý„+AòÒõ¢58õEdaü4ã,â™‡lPX‰Á„³¸¯ÎbŒŠºÁÅè{H½Ò’>Ñ¬eÎ.Fð2z<wc­Þ™mÒAÖ©øÜEßÇb>²I¦Ù-@’þCB%1
Ž´ÁJ>ôáƒBÞI‰ {Ù	»†¢…íÅExóôùÛ¼yx›Á£.i6_ŒÏýbÞ4÷‹æ¡¹YÛ Þ
ó/00¯(Ÿø_anþ˜1‘tv¾øY,‚vöª cÒÔ"ì Kaù2„ò…½‡U>S=¹.é&ÃÔÅþ•|2¦è»{	I£}¸¢˜ÃR/8h©ûHÚ®S3§Fí¿¶!3,ŽËK-7H½Â+e9û;Ý¨òDôÚJdxFøeIèm¼ü'”´b·!QÆi‹•c\Œ‚xœ×QR.B§}q‹û:ª\…#Û4 b'ÿ.3…;  0707010001f08e000081a40000000000000000000000016a102a8a000000e0000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.ibmsvc.conf.gz   ‹     ­Q1nÃ0Üõ
žï.Ú1c—ŒE(Ô9êH.)%Íï+×S «näÇ»cÓÔ„i¨"f9<ë…«ÉÕuW·;ó¶„}7³ÓÜ®Q\÷8¨<Àå…ÜTÄÜŠrœìqÄzmgG‘à3{ûÏ8ô6éað5ÜÅ@Å;d!uûÞ3½Ð	âù'aQP.ûß\4Y±g$õQ( Ž7–“¡;LY‡¥Ÿ…è@±¥=ðç±Y¥ÕÁ
Z¹½W¶
®½…d¿¶ÅÈÓ:{.kÑ˜o/DÔ  0707010001f0d4000081a40000000000000000000000016a102a8900000f58000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.ldom.conf.gz ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²M‚Û»¶¸àšæpI{ŠÂ¢%Ú&VU’Z¯‹ûñ73$%Òûb»ñ&ÝVAìêeDÎóÌpHÎ0OŸóÏ“§ìˆP\!ÍåˆÙM#XY¨êCÅ·uÇÕÝ“Ÿ°³Oµ¬qýó“¯¨Ó/©ÛOž`ã/Åf­tqÞ·!Wµå²z&x`|Ä?¨,“«†ÏKÑ}ñ½nÞÐâ—VjQÜ¸Ñhu%Tµ¬—tó[^ºSˆoKÛ7þ{UÓY¯„––×¹ÿN)ø‚½b+Á‹ÐÁ ßB»û¦¹\ÈžY
è»ÌI;ð“ÃïW‚É‚©³ðS§!¶^)C¿/ä²ÕÜB#™UL\[ÍsK£þ#’T¼i l¡U5o|«Y(u0µš5Zˆª±3>WÚ~Rp:@§»sx€©¼.dÁ­0ç}kÙÿúï€n¯„ŽZ0W
>U'Ø¾^0#,Â–edÙˆ½mDýîÇ°µ,Kæ•­2a„¾rH¯¥]1žÌUUAÛ ŸÆB—7¼»‰7èÄpÂÞ©J8Ñ–ë¥°‘8Y5¥¨ð?gX¡€ÐŽ¶iàU`—4¬äÆ²J‚=æúdÒw'”eTI–NaGìür}žm“*C­²¹`À_	ýÐú:í¤ÐcÐ‚ju.ØœiˆÇÛ‚€Ë‡;ØÜh¸öè(üPæk± `€&Ýƒ gÐ­øÒÖÀIhò-†IÏ{~	r-´oÃ =‹d.Æ¥¸%v¾W8ˆŽðL$¥FMÐ¯…ö„g	k÷Ö½Î
tZ)+fzý10î û3û)«7x×kž@ó„H ð&u	º0^CçÚ·ßc=´·]Œ>Ës@øã"ÌsR8çÆ_²çd„®¸ÎËÖÈ+1}-Á‹hnDÕåæ„pûë]Þþÿ 7±¿ç0ÊaPƒ×)œ1 ž²`ó‚:/Ä¾¹‘ÝD"Á§iqsŒbÝp6/U~Ü“câ¸mh #¿å/°57ø|$ª„®YYqOvÐ?ÇÁ+nØçø!Œµ`HÃ†¡¢ ÓÎ-âõHÜ\AGÑñ™IÅcgoaÔŒ¤¥ãç]£f/b—¹‘š ³F;ë¸>Œ Þx.º¨p2¹–u¡¸È[ _X‘šVçùáRõyÖõ$cî°Zk¥4$è¶qa½¿‰ÞÌýãcú‘|€ò·BHªÌ¶ñpIÌÁÂ_u1ÂÚ#e¬ÛìC õ2ÃU5žÇ±LÕÃ0Us~m=Œ•d–‘j«UF"öU‹!ú9Ñ¹X(-:ü‰‘9 Í!øù0$ýDEpˆ}Æ$?4øÙÂç X‡Ðçhvù¡±Çã ø†ÈçáŒòCŸû1Å‹Ú>¾EÖ‡ÀïŸ¢lk =·5¼E^Í¦JvhÃj"Þ@š[¯d¾Š7Ø«ñ1`€ªÇ9/Ëns­ªy+~…Ë†ø@#4w«å<ÈDÁW7´øˆK„­iAÖ?Ý¨ÚHÐ1îÙøý^‚0¿ðBDå¶½ûÑWÓ/‚"{
¸Uô¹`rYƒÓÒl²ðÜ6D³jm¡Öu¼¯öÚ|Æ¶iÃË5ßàÒ7’eYý9Ï²	cï„°¯¿ùöâ‡ïÞˆŽë•*c²„o‚ ÐÆIÜÞp¿P‚Ö¼ÙJA{ï	2jööxã}™`þ‚	®È$;Ì•($hšÈj[úhs°xÄUÅy¦*ö· ^…æe´€Ëà±$Z÷ž„'¡Sýö€‰úgÑ’¤ë[¾âõR£¤{é—Eí>ÜéÃ]¸	°[T÷—w­¨‹:çÍDqc{—î[áÏÎÆÒ-"´4Úe¡<qMø9zLh4“6<NH%ï¥#±žË*À‡xSgãFaè1p!šRmDCë’pè{»èSZ–¬r ÐQ¼58Ü Ô>Ûj¹\'8Qœ”Ì#Lºá¾‡¢Že}iBf¥ZÜ¬Ì;÷$DÇm;Ú„¤kèJÁ#¼È]$PºT^>: º<n.!42›:7i¼·@ç{V+7r<1òØâØÚ& MCDÊj|\–-´Çî ý¨Í‚•ŠÚ=om—2…3§4ÃÅu	<À/-˜»€¡ª4ŠÈqCj4î£0ÐŠKÜÚ	çÞÅŒ/T{Æ¯ ¡îEÐ¶¥Ï?½þ7¤!fô/LØÂ·=0¶ÃO˜¯åîF|D-IêˆÕÀ[«*neN±hp«õøNØF,Úyp«”R^Â ÛV[ÈR˜±¢2ÄqÍ19L¥Ó×Ë#°ê®t¢ål^^J5[¹\ýÁV0ÀD]˜^p®0jÖ,qHû	Ûüó­&…“Rp ‰Eý2/^¿….–À“+ðµ¤Ý	û‘—-4p.ìZˆšN	ÃÓét:aÿºeÄ—®³tŸ’ˆR´€_NïA6oÚÙ/­²|€õ^X·’aBšSþæÙàÌÀkšÏ‚Í«œ2cœc‚NaXÏ–ZÁ8ãægé<¸KóóÖHAÓÜ¨—E®(Éˆ.&K&éBšWÂ
=aßöŽb„îú;Y·×,§7À9dÙøBÅ
BœÌ—²’6Pš,k”‘ÄÎ­ö¸)œl«9 ‚JæZô/¶Þ:…ž¯PwRÁ¤+Ë¾œ~ö7¸×5~§|]:P—ÐÎ4<uoœm?o×Ê= #5±Ÿ¾±ÃÌ
¼¶là®Í9©ÐXOI+G…_êViàòéôìæô?a¯Ó|g€f<W-Ž¶Ò¶”Ó6R›Ö/Þ˜¬Ã7 "¥»]ÊdšLß@¾€TCæÜ{·R
f-q~ëTBË1Ÿ×.”PÐÚ‘Ð½…<=»Ÿ}ïöö½¨ùöà‡¹Äue\!^t‰Ó¨Þ	ûü½•Ù?Â1Ø2W¥5.‡°s6ŽÎðÂt|ÖCŠ`â…»Á¬D5#_: z¨'1ò×tÎñ™¹ÙÇÎ{, ¥7ì¹¬“u˜ Ÿ8CÛÚñÛÅø{îæ·`¾K\Aq³\·>‘cî¶ZÄst7T¬¼CÛù”þì`‡RÕW<´*Žc´¹`‹’/Ùó)šëé	"¬Q¼ä$ } ô#qöÄ}DyÈNNÒOFn^åäsk©žÈ*šÛµ•cc\kæ>Aôìö'Â¸?ÇË¿Ô"¬—½}û&äéÉûöM ,œ¡qðZk¥O!÷} 3TÓ’Ð2ÓÎÝ¤ò+ìEX¶•vÄ\eÂiÌòˆµÀõÓ7k*´ƒ(~Šã/g½V}H*‘’Ù@ŒZôÆKöÅÏ4`õpAˆ79Wß™ã›èº«ž80(AÜv%WCTò»ˆJþ‚öøÐ¡	Iê¿ìgë­¡eö%õZSó¶÷ãx&^ä*qp¢¶#ÆÊc>IyŒ_i¿3ùåâ˜¡&æãÖÄì…ÝI¡C!Ì§,„ÙÅ½RA‡ê—Yý²'x»@‡’—OWò²„{§}u.Ÿ¦Îe'ŠûW¹Å-·¸eèö‰b†Š–OXÑ²7ˆû1CËG,cÙ»1ÌP»òÉjWöAð€æ±B„ýs¬)OŽ’q.e“Z* ÒCƒDIÍ(µZ3aÿåÚ)ãßÝ[xæœÄ\}a¬nsÊ»q¥/t–f¦›U˜aˆk~?D¬ —ÕÆô«“Üòp¼TÇRò.êÌ<žokàa\Àz%FÏÅØÌµÂtR·J¿@Òü¨„#Ïs–¬ÛºNwâ8{ÖõçYç	.z1´ÃwÝàÚ.m$iP/´,^=A‰º¯F“Éäë_ðà$¼âöT…Ç-˜ëÕ6#cq¼n›,Á‘¬ÖIw‡8ù]Dø¬;¼ÉIKÉÓ‘âž**ŸÎýèh3}ÈÄêÛvöÞ‡òˆäŒ1¯>àHç]^Ô¯˜•hÝ5à‰Ê”Qsž_†ŒÃAßóª“„cÙF¿¯°`çiRt\Þ£RWîCgk&õX´óÑŒºªHH†O,´úUÔýS]VÈÚö’ÜsÝg0q°LNWCnRÖd({ñ¥hq¶3[¹CË@c¢¦,ô†6ËSòk2ž¾Å;-ùp¿‘òj:J;·!—ÀÝÐ½Ð.¿}Óø˜Ôj913“¯DÑ–"Kò/çx	
çƒHÛÞÜOD{ô²'î¾ïØ¬%‡	@áÏ¾LÁ§ºÓ%!c†¥†®w>úïóÊWSD1ÿÔÊ¼-yœTÑ%°ú¼½9Èab± cT{²›m¡£`Ÿ7døgŽHîwLªp›ZìŒ\Ÿ/kCñgä½ÂG^²éŸå3x®Øsþxå[NMe•¬eÕVŒÔ–ð¥÷.9î,^Ã«[ÍsŠÖ+x=fj†…šÎ~N&øÕ4©tÊž×*Iˆ‡vœÄ ;Dïƒž25‹¡˜ì¡ª?4X¥f¾lˆ<t‰‡¨nœo/0ù\žôˆý]ÀóFUÍÈ•Õ\-Ù¼•%žÊÉÞ]|ïÖ»ä]K¨HÙ•'•·Öè¸´,Rw•éôÝ¾—$ciHÅ,µåv$çÞp§Qº—wü:,‹Pµæ
Åëù7Â,¹Ú¾K£NˆB\½6Ì™DE!x
{| €‹Øß¹’xËÙ&9]<¼âhÓÎ1Ì çèh\Ø“Fh’Bí¦ÁŠ2Œ[Æq–<¦„`D´©ógxæ*e­×ª{R¿Ï½
è€âEG÷8gQµut8,–Å-0Ó	ß£ÊŸê“	[ûì9ûæ0v»‹©1˜Ðçsß¼¸i7ÊÚ¢õÅÖ þ}˜¥>ä,•tüf¨)î™âT` Í“F5{qæ.¦Ä‡ìË™Û˜rcæ¸Í™ˆ
÷Q†\ôŸ4}2&Š1rY§±M»÷œ¦œrÝ¸vï2µåË?±	Þ–pŠ4KL3X!ª
lIÏ¥ÕŠà÷]ó—ªÊë©	[ÿò@Ý¿ŸÒ·
¤ù¥“+ëEIõùÎ ´¼JŠ%æhqI±émÅòWJX‰¾ñ‡ÏÐô|Ëgøð´øþknÈ¯ŠgY(véèO·XüWÝšJ†!EòtÝj†5Ÿ»¥pêãÝæþ˜wÀïöI¼·uc›$Âz¿ÝŽt§ì¾ýŽèÉ!,xÐ° Òôcˆ(o#yÿD
„Qeo  0707010001f059000081a40000000000000000000000016a102a8a000001f7000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.pool.vg.conf.gz   ‹     å•MoÛ0†ïþríšûŠîØã.ë­(E¦b-²¨éÃMúëK)Ž“6p°-F[ ºØ2e~</MÏfS®j®ìÎ™+ˆ[‡Ð­.w7mvÓ²«r±3¯m›Çê¦}ËeWUN}Û'òõ÷CV´È†o®Œ(Hrbipˆt'LÈÀãŸ¤=r¸÷©jT"™xÈí'YÜûBà´ÑïŒÁ¡ÔJKø+´èµ,Å±‡ ùÄ}ƒ¥, ‘ï;2‰w+OÉA$Æ‹1ãêO0´ÒR˜#÷m#]s”›âøÑQ™úÇB,'T7*ü/T¥†mˆØfŒŠ|+âb–ŒêIÇ¦ êA0­s„V»^šõ‚\ü¬¼ÿ€$Û¡?z;47' Ÿ»<LwÂÖ@ŒÐÃ’›rÝCéQDM–¶m9ãò6dîi—h¿øóè„Ç%Š_–KUÅúb"äšGøžiïê×y¦6¾Òýý¢—×–’²~Êf˜¹k‘a2þp;&çhˆ"¦°²Á:™O;H/!Ø	“J—Œù’a½){æõ_ìèË§åo”1¿³”§và~¿¹™ý<4<æ5Éùà››;<ò6˜ÂÖF±)j½Í¨Wí=&¯Rï	   0707010001f106000081a40000000000000000000000016a102a8a00001045000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.service.sync.necismsnap.conf.gz   ‹     í]QsÛ6~Ï¯À$ÓI2'+²ÛÞƒšdškÒ¹Ì5ÉÍ%é=t:&DBÆ$Á euîÇßî AÙ–([Šª–~ˆ‘Xû}»X,Õ£G»üyðˆíðÅée˜Y‚å"–:Ó9/î.n·½Û­îü‚ƒ}TÊ<W¿>øŽý"öƒ8„±\¨27=áeÉ—påd‡?¨+«‚ORQ?êGžjWJñ[%KÑtâSYÑ…¢T—RK•Ë|6n5IÄ”W©iºý^åtAæsQJÃóØ=(|Ê^²¹à‰ï…`0bQÚëº Le÷ÌDmcÒ<AÇpÇ{žÁíSfæ‚½ó{ûñÕ3Ši‘',VYÆóDÃChú½ú‚M•ºMÅ‰¸Ô½††_±Å\¡‹-
^r#–JmPñQôñçñO¯£ˆ”‹š[¯ÛIªâÍy¡´9¯Ç÷%Ôí•zMÛµRÿêv„…6Ð¸”…A*‹+WF0>5¢$¶—B«ªŒEàçÆñ	tÕ#‰˜ýhÈÞ”¥*5t$”Š)î"bw2ÛÀ©/Må]¡$UF¸wƒ‘Äl!ü“'“eän2Ômt@ŒípUEçn,S»0LUl…D‹çIÙc¸k›tz½³=ºö[c™+HæÐ$ÍÞO’°5¢Ð5£JÑcºLnï…ª“q\áWë~`…_÷E~mjU$°ŽíAÝ¨Vµ÷ÕŠØÔ*ïs	{5Pí]ADtµ}vh[D'bŠÜžÓC!2[ Ù'‡î‡äŽ³C‘mìsCû1Éû&‡VðÙ
Ö>5´3»¼onÈá±|}fhOyÔP€Ë¶Pö‰¡½yçÌP›míóB{„ô‰¡|î kŸÚªwÌµÑÙÓ>+´?Lïžj£³¦}Rh˜Þ3+ÔFg=¦Øa‘›ÃnÉº†ûÀïŸ"-4«´ =W9´¢ŸL¥õf8¸~)aq‚×<z€{¹ây ¨Ô,ÇÛ€*?‰yš2]…*+„*@Þœ_Â@é†BàÞ/¸‘q/?O]2ÜÉ•þU k‰.T®%è.‰NÀüÖxR#*Gg³õ•AïŒ60`“èìô¸é&û_ó€Xå—¢=Q >ÏWLÝ)²¡ [H€x"˜œå`ç	‚é©Béƒª Êèyeµ@«­ñÖh¼ÇTiÃÓ_jŒdAQ”?ãQ4dì£0  öúÍ¯>ÿôi@t\ÌU’Å?ù>Ãþúë‰‚x	5WÐß5™“ˆº½ºw¾GÙ§¹ÐÞéÐ €‹‰MYMU‚>ªlÚ€¸,G*cÏnæ/}÷"ÐÜ®…	%/ãÈzèï„AéÆþ‚ñ´$iÇÏy>É 5¼ö“En\ëÃ~p`m÷aÚÁÄ,cn±3‘Ç¼ø“YÙ¶®uWÆ†¬'EZGþmmø/ e AØ•$H6âÊ wðcô˜Ði&¿jµ!Jb5Ü–	TJ€´ÉÒÎÂ0b0àD©Zaœ`oh(•KÐ‰%=o}2@ËUöÚ‰·‡ë•zÍg›RÎfÀ	NÔ'%cà“vºo h¦c™_h¤ž«*MÐé;çÞÊ^BçÉ¡Lè3t¥àŠ‘Nä&¨]*OŽh§“0Ãõ„F¸ÄÐíx	.Îö8Wvæx<`ä±é®´J@›¦ˆ6C¨ðp™VÐGœ»½öƒ>c–*ê÷¤2@*‚—’Ê üµä4x€ß*0wSUª‘ãšÔ`ÞGa •RÌð@H8ÛVæšÔÁóKè¨mzÃ¾4íÜÝAóÇ
4 1£k0dÿñÏv ÀÜ#…ñ]Æÿ´ÕÀ+£2ndL±¨w«õ¸Ùg-¦UŠ<¸QJ*/`’­²‚Me*ôR‘ib¸âÄÇd*µ¾^ì€U·ØY1;Ÿ¤R/„œÍÿd/R·0˜ÀDžèFp\
`Ôy1Ã)íìó¯7š.JÁ¶,ê¸eo?ÀSàÉ%øZÒîýÌÓ
:8f!DÎNG„áéh4²‰2iÀ—z°t±‚ãÛÑdã¢:ÿ­R†÷°®…5€£XÆ¸’„e©T¸ò…Î ð%­gÁæUŒQ¶sL0(ëÙ¬T0Â¼ùU{ìúq¸µF
š&Z¥˜¹D:Ø3euLÖÈÂ#R™0¢²G1@wý“Ì«+SpQtr
¡b¡ .æS™IÓ^ é8+”–ÄÎ•þØ†NVÙ‚PA&ãRiô§3o¡,¯Áã9êN*XtEÑ·£¯¾‡kuGàÿ(s¨Kèg;<µ-ÎVï7eï÷P“šØOÏØ`z^[÷6°×f”ï¬#Œ¤ÌQâò@u–>>}Ã¬þ‡ìmÎx¸)ª“‰ªp¶•¦B[ËÁFr]¹DáµÅ:<"Rº
Ñ¥l-“éÈŠs¢íj¦Cè7k‰ë[«JSxr„|^ØPBA7Jßm+dàéÙzöõ¼ëì{_¡ækØ½&óÊ˜!^xŠX­~€1.ÙPßÂµ;SZ‚;Ïg‚Ùhp:8ÃF'g¤&~p;˜™ÈÎÉ—öˆnëI´ü½½æxCf®»Øyƒ%  Ê%{"óVÈO­¡¨ÌÉ‡éÉ;{ß¬oÁ|g˜A±«\›Ÿˆ¹FO®1Ða\kP¥·òmkä#úÙÀ¥²sÌx”*í9²‹Ùæ›¦|ÆžŒÐ\OŸ"!|ŽRãG.!iyèãÐB? WOÜI0Mœ¤?Øu••Ï¬Æìë+\ÛU™ec Ê‘èY¿Ÿðóþ¦©#‰Ï—}øð.d;éÈûá§,Lœ¾sÐÌÏµ >*[º1€™ ªí7%¾gºšØEåw8
Ÿ¶•fhÀV°Óåkë§8®¼(dÓS|Ç/gV]HEkýÕÕ@ˆZÐâûfƒgê±Ú_âLŽ²èá{­wÁçä¶J·AÉe•ü!¢’¿¡=î;4!IÍ“Ýj½Ò”fŸÑ¨KêÞêûÃ0ž‰¼¹C\¨mˆqúÊ*©¬â2í·n~Ù¦®J_NåË–Sé„]‡ó2}•CÖPéŽb§S2}á”/Y8¥#x›ÎÆôÕRQ-¥xNÄô%RU"¥;ˆÎÁôuQY¥;”N¿ôÅPWe+(7žyé+ ®Jw$»œtéËž®ìI'$;Ÿoék¦ÖÉF»W:éœ|Ù'] ë’®é«š°ªIg»ekúR&_°”I7ì6&kúú%¨_Ò	»n¹š¾hÉŠ–tÆ°Kª¦¯TrÀJ%‘ì–©éË“¬<É6HnNÔô5IV“¤3ò4}!’ƒ"éäišc…Ðû#˜§Ct²êBíÕ Ò@ƒDi›Q{e¢‡ì¿¼´ÊøwÝJfE*±ðÌeÓ!*[Ç›ÐñÎöÎc<.Š¸>¬ Æj©›­fÜpÇŸ†9¤äMÔ9wx_¬|€ïÃ3lÒüÈ„%c–\VyÞÞVÍÙãz<kOðªCÛµ¯
Ü¨G{;[gÚž—2yùä9º~9‡O±˜ÉÛ©-Õa7†ªL< ·WÅ@ÌITEà@Ve¥—ý–px¬ˆ/ü¡Ûä©I±¦$Ž;›t´íó”üMÛ´?ùZ¾l} Sp¤ö.Ïó—ÌÈ´î¦ðDi›Q_øã£¾BÃ«Z’Ž58šM¢S6nŸpkuà¬T×nA×&[Åuhk1¨«5PÉ‡ÉðŽi©~ysW]TÇÁw’ì}õcðhxzÙr“ŽÀú&®Z@xtÍ¹vÅ"@¦w
:ùÐ&IfÃÛ­x­%÷îvÅ¾Úƒ{†·Kàvêž–¶Xµ¹=vÑ*¼cÅœëx.’*Që0mbý OAXÿb"ðH[=©ÑíÐÈÚën`ç‰Hù2r=Ó+å(áÔ'Êñ¸¬k	S×G—álŠÌ9ž÷ÅÃÄFÆUÊÃ2õidwsr˜˜N±lW@v½*tàíóš, ÿÌÉþOÈØÊìŒ\Ÿ«Q„âÏÈ{ù‡¼`£>Ë*¦÷\¡çJ*‚´\×kbËd.³*c¤¶_ïâ™Cµ™æÙŽæ	ME‹94™aÕ-k?O‡øÔö	á{’«VuèÇÓt‹è:è½üiW;~b¯‡NëÁ`Ø[CÏWËE¡Øy	=ÿÛj-î	à2„h0Ï*]>£ãÎÏ?ƒ‡çú2~V{«¨>ªä?Bá†_µ˜3za1D»¾ïûåáh4NF§ðï÷?Åéðá€=Ñ§_»OMq\•?üõV^`ÿ’¾bÔ¾JŽÝwåëI¹Ú@4s§%niçü+lÀ”“À‰þ£ÇóZéœ­s9c“J‚¡€´¯ÞcÐs¡ë£0uO¨¡­A”ÞXˆÇžÂJ”¶ü$=·%	äX¿¤âQÔÙj„o[X^ÛÆ
[õ¯„·)Me«AÚ äß ÂæT¿ÁE#>:µEa--2Zšµa«†Z±ˆýµˆ¦‡»PÌw×ÄÒ¦š`ø	“¦}-BñžÇž4B‹j––Âxö$,…ç¾0R·ü84¥Ò¹ÊO©ˆßc§‚L·8º‡“U•»2XOcí«)gÄvTÕ5]bÏ¦X…r8d—Ù³×°¶5«Clƒöc»î…]»V»*x·¾Á`Þï³ûÌ^Ž!sÑ&Ãš¬.{Òì™4ªèÄ™Û˜VøíÊ™›˜r-£°Ê™€
ë(C.ú/zF:$ŠÖr–·c9J›Ôí¬¦¬rí¼¶öõ-P&Ë]ô¹_‹¬}¾ü:-ÖY§ÛÔShÿ5BJl¢OÆ¯¾\Jp·_èpš|=ÿiÁóLæÕÊës.)Io/ÑêÉ¦ì‰U6#Ç'
k˜Rq?Êôà6—YoW«X­Nç B.ÿ‡å½/Ó0†d¿"µ)ŒE
&WQØ¾‡Haé—2>+…À/|pU1\RÉVÈ_¾Ö)yÝzªO€RÓHæ>ÙÞ®LÜâ95­?Ã*ä:’Òú°÷rû÷r¤è£ñsmZlòtnÇ^O£ýÓÈ©úhˆ´JT²¿z&íŸI˜Ü;""…ÄØÄ#»c±çÑþyd5}4<Z!Æ>ûóæ¦òt˜¯jñÉSUÈ—iJŽ;‘àÿþ5¡D²…Ýü—qPZáu“Ö_H3@ Bt•‰F®Ì§)}›‡½”—­Òª_çâZ¥éoújK%üÞŠ¥ûª*Úÿ±Bt÷u$¥Ê‚§ÙmFnÛeù¯ÆxaßQ¶¿ãÊ?Õ®Pèuì«úÙE¾yf÷ZÒogå1o±>Þý¹áæékûp¬»m§moÅ^·¡6¸³ŸËö:—š>†¹ì&b×ø?æø9üœ     0707010001f0c1000081a40000000000000000000000016a102a8900001085000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.lxd.conf.gz ‹     í]msÛ6þž_™L'ñœ¬Ê¾ô>8‰çr×v.sM}sIÛŽ‘ „1I°(Yûñ·» H@¶,¹‘º¡§é$|Yû<»X »ðÓ§‡üyò”ðÅ¥ª2\V¢1³®+®²wØÖVwO~î:û´‘U&®~yò’:ý»ýä	vàR¬WªÉÎúvÌ[¡Òpïø€?¨+ªšÏ
Ñ}ìCÓ
¼Ñˆ_[Ùˆ¾ßòBÓºQK©¥ªd5?‹îd"çmaú†¯*º!«…h¤áUê>Tž³s¶<ó­ú,{_×"•¹Lá™¹ eÉKy•ÉŒ¡Ý'ÚJ^±ÿ±hR­4)¡S¸ýakÑpÍdz­(¡ÌÀõ¥lLËVòtPŒáÅ—NÅ€
ÝÄBiSñR|ÞHDj–šUBdÌ(6LÃd~“–;Ý1x#“y.Q™À^óF•ô¢&lº7_³\©mØ<J\¦Kð=><Lï{Xú¯ÝHÃµf=yé@¸©ÑÛ@QÓº¢¬Í”ÏTc@ÝûòaØZðaÝw@±KÑ-˜)Ÿª"<ßædi`sIb@@’ŒØE-ª÷?þ¼aQ0§th•– -š%xBUÁ]³`<0;ÿdªÊÚýWÉ3¦rÆ»›xƒþ †cö^H´áÍ\„V,Ëº%‰>ÄRÀhG[×ð*ÈVpmX©2Áž}u4î»J’U’$ S+B˜;»\%›¤JP«è}xj$ô¼ô4ê¤hŽAªmRÁf\Kmù¼!¸mqØÂf¥—é´QÊLknÃ Ð³†' Ÿ£œ‘J¼( Ž¥.ñônùA8Œú€!X¼—bÀµ”-±·#Ë˜}]³ƒ/ÀlìÎðzÞ…Ã&³1ÀÛ®i]»ÈÙYZXFÄ¾Tµ?¢‚Ûh”Êˆi³zJtø~ÎþÍ4k¼ë4OØ‚Yx)ËØ•4Ð…ãtN _ ìÁ‡!Ø=n»}õ¢Â‹0OI0á4éø+öœ€ÐWiÑj¹#Ð×\jÓp°qUë#Âí¯èýÁã4ýp(„ÿ4t¹0zø*]‡ H_jPO‘±ÙÚ]~Wt%­_	DjÆq}lµÑ1ù±B¥—Ÿ–Z&·õØ{P­¸ÆçQtÍÈ’;²ƒþ9~^±Ã=ð85Þ£Í6f
:C×q3­`ÖãŠ­cno~´¤Åãî¶Ñ¶±ÓÜPSè®PíãO_Lin›Ö”µ‘-2§Ÿ¸„ÐP{åoŸDp]bZðÆ€}ÜŽ \9—EÛØ?Ò<À„}Ù
’ª-F'§£^ñ veDo«t1·1ÿ´ScãBþÆ.XØFÒ	ú<
+#¹º’ªCâÒÐ¬­L{vò·ñäÅCØˆÂÔ0¶ªý>B/
quŒ³´ÌÎ’â*ušc¯HÛçl<³W¸¸pÃ‘Õ&SÛ@òÏ¼fÛ ¡ÑT0­aJ0í„ó8Ç›n œ”NYST ®DÚ‚uðçdè üà„ gé±uVgI×“Äù/–šF58!	M‹bÂßD· sû‚_ü\”¿Ê@Re²ßÝFsgÑg³õ€äa2Ômò1€:wÃUÕž‡±LUÂ0U}üÚj+ïÉ,Õþ^«Dìj#†èç®ˆº™Ú=‡?!2w@s~>ÉG?‘»"8Ä>÷c’ülàs'X‡Ðç`vù±±Ããð‘ÏýåG†>1:·cŠ¶ÉFlïî>ðû—(jÍZ-@Ïmo‘W³€©B`âíE¹M*¼çÔ#¶ZÈ4Ü]Çô/| ªã”E—ëQUƒ¼_ÒÂ¶rùxÂn9ù(øêšö´pç©Õ-ÈZã§kUi	:Æµs"ƒó–•¾LjDåO–môà›´o¼"{
ØÍÙ™`r^ÓŽ_´ŸÙÖD½hM¦VÕ8 ø­ÑøŒi5Ò†+¾ÆU$J’êKž$c—­V±¯¿ùöÍß}WU„dñß¤Ü5ÛÆqØ^?S‚¶RÙBA{o	jöæxã|ãZµö®HG	O¥Èp÷€ÈjÚôÑ¦`+ðŽ[ÜV%{…;Ëç¾y	mOãjv(‰öY]§ÇþIèT¿ë¬ƒþ´$iû–.x5Ù(ê^üeQÙwú°®l÷jÝå]µ¢Jy=äôÄØÞÇé´7E6gøü)Ú¼§­BqE	zø)zLh4“Æ?NHEï¥±žË*eÀ‡0W`ÝçNE]¨µÈ¼`ohÝ¦¥%}o}J@ËU:ˆ·‡ë•zÍg›FÎç¸§LÔ'%SàˆOî¡è‡cY]j¤^¨s`sÎ=
Ñ1ÄåèÁ5t¥àŠ‘NäÎ¤JÚ·ãÅ£cÚ)íç®/!4Òë*Õq¼·@ç{V);r<19Œù¡´I@›†ˆ˜!6À%h»½öƒ6cV(j÷¬5]/Îœ øk30ð ¿¶`î†ªBÛ4ÌkRƒq…V1ÇŒ¡pö]»!¯<ãKh¨}ô†méßsO¯?Ã<'ˆÝcö_ÿmŒíð7L¶·#>¢å
†jà­Q%72¥XÔ»UŒz\Çì-ò¶@Ü(¥—0È¶eÍrY[‡¢‰5âŠc®´M;õúz} VmËROgÅ¥TÓ•óÅŸlµð¦˜¨2ÝNŒšÖsÒ~Æ6ÿr£Iá¤hdQÿ „¾·ÐÅx²_KÚ³1Cù¼¢Y	Q±“	ax2™LÆìß¢©Dð¥ë,Ý§ÜÔ- ÆW“[Mëvúk«`½Öä˜¦”ŠéX8ó…Æ ðÍgÁæUJ	—Ö1A§0¬góFÁ8ãæñ<Øµâpk4Í´*pYÄ&¬ƒ$-º˜,xSü˜}Û;ŠºëïdÕ^±”Þ ç$Ç'*–
àd¾¥4ñ<€ª6X­´$vn´Ç¾Hád[Î 8 ”2m”@ÿl#ºSˆàéu'Lº’ä«É‡{]CàßT>AêÚ‡§öÓÍçÍJÙç½t¤&öÓ7v˜€^€×ÖƒÂµY'åë#iå(së@Ý*\>™œ¾`Vÿcö6.¿hŽgªÅÑVš–RÁF*Ýº…Âk“uøD¤t¢KM“éÈŠc¢OÈþ`W
±äÐ™µÄù­U	-Sxr„|^ÙPBA3ßŽˆ€ö-dàÉéíìx··ï}ƒšï`÷~KH%®+ãâñÂ{TÄhõOøÿFŠ}ÿÇ`K\•np9„±ÉèdtŠ&Ç§=¤&^Øf)Ê)ùÒÑ»z-‹çß™ë}ì¼Ç PÍš=—U´ä#kè­9¾ÈßÙç.a~æ;Ç;Ëµë)–©<œc Ã¨¹Ö JoåÚÖÈ'ô³ƒJ•S\ñhT1pä£Í–|ÎžOÐ\OŽ~Rã%· iyèã ¡V’+\å°A•·X9H?Ùy••Ï¡òV£hn×––(GD¢g·?áÇü{¸üKÉüzÙÅÅ»@m¤#ïÅ;OY8}ãà5?ÖJW™äú f¨Æ;%¾eºÙIåKì…_¶•fÄlÁÛIÈò€µX8°ƒãzÅëZV`0ÅAq\àå¬×ª©h®¿9QÞxÍ^ìðLV÷„8“³es9¾®Û¢¼;%ˆÛÎ d9D%ˆ¨ä/h÷š¤þËn¶Žåð3{ÝPó6÷Ãx&ñ^d†88QÛãå1Ÿ¤<Æ­´oM~¹KqÌPó°51{a·GRèPó)aöGq¯TÐ¡úå!«_öoWèPòòéJ^ö‚pï´Ï¡ÎåÓÔ¹ìDqÿ*—¡¸åa‹[önŸ(f¨hù„-{ƒ¸_3”±<`Ë~ØíŒa†Ú•OV»²‚wa+„^Ø1Çšòä(çRÖ±¥†§ËPòLlF±Õê1û‰7VÿéÞÂ#P%æêmš6¥¼w5žOƒñfUI‡TÙ+èeµÖýê$7ÜŸZØ1‡”¼‹:S‡çãÛ¸°ZH QÍSq¬æZa:©]¥Ï‘F™´@?Ja	ÃÓT€%7mUÅ;qœ=ëúó¬óoz1´ÃwUãÚ.mDiP¯™?E‰ºç£ñx|„õ/x^±{	ªÄSü2Ìõjë‘68^·u’°èÌÔÖJ·gº]Dø¬=ÐJ‹ÉÓ‘â–**—Îýèhs¯G»Ý´³÷¡;Z6<ºÒ©8Òy—WÕ9Ã“Û´jÀ1£f<½ô‡>ƒ¾çU'ÉÇ²~_!ggqRtXÞ£RWîCG=GõX´óQºªˆH†OäúMTýS]–ÏÚv’ìsÝg0q°ˆíDnRÖ¤/{q¥ha¶3[Ø³0Ac¢¢,ôš6Ëcò7d6<~‹wZrán#å|2Š;¶!—ÀíÐ76¿¶çeewê£Z-+fªÓ…ÈÚB$Qþefý /Aþ¸ê,i››û‘hÇ€^öØÞw›f¢à0°-Óñ‚@wº$dÌ°toÂÐõÞEÿ}^ù‚cŠ(æŸ™¶“*ºV—·79Lä9êÝ“]o
yû¼&À?µD²ÿÆ¤
»©ÅNÉõ¹²6JÞËä5›ìðYV1ƒçÚy(å×ÄVÊJ–mÉHm_zïâ™c†×ö×-\s4Ïi(Z-àõ©	jZû9ãWã¤Ò	{^©(!Úq‚n½zÊÔÌ†b²ûªFüØ`Ù—š¹²!òÐžÍ½¶¾=Ãä{px€0:ô÷ÏkU5#[V³œ³Y+:òý›ïÝyÍ~—¼k	)Ûò¤âÆ›V€Eê¶2¾Û÷’r,m©˜¥6ßŒäìöpVûòîŽ‡e±½«Öl¡¸uý!ÿF˜%W¹3ÝiÔñQˆ­×†9“()a°béwmŽ\Q¼Õÿªw÷Š¥M;£ãIs—DãºÇž4B“<jÖ5V”aÜrfÉcJFDë*}†GySÖz¥ªcG*â÷™S{ŸwtsU[gŽcY\Ž™NøøP¬±e9¨Çlåf°gìk˜Ã˜Í.ÆÆ }ŸÏ\óÂ¦]+kÖw÷=ÌRïs–J:~3Ô˜·ÌNéì4÷KUïÅ™mL	ÿØ—371åÚÌq“3n£¹èÏ4}2$ŠÖr^Å±M»÷¬¦¬rí¸vë2µáóÏØoJ8EšE¦é­U¶ÔÌ¤i8.Á¿}tAÌ lª¦/¯§&lüB?Û_çÕ·
¤¹_ÏÕÉ•U^P}¾5¨F.£b‰™Àš@\TlzS±üRÉ+Ñ×îðšžoøwÀ€ÿrökvÈ­Š'‰/ví~ïK¼Åâ¾j×T)RŸ§kWk0¬ùÒ.…S·›ûcÞ{¼Û'áÞÖµm’ ëýv;â²Ûö;‚'‡°à^Ã‚@Ó!¢¼‰ä5þ$éìy     0707010001f065000081a40000000000000000000000016a100daf000001d7000000e600010003ffffffffffffffff0000003300000000root/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)
	$OSVC_PYTHON $PATH_LIB/files.py ${PREFIX}_FILES check
	[ $? -eq 1 ] && r=1
	$OSVC_PYTHON $PATH_LIB/packages.py ${PREFIX}_PKG check
	[ $? -eq 1 ] && r=1
	exit $r
	;;
fix)
	$OSVC_PYTHON $PATH_LIB/files.py ${PREFIX}_FILES fix
	[ $? -eq 1 ] && exit 1
	$OSVC_PYTHON $PATH_LIB/packages.py ${PREFIX}_PKG fix
	[ $? -eq 1 ] && exit 1
	exit 0
	;;
fixable)
	exit 2
	;;
esac

 0707010001f096000081a40000000000000000000000016a102a8a000000dc000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.nsr.conf.gz  ‹     ­Q»nÃ0Üõ<'Þ]´cÇ,ƒ V©s-Ä‘RB›¿¯Ü4N¬âÈ#îÅ¦©9¦¡Š³Ð•štuÝÕíÎJØ£YlžpýŠâº‡šò—'pSq–J”ãl?&¬jïvÒEˆ—ìîqlžÒÃà.ÜÉ@Å;äêöƒgz£Oˆçß„…A¹\ìÿrÑlÅž‘ 4D¡4‚:ÞXN>†®Ÿ³Ž¥œžBt ÛvK{àßK³J«£´.r{ïke[zÉ~o‹‹—u÷ZÎ¢1?ÍZÛ¸  0707010001f01d000081a40000000000000000000000016a100daf000002db000000e600010003ffffffffffffffff0000002300000000root/usr/share/doc/opensvc/AUTHORS    Adrien Mahieux <adrien.mahieux@gmail.com>
Arnaud Veron <arnaud.veron@opensvc.com>
Bruno Marchetti <bruno.marchetti@amundi.com>
Christophe Varoqui <christophe.varoqui@opensvc.com>
Cyril Galibern <cyril.galibern@opensvc.com>
Florian Bany <florian.bany@gmail.com>
Franck Jouvanceau <joknarf@free.fr>
FrÃ©dÃ©ric Brin <duckx@mezimail.com>
Gerald Millet <millet.gerald@opensvc.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>
SÃ©bastien GuihÃ©neuf <sebastien.guiheneuf@opensvc.com>
Stephane Le Bihan <stephane.lebihan@gmail.com>
Stephane Vigan <stephane.vigan@gmail.com>
 0707010001f089000081a40000000000000000000000016a102a8a000000e1000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.hcs.conf.gz  ‹     ­Q»nÃ0Üõ<'Þ]´cÆ.‹Q¨s%Ô•\RB’¿¯Ü4N€®âÈ#îÅ®k9¦£†³ÐyÖ–tmÝµíÎ¼Õ°„èp~7‹ÛO\NIÜpUöpeB7giF9Íö8aUÛÙI!|— pÿ‡Ñ–)ß¾¦ˆ¨z‡\AÁaL/ô	ü›°2(×‹ý_.š­Ø/dI({ÐÀË9¤8æ¢¾vt ˜èºÝÒxølQéÕ[Aï÷·¾V¶u¡—˜íy[]<­»çz–Œù®4Ù     0707010001f02e000081a40000000000000000000000016a102a8a00000248000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.array.hds.conf.gz ‹     í˜ÁnÛ0†ïy
¹f1ºíÔ.½tR`Û¥Ý.Ã°(2«u$M”³äíGÉq¬Èi.
Ô%±Dý&¿Ÿ:Xãñc4†G’S!¨ÝâÎ#Ô%ý¯Ü°ÙËnô#;Æ–¸ý9ºÊEÏRÙ£QJþw\(/9,åù7ŽÄˆ´ójÙàáEŸTC˜VþnMÀòéJ‰•j›xÌí«³Ø‹!pÚºEò¨Me4\Ã
-£sq¬@š#îk„ùÇ;X+«V ·¨Û˜Òè %œrøÎd
çcÁx
~,ææÆ­yWyóùö¯‡€¿¼Šõk„–ê‚ÚQ4v‘'ÔF±UÓ¨hœÝL?ULq'§âaÆÜóêY'Ésl­ZãkäšêWe¤ù€Ná¶ë"øà6¦Är#^*ý˜Ð¦8Bï™ôƒÚª2ÛL6«Î`þíîû—éÅÛwïÏaõŠ(M½,ÚûÐ>GÇv¥ôeãVÜ²îOKP
>¤&¼æ´*huU§`QŒÓ¬Úý‡uK–ÈjÙ‘Å‚vq½XdSÈ+VáNïâjµ9õ©ÛÐ§Ç[Ø–ìÚ!ãtr¿­È]q!Šb (Šb (Šb 8mh^¡w\Uö³{Çû‡ÆLÀ;c—ýuû kVé“üÔÝÇyâ›TgPÇèé²HDûÝÅY¸„áåï/ž‡ð¾´'§£#Õ¯Î 8…ð>ÂGøá#|„ð>Âg8>âxp"  0707010001f06f000081a40000000000000000000000016a102a8a0000011e000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.node.array.ibmds.conf.gz  ‹     íÐ±NÄ0à½Oa©+œDGNÇ‚„XŽé6ÄKÜkD'ôíIZÝUb€L`/©êÔõÿÕuÉªj(XyœbVÓÄi@°GgÂïÆ•Ý®¬]õ<‡­Ùzƒ/Õv½[bWU^ÿ§wbs»nÑ9}“×+3Mƒ:öxùÓƒêæãÛh×<Îƒ­û¸îöDÏ³ÒÚÈK3¨mk5ÜÁ	=²Õs¸4!ètãÐ!t"xåÒw-Äôb`ëOð¸¿ß¤ÛÛœ;Ùäcã&M<|#Ôü¡€š¼ùbÔ,FÍFc@Î³þ Ó9DÊÏùèéÖÏD—î˜(Šøˆøˆøˆøˆøˆø”óùŽY¨p    0707010001f0d5000081a40000000000000000000000016a102a8900000f70000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.loop.conf.gz ‹     í]_Û6Ï§ Éâl×»×¾l“àö®-.¸¦9\ÒÞCQ¬h‰¶‰•D•¤ìuqþf†¤DzÿØn¼I·UÐIg~3’3Ü§OùçÉSvÄ?H®æjÄì¦¬TªùPrÇåî¸²{òö©–u!®~òú%ûÉdþJlÖJç=sY
x1>â’ÉUÃg¥èzz¯[ì‡iñK+µ(n¼h´ZI#U-ë½ü–—†ÞbÎÛÒöL¯jz!ë¥ÐÒò:÷ý”‚ÏÙ+¶¼LãÚ½7Èå\æðÍBÔÐ6'©@&‡/Þ/Df<¿‚g+™†?G$'6oË’ñ™Qekk¸]N€ÀWôî%›+u—”kuÙh!ªÆ^ò™Òö“J¼ì"ïÞEæŒå¼.dÁ­0ç=·ì}?¹ªWBGÌ”‚®êDa¯çÌË¬bYf@–ØÛFÔï~ü[KÐ•:pe$Â½âoí’ñÈœÃ—¹ª*àÆi,‰©9ãÝK|Aÿ£'ìª„#m¹^‘“USŠJÔ–º3¬P€à£mhÊìRVrcY¥
Ážs}2é‡Ê²9Š$Ë@¦Ž„°#v~µ>Ï¶A•¡TÙL0ž[	ãÀ}Rè1HAµšm¤!ün,;=ÜæFÃ³Gá‡ò_‹¹¬…˜t‚œA¶ÔŸ€¤NBƒöj˜DêyÏ¯€N£¥þ6LÐ·H|Ñ¸+QâÀ ¿ÂiàßDTj@ÔÌgø	ß’®]«{È´RV\êõÇÐq§°?³Ÿ²zƒo½äIé`žÂOB&u	†0^ÃàÚ·ßc½jƒÞv0ú,ÌAÃWÃ<'€³ÀØlü%{NŠŒ´+®ó²5r%F ¯xÍÁ¨ºÜœÞþz—wÿÀ?èMìï!BÁ^é9ÂÄSl¶qSPç…Ø7×@ˆ$ø4-nÎ‘àO¬›Îf¥‚HÉ»'‡ÄqÛÐDF~Ë?`knðûˆT	C³²âì ŽA7mŽsìh˜†)CAÁ [Äç¹™‚¢ã3“DwÎ½„Y3¢–ÎŸwÍš=‰æ&ýÆØáÅ+;N«cÙ¶9‘lÅÞø.ëãpt;¿èøv‚ÇÏ^:ï8yÙ(c/»æCÌâ¥}ÑÅ¹`&×²!ƒ×"Ç•Ÿ[‘†)‘uœçc4UŸgÝH2æÑZiÝ"Ù¥/Qywhæþˆ$mDnePåoUe¬@e¶åúW#‘9X…ðW]Ì†…Ä‘2–mö!
õ4Ó«j}Ç2UsÃ„Yô ýµõ0W>YF¢ý­V‘ØW©ZÑÏ¡‰¹Òâ¡ÃŸX3hs~>L“GŽ~‚FÕàû<ŒI~hð³¥ŸƒÔ:„>G³Ë}¼>Pßù<œQ~`è“jç~"Ã¢¶o[û!ô÷OQ6†µF€œÛZ‘Ws
Se·Õöoñ]P¤±õRæËøHÓ°?¨zœó²ìŽ3¡ ·ä+Ü¨Å¡¹;Ÿà~D
zÝÐv/nÊ¶¦ZìºQµ‘3<hWš@àÕ	:¿Ká…4¨•?ØúG?¿¸‚ì!àÎ-f‚ÉEvN›áÉVÛdÌ²µ…Z×ñIækkðÛ„/×|ƒ‡ ”eõç<Ë&Œ½À¾þæÛ‹¾{?"8®—ªŒÁúBÇIÌox_(A§l©€ß{ÂƒŒØÞžo¼ï1ÜŠ6Á™äL¿…IXm«Am¶m€\Uœgªb/ðÐåU`/£“<xˆ)Ñ„ô$|	ƒêdL4>‹–$ÝØò%¯¢%ÃK{µë¸“‡{pSÁîÃ?Þu†!êœ7CÚÊQŒí]zRˆ?;KåÐ2Ð è\)[qM9(‘òsô˜À4“6|NšJÚ¤#²¾Ë(€‡ømãfa1p!šRmDCCª\‚L€¨¿]ð©@[–¬r ÐQ¼58Ü Ô>Ûj¹X &8Aœ”Ì#Lºé¾WE?ËúÊ „ÌRµx<,˜wîIˆŽ¥tìKÏÐ•‚+Fx’»@ t©¼|t(@;u™;Ü\Ahd6unÒx	^Ìö¬Vnæx6bè±ù±´Ð6M)Bˆè\–-ðˆsw~Ä3†`¥"¾g­í’Ôpå”æ¹!ø¥s0U•F8nPæ}$RÑb‡é1à\[Ì±Cq°g|Œº† 7ä¥oç¿Žš?Ã ˆ}ƒ	ûOèÛ+ævø	3äãnÆG­%i4±xkUÅ­Ì)n£?À	ûÁˆy["n¥RÊ+˜dÛÊ[›±¢2„qÍ1L¥“×Ë# ê®®Åå¬¼’êr-äbùÛ-<ÀT`uazÂ¹€¨ËfSÚOÈóÏ·š.JÁ&õwÊuyý†XNVàkIºö#/[`p&ìZˆšNI‡§ÓétÂþ%t-Ê/Ý`é=¥m¥Ú`|9½G³yÓ^þÒ*ËµÞ«Ö­ÜXæ”1…™M¸òf@ñšÖ³`ó*§\$ç˜`PÖ³…V0Â¼ùYºî+½5RÐÒÂW¤dD“%“-Í+a…ž°o{G1Bwý¬Ûk–SpY6>…P±‚P ó¥¬¤M×”˜Ìe$¡s‹×ÂÉ¶šAp "¨d®• ÿb+Å°ˆàùe',º²ìËégƒw#ðoÊ† e	|¦á©kq¶ý½]+÷}0€Ô„~êc‡	˜%xm3ØÀ1\›sRYI;G…ßêviàñéôìæä?a¯ÓsPÍx¦Zœm¥m)‹l¤6­ß(¼±X‡> "¥·]Êd™L} ^€*Î‰!Wñ½Û)†‚Ycž˜	mSpÄx^»PB:ð‘ ÐµBžžÝ¾w{ûÞ”|§öàA3‰ûÊ¸9B¸èRÕQ¼öø{+û´ÿ„c°e,îJkÜaçl::áƒéø¬W)*Ü­ÌJT—äKêIn¤p~Cfnö±ó^—  ¥7ì¹¬“}X Ÿ8CÛÚñÛùøûî
Ö·`¾ÜAq«\·?‘c¶¼šÇkt7D¬¼Ó¶3ò)ýÙ¥ªKÜñÐª0rŒÙæ‚ÍK¾`Ï§h®§'ˆ°Gið‘ßt8íãh?R2®ž¸’(óÛÑ)€úÉÈ­«}n-UpYEk»¶rhŒHy <»ó‰0oàÏñö/1R„ý²·oßD„“¼oßÈÂÄ˜ƒfa®•>ißÌ´šž”ÎL;s‹Ê¯paÛVÚsµ §1Ê#ÔbªôŒ›5o,b&¶£@7x9ë¥êCªPû•¬b­E-^²/vx¦AW„x“s%‘9¾‰ž»z•ƒÔÛÎ d5D%¿‹¨ä/hš¸Ê—®g¿Zom³/hÔšØÛ>?Œã™,x‘UâàBmGŒ3”Ç|’ò¿Ó~gòË!Å1CMÌÇ­‰ÙKw{$……0Ÿ²f-î•
:T¿|Ìê—=•·+t(yùt%/{©pï´Ï¡ÎåÓÔ¹ìÔâþU.CqËÇ-nÙGuûD1CEË'¬hÙ[‰û1CËG,cÙOw;c˜¡vå“Õ®ì£ÁB˜ÇªÂ@ì÷˜cMyr”Œs%›ÔRA!½j(©¥Vk&ì¿\;aü»k…·üIÌÕÆê6§¼WúB·'aF`zX…†¸ç÷C„
j¬6¦ßä–‡½:äwAçÒëóñ<ŒX/%À¨á¹¹V˜Nêvéç£B:E>*á Ãó\€%ë¶®Ó“8ÎžuãyÖy‚‹žð]7¸·KÇIÔ-‹WÏ_P¢î«Ñd29Áú¼ª
Ÿ¸³UáWæzµÍÈXœ¯Û&Ë€pD«uÔÝµYþºu×e9j)x:PÜSEåÓ¹l¦™X}ÛÉÞûP‘ÜêæÅé¼Ë‹ú³²©û©<Q™"Š®™õ‡!ƒ¾ÇUG)Ç²þ\aÎÎÓ¤è¸¼g¥®Ü‡n3Mê±èä£u	þT%€¿˜kõ«¨û¯º:¬µí)¹ïºn0q°Lî³ClRÖd({ñ¥hq¶3[ºkâ@b¢¦,ô†ËSðk2ž¶â”|'Ü¤¼šŽÒÁÇœá'ä¸›ºçÚå·oº;6Á“Z-GæÒäKQ´¥È’üËÂù^F„Â¬EÔ¶÷Ò=í‰{ïvYˆ’Ã ð·¦Ê§†Ó%!c†¥o	S×;ý÷yåKŽ)¢˜jeÞ–<NªèX}ÞÞè01ŸÓÅµ=ØÍ6ÑQ°Ï´@ùgHîß˜TáµØ¹>_Ö†äÏÈ{…N^²éŸå3x®Øs­An¹§–U²–U[1[‚—Þ»ä¸Û¯nu4Ïi*Z/¡yŒÔ5ýœL°×4©tÊž×*Iˆ>Nb¥;Þ§zÊÔ,†b²‡ªFüÐ`9”šù²!òÐ%^[»q¾½Àä{px·&:ôwAŸ7ªjF®¬fµ`³V–x*{wñ½¿Ê4œ’wœP‘²+O*o­ÑqiX¤î*Ó©ß~”DciPÅ,µÅv$çZ¸k(]ã]wÛbT­¹Bqçúcü0K®ö×Ó¬¢W¯k&QQžª=¾PÀ‘EÝß˜¹’xËOÙ&¹Ï=4q°igf€st‰@4¯Ý“Dh‘Dí¦ÁŠ2Œ[Æq–<¦„`D´©ógxË-e­×ª{P¾Ï½èJèy÷8gQµut/–ÅÍ1Ó	ÛÑ…å†~w¨O&líW°çìkXÃØí!¦Æ`Â˜Ï={1k7ÊÚ¢ýÅÖ þ}X¥>ä*•düV¨)îYâR` ÍƒF5{aæ.¤Ä—ì‹™Ûrcå¸™
÷A†\ôŸ4}2Š1rQ§±-»vNRN¸n^»w›ÚòÅŸØoK8E˜%¦¬E¶¤gÒjŽEðï]òA.U3”×[¿ë!ÌÆî7Öô\5ÿh:º²ž—TŸïJËUR,1xAKŠMo+–_)Y`%úÆ_>CËó-Ÿá/ÀûùûÞÜ.ßÏ²PìþÒÿJ„ôˆÅ÷êöT2)ò§ëvk0¬ùÜm…Óï6÷Ç|öxOâ³­Ç$‘®÷;íHOÊî;ïˆ¾Â‚"I?†ˆò6`×ø?["¤p  0707010001f0a9000081a40000000000000000000000016a102a8a000000e5000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.node.stats_collection.conf.gz ‹     ­RËNÃ0¼û+VÊ¹®A Nü@¢f=!&»ŽÚþ=NÒ¦Žx;ö¼ìª*9¦¢‚3Ñi²I_9† N>öÿ¤+ë®lwf÷;ì‹™<àtˆâš«´r7dpSp¦~”ã`ßVµgt"Á×èî/âÐÚ1¤«Á§»Û([‡,˜`ßz¦GzGñ<ÌÊùÄö‹+ö	BmJ¨á+iöçz–Ÿ±§>:Ð‚Ýüxà-@£J­Ô.r})må\zê“=æÛæ~Ý=Ì!Ì7ó–_º     0707010001f0dd000081a40000000000000000000000016a102a8900000f63000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.disk.veritas.conf.gz  ‹     í]_o7Ï§ ‰q’jûÚ7	ÎwmqÁ5Íá’öŠÂKíRáÝå–äJVqþf†ä.)ÿ‘ÔÈvÝ®ÑÉîrHÎüf8äÌÐÏŸòçÙsvÀ$WHs9bvÝ¶ZZn>‰ÜaGwXÞ=û	'û\ËºW??ûŠ&ý:LûÙ3ÿ¥X¯”.ÎúaÔ¼ðb|Àä“ÉUÃ§¥èzú¨[ì‡iñK+µ(®½h´ZJ#U-ë9½ü–—†ÞbÆÛÒöƒþ^ÕôBÖš\û~JÁgì[^„AóÚ½7ÈåLæðÍ\ÔÐ6'®@&‡/>.ñƒ©³ð÷¥*[ø×\«¶¿¢w¯ÙL©[¹©.-DÕØ>UÚ>*g;îÁÚîÍAxËXÎëBÜ
sÖ–ý¯ï'W5 4ÁT)èªNóvÆŒ°Ì*–edÙˆ½oDýáÇ°•,Kæ™£2a„^r†·vÁx¤¹áË\UŒæi,L	EÎ»—ø‚þGNØr'Ò–ë¹°9Y5¥¨Dm©;Ã
€q´MMEÒ°’Ë*UöR˜«£I?ˆP–Í%Y<u$„±³ËÕY¶	ª¹Ê¦‚ñÜJ˜§€Ñ×é$…T«sÁ¦ÜH3!ün,;9Ü‚æFÃ³'áû²_‹™¬…˜tŸ·ÄŸ€¸FB“	b˜DâùÈ/N£¥‚ñ­™ o‘‚ÌÅ¸KQâÄ ¿ÂIàßDTj@ÔÔgÆ¾%Y»Vw+ài¥¬¸Ð«‡q'°?³²zo=çIè žÚ£àMj4La¼‚É	Ôo¶ÇzÑ¹mS`´Y˜ƒ„VÂ<'€±@7lü%{I‚Œ¤+®ò²5r)FÀ¯9XÍÁŒ¨º\‘Üþz›uÿÀ>èulï9¬rÐ+='Ÿ× {Ê‚M×n	ê¬ûæ
H÷c’`Ó´¸¾F‚=±n9›–*¿æÉ!qÜ6´‘ÝòØ
Ükø>"UÂÔ¬¬¸;ðŸcgÐÄ-Û€ã;š¦aIÃ!£`ÒÎ,âóˆÜTÁDÑð™I"Š[×Î^ÂªQK×ÏÛVÍžÄÖõriWÏnõ¥Ã‹u¥K@úUˆN4m„€f±62ç¥w®ÍM¾¶³rKs÷òE „	^4ÊØ‹nÊƒ¿âEpÞù¸ &×²!eW"oAùÌŠÔE‰4ã,£r¨ú,ëf’1÷lˆÖJ0PÐ-’]ˆðEw‹dö'™”A”¿U”± ‰•Ù†ÙÛ_ŒDfoÂu16VÊ˜·Ù§ÔÓØO®ªäyÍTÍ!S5ûÈ¯­‡µòžÔ2bíoÕÊˆÄ®BÕbð~ö•èTÌ”÷íþÄ’ÙCšƒóói’<°÷$²¯ßç~TòSŸùì%ÖÁõ9˜^~ªïãå±‡øÏçþ”ò]ŸT:wË,jûôŽ´ïC~ÿecXkð¹­¡Y5'0Uv¡ìpv‹ï‚ Íˆ­2_ÄáLÃjü êqÎË²e6B5@oÁ—xH‹4Bs›à~D
z]ÓQ/È¶¦ZkìºQµ‘ÀcŒ¼8Aæ·	¼¥ò¦?xìâ<0²‡€‹YL“óôœÂ“cþ¶!È˜Ekµªã(æ[kðÛ„/W|Ê²úsžeÆ>`_óíùß}WUÆ`	}¡0ÆI<Þð¾P‚"l¡`¼w¸{s½ñ¶ÇLð|ÚSd’x~%
	œ&°ÚV?ÚtÚ ¹ª8ËTÅ^á‘ö›0¼Œ¢6tˆ)QøÁOz¾„IõÁÍÏ¢&I7·|Áë¹(FÉôÒžEí:îøá\°aøÇÛâ¢Îy3¤¬DÙ>¤QBü»Ó±4 ‡š
A1-¤lÅåŸDÂÏÑbÂ ™´ás’TÒ† ‘5ðh&@© <Ä!´µ[…aÆ À…hJµE ©r	<q ¢þ¶Á§iYÒÊ@±Ö`pS¯Ùl«å|˜à0R2Œ0é–û^ýr,ëKƒ2ÕbhX0oÜƒ¤ò¥ghJÁ#<Ém PšT^>9 žº¬n.Á52ë:7©¿¯€ç{Q+·r¼1ôØìÐÚH›–ˆ!4è\–­ÆHjÝq?3º`¥¢qO[Û%¨áÎ)Í'rSðKê.`©*‹Ù^£­ûH¸¢Åé1à\[Ì¯Cv°|	uo8–¾ÿ:jþÃÿà3úöŸÐ·¬íð7ÌŽsw+>J-I¡‰ÙÀ[«*n1üëu0«èõø	NØFÌÚqp#•R^Â"ÛV›ÉR˜µ±¢2„qÅ1T¥ã×ë ê¶\ƒùÅ´¼”êb%ä|ñ;-ÜCU`uazÂ¹€¨‹fŽKÚO8æŸoT)Ü”‚M4êï”çòö=L±œ,ÁÖw'ìG^¶0À©°+!jvrL2<9>>ž°	]‹2ÂK7YzOÉ©´ _ß!Ù¼i/~i•åƒXïëFÂ	lHsÊ–Â¬&ÜùÂ`@ðšö³ ó*§<$Ÿˆ¢ºõ.ã×ÍÏÒ}p—Téµ‘œ¦©Q%‹,HÉˆÎ'K&ÉYšWÂ
=aßö†b„æú;Y·W,§`²l|®b® næKYI›î()™5ÊHBçÆx\Cr'Ûj
Î° ’¹VF ü‹ôÂŽ!‚çäT°éÊ²/?û¼ëÿ¦ìhp:—0ÎÔ=u-N7¿·+å¾
ÐšÐO}lQ³ «m8„isF*ÖFÒÉQáÏºSx|r|úsüŸ°·iv9ˆf<U-®¶Ò¶”A:R›Ö^Û¬Cà‘Ò[ð.e²M¦>/@×Ä§øÑÂ€‚ZKÜß:–Ð1E GŒç•s%C‡q$ t­'§w£oÀÝÎ¶÷9ß‰=ØAÃTâ¹2Ž.º4udï„ýþÜÈ<í?áÆçj0çõ\°3v<:âƒãñi/R&>¸]˜•¨.È–Ý×’ùkºçø†ÔÜì¢ç½,A J¯ÙKY'ç°A>rŠþ¾µã÷³ñ;÷Ý%ìoA}çx‚âv¹î|"ÇLy5‹÷h0n°2hy'm§äÇô³JUxâ¡U9`ä«Í9›•|Î^£ºž! Â¥ÁGþ@Òá¤@"ýHÈ¸{âÞI¢¬oG§ êG#·¯rô¹µT½eíí0Ñ‘ò@$xvñ‰°nàßãã_HÎËÞ¿rƒôà}ÿ.@Î08hÖZéöý@M@ªi¤$ŒÌ´S·©ü
gŽm¥1Wr£<B-`ýdÆÍŠ70ÛA Ž¼œõ\õ.U¨ûJv±Ô¢¯Ù[,Ó «ûsB¼Ê¹j’HßEÏ]­ÊžN	Êm«S²¼’ß…WòÔÇûvMˆRß³ß­·†ŽÙç4kMÃÛŒÆþL¬È2vqp£¶ÅÇÊc¥<ÆŸ´ßšü²OqÌPó°51;Én‡¤Ð¡æ1av—âN© CõËCV¿ì(¼m	 CÉËã•¼ì$ÂÓ>‡:—Ç©sÙ*ÅÝ«\†â–‡-nÙEt»x1CEË#V´ì,ÄÝœ˜¡ŒåËXv“ÝVf¨]y´Ú•]$¸‡óTEˆýs¬)OŽ’q.e“j*¤%U£TkÍ„ý—kÇŒw­ð†?‰¹úÂXÝæ”wãJ_èæ$ÌLƒU˜aˆg~?D¨ ÆjmúÓIny¸Ì«C1yt.¼<Ÿ^hà~LÀj!FÏÅØÌµÂtÒpõ¦=I'(ÀG%`xžÐdÝÖu‰ãìE7Ÿ%8ïÉP„ïªÁ³]
$iP¯´,Þ¼|E‰ºoF“Éäë_ðš*|âb	ªÂË­
Ìõj›‘±¸^·M–áˆVë¨»+³|ºuWe9j)x:PÜQEåÓ¹ŸlŽï3±ú¦ÈÞÇP‘ÜèæÙé¬Ë«ú³/±òKX¢2EÔ”ç—!ã0dÐ÷¸ê(âX¶ÑÇfì,MŠŽË;pUêÊ}è&Ó¤‹"Í¨Kð§*døÅL«_EÝÕÕa…¬mOÉ}×uƒ‰ƒer—b“²&CÙ‹/@ˆ³ÙÂ]5e¡7,OÁ¯ImxÚŠw\òpHys<J'?!“ÀÝÒ=Ó.¿}ÝÝ¯	˜Ôj92&_ˆ¢-E–ä_Îð2"nc-
 ¶ÜOH{ô´'î½ŸØE!J€Âß4š
Ÿ\˜N—„Œ–¾%,]¼÷ßç•/8¦ˆbþ©•y[ò8©¢K`õy{S ÃÄlF—Öö`7›DGA?¯ÑáŸ: ¹cR…j±S2}¾¬ÉŸ’õ
¼fÇ[l–cÌ`¹bËU´Î¹áŽZVÉZVmÅˆm	^zëãn>6îðk†æ%-E«4‘ša¡¦ÓŸ£	öš&•³—µJâaG±ÐDï=ejC1Ù}U#~ª³JÍ|ÙYè¯¬];Û^`ò=˜¼Wú‡ ÏkU5#WV³œ³i+K¼•}8ÿÞ_c¢äÝH¨HÙ•'•7Öè¸´,Rw•éÔo?K"È±´	¨b–Ú|Ó“s-ÜÝŸ®ñ¶kv‡c±=ªÖ\¡¸3ý1þF˜%Wû«ŽiÕ	^ˆ«×†=“¨ÈOÅ_(àÈ¢ì¯­\‰¿å—l“Üåš8Ø´St3À8ºD Z×ƒì‰#´É¢vÝÐ…¦`)Çq–<¦„ G´®óxÃ-e­×ª{P¾Ï<è:èY÷8gQµut/–ÅÍ0Ó	ÛÑ…åG6ÃõÉ„­üöŒ}{»9ÅTL˜ó™^<´kemÑùâm û>ìRïs—J<~
;ÔwìNq+0€æžA£š0sRâË?vÅÌMH¹¶sÜÄL…» C&úOš>Å9¯S_Ž¶Ç];Ç)Ç\·®ÝyLmùüO¬‚7%œ"ÌÕZˆ¬]ÒSi5Çƒ"øwð.ù —ªÊëi¿ç!¬Æî·Õô£jþ·Ïtte=+©>ß)”–Ë¤Xb*ð‚& —›ÞT,¿T²ÀJôµ¿|†¶ç6Ã_0€wó÷½¹S *že¡ØýµÿuiˆÅ÷êÎT2t)ò§ëNkÐ­ùÜ…ÓoW÷§{ºá“8¶u-LÉz·hG)»+Þ}9¸÷êDœ~
åMÀ «ñÎ#œ‹p   0707010001f0cc000081a40000000000000000000000016a102a8900000f9b000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.advfs.conf.gz    ‹     í]Qo7~Ï¯ ‰q’jûÚ7	.wmqÁ5Íá’öŠÂKíRáÝå–äJVq?þf†ä.)Ù–ÔÈqÝnÐÎîrHÎ|3rfè§OùçÉSvÄ?H®æjÄìºŒË™ùHrÇÝqy÷ä'œìS-ëB\ÿüä+šôK7í'OpôWb½Rº¸èQóJÀ‹ñÿ —L®>-E×ÓÝb?L‹_Z©E±õ¢Ñj)Tµ¬çôò[^zSˆoKÛú{UÓY/„––×¹ï§|Æ^±…àE„`0_¡Ý{Óˆ\ÎdßÌEmsâ
ô`røâÃB?˜š1?/UÙÂ¿æZµ|ø½{ÉfJÝÊMuÙh!ªÆ^ò©ÒöA9Û1ð ÖvoŽÂ[Ær^²àV˜‹~´ì}?¹ª—BG#˜*]Õ‰`ÞÌ˜–YÅ²Ì,±w¨ßÿø¶’eÉ<ÓaTF#ŒÐKnaÂðÖ.ô6|™«ª‚±Á<…)¡Èy÷_Ðÿ(Ã	{¯@îDÚr=6"'«¦•¨-ugX¡ 0Ž¶i ) HVrcY¥
Ážs}2é§Ê²²$Ë€§Ž„°#vqµºÈ6A•!WÙìYn%ÌSÀèët’BªÕ¹`Sn¤™~7–nAs£áÙ£ƒð}Y‡¯ÅLÖÂ LºÏÀ[âO@\#¡É†1L"ñ|àW@§ÑRÁøÖLÐ·HAæb\Š¥(qbÐ_á¤@p„o"*5 jê3ã	ß’¬]«;ð´RV\êÕ§q'°?³²zo=çIè žÚ£àMj4La¼‚É	Ôo¶ÇzÑ¹íR`´Y˜ƒ„?­„yN, cNØøKöœIW\çekäRŒ€_s°"šƒQu¹>!¹ýõ6ëÿ}ÐëØÞsXå WzN¯ö”›®ÝÔY!öÍ5îÇ$Á¦i±½F‚=±n9›–*¿
æÉ!qÜ6´‘ÝòØŠü>"UÂÔ¬¬¸;ðŸcgÐÄ-Û€ã;š¦aIÃ!£`ÒÎ,âóˆÜTÁDÑð™I"Š[×Î^ÂªQK×ÏÛVÍžÄ.uSqówºb†&[švL%ÛÔ!³(Åõ–“½œç`ÜÐ®9Ž¡[0ƒm‚XÎ“±“Âw/Ù­‹ÙÒ<¬»us+ßê¨ÜÀû4}‹õø]4ÄwTËf±62ç¥ßñ˜›6@néYš»}
²0óËF{Ùñbp"½^w0U&×²!&®EÞ‚>ð™©ß™«‹|ŒKÕY7“Œ¹G`ØµVÚÀ€‚n‘ìB„—(º[$sˆ8ÉÎ¢ü­¢ŒH¬Ì6Ö¢ÃÅHd!üUÓagwd¥Œy›}Œ@=ÃäªšAžÇÑLÕC1UsˆüÚzX+ïI-#ÖþV­ŒHì+T-ïçP‰NÅLiqßîO,™¤98?'É#{?A"‡Jpð}îG%?ÖùÙÏAb\Ÿ£éåÇú>^ˆoð|îO)?ÒõI¥s·LqÀ¢¶/Îpòû§(ƒ'šÀç¶†VdÕœÀTÙå„u|iFlµù"Ž1Vãg€ Us^–]|¹ªz¾Ä“sü š»€ô#RÐëšÎßñ”¼5-ÐZc×ªcØ’@àÅyÇk!Jå–áðÉJ¯#{¸@ÒT09¯AÏ):‘Ä^Ú† c­-ÔªŽCËo¬Áolk6¼\ñ5F,@(ËêÏy–M{/,0€}ýÍ·¯øîÃˆà¸Z¨2Kè…1Nâñ†÷…öaã½Ã=ÈhØ›ë·=f‚çÓ&˜"“$YT¢Ài«m5ð£ÍAW «Š‹LUìi¿
ÃË(”†‘ ˜Å„ü¤'áK˜T!3Ñü,j’tsË¼ž‹b”L/íYÔ®ãŽîÁ¶€]\É?ÞTuÎ›!è(Êö>ÝâÏNÇÒ()j*‘²×”	?G‹	ƒfÒ†ÏIRI‚tDÖÀw ™ ¥ðÇ5×n†ƒ¢)ÕZpP4¤Ê%ðÄˆúÛŸ
¤eI+ ÅZƒÁLÝ²ÙVËù0Á	:`¤daÒ-÷½(úåXÖW!dªÅx=Æ@Yç+â¹¦8<=CS
¦!àIî˜æå£Cê©K¥âæ
\#³®s“úKð
x^°gµr+Ç³@ÍŽ M€€´i‰HBc€ÎeÙjŒ¤Ö÷£1£V*÷´µ]Ö îœÒ$/7%° ¿´ î–ªÒ¸˜íÕhÝGbÀ-æ˜ÝÎµÅ¤Gd{Æ—0P×ø†céÛù¯£æÏ0'|Fß`ÂþúöB€µ~Â”E7p·â£Ô’¼¦˜¼µªâÃÏ°^³Š^Ÿà„ý`Ä¬-7R)å,²mÕ°™,…Y+*C¨×ó3£à×Ë# ê¶$„ùå´¼’êr%ä|ñ;-<@U`uazÂ.Ýã²™ã’öŽùçU
7¥`@ú;%½yS,'K°µÄÝ	û‘—-p*ìJˆš’ÏNOO'ì_B×¢ŒðÒM–ÞS2C*- Æ—§wH6oÚË_Zeù Ö;Åº‘pÒœRØ0Õw¾0¼¦ý,è¼Ê)9Ì'¢h…n½Ë8Áuó³tÜeºzm$§ijT‰Ç"K„R2¢óÉ‚IÆœæ•°BOØ·½¡¡¹þNÖí5Ë©‡,Ÿ«X+€›ùRVÒ¦û Êg2’Ð¹1×ÜÉ¶š‚s ,¨d®• ÿb#ç³cˆàùy'lº²ìËÓÏþïºÀ¿)eœä%Œ3uO]‹óÍïíJ¹ïƒt &ôS;TÀ,Àj›AŽaÚœ‘
ƒõ€‘trTøs î”ŸžÁÿ'ìMšò¢OU‹«­´-¥u‚ŽÔ¦õ…[›uÌ·«ÜÖ¼K™l“©ÄPÅ51$~p'…0  Ö÷·Ž%tLÀãyå\	ÃÐa	 ]+DàÙùÝèp··í}œïÄì Èa*ñ\G]í ²wÂþo¤÷Ÿpãó5˜óz.Ø;ÎñÁéø¼)
Ü.ÌJT—dK‰jIŒü5Ýs|CjnöÑó^–  ¥×ì¹¬“sØ Ÿ8E×Úñ»Ùø­ûî
ö· ¾s<Aq»\w>‘cù‚šÅ{47&$ïÆÒvJ~Jv C©êO<´*ŒcµyÍf%Ÿ³ç§¨®g'ˆpFið‘?t8éãH?2îž¸w’(ßÑ)€úÉÈí«}n-•ÔYE{;L'F4F¤<	ž]|"¬øs|üK)ÂyÙ»wo#Bn¼ïÞÈÂÂÍÂZ+}…Ÿ¨	H5”„‘™vê6•_á,Â±­´#æŠsÎb”G¨¬ŸíÀ¸Yñ¦Áª²aa;
Äñ€—³ž«Þ¥
ÅxÉn –ZÔâ%ûb‡eduNˆW9Wâ©ãÛè¹+ :Ð)A¹ítJ–ƒWò»ðJþ‚úxß®	Qê{ö»õÖÐ1ûœf­ix›ñÃØŸÉ‚YÆ.nÔvø8CyÌƒ”Çø“ö[“_)Žjb>mMÌ^²Û#)t(„yÈB˜ý¥¸W*èPýò)«_öÞ®Ð¡äåáJ^öáÞiŸCËÃÔ¹ì”âþU.CqË§-nÙGtûx1CEËV´ì-Äýœ˜¡Œå–±ì'»>ÌP»ò`µ+ûHð æ±Š0û=æXSž%ã\É&ÕTH/JªF©Öš	û/×ŽÿîZáµ‹sõ…±ºÍ)ïÆ•¾Ð5A˜˜«0ÃÏü~ˆPAÕÚô§“ÜòpÃZ‡bò.è\zy>¾ÐÀý˜€ÕBŒž‹±˜k…é¤áê!L{’Ý½O•p€áy.@“u[×i$Ž³gÝ|žu–àuO†"|×žíR8 Iƒz¡eñêùJÔ}5šL&'Xÿ‚w‡áKPÞ8V`®WÛŒŒÅõºm²G´ZGÝÝcæ£ˆÐ­»¿ÌQKÁÓâŽ**ŸÎýè`szŸ‰Õ7Eö>„òˆäš=Ï>ÀHg]^Ô¯˜•x‰•_jÀ•)¢¦<¿
‡!ƒ¾ÇUG)Ç²>®0ciRt\Þ«RWîC×Ë&õXùhF]‚?U	$ Ã/fZý*êþ«®+dm{Jî»®L,“›”5Ê^|é j@œíÌîÞ>à˜¨)½¡`y
~MjÃÓV¼ã’ï„û@Ê«ÓQ:ùxdø	™î–î™vùíëîÒSÐÀ¤VË‘¹4ùBm)²$ÿ²pv€—¡pEnQ µÍà~BÚ# §=qïýÄ.QrØ þú×Tøä‚Àtº$dÌ°ô-aézï½ÿ>¯|Á1EóO­ÌÛ’ÇI]«ÏÛ›&f3ºI¸»Ù$:
ú¹E„î€äþI.¨ÅÎÉôù²6$NÖ+tò’î°YŽ1ƒåŠ-WÑ:ä†‹ƒY%kYµ#¶%xé­K@Ž»ŽÚ¸»Ù·ÍsZŠVh#5ÃBM§?'ì5M*=eÏk•$ÄÃ8Nb¡;‰Þ%zÊÔ,†b²ûªFüXg9”šù²!²Ð%Þ#¼v¶½Àä{0xÙ)ô÷Až[U5#WV³œ³i+K¼˜–½ý½¿[6DÉ»‘P‘²+O*o¬ÑqiX¤î*Ó©ß~–DciPÅ,µù¦'çZ¸Y]ã]wÇbT­¹Bqgúcü0K®ö÷OÓª¼W¯{&Q‘žŠ=¾PÀ‘EÙo­\‰¿å—l“\°š8Ø´St3À8ºD Z×ƒì‰#´É¢vÝÐ…¦`)Çq–<¦„ G´®ógxí0e­×ª{P¾/<èŽîY÷8gQµut?2–ÅÍ0Ó	ÛÑ…åG6ÃõÉ„­üö‚}{»9ÅTL˜ó…^<´­²¶è|q‡6€}v©÷¹K%?†j
†;v§¸@sÏ QÍ^˜¹)ñåûbæ&¤lí71Aá.È‰þ“¦OÆ@1FÎëÔ—£íq×ÎqÊ1×­kwS[>ÿ«àM	§Ân¨fÐBdè’žJ«9Á¿ƒwAÈA¸TÍP^OCØøåa5v¿B¨Pó¿¨£+ëYIõùN¡´\&ÅS4¹¤Øô¦bù¥’V¢¯ýå3´=ß°þ‚ü…	}oîÈŸŠgY(véGEbñ½º3•]Š<äéºÓtk>wGá4ÇÛÕý1GÀoø$Žmm…I"YïíH#ewÅ;¢/·à^Ý‚ˆÓÁ£¼	d5þîû³r   0707010001f061000081a40000000000000000000000016a102a8a00000205000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.cluster.switch.brocade.conf.gz    ‹     Ý•MkÜ0†ïþ{Iaë%×¦Î­…^zÊ-V–Æ±ˆ-¹9»†þøÎÈ1M—jh‰.«•F¯æ}F’w»-[¶ƒ›ÈÑÉF]ï!B¼VÿBnÛì¶e—ÝfwÁ:ƒçoÙM2]Ì¶³L<âpòÁ|xN„Gxüý†M@‘ö*\6ú¬B™	ø½·ÍËƒ•ê›øœÛWïpCà´1Œ“Ô¡¶•Õpè0XÌ±iŽ¸«:kˆ¢ôƒ}RÅªõ„òÓø°.Œär¹‘˜²üýAè\ ×b¬½ù_ÙÕ¯F •3Ö0#š"6#üu~a;:_¡ÔÞ9Ôqæ=á”µÀÝÚ÷T¢OW×È!’¨N¥•¥u*©Ó&EÊàB	œjñ-^ñõ{¨peó>€í@è]_*p>
âýŠq¥š¦Túq‘aIëÝ(A}UÙsÂþ3låèt·C®}è.q—zÉÐÛ|8Fo/^Š=à¹Öh@èÌR°ÝrjtZ¢Wì•Ðó$<ö¡í)B‰óÓs<Ò@Ûã1•„:Å*|SÜJ«VO8-˜3ä%\š>ñÃ É†ù`È]›7š<£Iu^ìÐœÒa©å…ZÏ²ÿ¶Öw¡ß¾Ô±?}–À‚ï[k]–ýu+ºaù     0707010001f041000081a40000000000000000000000016a102a8a000001dd000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.hb.multicast.conf.gz  ‹     Í–ÁnÔ0†ïyŠ‘öZ"º€´[‰OÐâ0±'¯ÆÎ¶ûöÌ8-	*ˆÒúbÅžŒÿùüÛÉf³f«6°bÓt]sùÔŸÁ”ÏJ·®ºuÙU_ºfÃ.XzüZÝ”¢wSÙU¥ú¿Ñé!²ý8É@kY&^­Ø”S2±ÇÆÓÏ•îy `ú>8¦IÂgô©ÌXjQÔNÚ¶Û·õ›zû¡~ýœ’@Ô1©'ãZgàöˆ)5J¢d$â¾›ïºÖI)AŽ(XíQ:ïR¦ 1Ôòî	´ùÂØ\Èí…bÓºæÑ«LâAnµ¾»e9åvø¬ S‚ì‹.pì#çåxýZÚ_Ã01‰g	”ÃžøH­¹ !4„hiîJÿÕ”%|÷$gÙŽÄGôŠòÝ9­˜*»~oH)Ê$Àò#äŽ	m‹ÒôìÒ1t'ZêF¤øÉûQ_}IõÕŠ%-ôÈrQº^H‡½Ú0ËTG2ÚæÂpÜÍµ,Ìî@q¸ØsýÜhÉã	’zAÊ³×¯“!w$;¡„–ãp<öÉ5@³ÿ
Y.9‘T6¡si“~•]xb«‚8ùP]À	     0707010001f085000081a40000000000000000000000016a102a8a000001c1000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.hb.disk.conf.gz  ‹     Í”1oÛ0…wýŠ<¦5!KèR C;e+:Pä“uMª<ÊŽûë{¤jG€k @T$$ƒäÞ}ï|«Õ’«YÑ‚«¤ëÛw”È±<¾<Ý²ê–e×|ïÛUâàðô£¹¯EojÙMS¤?âxˆÉ}xVà°×ý÷®BHlLëqþÐCQ~Žœà.:3úü¬ì[8¥©h¤éPXîØÒGÚ" ±­¥i±zã¡G)Š-(G:$ÎúC÷z˜ÔÂd)Û&8•bu)îÖô%Ón”L-fö:8¶&Ã•€’Áìb QP"„„¤£wI·_é¦<$²^Ó!Í²…è°V¡÷ªM-éb¼æÜÿª¶|6^þîËÝ?›BdcØ#Í‚Ý˜Læ.;­ ó”Ÿ”y±H”{6$N*ÀóÕj¹Â°Ð–7
ð“÷“¾õKHz–|A±¦¥Á¤Ì–%¶§æ-íŸKÿW†ÓÅÍ\Ë’™wˆc~£,oÿO7:xsÔÿw°ÓìðF‡C‚ïuœQÖéA¦ÔÆíbšÏýœ°Jª&ô,Ó5}ou²Uþ°U´Žæ7Wa*F     0707010001f095000081a40000000000000000000000016a102a8a00000edf000000e600010003ffffffffffffffff0000003600000000root/usr/share/doc/opensvc/template.node.node.conf.gz ‹     í]msÛ6þž_™N§½Öf$9Nk·ÉœÇI;¹¹Ô9Ûé}èÜÈ	I¨I‚€Väé¿Ý(’HÛ‘šS¥3LD‚ðÙ,vè_lòÏ“/Øÿ ¹\%bƒä6;ºÍb÷ä7üØÿ<Áq^‹éDéäxÞOS5I?ž\h´¿Á?ˆ‰UÁ©˜õúOÀ'Zü·”Z$ËO1äejçív"úïé÷¬û]/ê>§½g¬{„¿¾§ŸÝçU‚Ág
íÞ7…ˆåPÆì%‰\hc³Xå7B]¤ÒXB	z71Ü¿ºÉÔÅ2ÑlªøÚ0÷9P²ÆÁgX¬·RåØrÀãk‘'Txl™Ì­ŠØÅX•iÂ>ßXŒ	³ŠeÜÆc6U¥fFZãƒÇ^2ë‡EN±÷¥‰÷ÆÛùÍ¶2ý•‹{s4`ÜIî¾*9Ö ÄEiÆø720Vi*b«4ƒgø€ÞØØ¦@<´Ld>r/”ZÏÓ)3ððì†§¥p¬©€ž¼Ó*)cì±	öT¦´¶ydÈŸ9ÌÜ=Ì€š)TN8Îpf‰ÒÐxÊ
­Æ”î¨Ó;lQó<?6ø. DÍr=hàõÜÜÄÎÜøŽÈ0%j’§Š'Ð¨,FþaØD¦i Àc•øüÍ7C˜ú,CI§ŽŽ¯'ÇWZª¸]1™Ê! æHì±ÉX‚’†i«,sÃ˜H;fx<Há=QÇXz•XzÔÄÐ™>>6ž"³ÌÔX‘1‘ã(;®^üzÊNg6è€Å`ßýD¢ñà)°­ÖX<H@(âj¢A.…ÀþH­rà†%FÏí…€Ñ2Âv	ï¾”Žq-ˆ¥âCü“hè ™‹„DeJ€Œ¼^µÊbvò0·‡sÛú‚­-ÌñÓ§³{‘—é$çNaðCtfÒ@%s6Þ0Ï-'$5¶óULÄ÷$ø g"bÿ’œŠTÁ-il™‡õRã¥U8[b×Sï€À\f˜´¬4ÕDè¦Ôcò¡£ù¾=u×…¡Ã;dÂ¿{·h8¹ý¬DÃ«ê&DÃ“úô¢á:^?š»DÃ³üNÑ°2ª´ŸB6.uùPÑ8\g•’”šüÙ¥9&ãdVf¿Ü	Ö Æë²€ŒØ[ßÚ¯*‘g½Î‡<¤À¢Ãfüó÷­Ú-(´ ¨œáÙ¢‚åìçÓ×3½5{5'€¾ÊTW˜,Op·ç]Åò¼zÑAô¬‘uEÙW]˜GÁ¹jËh1ÂÙbS‹•b n¯ÖDîP}ªJl¨CøÈÝÚy8€;ˆzìçñm|ÌÏé¿ü ð$-Æœ½þõ°B;Ö‚';Í~À3€¸q"JýLæýöq&ÄmÅøygãŽ\ È=« `a'\÷žó9.2t3È—Psß#
ü„¥†¸€Gïc5~ wÜº¿j®„-‹Ç<ÕÝŽ8nen±K#ò¼Gy#ªwjã`oýCsLX=ùawAàa»8”EÂ­ØzèþùŠOA@mâƒ­±¹Î=FŽøÒêh‰7Ý7PS–¡…;ÎtþDÖø¥-¸hõ²g¾,pÖÌÂuu#4\bÉ2;ðY#?`‘\¤WÛÊ‰ Á´` ³W…!8“BiË,ø
¿äßs6‡ÂÊ9L*Ã¡ÀÅ,¢uÂë¥–U uÆž¯0RæìC–ê"öãr¡vŠ‘\c"°ôt”bÜ@°T-+s?‘DŽƒsŽ ï6öñªÑ¶r¾Š”|¬”J¯ëàà"%gÐa<k–áPƒßÆ©¸‹’>†¦<ÌOh[‹|,.ä#'0Šñ›ðàØ HÅðp=L?£°ÇÄL´r¸48/Hè…C¦¥±ÞÞTáÿ—­ˆø3ÜxQñùÜ­Ñ4­Ul¯ù^ž¥HàN¶ú¯z¬)ÌU6vÓv)éÒ¤‰.;iõ´®Þ˜¬,sÃ”Z¥+ÙW	ÛGf®D§Ê”ZìV×Áâo†J[\g‹Q./.bâÀlItÛŒ§púúü’ýÁ^½þ¯çïàúÓÙ9\ßü‚÷ß¾¿€ë»³S¼žŸÐõ]ÿ×wçîŠ·þõþäŸð×ù)¾wþß¸¸ü®—o‘*Œ®ïO.ëÑkP†|	 ÎÉXB¡‚€R´*GcÐ«ãÖœºÆôó?èw C!­ü´­Þ¬:¡Úº¯%ÞÂo5Ò–äïþÍ‹VÉ  bª¸aì]a½¢¤
G@û;†“é Å*Ñ†û ”)ÒÞa¿>ö–€ÿ©ž‚¦¤mÈÇÒNw¨¯:âˆUx\KÓŠ·*sçì _r% >ÔmS¥vV}€˜vì¶Á­y|½C{}´G ûüY¯mEq;´×Ep¤˜s+Ú·²Ø½>Ø #z‡‡îwMhãŠÝŠã—ý¨‚è0t•|F9¹“Ùm¬ÅòøçKåµ`WWÝì c®®ö\±,?VRÏ—˜©Å£,@j`q¤á‰Åòý döµOåàæ
ˆèª¾ûo´²
¸å‘¯ýšÀÀXÏs˜™cú*„ÐÅf–)R†×jäÂŽaßõýˆ†ëD$?œ}y˜$LÌ	Ó<bÃ‹„µ–·2ãÒb±»CÄNŒC—»:u"-:ÜE}1c-€ó´úÐ‰Ê¿¢©¿¨<LZ«¥œ89+¿ÂáË0XpÍ3a¡½™mçáÉï¥ñ‘FSð<àÿŒ›˜"õkÒ«ÖlhÆórÈc[êGX÷ºN|)|%G²e½•ñ}d ÓGšJ^U½q‚•ž`vªt¥ºÝ!WÌ”€Džö(T…PáÍTB¨Ò€ž‚`ÇYr|söF¸¹é% ZÑc?º úË+
½‚½1ž_ó>[3Ì™Èúž_ïêlî­
bmU6Ôhjoa¢‘·bÃ˜"\¸è:|žÚp5©²;q}´„X«¸Ê¼Ïo¸Lû™ØÚUVïË
ìePôS‚”£ ”ó\¨‚zŒÇ¸¸ª,dCÐPp¿¼^3áÅöÎˆŸ`„â~ðRË4¾&€wå¶‹jïKmÓë4f$T,ãfPO„/Öj•Ö*š%¸µR×“Ú°Üâ.-•€óÄd˜§Å	sîaCŠÇPƒŠ¯ýö*Àƒ²J5lú\?¾]ÔkI¦Ç÷9EÝÞÁ³ì ç|§×‹è9TîƒŸ†7»J‰ =,m;íèœ„<Q»(Àv•»C Åõ¨ïÂ¬ÛŠ n¸]§æÄrÍþðT€OF°Òð‘ Ã¬€y84pç!àÜþfUí1Äw]]ë?Àš:iZÃ½mÅøG9À%/?út‡ØÈei, Äbµ6…Œáónp-ÖBãcUj+”ˆ ¸8˜@±†|ºåAøÃOƒP~IÌ¡`½¾©jõ)<K›df5Nó}µ9ôA¸_]ù(t@î„Âà. Š£YáŠ ]Ð†î2Òø€wÄÞ›’vÒªA7Ú»0e8	X*P,‹””u,€Ú@p·õ—ŠÇAr
FÛÜ¡Åï
Ö.…dÎÑ§Næu²¥rÜ]†ŽCÁ¾ãyCDÒ‹¿s5çƒ	x'ê„îÊH”½ùÆ,´syˆ9‡å*ö*Ìë6MUIÂÉ‡ í”¡áÎ-N[…bá6t‘Ø/ìòI(JfHXˆ¤ 5xÂŠ'æÁ‡˜áµÈ°Ó-JY/šK"zÔiÑB}uÑõÃª‰?Þ šRù{mGOP(ñpÑ­,
Ì8q= Ã¼ë%ež§Â3>æÓP@kÁ'®¹ýFtö¾ê!Ž¨ÏŸ‘zý0ÀXðîœR‹ã€Áçgg—³ßî¯þÈÚýýn§ÁgF£[ÖØ¢ë[,qIp¶ÿruë°­g‚	û¨½0¨õžÀwÚÚ@®öÛ”¹2ûp·>&]dA£ìE§ÕÏZ<>«éÓ¼0íC›ýÃN°úËaƒgGØ N«• <‡Q±¦!‡ž4ß?jÇªðØnÉ(”¯Á?~ú´*¼Ç»,	Š6ËLþŒ¬G°Á¬Á,,ì†¹—hÖüÚ>'²ÑÇk~X‹®7©z­;T¾’0|8—²yÓû	šÓêÏ}–Z¤0Dy·HÑL³,G+Eö~sIh£+]·t¡Mt³n2v+-µ3Ô0•MwÙb³†y©Ò’éþ(ËÝn¸ëv¡œ)iÒ=6Ni”­giådècÕÉ)á'¢«]ºý†©Iªñãµ*&Ú˜fÌx¡<K›i»:ª\þ?¬)À™wÑy#S1šo`™Ž~Áé*\fâr¥òã¼Žä˜}Ã@[¿q]‡¹‹pX4
é{¡óÆÀÅ×Mäò8nÁ¦Œ·û;KÍ¢(t!ßÝGŠ02i1¿QuÇÎÉ„ÎÉÂ¡æb2K¦`·Ö Æ/ËdÍˆ¸ë,ä®†tÒ
GLäe·Ý¸Áf÷‡Âno®éOÚÅl‚-Æ{³ÆÆ¤4¬¸¹-Žé@}`ú•óÑÂË;¶ƒH¾KO…	†¬1Œ¨ƒ	ªÃÆóÑM±;¦{ˆÅÂ!Ý- ¦Òö]	à¶BknÆë$Zˆ ûÃÇ»j0ŸÒ¦ŒlpÇÊŸ½§·Ó"¦bÐêˆ½ò“NuH5ª¹ÁH¾¢‘Ñ!@Î»Æ85,ƒÿ—u muF·‘ka½BMR@S­‹áU="ƒçl;„X=gt:ÍnÏèš“éI´ëo~¹l…Ü”žC±} {,i[úûw°ßîf`°·€×·^óiTe)“ÇX¿…žÈâµÀ\Ÿº‘É<ÂQÍz¸C‹f‡õ¡‡ØTµYÿö/,i   0707010001f10f000081a40000000000000000000000016a102a8a00001124000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.sync.symclone.conf.gz ‹     í]ßsÛ6~Ï_I¦“dNVd§íƒšdš»¶s™kš›Kr}ètLˆ„$ŒI‚%HËêÜ»€d[¢lÉªZúÁqHbì÷-~,±Ë'Ovùóè	ÛáŠÓË<°jYø3‹S•‹ûˆÛmëv«»G¿`gŸ”2OÄÕ¯¾¡N¿nºýèvàB,ªLÆm;b•k©+‘Wpûd‡?¨.«‚ORÑÔ÷©¬Þ(Åoµ,EÛxªéNQªK©¥Êe>w1åuZµmwÂd>¥¬xÛŠRÁ§ì›ž¸VÝ¥¹¯Ë©Œá™™È¡lŒ&.EéÉŸ(’rRÔ®c¸õY6VE5ŽNZÍEÐ„–_±Ê2ž'z¿i,¨Á·àPpYêƒBànlÀOH¬="‚êõÿ<—•ÐÅ‰/y%zŠ©)<s)c¡Y½Òeüfü*ÑÕ›(¢ë‰Q0«”gÆI)/[ÈjÎª¹Ô ­ê2„œyþ5þþõhŒ¿Oéï3úûå­8–:»ìi;c²j‹˜Ê¡?q) Yƒ¹±Áz\‘óJfBÕÇ7–½ö©ý¤S†êÏ¯dVglÁeÅPqlªJ°Âh «ôÇss‰ô›0]9dVua/nEHWªìÚ@VÁ!BÎ×ó—L–FÇ¸ÈSý»¦l¸/JœYP¿ ŸLT¥¼b¼,ù’Í•® }æMf}Vñr&*’±³’™O
%s4£É’/ãˆ®F„ M•º£Iªâ¨ô¼€ÊÏ51íµ·nñe p)‹
D\‰¸® Ÿi%Jg,™‡Ó8>á1š €âz1siÈ¾/Kð!xeY£Xbo"x· ³œ`·eÕCyW(} I•‘îÝ`$1[C¿òd²ì‘Ü­Qúºî¨•±®ªèñÜeªb†©Š­ð[æñyRöîÚ&­^ïl¶üÖXæ
:Ò£¹4I³÷Â“$l¨Ý«ô˜îS«Û{¡jeÜWø§‡u?°Â?÷EþÙÔºHxÕÛê>@5ª½¨FÄ6 ÖyïKØ¨žjï
ª'¢+¨¥è½CÛ":Sœàöìò‘ÙÍÞ9t?$wìrˆl‹`ïÚIÞ×9´‚ÏV°ö®¡Ùå}}Cmàë=C{²È{¸†<\¶…²wíÌ;{†l¶´÷íÒ{8†Vð¹¬½[h¨ÞÑ/¢³-¦½Wh˜ÞÝ-¢³¦½Sh˜ÞÓ+¢³Slðî¾ýd×]0Ü~ÿi¡Y­è¹Î¡mø`*ÍY/Qâ¡-:Ðå€Ô¶˜Ëxî*5Ëñ1`€ÊObž¦L×E¡ÊŠB oÎ/éŒ<P<­Œç[¹“ï‰‚Z—ŒwrUAûjµÄª<?:Æ“ö@9Á	˜ßx"5¢rt6ÛÜ¹ômWl;=n›Éþ×VÐépò[§È–l!â‰`r–ÓQK ÓQ…ÜuA”ÑóºJÔ­¶©â]¥édf­‘6<]ð%8G²€ (Ê_ð(2öQTMðÝ÷?¼ýüã§Ñq1W©OW'rmúíu÷ëm$Ô\A{×xN"jöêVÜŽ=zÈ>Í…vC‘ö ¸˜HÐ4‘µªKÐGƒ­@—%ãHeìUÁ«ù×<<âkQù’è0¤íôÐ=	jåk¯xÂ•¬úÏy>É è^X³ÈMÅ>Ì…ë ›˜{LÌ0æ;yÌ‹?™•Ýá$óNŒYƒdUP:Šðocca`Z„ÙI‚äJ\UÈ]|Š¸F3Y¹Ç	© QÚ«á9°L R|ð¤M–f†ƒ'¢HÕc;CC©\‚N¨¾MôÉ ­Š¬²'ÐNFkpR¯ÙU)g3à'êÀ %cà“fºo¡h§c™_h¤ž«:MpÐ·ƒ{à½„ÆÓ€21çï9æD+r	TC*OŽh§&ëXáC‡ë%¸:OØÓ\™™ãé€	Ç¦»fÐ*A mš"B†P r™ÖÐFœ»ö½6ã,UÔîI]©^r*ð×œÓ0üVƒ¹˜ªR­ˆ×¤zó>
­”b†Á4>áLY™kR{Ê/¡¡¦ èÛÒ–³O{ÅŸ*Ð ¬m!û«Û‚ s;ü5ŽfÆ·>/FÇS¯+•ñJÆ´uÃ*®zl‡À5­SäÁRRy“ll*S¡—º™&Öˆ+žÁú˜L¥Ñ×ë°ê¶¨¼Ùù$½ê|!älþ'{‘º…©À&òD·‚mW1Ã)íló¯7šnJa ,êï¸eï>@Sà	†R’v‡ì¿<­¡Q-„ÈÙéˆ0<FCö/Qæ"dlgé>Åü…h1¾­A6.êóßjUñÖµ°zÐáê6¤1î$)ðw¾&x¹¤ý,Ø¼Šq•m&è.ëÙ¬T0Â¼ùE¸¶í‚u¸±FZ4M´JÑ-r‰t@IZ4k²@ 'ƒz3Q‰rÈ~hŠ×?Ê¼¾b1•€Á!ŠNNa©˜ÁR 7ó©ÌdîtœJKbçJ{LAZNÖÙÄze2.•@ÿd%V¸QÜî¤‚MW}5úâ[¸×4þ‚Ð€º„v†ËSSâlõùj¡ÌóÎ Rû©Ž& ç0jëÞv1´™AÊ5ÖF’ç(±~ ÆK—OGg_2£ÿ!{—3îïGŠúd¢jœmeUS'ØH®kë(¼¶Y‡:`EJwau)ƒm2Õ|©8'Úxu²g˜¡AÎ¬%îoJÈMáÈáóya–
šQºv4¥§gëÙ×ó®óØû5ßÀîÆAÀa"Ñ¯ŒÎâ…Qk Õ?à7®Ë|64pm³ ”0œç3ÁÆl48œá…ÑÉY)e48Yf&²sK{D·I´ü=Üs|Of®»Øy‹%  Ê%{&óÀäçÆÐ?ÔÕÉ‡éÉ{óÜìoÁ|gèA1»\ãŸˆ¹Æ‘Âßcà€Qp­A•ÎÊ´‘èg;”ÊÎÑãQª´çÈ.f›·lšò{6Bs=}Ž„p>J—¬CÒðÐÇ	 @ßwOÜ.’`.šZ9	H>0û*#ŸW°3¯¯poWg†ž(KD¢gó~ÂÍø·ïþ¥†$Î_öáÃ{Oi¤%ï‡÷Ž²0qºÆA17×‚ú¨liû f¨†oJ\Ët=1›Êo°Îm++˜¡[ÁN}–{¬®Ÿnà¸^ð¢9LOñ]P¼œµZµK*—Ï"Øø¨y%^³/7ŒL=Vû[„X“#/ºÿ^ë½w…m%ˆÛÆEÉe¿*ùC¬Jþ†ö¸ï¥	Ijk¶»õZ“›}F½.©y«ïýõLäF‘K‰ƒµkœ>³ÊA2«XOû­‡_¶É«Ò§SyØt*°ë/ÓçP9d•î(vŠ’é§<dâ”ŽàmŠé³¥"[J7ð:EÄô)R•"¥;ˆâ`ú¼(‡Ì‹ÒÊNÑ/}2”Ã%CÙ
Ê1/}”Ãe@éŽd—H—>íÉáÒžtB²s|KŸëä0¹N6¢Ø=ÓIŸàäaœt®‹»¦ÏjrÀ¬&Aìæ­éS™<`*“nØmtÖôùK¿¤vÝ|5}Ò’%-éŒaWMŸ©ä€™J:#ÙÍSÓ§'9Xz’mÜì¨és’,'Ig ;ùiúD$KDÒÈ-Ü4Ç
á®¿Œ·Ë€y
z¤ÈªY„» ¤…‰šQ¸3ÑCö3/2þÝ”’Y‘JL¼ sgYÇDeò˜`
ïOc¸(àúì±‚
«¥nšñŠ[þ´ÌYûiL÷Ü¹ÅóøÖÊûë¾†‰1lÒ üÈ„!c–\Öy«æìiÓŸ§ÍHð¶CÇµ¯
<¨Gg;ƒ˜¶W¥LÞ<{EQ×oÃáð9&3y75©:ÌÁP•É
ôàñºè
}uE Ø“UéÄew$ªqóñA’§!Åš”866ÿèh³×OÞtLû“ËuáÒ&,ÍG7·£Ë«ü}‘P»©F¢4dÔ„Ç.|Ô¥ChyÕHrÂ1G{HtÊÆa„»Ÿ«g¥&wm2H®CÇX‹A“­R>$Ã'¦¥ú]äíSMR‚o%™çšj0
Ô^6Ü¤X—ÃÄæ@ðC×Ùœk›,ä¡{§ È‡ü%™KñFK¶nOÅ¾ÂÎû-ÃGhHàfêž–&YÅZÈÜû
¡K¼cÄœëx.’:QL›˜q€§ž Ì1x¤­Fj¢-ZÙCsßvì<)_F¶ez%½-A ;MD9†ËÚ’0u}´Î6IÀœc¼/W2®SîGÈ4ÑÈ6sr˜˜N1m—Gv½*tàìóš, ÿÌÉü#dÌ	evFCŸÍQ„âÏš/sÿúhÃ˜eÓ\?–ú1‡e2§o¦’Ú¾´£‹cj3Í³šg4-æPÜgj„Y·Œý<b­a„ðˆ=ËƒoLc;žû D×AïläO»Ûq{ÓuÚzÝÞz1Dëáùjº(;/¡å¿s“­ÅÖ C†ÁæE­Ëîü"Qñ¨<×—ñ‹f´ŠšP%w	…Wü*`Îè•Ûï‡7Ï½f¿<áÄOF§ðûÛ¯Oq:|<`Ggtõ¥½ZÕ°Ž«óÇ¿ÞÊl_ÒgŒÚWÊ±ûn¢\>)›ˆfî´„Î-ÍœŸ`†˜R`b8Ñtx^K30¹s.glRK0öñíO¸è¹ÐM(LÓÊDhr¥7&â1±C˜‰Ò¤Ÿ¤zÛ^’@Žù‹@*†¢ÎVWø¦„áµ)¼!±UÿJx›ÔT&¤Yøü`(lNùìjÄ­NMRFØK‹Œ¶f!ì~ÖP#±¿¶¢	Öáv)§ƒW»"†6õ—Ÿ0iš×"´ÞsØ“FhóB«ei£p={â§ÂÀ¸/\)Ã°ü84¥Ô¹ÊO,©ˆßc«‚LU±t÷“UÛ4X¸žÆÜWSgÄr”Õ5]bË¦˜…r8dëÙ³ï`o[­v14íú<¶Íó›v-w•÷n}ƒ5À¼ß{/öé½ ƒç"$Ã¯n{Òì™4ªèÄ™Û˜âgøíÊ™›˜rÍ£°Ê
ë(CCô_4FÚ'ŠÖr–‡k9r›4åŒ¦ŒrÍ¼¶öõmP&°–»è-r¿Ù(ú(Æòë´XgöXPO¡ýSÔ|4ò)±‰>¿z8—àn?èpš¼œ?„[ð<“y½²ÅúœKrÒ›[´{2.{b•ñÈñ‰Â¦”Ü<=xÌÄzÖÃl«¹ÃiãìeÈ¥äÿ°½w©£qzÆB—Ì× <YKÂxQ¤°a²…Í{ˆ¶~)ã³RüàƒÍŠaJ&C®ÿòµqÉë Vç %¦!Ì³=ÌLé=â8µm®arIiØrûåHÑG3Î…´Ø4ÒÙ{=öO#«ê£!Ò*5:PÉüÓ3iÿLBçÞÉ'Æ&™‹=öÏ#£é£áÑ
1Öð¨â³¿0onJO‡þª€OŽ:¨*@¾œÈªäx	þï^S:@$“ØÍ}Œƒš,¯[·þBVsßÒ`‰®2ÑÊ•ù4¥¯yÐKy¤VüœˆRÓßôiK%ünÅÒ~ªŠÎ¬Ý~Ž¤T™W›9fd]F‘û4ÆkóŽ2üÆ•«ÕìPèuì²ú™M¾yaÎZRogå1±>Þó¹þáékçp=¬»§b¯;Pë=ÙÏe{Ë<MÃ\v1hÔø?ÒßÅLè£  0707010001f0ea000081a40000000000000000000000016a102a8900001332000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext4.conf.gz   ‹     í]msÛ6¶þž_™NÇö\I+ç6ÓY7Él¶M÷vn³ÞY§ÝŽ‘ „5I°)Y}ùï{^@,Knä$Þ2ÓÆIç<çÀ9à'ŸóÏ“OÄÿ ¹ÄDµ.”P7ÕgïNî¸½;.ïžüØOJÇêæÇ'_Ð _Ð°Ÿ<Á®_«õÊ”ñE×ƒh¡¢ëI©d·‡GüƒŒ²‘)ä,Uíû¾–©Ux§T?ÕºTñö¢4KmµÉu>¿îÄ*‘uZuoïè|¡J]É<r¯J•LÄK±àaQ?”€«’ïÛBE:Ñ<3W9´ð±Hæ±Že¥¬{ÇÛ²Vâ×î=‘É—ªôz03^•o¡6‚[ß$ÂªJTFL§˜NEµP¹ˆkËFnM]FJØJVµeàðx¢SåÌ®m¥2ÂáÇ€”¶¢¶*+$‰­°Mó$ÜÌLWp†‚·=jL¾ytüR>ÕX[U<oðOà°/-`T•Š*‡0‰G-O,Ž :`±±ÚxÓF÷/ÄI¥3eêÊ£ö2câ®ÿF¬ÃDü’åÕo'4+“¦f…œŒ$€K§ºZ_ÃŒKR²£†Ó£ÄŽ:˜ã §ÓÛ:J,ñ(ÝÂf;4³Ô-¾irÀV"~8žœþøìdà:±ÙŒoèèçŸþ9w?¥k5×ÓºTâ?|:‘*ü@•á“?¦ðhbŠ
ÁYI[è^i¦Ó³ ò‹nÜ`Ç;A¬–ïÃ JÝj š÷Ðÿ¿ç¦þž#4f©µ¦èÈAX?UËäÜÝX[!CuóžS@j9_é$Q¥Ê«†2ªÙL‘Á¨@ Í=áëšŽÚ»— ÄÕò/x5—‚Ð®Aô7(o¸‚NŒÙ%æyiêâƒ
ú÷Xú‡”4qø
&Ç.LÆ(³‚‡Q«‘ßÊÆU¦\ÄkKè‹/¢:Câ»Îf&…ßSf¤„ü†hgv‰%#÷ÚçdBê"
£áo0¹¥ju¨õy¬[Ä_”Ï w&²‡ý‹+ÐÛ°‘É¬ÌÍ¤(•ÊŠj"g¦||<},Aã@\*¿úþK±Òi*Ó¡WVc©Ê¥¤¨lÖHHÏ5OF&Ë0r‚ BÿÆL¶7ñý2‰+“)&]Ér®|ç£³"U8$z„B0 ý¨‹š‚:B”J[ b%N•½9uÃ	ãOd	¸+0’DBUqq½º˜n‚jÚøE	‘'Œ/i0HUÛHz&­foÚô ™¬·
žU@†hkIÒ[®¯ñ¹T#ñ
Ât7£)9Ažˆ°¹‰`Šà%)á¿A2çŸþùNÁ”p­—Œ“ÌW*Ñ9„€²{x ¯0”ô­pãb~;ÿñ„òV^¢Ôú·,=‹ ¦j©R¼/f)Ü2•ÄÐsv-iúÓ<Ëâ¥Vwz45Üí~Õá=9
ôîÊˆX PÂ	ìð™8Ýœöª›fŸ0… ¿æ€±Rb §ë3’ÛÿîÂüè)×¾™–àœà­tgÑ×W Ä^³çh1*^ßhžFy$ñ¥Úvm€¶Š½ÏÒxqÙ ¬†uAþ‡Pí.ˆ•´ø¼G*…¡U0‰«=	ü—ø2hÂÞÖàª¼()Mž;†Œ‚A³ÒàuÜÌÀ@iž6
D±ÓåuÐ8;Zèöv9»ŽÄ>7gsYL¬þYõµSÂŸ?£·bVëÔñY†Ã©™ëœØÒ¤0ÇCAjë›B *˜Ìu×àôß¸Þ¼¼Á¿G£Ñ¿$OèFð”çó¡Iu#çãO›i§×7”
bº¢vÅ‚C®è&­gùK‡ÍM\Ê+aiÆªœ×²J»Î£!­ão­.ÚMbu®‰Ð5—³5*†_ª=¦Þ[Ù{tð{vL;¿iÆãº$+·=5“7:«3°ZÀwd¹tÉË¢€Å¡ Ò*LÄ¿pIRÝ8ØÁª6›q)SÍKƒøáw ³’e>€9¶Ž"Óó¾ ¶èÞ^~)t²Ðµi©paà­OÎ€ˆg»ð ÄËÞyÒF†<èò½à…(8ÀBù™Ç×¥
2Dàê*[bTˆz@cÿ>hîøkUk€y‘Ä•\ÔµT­rt)(Bi$lHé]wÚP¶à·Xó‘
ªi²%§~|H+j©º	ä÷*Ž52
œ¶ãºBZú‰RØðéÌÞfÁÄŸ.]¡{ÃùAãC™Òf
 €÷WÉ€a°»!¯û¯B~4â{½©A:È0!·„Ék•Ž™wrü#Žxwrû¨Fí6~O6¬EqÎÛlèjNÓy·²„Í#qé;¢TçõÍ€f40vƒ¾BˆpÏ&aÏó—Ÿþòõ?_¿þõß_ýúýß~ãØ£Èóñ8Û%Ëåü-É‘9rÃYu»‰ÌÜcV3i77Å%1,çw»!šmÃ¸'…±Õ¤%Ñãi×Ü1‹J]Ð*€ºQNÚdR©peÎÈE4””fq1mG2|	b¾²4}@³Çºà]0w%·C2÷'­•ô¢ü½¢ôH¬œn¬çÜ_ŒDæÞ"„¿òxÖ¯Y)}ÞNßE ŽÆýäjú”#i¦)Ž¡˜¦¸üê¼÷•¤–k¯Vz$j©úèç¾)˜=¨‡|ÉÜCš}ðón’<rôÓHä¾ìcŸ‡QÉw~6äs/±ö¡ÏÑôò]c'{ˆ¯|N)ß1ô	¥³gÿ:¬ÞO†òqsuB~ÿ§Ò‚Šp€Ïu­Èª±ÀLÚ.Ë5I)\¼âvÖÝN«Ÿ^iEŽL>¤ÍÝ&µ²P¦Àò¹¤Äx P%nTp‚Ó÷g×´G™&µ­ÖÚm[™	 ,Þ´`q‚Ìwƒp½ËYrï{OÊzÕµ™’±fJèyŽ{ç(Ì ©.2vQW±YåþnÔ7•õ6òeº’kÜ˜D° ¡é4ÿ“ÄÊ&q¥*`€øêõ×¯¾ûö-oT®&¬-sï*¿¼þ6÷c£(uJ,ô÷Žð`JÝÞô7ÎöpJ‰mL‘ò‹3kà4µªKàG®@ —ÅS“‰ç¸iú²éÞ”ÒÑpÎ§DyUnÐ£æIT—eær!iô8¶h!ó¹ŠÁðÂ7sÒÌ´ã_Ø0ïO¹Ëû³TÉ¢O¡?Š²]m]§ca¦!j*UaÑœº¡|x¿ò-fŒIUó8I*hÃ%„~BLµ Í(Å€?7pÍ^F\aþM‘š5å~Â•ïaá"ˆÞ·>.§Ð±¬5Ü.­iÃfW¥žÏæa!ÀHé0"´KÒiEÑ¹c_[„P—×ãŒ{¢ã^)å²Ò54¥.£Ê‘ÜÞ—é£Cê)o)K{¡&!Ú0^‚[ÀóXœä†=ÇÉ@( '’c#h  mr!B¸:‘:­±“ž÷½>c–ê÷¬®Ú‚œ9…Ùy<$° ?Õ îXR[æT7±IÕóûH¸Rª9fuú€ã¶Xïƒì'r	å†À7ìK×Î=íWcj*ÄŒ®ÁHü³y·øvø«u¸ãìñQj·d2d]™LV˜P þº1«õ¸ŽÄwV%uŠ8¸•Jª¯ÁÉÖYáml[Bº‘XšDªÒòëÅPµ«$e>™¥×ÚLV
ó$ÿ»æ³÷Pp`*íÅfvZ1G—ööùÇ[U
'¥sLþô4ê¯”ÀÿÍ%1¥LjÁÜ‰ïeZCgªZ)•‹ó1Éð|<Äÿ«2Wévöôß§"”PZ˜‘:¾C²QQO~ªM%{±Þ)Öír±ˆÊ@0‘g¾˜Ê®×7X°a‚AaXïrUÀo~ÎƒÛZ"§4Í¬IqYd‰p@JVµ1Y@0¨:)e¦*UŽÄ×¡ ¹þó“DD-À8L§Ãs3p2ŸêLWá<€Š$Ea¬&tnô‡R8Yg3NÙÍtT« þ±+ [†(-wÚÀ¤k:}6þô/p¯íÈ3®Àµ ä%ô3O¹ÅÓÍç«•áçhAMè§wìQ» «m{8†ic#Õv0`Üñ-MÆ}³J—ÏÇO?Ìÿ‘ø&¬vÑg¦Fo««šŠ@Gr[»…Â­É:fðg<õèRÓdzâ¨¢Ol
°ÞòJaÍ›¨Öx¸…c	-S4àðñ¼âPÂPNºëG @n…<z7úzÜl{_!ç[±7vä0Ó¸®Œ‹#„‹¶:Ù;_Âß%uÝ#ƒ-[áªt‰Ë!âBŒçƒ§xa<|Ú‰…‰v3SÙ„li/ÑûZ’­üÜ×¤æö=ïd	ÀzíÓ H„J·ÎXÑ/ëjx™ßðs×0¿õã

Ïry}"’V…Ç>Uh00•XÙhy+mVò1ýÙƒc²	®x”&í1roóJ$©œ‹Ó1ªëùñ¥ÅKnA’qÒGHß?ºˆªÍ8H¢r—¦bðt|6àyÓ—UE§IPe"à4c4z¤	žíþDã7ðwù—:7ëe——o<BÜIÞË7dÁq6ƒf¯Õ®ÙNÚ¨ds=³õŒ'•_à(še[]Ü	iç>Ê=ÔbºúŒÛ•,
¬ÛïÛQ Ž¼Rt\u!UsÜA0ð¥æµx!>Ûc™zY=\âTŽËä=u|ã]ç"ü{%(·½AÉ²J>Š¨äP:4á²¦öÍn¶^[ZfŸÓ¨©è6ßÜ?ôã™icE–~ˆƒµ=1N_óAÊcÜJûÎä—ûÇô51ï·&æ ÙÚÂ|ÈB˜Ã¥xP*h_ýò>«_Þ¾Ð¾äåÃ•¼$ÂƒÓ>û:—Sç²WŠ‡W¹ôÅ-ï·¸åÑÅô-°¢å`!Äôe,ï±Œå0ÙíaúÚ•V»rˆïÂ<V6Ä>ÆkÊ“£dœk]„š
éDƒ@	Õ(ÔZ;ÿ’%3ãm+<q\c®¾²UYG”wÃ¥/tÆf†›U˜aˆk~ßy¨ Æfm»ÕIYI‡Ÿ9Ää}Ð™4ßžytz°Zh€Q!#5´xÄ¥“ò*}‚0â»h' SE
4¹¬ó<Ü‰“â¤ÏIk	^udh‡Ï;A*Hƒz^êøåésJÔ}9ÀÃV±þÏçÃ+¼—`2<µ7Æ\¯ºØ
ýu]Ð—{ü#NÝùTt ’ÛE„×òÀL-OŠ;ª¨\:÷£ƒÍø!«oÛÙ{Û”GGU;öFZëò<I¢ÚÆÕ€%JCDÍ$~NËøIù®ZJq,Ûèöq±ã U—xÝ–ûÐ—‚z,Úù(m‚?U	„gÃIi~Vy÷T[‡Õdm;Jü\ûLLƒCº›‚æ²W:€àg;‹Ÿ}zú´Y‚¿$µ‘a+ÙrÉ½Dº”—ãA8x¿gø™é>ÖRr~ûº=V40¨Õb2-T\§jä_vçÿu„š¯CÄxŽíææ~@Ú! £=âûn`“X¥& ±;`?>… 0œ6	3,]Kp]W.úïòÊñÃ˜-
÷uT§ÒOªhX]ÞÞè•$ôìv“è ÑÏ-Z ü§$þ7&Uð¦–xJ¦Ï•µ!ù§d½š—¼ã=6‹Ó[®½ç5ó§D¦s:²™Øà¥³.røK,Vf·šÓîó~>R§tt3éÏÙß&•ŽÅin‚„xèÇ™/t–è]¢§LÍ¸/&{¨jÄw–›R3W6DšN£]³m1ùL~0 úU#Ï­ªš—Õ,çtâ=~ÜA\½ú»û>C³Kîi\yRzk§ÐéöT™NïíFI›Ó@1Km¾Éq>
”ïû A¿,vª5.gÓïão€YrôÝŽÆë4Qˆûg,UF!x(vÿ@&‹²ßò\A¼Õ}èÐ—»kÂ°©gf€qô¿wèdO¡I­ÖQ–règÉcJFDë<:ÁsM)k=7ùÐŠð}áX™Šïf¸û9‹îªî#X—`¦¶£Ò5ö,ÁõÑˆNÐGÉ_ˆ¯`Sm1TÛŒùÂuÏïÚVY›·¾¸GÀ¾÷³Ô‡œ¥Ã5Ã³Sœ
ô y`Ð˜â ÌìBŠøÇ¡˜¹)[3ÇMÌxP¸2d¢ÿ é“>P¬Õó<ŒåhzÜ¶cN1sÙ¯Ý¹L]ÉùXoK8E˜ªÙh!²
t©œéª”¸Pÿn¢B>‚S5›òzêBPˆÙycþzf×+ æ¾†ÙÒÕy’R}>+}\Ü_oRx@ŠMo+–_c%úÚ>CÓó›áÀŽuoãU ·*>6Åî/ÜwÞÂ-÷V^S™bH5yº¼ZƒaÍŸx)œÆ¸[ÝóØãÝ>ñ÷¶¶¶I<Y¶Ûî”Ýµßá=Ù‡xœ~åmÀ «ñê '–ø…    0707010001f07c000081a40000000000000000000000016a102a8a000000e9000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.cni.conf.gz  ‹     Í=Â0†÷þŠƒÎ¶»¢‹ ¸8¹‰C›\ëa¸Ô$Eý÷^lý@qËàM!w¼Ož§œ,‡„åSJ¹´éÒ²ËvR6wÄ/û,¦=âõlž¾L•å†ZYMNäâ•íªÚàÓkUqãðÔ“Cý½ÑØT½	¯x¥íB)%JÆPè‡,‚t@7œù5¤`-2:R÷¦¢å•\lZÜT°î
ëqA~—›5ˆ¬ð8ŽzW²ô…ÈÌ†˜ù³3}KìÿžfMœžåXþÎm|¿ƒ‹žÙÈ°M`     0707010001f0b0000081a40000000000000000000000016a102a8a000000e5000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.xtremio.conf.gz  ‹     ­Q1nÃ0Üõ
žï.Ò±c—ŒE¨Ò¹êˆ.)¡Îï#7ŽS «näÇ»cÓÔ„i¨"¹9	ÎkÉÕuW·;ó¶†m$DùÝ,Ž¿pùañÝã°º>(ä®"–vÔñd?Fl×^ì¨Ë!|ç ðÿÞæ1=¾rÄ]T¼Cn¤Np¡ŽžéÜoÂ¢ ®l×\4Y±g$õ,”PçvÖ¥À±;MY‡µ§Eö ³§#ðçÃY¥ÕÁ
ZÏ®½w¶)n½Ädç}qò´Íe¹ø‡ Ï     0707010001f064000081a40000000000000000000000016a100daf000004d0000000e600010003ffffffffffffffff0000003300000000root/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)

0707010001f07b000081a40000000000000000000000016a102a8a000004fd000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.cluster.conf.gz  ‹     íXQoÛ6~Ï¯ —ˆ•%ØŠ%M‚Øìe/Íž†¡b¨“ÍU’’ëvýïûŽ¢:®²$3
o˜_d‹äñø}÷Ý}x¸ËÏÁ¡Øá‡Í©ªsžì®ÌíÖ»Ýbwð[<ìïìê;Z--ÎïvT¯g;ü0 N™VÞT´Þç'Y9âK·¶TlTÊ®òw®¹ü®©c%gñ¬éjÜƒCvXãZRºÔJ\‰95dµâiÊ4=ÙÄl¥X`G§ðþµX.´'×JE3G­´ÒSæ	SŠVú…ã/~AÎXòN,Œóº™‡wÁ5œØ‹€	‡QéÃ¶Å.‰Î‘ðFô²Ò¦ 55°—.ËàÝ+%ÅåƒLÑ	CÿBáæ3)M˜»´LÏ&;›äÜûy:îJc…¾rÂõŽü@LÜ#cÛû	:"okÓ¼…ùZ~r®m÷Tn~1=EU²)B»hB™úñâO3Zý~‹'Kls„?«`©Á`Ñ¨cu‹îHèR4ÆzßVZA•Õ
¬zq³Jxb
g³NaZ¯M“…=F'Ö{iÆký‚fz+iç)å‘ÃAsÛdöhs’àÆí)£;Í‘×Q5¬´ÆœÛL F:†@èfP›i¼Ô°ëà©3UÏÐŠŸKžztAGÒªâ ªaÄ‹0|®f0:K¦†Ýóü"¨-°¯2×«lC‹ùÑß˜\)„-Þ˜<Ä)&£Ã¶­}ÍÈOUý#â’‚gI^-XxÖÔò?â$j‰{UYÀ]EºO¦%ÀóŒ0†ƒÂÙ^«h¥žw¨Ô¿Èó‘™ðOyK1/ÏNF²¸w4P×‚ÏÒ˜)Ju±§z¿7³À÷/Ï,¨šµ<$?FöÉ,¦å4]0²È@ëo¹;7kmO/žÛ¹Œ·>!ªXp>eÈ>@˜q fÖ8y¨7wQ­°'—ÍaÖÇò¶h>ÝS‡¥Å°Q´ž&äC¯”ØÂ»
ÅTÔä%Š»\/ˆ'<Úl¶êwl¶cë+í¿Àø“ýJ|õ@gûbÜ5MÀÚb×&ÊÖêžß"Ð6½Øjé^dÛ,¹…éªÔjV×èÛChVfK]„ÇZfõ|æ… (tª¦í*:Óx@iL@Èj)WÈ fIVIÄ‘•Ñû¡sb£:ÔHy7iÈax™Ø’½Ì8:û£'J;m*UoI; t¹VÆ”¦ÿ¯‰_°&>» >¢ÞvÆvõ¾R¹y.—7ÆT$›:ß„ ý9\Qh^ã
?f¥1]0£<ÚáÃDý3ØÁ¿±?f"òÜ£åè`[ìë†$‚kÿðâTH{£=6–o¨beºô.#‘ëMUq‚éÚ¹•—pô­®($áf3\uÞÀí Ñ»ë±—ýŽ…b î#Öm„@ä—Ú,TÓ«Z™ÝBÂ—C&~uƒ\b¥<.(<—Êá>ûúÇ7§ß¾ÌR‚øc©Â7Â,¾ç³ÝLr#)¾¢…ËGz‚Í^·ÿ…?&¸êµõ¬„nƒ ÆdÈ4t0!øPÿÌ¥ÙåùØôê !S–RW¦çˆ¹¬ƒ9§!Wbf“AÉÉÙivòò»ìì,;9=>ýæ{ò‹¯þX…vP     0707010001f070000081a40000000000000000000000016a102a8a00000109000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.array.ibmsvc.conf.gz ‹     í1SÃ0…wÿ
Ýe²Ó##S7ŽÁu^[©d§h³°ácà¤Å>=K~ïkššeªX²Î2Ûù†ò<‚üî”ÎîWëêº«ËÎ</aö¡ÇÇ‹Ù,¡»ïØÆˆÿWÌï‘ûûÕFé”þmÅLÉÅÑî\?z´C‚(Œ·É3V[ž¡ÇÞNC^­=Å€Ë.PqþÓç÷ÞÑÀÞ-ÙÊ†äÊ‹í4Ú|¤)ËýÙfHRiM	rñ@>Ü•Éµ2ÓæØ
…¥°þV™å`Oø‡Ä.Ñ~FtU;â³òQ>ÊGù(å£|”òQ>ÊGùÔãó	²’ÌË²     0707010001f118000081a40000000000000000000000016a102a8a000018ad000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.task.podman.conf.gz   ‹     í=k“Û6’ßó+PqmÙ®“4šÉæ6;ñLí\Ö¹ÛÚd}µv6R)	"!	7$Áàh”Ëý÷ën $ ÷ØÒ8“0UqbŠ ~w£»ñìÙ1ÿùä;â?8áú¶ÇÌ²¬TiÎ‹›î¸«;.ì>ù7û¬’E*îüäKÚô•Ûö'ŸàòoÅr¡ªô²]Å$SÉ­,f£Ri3ªêÞêñ„™NTÉ'™h>û®ªþP‰ŸjY‰v=_óLÓ/e¥î¤–ª€¥]F¿¤bÊëÌ´[ø‡*èYÌE%/÷¡Lð)»fsÁS¿
Á`÷¢²¿ëR$r*xg&
›Œà:7nX¢r€\
c`p%KÃŒbâ^$µŒO¨˜™Ø…Vu•v™ôyb`Ñ—c ä8 ûxÀ^W•ª4,ÆV5NãÝðñ/×°ø›*µy•èpw8î&bª*q:äYlìÆ]2Éíïa @‘L¹ú²ŸýÂ2îÈ¿ýßÑ‚WE„ä¿M™„×çþ¥ç=B
þ•áðJ˜D¥‚IÍj-R|·¥ªã¤,7„ 7µlšžÖ°údMÔ|3•ºÌø>Ì5|±ùN°¦oÊÐw^8X¿ìsá‡ìz˜šº5ãÄ<[ð¥fÅ'Ê#Zr#ìl£7Ë¿{ŠSÅ¨‚ùõ<÷]½ {ñY:R`rü0á¦eª@2°xp£®Ø™*Í™QyÂÍÙDgð_žÉ‚ô|;‚Š©œé‘(îd¥Š\æÉ!ë‡U‹¹ùQrd]Qò
¤IÊ2	Ü	,3¿ºãÕõÕ+]Vð\\Ÿ½è³’›ùõx<Øí>!²P"\ã–ai™DELWÂÿ‹´Ç´*¨ˆì§ZÁ·QŒN	UF¥j øúÖïÝdKTO‹¹‚™VÌµaÉì_¼’ˆEXè±º,E•ppŽ
×	(P³‹ðªœ£"{r×ür*„«5Ô|g*'
ðÇ·©&Þc(¶Áä`@'d> Õp'"È“nª­ŒA1Òå»9ß4ã3GiV™á„)/fˆMÓt"
„MØ€‘ÓR„ÿÎ•ÛË6jX¤éÐøÕÁK((¡m*½	 v‰ø"õl§@™£aYh_ù³¢ÊÝÂ‘Ù‘•M^ZZ¤»ÍÅTžÌŸfüd§R	›˜ïŸÀE­zµ=áÉíŒ$ò€½µl9OqÍ^#+ÉBš _ÍØ7SÅLK0ê.o—c°È*322ª6cFvÂœ<Ãg—ãBY“-ô!àÂ³E# š ³×w2ºÓþGÐþs@”Tçå««òg‘øñoŒÇ=·ž%r;¢ÕÏ€(¡-mé¹ª³”Íù]¤üx^Uãvä.ºZ–J>A‹ïT¢qëwàM0¡9ˆ\gŠ§Þo01`7ÕL³¼TMé^Y(!¶uÖ¹¸-ÈÑZG}»eÞYä§°ÈïxV²fÙàëýpovÓÌ:˜n‹©Ý®`§=}¨*¦ì¸5‘EçöÔ¢ 2˜,mØM)ƒVlÕ#£WŒDæT€>l`UÑhBýe§á4«ÑÖ]¤mÅ¯©y~ÍB©Zè5†IoQê%À=÷"XÁÔ<c9˜1 y-,ˆ8é6D E_¶ãf2E#{Âƒ(‚õ‰`ý	¼P™ú@&Ý¿½›þeÎgæþ‡_‰UáxVÖYÖ#ÞûÞ-6ÒÌ	ÐöÕ+öÅgÓþYš^üù‹!ª“zR¦¾Ì«ÌNøð;£Re2Y>9úW ýSJ"œ$‘iïG³`€·CÀ‰£çu	æ‚@÷ªÁa DÈiëÁ üœ"-›ñ¬‚=,éè¨&ßb?D PdÂÛ Ol_±M%Ñ7–ÿ.D	èIAÃÀ¬€Â;øÃcÆ ¿½S(ùðyÄ¥¡ÑØšôh,Z£Ò¢0ˆ¹°Üþ’eRèNEµ*J»ØFž^Ž/¢¨ì÷Rc0—g5Æ±ÆjÏIB›‹\Ïu#ó»ÑhÀ™û«öü™*°ÆqÆlú00s58ky;¢¬äJ€ÑàLÁJ‚³ë(À¤¡\˜ƒÉßÎEEDE³Âl™mûLÍ{Ï†0×B‚¸¡7bú}6´KµtJ‹¾
ÝF§™šuÁÀ‚ß¨YRgª6em´·eÁýÀØÂ’ÀÿÝ™ËùýZPÙ“ÿù)a"v†üfad™Z ´(|,°Ãªd	Ž8FP5N•ÔUB$[B >Î·!ãI:¯xm£"×§2rÉ/0
¼j-g…?«ˆ­[aî„wÂ8”gÍÉÇ˜	NIAëÁ€þr=hão¯*tNÒ{<Ø¤oG3ážñô)œ.Ýšœã˜U°lC9ÊáN; R¡v~œ>Æ­–p†Õ¥*ÍåØ•Vû¶D B“Â¨˜ëqå mko:9±J£ã@ANIïP¥^Þ„Û2/A¥hàý®àaRÈq›Wƒ“ØÓïô,¶–!¦)­ô—:Š¯¸} ü\¬Äƒ¹v¨s,å¿ÂÁj¬Ò©Â3XÆÃýµ o¿uIì…®p^ïš(è:Ê\ðëÅ˜ ° ­L·ÇvMÄoþƒLŒBa&›ÈK3âU™.7àÈ¹ã±	ðÈéø=oÿõ•%+tX•–HÛ`¥Øse¢Ìzü›>ï(‘òæGüþEØ[•;58Ä3šÂ2­Œò‚>†º²" .)eŽÈ™ÒÞrÌo{!ôýË M.¢Æætµj½’«D5nb÷èžÑIì4Ú¤¨ú‡O¸–ŽW&Ú—× Š‘ÏÔë´Gœ#$¹ìWa]†+é‡ð-Îx¬+îs—Iò Çó5]Ýý/
µÿ;Ãä”3{²§ÏÐùwäÆ)}—Œ0ê?ÂSÚ…ñ‹—VËHmu…²î$žÜbðlõÐ´GrÝ¹ÀÃ6:HEÆµ‡©ö¶fãÿä´;žurŸOë,s¸	þÞðwKkÖ5­TÎ,O[vŽ1ì3(1?l#‹R¦9x9Ú¬ÙNÛd¼ÀwoßéÚù!]Ñ(zÁÙ„¸(ÎfJÍ2qVr
ìQ€—´yÉ~VùDiDÆàšFhY]ÑN­±ÌàÕ–Ù¦Um´Ñ  ¢Y©³Î`“ÍWøå¹vkCž°p;ÈVëªw¯zÇ™E[ë@«ÝéJv¯dg/Æ)ØA(31]ÅAÇ:ÿ‰§-âÞˆ"…ß@O¯£
<tx`@(h[<6ÐþžýRc“žämv*Î&7RE>OûØùtøêðìñ¶Ïa£W—dûä8î"?%¿¥uåª5ü}Ï%™/ì•ÿùÚKÍT$¯¼-ÞšQÎµãlÊeV»bî€#ØÉ!ŠG˜Z×ÉÃCäá·xŽ# ¬=J„›‚äsiV\\)e‘ÐÑ@yßÉu%fðÕj9J <W´vBA1- :¦9 ÄçÍ¹:;È#ÜÅÿ£9 Ð¡S4—É<>¹F™˜©™ÜpÊb}ïvm:b±nX¡RÑÏÄÈšAx‚c"9x5XÐ¥€Å8GÍ‰ÿíûÇ}ç?÷Á´ÚJ6y×}_õÈÓ´ñ«|ì	 ð¼ñ „¢¯9¿}=];cÖ»ŠˆÖJäêÎ@ƒjdUZTçû¦?˜éÄñ
:Å}ÉÑ­§«¿¾7o#d›–\ë˜tÞIÏ–ØÝ©F0[PÝ:á•D—%lÙzð%€ºþiÙ)Ž30íLv~iÿ³¨_²Ïþ4øüÏƒ?.>¿übøÅþØ…v‡†§‡úÓx’mB½ È#•ö€°œËÔX´SŸ¬º’ì96h\Ê›väYW©`KüÇc<¿~ñŠ´ÿuo0¼Œ+ŒQŠ›T@åÒþãq]ö´I'KV—ãq£Z\R¼[\ !Ü•­!6O@pÛèEc>]ýv“ý9w³uL°ë÷NÔ¦X!¥° @ð3bM¡ˆ¿Îj]Q„¹Ý±Ïü À/šéd€¸GL/Áf¸ü§©¨l‘PÛ¼wÅ~øt8¼ûÃsøó/ÿ~ÎrU|ÚcŸ/èégî©©öëâÓ·“–öp±s ×æ	 Ûy,KÑÿœ½ ?9âî$«µ¼=orpÐ	xIxûE5<ÇˆuxB¬({¶ZF‡:fN4|ŽIn5€'s•8duXSc ÊPÚ›`JTëGÎÖì¥"ìzÓ¤Ò‘£ß¯Ë?›òØ‚ëã7ƒ­ŸÉ],I*Âè\+âè¬h"pa(Ø43©¢çÁtE:êÝ²ã(ºå€ñ†Ñqô¶CèvŠ}Æ™u<ºî©%¼9°’Ð9u›eÜDù¹ïßÛÃ•Fb~ûw·ÚÒÛcí¬IÔ…R??=U?|ôèÙ[T”ì‚g«ù«D#M‚K¼	Ñoµ `@B›<ŽØÉ)³Š§®éÇœšyTDf),?qÕ"ñŒW¹nì¿’r X) a	Ä¾Å¶úèQ>Ù¸êùÅðÉVCÀc^óÅV=UìœÚ‘ÿ0ô4–ûì„Z¡<P3496?`_ÃØ;6®yˆŸ?Ê²«¤=ýh±ÿùÖ€œ1]@ý €:–$h=‚y¦Eª>€Î‚Ùì–×¹ÅV$÷Ar‚å¦ÓÐ4ÙE®­¬›„íË e..¶BX‹.Ù20êy€¯ÌÑ{S’îb.Šõ
':¢´ý@²ÐDR·mèÁãi.‹]è(ºØðª§Oéñ¿¸Ï˜LgA¹#Ÿîàb;ÓÁÕk	pv¼Ï+[I}sùdö†N(£ÚJ(Fwt²­1-î`:1úI—„5*ôKˆýÕjÔšæD	8rOÇX{Æ1¢úÛ®ÙQÙS#ÄÂ/h»„øJeµÚŸÌ–>3×ºÌþE••£HÁ÷¨<Èc¥¸TXÉEËôœ·5Œ&º&=²X›k¥~+ß7›á·0íì¨µÖhDuíŒaÄMÑèl‹÷Muëë|
e³“ñ{¶«o&8´i=¢øp‡Êcd-(?üÖšæÁ(Ä,†t²ì0y\¦a;þ„º9†WÕµ³;gªòŒ©Ê‡à¯.:]y"¶@û¾\Lñ€K^:Œ~xÝÈ	ÌŸ3Àfgü¯èÖÇÈC1ØÙ>§aÉ5~Vðó ´v¦ÏÑøòCm‡ ¯³|NÇ”húÄØÙsE,ø‘ò¤Ž›Ÿx
üý—ÈJÌ‡?ERÍ5ÓÎ¨¼%L^¡–¶‘ºgëVâ a¯¨¢}¹š6/¥PØœÍÃàÔ
ZØD87|±É’R•
jémËÿáÓ¥*´ÄDª©kâÐ¹#59•±ò+HyôDÔÈ4¸r&‚ÉY|NYQÎf]ÉèymRµ(¢”£}êR{õœ½FOàWÅÇh4¥IË‚ýõõ×7ß}óÎ†‰).<•pß´—ãÑázýïM÷Ï¹*¢ÛõÖ,>Zöª¾q²GÓ	ªö¢HG½Žr‘JN	¶0€N3w–£rF×ô\ûå¹¼>-Œ^½[Âmzàß„Mµ™µ:Øf1øÞj	]/”ö¢íÅ_…ýpû`Á6¨îïKFEÂË®ìë(Ìö6Nù¦~â±8»Ú—R‚2ä‰{³R?” ÄLñl¿©P$LEclR@;-ž>g)¥Ytk‚»iÒ–0é°ÌÔ²½:Å3Z{èìîº€ïí#Ÿ°ež`'­_'ôÃ|QÔ5™m*9›aJ!‘)II¢.µEE«Žeq«‘„ÜíDx]\Ù\RõJtžðª8ŽÂÜ¶_µSîí§F'~üéµÁE>µùMxc^KoÛKx]v¢x^(«9ž÷˜ÀXÓSWžnNt³ùŠ.[Q£îöÐÖŒ&X¦hÝ“Ú4ÍûÐsŠSR\ú£`R»É´-w\›5ÐûÔ}6«ÄOµC‚Sq!ës~µn¸–vœ{;þk9ÀftìŸþÛ	íµjváVã#Ö6ÜÞkÁ÷›õb­·AjË4­3¤ƒ³Ø{ë¼jómúµ¸çØ&Ñ6-óðº:Umkƒ1M²[©F!góßX´ð¬
L©n'¶ÝÙGåUÚ¸æîLýT´ô·7°ÅŒÓµ ºX}‘Õ°À‰0!
v>$ž‡Ãû»¨
åløÍÒïÔ<#Æ&úw`6)ëÖ™ð­;Ñº’gƒ%1Tú†‰©èùbrVB6—Å¥;ÁäÒVý-RSö‡Øz0ßåO´Ê0,b;=¸¤Xg:EnjŸ8`_·‚¢‡âúYÔ÷,¡Ë}ûç`*bûdtæ3™KûÔ°»ºI¢Î•õØdNÖùÛyOY.“JiÅÉ+­x€ÐÝ( ;©èº…Ï‡øüÖ,þNM2Àè@XêÞŠyjG\¬¾oÊ¾ï !j¢~úÆ°íŠ;8†h³Bjó%.ÔDiàñùðâ®]4õCû6júºQÈ°¦$pà‘¢é]±æ¬c¹Sn]l¾¹É6ëïËu¢/:}g#…x§˜ck‰þ­	…)<q„ô¼°¦„MœuëˆÐuÀ
<¿ØM}Ý,{í…í^&²°-‹V®DFðØWðçJqp·€v’ˆs¼mù’{ç½|0ì_´(EdâƒíÈÌE>"YÚaô¡’DËŸcŸã5±¹>„Ï[\ðfìÑÝ«“%8È/-£¿©MÿÍ´ÿ­}ïü[`ßFP¬—kãÔŽ5¾<6fPz.o°m™|Hÿì¡¥òF<*•u4rmsc¯¥A÷á¿D‚ð1J\@ÒÒ`@„ý Éè=qg$Q¶¼'…Ù_öÜýö4?7†:Ûc‰-øv˜˜ŒÔLå‘È³9Ÿðzÿ?ÿÒBš›Iß¼ù6¬å¥E:â}ó­'YlüâÃš‹5\÷·`“¸•lìÊt=±Nå—¸¶•¦ÇlÏÄóÊªÝuŽ{[/¨0LGâÇ qðrÖBÕ™T¾B.òB¬#®Ø÷H¦W§3BËÙÖ ;~<·Gh” Þö%wUò«°JþùñÔ¦‰½¶¾ù²óÖÝ­ô3ÚuEË[=?í™±—"w¡‰ƒŽÚ§+ù(å1õƒ?0å¥«‰yÜš˜ƒpw@RhWó1aÇâA© ]õËcV¿ˆ¼}	 ]ÉËÇ+y9…§}vu.§Îå[R€ÂÎŠy¼â–CPwˆÓU´|ÄŠ–ƒ‘x˜Ó•±<bËa¸ÛkÃtµ+­vå>À„yª(ô“ýs¬)OŽ’qnesªmOçPƒ„³QÌµzÀ¾ç•Æ7£ðöc‰¹úB›ªN(ïÆ–¾PlÌŒ«0Ãc~ßTAƒÕR·ÑIn¸¿øª¡ò>Òé.u8é¥Í~ºÚ!nü¼é’‡Ã¯vˆ;ù­^ò°ñj‡u¢ØuÁ¦¾¤]vþ©Ê;>TúøÜ}—‡=çXT…×|-A“	!˜Íä‚]çñìù­ÇçZš²¿ŒfÆ&µÌð† ööæ®É¿?vhVBU_6ß;Û˜ôlÏi°êÏ–úÑwÛ]Ò„sÅaV<öŸ­²†a›ÛÁûºâw~ÆCúÝRåõãBúëaÚAáîY$ÏÀ'ãk×B”‹œdZŒö°BÓN‹¸÷s´•e £²{ówˆw7Ä’M=ÁdY5u'«”áqO!­	“še‰)ú(ûaÚ!ž±abî²Hžãý”X¨¢ïˆŠèûÒ€î¢œ6ä&`k¹ö¢
¬3˜âÑ1õ\Ç
Úl‰+›bÅß`ÀÎ$¸d¥`V·3ƒö{¾tË—¶V'8l{¸¡2Ú?©Ú'?•Ã.umå;¢9-Ñ¨ò)Ü ‘Â.’!ý;ÍG		Ek9+b[I¡g!ekõÚN¿ßðÙï˜7eð³ÂšžTÀKÕDšŠc®&üÝ[Íµª”ûâëi	+—L{mL‘¼Ál`8«\´óÊbšQÁ£e¨
üû0ût"°ãLUïlª>¼S2ÅÒ¾¥«æ§2þ™á*6±	nû5r|˜a<öÕƒWî²°8få¾jË"éÆ¸Ä'>ÙšL4kÎllö¸ÝŸrHñéÆ£Â`áZÜ)Àõaá£8ô¸+€¼Ù™'5H?‹raÔø··ŒrÄ»     0707010001f09c000081a40000000000000000000000016a102a8a0000032a000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.pool.freenas.conf.gz ‹     í˜KoÛ0€ïùrM[´[÷H·CwYoE‘*2¨‘%O”òÂ~ü(ù·™³­ÉÚ 1Ê4ù‘’hw»»<:]¶Ã#˜ËQ=æ9°ÔhŽ[™Û­w»e×¹	Áv­Ô	Ìo;1èÏUØNð‹™±Iå·–/Hs´Ã#€Bar>TP?êÚz
?¼´¬)H¹WnåÛ7£¡2ŒÜ[(1!S)Ø%+EŽ,  ×c`šgtKÊÉ1Ä›h3ÓŒ
 /Žâ_ý»xê~
ƒ/ï˜Ôq¸6	4Rm,Ê£:Ê‘·ÜI£é™Ñ@€lÌðððððþ*#&(—ð¿r…¿§\kÖ0ŸŸžý5eLÁ6n/Bkàÿ¢”™Lé$wÆ"s†ÍÉø‚Í¤ï÷ÄY;)¤}j,Èñçv#Ûñ>¶ð&Ë- RJö•°ÔcÂçþ…2×‰L¸ì?0Á~Ryj “Z¾¥ßÑRæG§•pV	o*¡s^	ï*á}%|¨„$,Uaü~ø §W+ÈLÁTÌP=å¨Š²%O‰ÄÉÈŸ¿l–žgµ‰Ñ²n(oNS‚ŠØÔ(ŸRcl²ˆ°¦²y9À;À{xR#oaàr±¯ëk­yê6¤—Wàú¿lcM€„ºðµ6æÜ"¼*ÌWBy.©>CV`õ`‘^yýn)Â‡}7Oñ©ó;•
pAÝgVÖWÆ]œìá{œJA“:TdÄU‚ ^á™-´²ÉPM&wûÊëæv«†t¬`¾2X©iQkÅ!´Ek^Bd"bèd¨«Éâ˜<\Æyï±ùF@PÛC…	k™ˆI¨ùR(”Ö,P²^YBÌ-|7”w	µÀµ™©vÏ…´õu¶è6û|f¼v5²rÃ0ãòI0	¸‚Éæ]w(Æxµ·é6§\ùXe.œ¬GaW`*¾EÏÔü0¼á°îª’ØNÕÜÙw *f{‚cZN#NjÛTÜa`ÃZ­Â…v|^ìw<*³ö*³"ë    0707010001f08a000081a40000000000000000000000016a102a8a000000dd000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.hds.conf.gz  ‹     ­Q»nÃ0Üõ<'Þ´cÇ.‹Q©s$Ô•RBš¿¯Ü$N®âÈ#îÅ®k9¦£†³Ðy§-éÚºkÛy«aßÍbó—s7<Ô”=\™PÁMÃY*QN³ý˜°ª½ØI!œJ¸ÿˆÃhË”_SÄT½C® Îà0¦g:"Bÿ&¬ÊõbËE³û…¡1	exc9‡‡Ã\Ô×r“]·[Ú^ZTzõVÐ»Äý½¯•m]è%fû½­.vëî©ž%c~ gÆºé     0707010001f0c3000081a40000000000000000000000016a102a89000010ec000000e600010003ffffffffffffffff0000004800000000root/usr/share/doc/opensvc/template.service.container.openstack.conf.gz   ‹     í]mÛ6þž_A (’ÅÙÎî^ûeó‚ËõW\Ó.iû¡(,Z¢lb%Q){]Ü¿™!)‘ÞõÚn¼»ÝVÁ]‘èeDÎóÌpHÎÐOŸóÏ“§ìˆP\ª*Ãe%š3ëZ0U‹Jž^þ>qÇmÝqu÷äç®³OYeâê—'/©Ó¯ûn?y‚Ý¸ë•j²‹¾5i¡Úl*3¸9>âT™NUÍg…è¾ö±iÞhÄ¯­lDvíFÝ¨¥ÔRU²šÓÍox¡éN&rÞ¦oø÷ª¢²ZˆF^¥î;…à9{Ã‚g¾€~“‰ÆÞ×µHe.Sxf.@e2%ÍÀt
O|\«&3Æ5ò(—óZŸbIR©LLðb’LàÅ—^ kúëÓ³mšž·B¥TÑ>JÓ@8^e2ãFh÷‰¶’WìlÌU+}ÚpÍdz­(×—²1-/XÉÓPŸÀp*,Pè6 J›Š—â¯D¤f©Y%€âF±™`Z&ó›´ÜéŽÁ™ÌsÑˆÊþ1oTI/úðaÂ¦{ó5Ë•Ú†\™>860w:!5ŽÐ]VjU!N¦sT%¯øœ:¸Ú´Ú8	`éz/KÂÃkúv<¥÷C¢gàñ±ùÐ›Iÿµ¤7‹kÍzòÒpS£·¢¦u#DY›)Ÿ©Æ<:€º;w5¦`kaLé¾Š]Š&hÁL)øTáùmNž¬(IÀŽD’ŒØ{ˆŸ>üø%ŒNEÁœÒ¡UZ‚"´h–02©
îšã‘½Ù'SU‚ubè CÏ˜Êïnâú?b8að€DÞÌEèUeY¢"Ñç€X
8 íhë^bÙ
†_BtÂž}u2é»J’U’$ S+B˜»¸\]$›¤JP«8ðÔHè'xèiÔIÑŒAªmRÁf\Kmù¼!¸mqØÂf¥—é´QÊLknÃ Ü³†' xxð#€3R‰:r	\‚Ðnù (œõ )B°x.Å€k)q„@ìíH?a_C×ì`Ï0»3¼ž·Eá°	c(ŒÉð¶kZ×.rv––1ÂÀ‡ª6/hj²L·Ñ (•Ófu”èðý+û7Ó¬ñ®Ó<af-àý¥,cWÒ@Æ+èœ@¿ ØƒC°{Üv>ú:ëE„ïaž’
`,Âe‚ñì9 +®Ò¢Õr	Ag#æR›†ƒ«ªXŸnGïß é‡C!üO@C×‘ë£‡¯ÒuŠô¥õ›­íÐåpEWÒú•@¤f¼×ÇV;[!?V¨ô’áóÀRËÄq[O¼õØ
Âgx>U@×Œ,¹#;èŸãÇà;ÜSã=ÚL`ÃPQÐi¦ 3t=7SÐQ\ Ð“Š­cno~´¤Åãî¶Ñ¶±ÓÜÐµl*ëé¼Qm=´×V|¬†˜¬iˆ‚p2—†$ÚY’àÄÞ"ëõöù”–¿ë×5¿,I3;f¸VÝðè¼Äz«’Ñâ§`ØBµortþùé1–Íq#kr«?q	“TðO¯üí7àŒàþiÁëõ3TtOä˜r.‹¶±sÙHó€öe+Hª~´?fŒzÅDØ•-gnÄÃ†g[Wãü{õW4&(Yô{J^S0SÇÙ8/	ÑS?JtOÜ:<P(ý›Ö0¿šv=F‡ÀÛnEì@§¬)ÄW"mq|Îq‚‹ÐøH)ˆ§.Ò±µ‡‹¤ëIâL"Ï¦QÎ®ABÓ¢Ø…ð7¿-È'9ÇÊße ©2Ù–‡‘Ä!ü§ÊfëÉãe¨ÛäS u2ÃU¢#Y¦ªa˜ª>¿¶ÆÊ;2Ë@µ¿×*û‚Úˆ!ú9Q7¸ãð'Dæ 4‡àçÓ<rôã9Á!ö¹“üÔàgŸƒ`BŸ£Ùå§Æ>à"Ÿ»3ÊO}btnÇl3éÙFè]à÷/QÔšµZ€žÛ
Þ"¯fSE·üæwüðžRØj!Ó0Usñ1`€ªÆ)/Š.q¦ªy¾¤µSå’M…ÝqsòQðÕ5mâ6^«[µÆO×ªÒtŒË³6+ØÂ	˜o<“Qù“¥nÝûŽ÷[¯Èžv§{&˜œWª±Û§Ñæp[eô¢5™ZU“ àoÆgL«‘6¼Xñ5nO#Y@P’T/x’L\ê_Å¾úú›·?|÷qDt\-T’Å“m'a{ýýL	Ú—fí½%<H¨Ù›ãó=z‚ËÓÚ»"e•"“ i"«iÐG›‚­À; ®Ì.U²W¸MÿÆ7/¡½~Üª%Ñ¦µëôÄ?	ê·ðuÐ?Câ¶oé‚Ws‘¢îÅ_•ýp§{á:ÀvãÛ]Þµë-ª”×C‚äQŒíCœ[‚·6§qød4Ê„ Ý(qEÙŽø)zLh4“>[Ù"½C”Äjx,¨”ÂÄ‹uŸˆu¡Ö¶`#'º}1K úÞ.ú”€–!«to{ûN©×|¶iä|ŽÛ–DpR2ŽøëŠ~8–Õ¥F
é…j1¡H0çÜ£Sk\Â#\CW
®)àDîÌP­Ñ¥òâÑ± í”¶Œ×—éu•ê8^‚[ óŒ=«”9ž˜À„ÐüØÚ$ MCDÌ»cíö«5ŽÝ^ûA›1+µ{Öš.gNqÝƒÛ × L0wCU¡•Û»ÝŒû(´Òˆ9¦_…„³ïRª+ˆcÏøj_½a[ú÷ÜÓÁëÏ0ibF÷Â„ý×Û c;üs±mÃíˆ¨E‰—¡xkTÉL)õn£×Á	ûA‹¼-7J)ä%²mY³\ÂYib¸â˜xnsx½¾^UÛR~çÓYq)Õt%ä|ñ'[-<ÀT` U¦{Ái#€QÓzŽCÚÏØæ_n4)œ”‚,êŸ”ùí{èb<Y‚¯%íNØ˜î}^Ñ¬„¨ØÙ)axvzz:aÿM%Š€/]gé>%úÆh1¾8½Ù´n§¿¶ÊðÖ[aÝÈ7	iJ9¶˜ñƒ3_h ßÄ‰‰Î1A§0¬wé‹0n~Ïƒ]» ·ÖHAÓL«—Elö?HÒ¢‹É"7ULØ7½£¡»þNVíKépI2>ƒP±„P 'ó…,¥‰çTÃj¥%±s£=öE
'ÛrÁ¨ ”i£´ úgIéBO¨;©`Ò•$_œ~ö¸×5þMµ8t .¡qxjß8ß|Þ¬”}Þ@Gjb?}c‡	P¶¨là®Í:)ßXGI+G™[êViàòÙéùç6Çó³ãZ&€f<S-Ž¶Ò´”]6RéÖ-^›¬Ã7 "¥»]ÊhšLß@¾€T}vûG»Rˆõ´Î¬%Îo­Jh™Â“#äóÊ†
šÑøvD´o!ÏÎogßÀ»½}ï[Ô|»÷ƒX-q]GˆÞ£"Ö@«/á¿õ
ý#ƒ-mpUºÁåvÁNGg£s¼p:>ï!E0ñÂv0KQNÉ—ˆêIl
~ õ×dæz;ï± T³fÏe­CÀùÄúûÖŒßçãwö¹K˜ß‚ùÎqÅÎríúDŠõU*çè0j®5¨Ò[y‡¶5òSú³ƒJ•S\ñhT1pä£Í[–|ÎžŸ¢¹ž !ü¥ÆKnAÒòÐÇ B?<&Aá*‡’¨VÈÊÉ@úÉÈÎ«¬|nÕ
Es»¶´lD9"=»ý	?nàßÃå_jHæ×ËÞ¿²tä}ÿÎSNß8xÍµÒ•y¹>€™ ªñN‰o™ngvRù’ò¢Ý²­4#f«ÏB–¬ÅÜô×+^×²ƒ(~Šã/g½V]HEsýÍÙ@ˆZðÆköùÏ4`uwAˆ39[ƒ˜ã»àº­p<0(AÜv%Ë!*ùCD%C{¼ëÐ„$õ_v³u<[`&ÀC`¯jÞæþaÏ$Þ‹,Ã'j;bœ¡<æAÊcÜJûÖä—CŠc†š˜û­‰Ù»=’B‡B˜‡,„ÙÅ½RA‡ê—û¬~Ù¼]	 CÉËÃ•¼ìáÞiŸCËÃÔ¹ìDqÿ*—¡¸å~‹[önŸ(f¨hyÀŠ–½AÜ/ˆÊXî±Œe?ìvÆ0CíÊƒÕ®ìƒà!Ìc…Ðû#æXSž%ã\Ê:¶T ¤‡‰›QlµzÂ~âUÆº·ð<Y‰¹úB›¦M)ïÆ³ŽçíaF`¼YUÒ	ìöCÀ
zY­u¿:É÷G@vÌ!%ï¢ÎÔáùø¶îÆ¬hTóTŒµÀ\+L'µ«ô9Ò(“(àG),axš
°ä¦­ªx'Ž³g]žužàm/†vø®j\Û¥í€(êU#³7Ï_Q¢î›Ñd29Áú<Ü¯Ø½Uâ‘ˆæzµõH¯Û:IXt mk¥ÛƒÝ."|Ö°h¥ÅäéHqK•Kç~t´¹ÓÓÃnÚÙûØÓžêÔé¼Ë«êÃÃÁ´jÀ1£ftâ•
“ò^u’¼p,Ûè÷rv'E‡å8*uå>tnvTE;õ¨Kð§*ˆdøDÞ¨ßDÕ?ÕÕaù¬m'É>×}‹èTä&eMú²W:€f;³…=X4&*ÊB¯i³<&CfÃã·x§%÷î6RÞœŽâÎ‡-ÃGÈ%p;tçÍo§íyYÙú¨VËŠ™êt!²¶I”™Y?À‹@?û;Ë@Úææ~$Ú1 —=±÷]Ç¦™(8L lËôFF<… Ð.	3,Ý›0t}pÑŸW¾àK:†îË´-x˜TÑ%°º¼½Èa"ÏéˆôžìzSèÈÛç5Y þ¹%’ý7&UØM-vN®Ï•µ¡øsò^þ#¯ÙéŸe3x®ç~EÌa¥¬dÙ–ŒÔñ¥÷.ž9öœ}m»âš£yNCÑj¯‡LM°PÓÚÏÉ¿'•ž²ç•Šâ¡'!èÑÛ ·GÅÅdwUø©Á²/5seCä¡<è|m}{†É÷à:ð4ftè<ž×ªjF¶¬f9g³Vxr6ûðö{wøµß%•E¹ò¤âÆ›V€Eê¶2ÝTÜõ’r,m©˜¥6ßŒäìÁyÅ;O‹–Å¨Z³…âÖõ‡üa–\åÈ§QÇG!¶^æL¢¤<†=<PÀŠ¥ÀÚ¹¢x«ÿÝ†w÷Š¥M;Ã0œ£M¢qÝcO¡I5ë+Ê0n‡Yò˜‚ÑºJŸá¹è”µ^©jìHEü¾p* È;º‡9‹ª­‚Ü±,.ÇL'||(ÖØ²Ô'¶r3ØöÌaÌfccÐ¾Ï®yaÓ®•µë‹;¬üû0K½ËY*éø1ÌPc2Ü2;¥“–ÒÜ-iT½g¶1%<üc_ÎÜÄ”k3ÇMÎT¸2ä¢ÿ¢é“!Q´–ó*ŽåhzÜ½g5e•kÇµ[—©Ÿÿ…Mð¦„S¤YdšÞ
QU`KÍLš†ãBüÛGÄ| Â¦júòzjÂÆ¯ùÑØþ6Zß*æ~ë¬“+«¼ ú|kPxpz8¯Ÿ	< 	ÄEÅ¦7Ë/•Ì°}íŸ¡éù†Ïpøä³_³«@nU<I|±ûk÷#:ñ‹ûª]SI0¤H}ž®]­Á°æ…]
§>n7÷Ç¼öx·OÂ½­kÛ$ÖûívÄ;e·íwOaÁ†¦CDy1Èkü·ƒ´%f}  0707010001f0f5000081a40000000000000000000000016a102a89000013f1000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.ip.docker.conf.gz ‹     í]íoÜ6Òÿž¿‚@pH‚[«ŽŸä>l£¾¾à‚§iŠ&m?…Å•¨]Â©ŠÒn¶È3CR"×öîºY'õE¾k`Kâ‹æ7oÎPòçÁCvÀìNÖÖ®kÁr]ŠæC»;ììK»¿Éúa#U.ÞýþàKzéîµ<ÀÉ_ŠõJ7ùt˜/%7pçè€?H%“éšÏJÑõ¶éÞhÄlÄ0‡ïxièNÝè¥4R+©æÓèN.
Þ•í0mß™TÑÈ–«ÌT
^°3¶<÷³ÞX4ö¾©E&™Á3s¡ m†eZ-Eô?ÓzRD3Ýdpëg#€—,Á`†	{¥¡[…×LË³KfººÖMË*˜ª¬K|ú‹Š›KVùÆ‘ªMÁ31aF³éåjšRŸ)3Ý•9›	fDËZÍ
$ [-„bµ6F=˜Ó—ô<@Kd¸Øl!²Ë‹Œ7¤áG€w|žµrÉ[ÁÚ…`¥T—ÌQÏÒ2aoBTdøæbÉ¤aœÍ€„Ê˜gÌç85fV²Íˆ[„ÉürQ^(Ñ^4ºkÅ½Ã¯¿s Þèª×ä y l!ç]Ã[˜–Ÿºay£ëfIP¶—ÌS²W}]«}{‘[1“
d¹,}S”÷<‡MÂ^*¸`gÜ8…¿‚îjÞðJ€l#´@Ì	ã*}Rn¦Ò?KâAla‰w_(s¡ ñ…éŠB¾»wœñƒVwÂçyN$^ò²Œ£PZ
pã›ÞÀdÐ#Cú%ì-\t²F€à‡â@·‹Î°B7oáv™¦§Øôìèt…³ä”×5ü[qÅçQGÂûž¥©:nPZoº«sþÙK?¨o$‰”0Óa¶ìý0Î^úÔwkÀ¤µ‘ó¹haE,É¯ã‰ûµŒ±Áü	ëT	J„0µÄüæÞ]ä=¯Ø¹ìÒâ8÷I~ûý.|)MëPð8¨ÏëÈÄÁÃç˜.AtÞÎ¾8…W@»Pžý6=]h¸K×OÓ -ì³‘3ä&ƒ¶¨ß,e&Pkppa9ÎÞüô‹H¢…JØ¯²]Ñ4‰‰³:I¦$1¹DçÃ›(Ã¬{ÉÉ´ÁHZ§XüK€¢7°y*Ÿ”Cü¿¥@Éîp²L#¡?²{]õæ—¯ÙJ–%è†5šëß#4æPpë„}Ízí¹,
ÑÕ²¢Ñƒé3ko€4üÓL`h79p-è,M¿Â8§4EµG‹^/°—E`ã@e ôèœ
ºyt
ïWœMOi¡pf¹Š^µXÿê´PÄn@—epæéZDËÓ¡QFÈ/Ö+að…%"hÎ½§euäWvrà+gË–×häîAìÉ«^ùÖ w9øÒvÆÔ	‚õÇ·UY;½;:M^ÐèY!hØË½¿ëñþÔ0È|à²Çïàñ »·h¨áÿjáÎˆ¢+IéÙ5ðÏ0˜&ùï×Ë(°~ÓœÇ7?ýÝåà›Ãã<ÛÍÀóÛßà’ïR”$ïÑ;‚BWA_í•­£@˜w­=@ß##[%O÷yÅÿÄ{¤É'ÂNïÁK4=°äÐ0:ø
NŠZQ–ÆaÙÄ®,¸1r®àå•X…«Æ®]ýrÅÊáºU @±É(N"@•õ¼8A¦³î"©—©üZNY:;î 4pÝçMåküx$y#ç2#Mÿž¬B	já=H5þrTž¿ÿü®—æªE¯mÌéL©Î¸`÷£Öat¸ž‘À ç'L`¼'¼áf˜¸IqÎž%'ÿº»¥åTŒ/\§7q	0†GqÄ‘Sˆ£l%8ùšÕeWÍ€S´•ÃFë6Àôñ6ÿÊ‘{ŽëÐé!Wd\ÖOöŠ<w~Ÿtpp—LÄ'ža­sV‡=Î»ªZÍ®˜©6CG8Ó'vÉi—•äDI´ž,×´Èã¬³¬EAfzùØòÝ4èãäùóÄþw’ãã''6 5û5cÿzF,éÉñ"hý<9ÞÂœÊŒ«ˆAçxÏˆÉÞ?†ÕZË¥³ÕÄ©ÞŽÐžê
£÷ý£·Qã…£:ˆîhBn¥¥[
 O¬4[×Î‘=pŸÂ„s¸LÂÎ1@Ûábe¶¦FCtÙ­ÀÉ\”Âí˜ø·}A›9Q47PšbTØ…}$ø{zœÐÿnäó©×î÷À?ÛXÏG.¶ÚEÞ<¨\ÙømšŠvqªÐÇÅ'ºúÊÂev.-Ñ¸¸—^žáYŠ£ÔÑÜ¯ïÊáãY†.ô{?¦qø;œC.j¡r3\h„ö:÷ï—ïÏpñ1t ³Àm³ûmÂ#Ÿ»£~{ÒÑ¬D›	
¡5¢äë|î{öŒ={~+.Û‹\Ý¿¬„ã»hçn“3"ÿ¯@*ríÈõ(;ƒÃF—ÁhÎdè§ö>K«C{X×‚7ŒØ¢È	¹‘3ãxø®±œÍhF+ßÚíR
ðÅ™ÂHÝÓÞûq!s‡€3ÛaP‰L1©uë?ãÈåa$ËãƒŠw"C[kÑ”–ï<?ßÝhdç@¢_ÿMYÎ7ùx›©´s¹O¸×1lšœÎÊšµ‘yVÌý–&ÇDQ¼pU“Ã¸Õæ2&^¼l,[‚®®,`lÛsì~¾ø^cŽ3]–"ƒå.X`;Ö†nùãù«À&+”9·]…X„qÊCÏ7¡2üpQÓ˜o<:º½fºªH†»5-"¼†³š:TèœL³#NQéiÚ¿IÊì¥„}Û4ºq.sÓÕ63ÅÝD o@f»ÅÈŒPþU(C ‰”áÞó_ƒÑòÛBÿ¨|¶‘<¬P†´M?P×ÇípÕõˆça$S×‡L]ß¿N¶òŽÄ2 í_•Ê ‹}AmÄèýÜÑ`1z‡îOˆÌ-ÐŸCòÀÞGä¶Ž¾ÏÝˆä‡:?øÜ
ÖÑõ9˜\~¨ïãð¸|£çswBù®OŒÎvLqÂB}Ùãqï¿ÿˆ²6V:w
Z‘Vsy-eŸgè“KðžÒLlžK\D $…ö™VG/Ë¾(±K|I%ð@-0³hø5)“0êšRWpç´3ôµÆ¡k­¨#³ÄNÀüÆBi•ÿ±*–^¾rî	9°€ÝaÃ”¢¹ÒM‰ò¬B6²«‰eÌ¢ks½RaXþekð™¶36·jÅ×:FfŽÒT}ÁÓ4¡ªJœýö»óŸ¿kSÉW]F;>nLŒï»9&á|ýý\CµÐ*
ì_ñøhÚ›öÆéC™RÆ«¢0‘J/æ¸ÝEÌÚvÐ££-ç©òiª+vZóvqæ§—Õàq#Ú°'Jåp/ø'á¥Ì Áûµ(IÒ¾[¶àj.òIôzñÈBÙ{zØW¶ù¥îòÎ: •ñz¬;T­X˜²ƒ¿[³é&Câ—K4ÑÐŽ¶4qk4.VÈPcæ˜PÔúÇ	©¨M²‘Oæª•ò2JXq9JÞ¸ÅÝ´ºÔë`+Î	Ú°¹j+„p¼]ìSZ­nF:”¶…ë‰zEg»¢CLãÇÌ
ÓÉLæÂçœPæXªKƒ,4à”{ä¢ãn;*»Mî6âmö1u¹‹	t*•—÷ŽPNm&7—à™µÊLì/1—lôHik9¹ÔîâÐ´É €6™ˆ+Ç€½.¸,©¸3õƒ9Sö°¦yÏº˜Ê¦²áÊ)Nm¶¯àÄ]¸¼FªÂÙì5°ûØP¥óúg—-BõðÐ{Ä—0QÛè†sÚ¹§Ã
4¬¨ŸÑ5HØO}Ú‰l;ü6M=Öâ#jabeD¬×¯x+3òE½ZE¯Ç½`‚gt`‘Pœ50ô‚iØ,ïªš²fmZQQõ,˜xU»Ó5zz½8 WÝ”“0¿˜•—R_¬„œ/þÇ¢…’hfþ.ê9š´ßpÎ¿_+R®ö0’¨ãz”½|M9g˜ïÇ,uöÖTMA+¶+!{zL>=>>NØÿS‰ÆÕ©½O©1ZÀÏ· ›ÕÝÅnùëVX7rM`AŠtŒòÜpå‹)Hù\ºOØöŠ	^Š2Iç;vó7Ó;i$§ift‰aWbgÏåp®SÔáuÇz$ì»AQLP]/U÷ŽeÔ”Cš=W±W ó¥¬d¯è<f‹Á—Wæc’;‰.’ ’Y£ öÏÍFÆž'ˆà˜o	”×°èJÓçÇÿø
îõ¿±#Œ -ÍdÃ=µ-N6ŸoWÚ>ï gjâ~c‡˜hm3ÊÀ!T›UR~²C¥5æá¹8P¥ËOOž1K:¸†‡ë‘º;šé­­l;W7!”é\ ðÊb“¾*»ôÁZ¬h™Lc ¿@¯h}¥ð[)„	y±–¸¾µ$¡0…gŽŸWÖ• ²áþD…m+äÀ§'Û¹oä»½uï9R¾‡ÝëAÌµ•WÆàñ…×¨ˆ5°Õ×ð/úe›Íô7îdÔ¹š6eÇ“§“¼p|t2@JY¹GÛÀ¬DuAºtDô¶šÄÈ?ã5Ç·$æf9° °ò±TQÈ®fñu×½.Ž^Ùç.a}â;ÇŠ]åÚø•cê"\c Â¨± 2†‰%z´­ÓÏîÐººÀˆG£Ë‘GamÎYQò9{Lu¤OŸØ}£¤Ü}´|è£ˆÐÏIŸÇ:ITŠeûÉ¡÷'»®²ýãaJ•Ý¾Âµ]WYnºrŒHìÙïOx»¿‡á_šH_Ó÷úõ«°*›&é˜÷õ+Ï²Xtë&Í¼­ò	 ÙÚ½ˆ	 ºQBæffº™]T~I5ƒ.l+[°Ð€­`OC.¸« wð¸YÑ±? 0#‹‚Å1ÀËÙ@UçRùZšh5¢´xÁžíÐL#Vwç„8‘£(z¸¯õ*¸NJá¶N	â¶Ó)YŽ^ÉßÂ+ù'Êã]»&ÔÓ0²[­w†Âìszëfó˜ Ú?ý™Ôk‘eèâàBm‡3–Ç|’òi¿1ùå6Å1cMÌÇ­‰Ù»=’BÇB˜OY³?Š{¥‚ŽÕ/³úeOðv%€Ž%/Ÿ®äe/÷Nûë\>MËN÷¯r‹[>nqË>ÐíãÅŒ-Ÿ°¢eo÷sbÆ2–XÆ²v;}˜±vå“Õ®ìƒà-\˜û
á¡?§tÈkû9%LÆ¹”u,© È 2J,F±Ôš„ýÊKŒûV²ªK‰¹úÂ´Õ½®ô›PFà•zPÌïç€+¨±^›!:É[z³ãkNýsþ¬à{ÇCw£¶~”Óž¤
O]î ç, ÉM§T¼ÇÙ£þ}õšà|èfË·	Òô´‘ùÙãSJÔ=›$Iòë_ð¼I¼b÷t%ñÀQÌõêê‰iÑ^w5ôÕÙÞía¢n†YëkELÌ<=Sl©¢réÜã¡x»vöÞúòˆèŽ|áÙv§êŒµ²ª;SSØ/…[	ôÝ&å|Õ÷ä;Ç²a_¡`Ó8):,ï@«Ô—û j“Q=í|Ô“>ÁŸª"&Ã'ŠFÿ)ÔðT_‡å³¶]Oö¹~L^-oF'ÕºÒ”€0Û™-¸qõÐº>5m–ÇÌo¿ÂãV¼§R»ÝH9;žÄ/Î± ±¦»hl~;mÏKewê£Z-ÛÍ…Áïƒt¥H£üËÜê<X®ïÈP÷ ·ÍÍý¨kÇCß‰½ï^ì"Ç£.S73³‘O.¼NŸ„Œ–®%˜®7ÎûòÊSD1ÿ´•YWò0©¢O`uy{3ü&‹(
¬ô˜Ýlv:ñòy¥/ ßnÿÆ¤
»©ÅNHõ¹²6ìþ„´—dË©ŠaFÍµó8ÏoˆsX%•¬ºŠÙ"~´‹çÊíe&þ˜X/xÉÑ—QBNMé F’Ÿ'	Ž'•³ÇJG	ñ0'!èÑmÐS¦f>“ÝU5â‡:Ë¾ÔÌ•‘†vßW Ýžcòý?²STèo<žWªj&¶¬f9g³NAooÎ@ãviú]òápHLæ°åIåµ5:6­ ‹Ôme:;¼%uHßÔ„^1Km¾éÉÙÄ­®ñŽš·1,v›ª5[(nUÈÌ’S”Úí¬Ž÷Bl½6¬™DE.x{x €í–¾ª·i¹"Ë4J´ñ±ÃÔÄ²M7C7C.ˆìºÇž(B‹<è´]×XQ†~ËÑÆ'9È#Z«ì]LYëJ«#ÇTÄßSG‚J·qìæ,êN¹
9ô›°,®ÀL'lG>”kœYêIÂVn;eßÀ¦Ý|ÅXŒç©›^8µ+emA|q‡4€~W©w¹J%ß‡jÌ[V§¸™æŽ™F×{ñÌMœþ±/Ï\Ç)WVŽ›<°Â6–!ý™¦O†Œb?è·q6?Ú0ßÎRÊ×Úµ­aê–Ï?c¼.áTl~
ØK!’
d©™É¶á(‚¿£/ 6UÓ—×Ó¢BÌÁ¯ð³nÐ[b?Xß÷+UQR}¾(:X=Œ7	< 	º‹ŠM¯+–_j‰ŸwPkwø-Ï7t†;`À~àÌf£@.*ž¦¾Øý…]ZÄ§ÖøQmL%µß‰5þk²­A·æ
§w¼YÜïóØýÝ>	÷¶®l“Xï·Ûï”mÛïžÝ‚;uJßò:Æ ­ñ_tŒ ¥GŠ     0707010001f097000081a40000000000000000000000016a102a8a000000eb000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.packages.conf.gz ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:³ž$VBì[Û‚kx;Gr ZO±Åîjvf¶®K¢ª© V:¯yÒB!º²êÊfW=of_ªUë„Ë»ÓÝNaÒŒ<l
bÍ%°óúuÆ~íIÏa=D‚s²óbÐë4Ç›ÀO¥:¥u—ëF
Ê ×¥àÁ¶·L4`Xþvš™çã¿üuÑoˆêPA7š£uKwò)Œ~N´8ºvt~ý7iÃ¨­qÜn¹íl{#\–¨?YÅýÞ{øë£ú`NÑ#   0707010001f0c4000081a40000000000000000000000016a102a89000010cf000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.ovm.conf.gz ‹     í]moÛ8þÞ_A X´ÁÙn’Ûý’¾àzû‚+n»=\»»‹–(›ˆ$ª"eÇ‹ûñ73$%Ò‰cgë$ÍVA[¤–4"çyf8$gèÇùóè1;àŠKUe¸¬D3bf]¦–å§ˆ;lë«»G¿u}ÜÈ*¿?zN~‰Ý~ô;p.Ö+Õdg};æ­ÐFi¸6>àêJ§ªæ³Bt/ûÐ´/4âc+Ñ·â^hºR7j)µT•¬ægÑ•Lä¼-LßðŸTEdµ4¼JÝ‹
ÁsöŠ-Ï|+ ÷&½®k‘Ê\¦pÏ\€²dŠ·¥¼ÊdÆÐîm%/ØÿØ
4©Vš”Ð)\þ° µh¸f2½ÖF”Ðfàó¥lLËVòtPLàÁçNÅ€
ÝÄBiSñR|ÙHDj–šUBdÌ(6LÃd~•–;Ý1x"“y.Q™À^óF•ô  o&lº'_²\©mØ<H\¦Kð=><Lï{Xú·]	HÃ¥f=zî@¸ªÑÛ@QÓº¢¬Í”ÏTc@Ý•ÛòaØZðaÝ{@±KÑ-˜)¯ª"<ßädi`sIb@@’ŒØ»ZTïù¼aQ0§th•– -š%xBUÁU³`<0;gªÊÚýWÉ3¦rÆ»‹xþ"†ö^H´áÍ\„V,Ëº%‰^ÄRÀhG[×ð(ÈVpmX©2Áž
}q4é»J’U’$ S+B˜;;_%›¤JP«è}xj$ô¼ô4ê¤hÆ Õ6©`3®¥¶|ÞÜ¶8la³ÒËtÚ(e¦57‹aèÙˆÃÏÑ ÎH%^ ÇR—xz—ü F}@Š,Þ€K1àZÊˆ‚ØÛ‘eÂ¾‡®ÙÁ…`6vg
øyÞ…Ã&³1ÀË®i]»ÈÙYZXFÄž©ÚÀ_QÁ…m´ JeÄ´YÝ%:|¿dÿfš5^uš'lÁ¬<¿”€eìJèÂxè {ðavÛ.ÃG_g½è€ðÝ"ÌSRŒE8MÃžºâ"-Z-—búšKm6®ªb}D¸ý½?x|ƒ¦…ðG@C×‘ë£‡·Òçésê)26[Û¡Ë"àŠ.¤õ+HÍx#.­6:&?V¨ôœáýÀRËÄq[O¼õ°×x ª€®YrGvÐ?Ç—Á#v¸§Æ{´™À†¡¢ Ó0±WŠŸâf
:ZÁ¬'[ÇÜÞühH‹ÇÝm£m/b§¹¡¦Ð]¡Ú‡2ž~}|HsÛ´¦¬mˆl‘9ýÊ%„î€Úù@ KLÞø°Û4‚+ç²háGš˜°/[ARõƒÅèäô!cÔ+ Â®l¨mev¯ÐøŸÅJÅå%‰¶’aì’ð((¼ÕÖY×\k?%ÒèÕ —Àáýò–@ý^¿(¡+^®!ƒçN!°FÒ‹^(ó,-àe¶ÛÁ×qß›B%V½9 àÐÌrY¸°ÅíBåxLÎaV¶„FÐÚP*ö…Bå×ƒvˆ“°û…c«sº4®6ô¢—p:KËì,AÝÌ5éVkÀUµ8%V,…YŽ;”¯ìë!¡È0­aº<íT2¬q8 ^w*‡\§¬)b"mAí<Çõ
k,6ð,ä,Ûü,éz’¸±&M£\,	M‹bÂ_Dü¶ s8)ª ü³P† ’*“¹ÏÍa$17†þ©²Ùz@ò°Fê6ù@Œ›áªêÏÃX¦ªa˜ª¾	~m5Œ•·d–jÿ¬U"öµCôsSDÝ*Æ-‡?!27@s~>ÉG?‘›"8Ä>·c’Ÿülàs#X‡Ðç`vù©±Ããð‘Ïíå'†>1:×cŠ¶‰xl_û6ðû—(j+j ç¶‚§È«YÀT!0)ŠöiÝ.^ó@ê[-dfž`j$ÞPÕ8åEÑåAÕBÕ oÁ—´Èª\®ª°¨N~ 
Þº¦ý^Ü•mu²ÖøêZUZ‚Žq_‰HààÌ·žI¨üÅ2ñî<áµWdO›¸0LÎ+°sÚöúÛš(£­ÉÔªš ¿1ï1­FÚðbÅ×˜m€dAIR=ãI2q™œûîû^ÿüã‡ÑqµPEHÿNÊë´mœ„íõ×3%(Í€-´÷šð ¡foŽ7Î÷è	®RkïŠt”XŠL‚¦‰¬¦m@m
¶Ï¸emU²¸çðÊ7/¡ÔÌ<%Q‚ëôÄß	ê32tÐ?ƒ–$mßÒ¯æ"EÝ‹ß,*ûâNöƒË Û÷ñ®$Q¥¼ò]blïãT!üÝÚXœ•ãs)±…¶AÄ%¯à§è1¡ÑL;!=C”Äj¸,¨”Â<šuŸWu¡Ö"ó‚½¡u2–@ô¾]ô)-CV9è Þ®Wê%Ÿm9Ÿc¾Qœ”L#>e¾‡¢Žeu®‘Bz¡ZÌÌ9÷(DÇL)—¿
Ÿ¡+WŒp"w&×èRyñàX€vJ¹.†ësôºJu/Á%ÐyÆžTÊŽOFL`~o~hmÐ¦!"fˆMµq‰6”óáµ´C°BQ»g­é²Ûqægˆ¸ÌÊÄsª
mS”/IÆ}ZiÄ³éBÂÙgiÿ7âŸð%4Ô>zÃ¶ôÏ¹»ƒÇŸ` ÄŒî	û¯·ÆvøSëmÃíˆ¨Ey´¡xkTÉL)õn£×Á	ûY‹¼-WJ)ä9²mYÛôªÑÒÄqÁ±ŽÀ¦d{}½< «¶epÏ§³â\ªéJÈùâ/¶ZxSLT™îÛ¬‚i=Ç!í7lóïWšNJç˜XÔ?)ÙõÍ;èb<Y‚¯%íNØ/˜½^Ñ¬„¨ØÉ1axr||<aÿM%Š0¿Äw–®SÞvŒã›ãkMëvú±U†°^ëFÚ	LHSJ™ÆTEœùb¢VJ1—‚	
€Kù&Ö1A§0¬góFÁ8ãæWñ<Øµâpk4Í´*pYÄs€$-º˜,xUqÈ„ýÐ;ŠºëeÕ^°”ž ç$ãKp2_ÈRšx@M¬VZ;7Úc¤p²-g€
J™6J ¶QcÐ)Dðtº“
&]IòÍñWÿÀŒßø?•VAÐº„vÆá©}âtó~³Rö~o ©‰ýôŽ& àµõ`‡pmÖIùÆ:ÂHZ9ÊÜ:P·JŸŸ~Í¬þ'ìM\šÐŒgªÅÑVš–Ò‚ÁF*Ýº…ÂK“uÌ÷*íÔ¢KM“éÈŠc¢/Vø`W
1÷Õ™µÄù­U	-Sxr„|^ÙPBA3ßŽˆ€ö)dàÉéõìx··ï}šï`÷~Ë«%®+ãâñÂ{TÄhõ-ü»Q~ÒßÂ1ØÒ˜é	î¼švÆŽG'£Süàx|ÚCŠ`âÛÁ,E9%_: zSO¢åñœã{2s½÷X ªY³§²ŠÖ!`‚|dý]kÆïòñ[{ß9ÌoÁ|1éÚÍríúDŠår”ÝO‹€h˜øªôVÞ¡mü˜~v°C©rŠ+*Žb´yÍò‚ÏÙÓc4×“#$„_£Ôø‘[´<ôq ˆÐOYP¸Êaƒ$*ý²r2~4²ó*+ŸC¥ß˜Zs»¶´lD9"=»ý	?nàïáò/5$óëeïÞ½ÙF:ò¾{ë)§o<æÇZéªö\ÀL Õx§Ä·L·3;©|Ž½ðË¶ÒŒ˜-=	Y°‹jvp\¯x]Ë
f ø!(Ž¼œõZu!Íõ7g!jÁ/Ù×;<Ó€Õí!ÎälIi`ŽoƒÏmÁêƒÄmgP²¢’Ï"*ùÚãm‡&¶x©{³›­ãQ3{ÝPó6÷Ãx&ñ^d†88QÛãå1÷RãVÚ·&¿Ü¤8f¨‰¹Ûš˜½°Û#)t(„¹ÏB˜ýQÜ+t¨~¹Ëê—=ÁÛ• :”¼Ü_ÉË^îö9Ô¹ÜOËN÷¯rŠ[î¶¸eèö‰b†Š–{¬hÙÄý‚˜¡ŒåËXöÃng3Ô®Ü[íÊ>Þ „y¨zaŸcŽ5åÉQ2Î¹¬cK@zh(±ÅV«'ìWÞXeü§{
–˜«/´iÚ”ònÜ1íx|"fÆ›U%à>a?¬ ‡ÕZ÷«“Üp¢gÇRò.êLžokàv\Àj!F5OÅXÌµÂtR»JŸ#2i~”Â†§© KnÚªŠwâ8{ÒõçIç	^÷bh‡ï¢Æµ]ÚˆÒ ^42{õô%ê¾M&“#¬Á³*ñ»— J<á2Ã\¯¶iƒãu['	‹În­t{n¦ÛE„×Úó2­´˜<)®©¢réÜŽ6·zìáU;{ºc—Ãc]ú€#wyQ½bxª¡öCx¢"fÔŒ§ç>ãÐgÐ÷¼ê$yáX¶Ñï+äì,NŠË;pTêÊ}èô¨‹v>êQ—àOUÉðŽ¼Qˆª¿««ÃòYÛN’½¯{&Ñ¶ÈMÊšôe/®t - Ìvf{N,hLT”…^ÓfyLþ†Ì†ÇOñNKî%Üm¤¼:Å[†·KàvèÎ›ßNÛó²²;õQ­–3ÕéBdm!’(ÿ2³~€ ”{–´ÍÍýH´c@/{b¯»ŽM3Qp˜ Ø–éŒx
A ;]2fXº'aèzï¢ÿ>¯|Á1EóOLÛ‚‡I]«ËÛ›&òœN¼ïÉ®7…Ž¼}^’àŸZ"ÙÿcR…ÝÔb§äú\YŠ?%ïå_ò’ïðYV1ƒçÚy`ëwÄVÊJ–mÉHm_zïâ™c¿6AÛ¯"¹ähžÒP´ZÀã!S,Ô´ös4Á·ÆI¥Çìi¥¢„xhÇQºEô:è)S3ŠÉn«ñSƒe_jæÊ†ÈCxnýÚúö“ïÁuàáÚèÐß{</UÕŒlYÍrÎf­,ð töþõOî,s¿KÞµ„Š”myRqeŽM+À"u[™Nïí{I9–6TÌR›oFrö	{4¨}x÷áßÃ²ØÞUk¶PÜºþ#Ì’«Ü÷Ð¨ã£[¯s&QRÃ(`ÅÒ÷gmŽ\Q¼ÕGˆ»{ÄÒ¦a˜ÎÑ&Ñ¸î±'Ð$„šue·ŒÃ,yL	Áˆh]¥Oð˜{ÊZ¯T5v¤"~Ÿ9ÐwBäÝÃœEÕVÁyüX—c¦>G>klYŽê“	[¹ìûæ0f³‹±1hßç3×¼°i—ÊÚ‚õÅÝGá³ÔÛœ¥’ŽÂ5&Ã5³S:"~ Íí’FÕ{qfSÂÃ?öåÌUL¹4sÜäL@…ë(C.úMŸ‰¢µœWq,GÓãî9«)«\;®]»Lmøü6Á«N‘f‘iz+DU-53iŽEð]øof°©š¾¼žš°ñeO~4¶_u×·
¤¹¯®ëäÊ*/¨>ßT#—Q±ÄLàM .*6½ªX~©d†•èkwøMÏ7|†;`À¿¢}›]r«âIâ‹Ý_ºïDŠ·XÜ[íšJ‚!Eêótíj†5ÏìR8õq»¹?ä°‡»}îm]Ú&	°Þo·#Þ)»n¿#¸sn5,4ý"Ê«ˆA^ãÿð£8ú5|   0707010001f0fb000081a40000000000000000000000016a102a89000005c5000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.route.envoy.conf.gz   ‹     íY]oÛ6}Ï¯ ‡&€ë´X÷’¢4C´YÑe†¢phéÊ"&“*IÙñ¿ßá‡$ÊŽó†_šJäåå=‡÷žKžówrÊŽøsæ´j,M˜ÝÔÄH®ÔæŸ™;®wÇÝÉ'¿ÙS-dN÷ŸO^ûM¿	Û>9qîÿE›µÒùeïEÉM9«U%2A#žñçâe2UóyEÝ’·º!÷BÓ—Fhê}ù‰WÆ¿©µZ	#”rq9x“SÁ›Êöî»‡B–¤…å2‹‹TÄö+‰ç­Ä°kÒá½©)…È0fAs37,SrE:±]	c}Ð°¬Éðü¶$ÿ©Â‡ù°m°£¹a…ÒÌb ‡bÊ®•TØ$EBžj>‹S…a†,³Š´ÅÌ[…%_T´šbõ×| æ>—Üfå,ã†f†¤V¬è›ƒóFI:¤	r?Ë\dÜ"€¶äQ!î/jnË7xÆL©š*gsb.ˆ¬âÔáž€vÐÝÝYìþîÎõ À«PêqÈœ#P-PE;‚òIŽ\°¹dtÏ3 ç`ÓMElIÜù@uƒc,» 78¬~Rµ	Ñö£.½ç"i¦àºú¥!½aÆºƒˆõ3š–jEù”}ÐpÍŒaû."MüºŒ[Ð}Xtîd^ø%aƒ·8ò!òáQBÄðïãBÉzÌi!¤Ÿ ·i±‹ubè+P€»·2Â~ìˆUSq“øçé.¸üì¤#ä'HZ„/$£³µ°¨vgþùÖ’cð(˜òÎ¯EU1©ÚÁ¢€G g¦™`EÎ¿”©‰­¸?Ó×¸¸á„‰! oØÅü“PŸí>2‚ØUfg¥2vÖþoäe¢ ]d ü´u¼‹€üöñ] Ðe€5¯k(>Ç	¼cW¼jÈcñpxÏýkkóíbÒ½ù·ôú\)X’;€Š%Ù3çg[xbÀÂnž‚ÌÓñ=¡Æw²{²K‹SW	0@Ç—>™MO’ßw/^&sÈÔJê¡EžfëR ¥:”M'>lÕŠÀŒááeÃ›AŒ"lÊ¯ÐÖ)<Qr‘ë¨Ú-5¤Ô€¦€ '0k<Æön¡òÆ×î6jp-HˆPóV6ž¡Ñv œ'õMà¾4Ž”Ò
Ä+µ6=ý@T0ÀL¾‘|	ç*ÔòLœ„uôˆ9Ê{,Ô¨íÆnqc€óäh¹?ËT>r#­ ×·· È¸msÁq÷-’„h!ØÊA–Eš-fo4xÿËïWog®>¾ÿñæêæöÝŸìéè|æ —±tò±žy)9úC
ý) ˆó=R`xr;µÿ6^øøó®}
·ÅxïófO
WîfY…æ€ô,È÷ñÄöw@Iàš¢DœK1—ùELä©Òjq~W¤Ï'íÝMŒþ„aÀ{Öp‹I»Ëô~krÊþ(In™vŽI/{å¿Axy’Æ/…}gœïï‚ýÖñÇÜ].ó\d…¸ùëÓ[\ÕÆU˜Í¶;…VËÔWž8Ó´R‹uð‘+T#sÖ^jS) óÄNëx®(L¡{aì`§šl£Q.Ù«¯ú,ëOÏ'á‰égDŒ:èé$X(½æÚ‘(ä@ßÀÆHou@Š×¤}Ý	ý!P¢u¯0‰·LšêŠgáêŠX£«Ø¡*Šî#E‚ÂWID?ÃŠ%á€À¯1)‡ž0Fgûûrâ ~„#´+qN^~z£\æm-žµV…DŠÃº®cpÙPcÙÌvØ£(fj¹D7û_à×ñÿúH›€wMšyˆxTd(üÛyD®êJ#Þ¯DFN%tŸiÍ$\l©7%WÉç®[c¦©Ld5©öJ¾òçjÒÈ±®ã­ýauÞ0®ÉÓ¦1ïübÉˆ±#˜G>Âé?éþ 3wE!     0707010001f07f000081a40000000000000000000000016a102a8a000000e7000000e600010003ffffffffffffffff0000003700000000root/usr/share/doc/opensvc/template.node.disks.conf.gz    ‹     ­Q=OÃ0Ýó+NÊœÆCŒüŽ©æüB¬†8½³]øí8)MA¬~ƒ‡»Óûr]—DUSA,tÎëQËÑ•uW¶»êyûR-F8qÝMOy€K#ò²)ˆ¥å0Û×›Ú“u"Á)yû¿qèmãÍà—11¹Ëï•”3@.G:ƒ}ï™éÄóš43)ç‹ýO>š­ØwDõA( ŽËÑ‡©;ÌI‡µ¦MÁ.óí_ß›TZ¬ uÛksß6Ðóíç.û¸ßf“TßSÑ\˜"   0707010001f07a000081a40000000000000000000000016a102a8a000000e8000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.checks.conf.gz   ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:ßzB¬qØµÅ]ÃÛq—Ñz
;«Ù™q]—DUSA,r<€OZL®¬»²ÝUÏkØ—jqzÂå#ˆën5Ó.ÈdSK+Êa¶ÇÛµ';êrˆïÉÜÆ¡·iŒ7ƒŸÆtÆ4æ.¿WQPÎ Y—tûÞ3=Ò+&ˆçï¤YI9oìòÑlÅ¾!B¨Bq uÜXŽ>LÝaN:¬=h
´;Ú¿>8©´:XAë·×ê6Ám —)Úó.¹ßf£T_&±é($  0707010001f0a4000081a40000000000000000000000016a102a8a00000318000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.pool.virtual.conf.gz ‹     íWMoÛ0½çWÈµM°k¿ŽvÙe½E¬Èt­E65Ivœýú‘Ší:íÒÃí6™¦È÷Ey>?å5›Ã	/qçˆìÄCh²¯rwÚèN‹ÝìN’{SçØÝÏ.SÒ×CÚ³™Ä¿ÁÝ–|~ñ†VN­5Ñ``ƒó^‚WÐÄþ-Ž+~V6 <ñø£1óçOr,Tcãcžü–Ôñ½ü"p*è÷vÁ¡6…ÑpX£7ZÌ4Õ-ú‰+kBLHð*Aóüm‰ vŽæ°ÞA,U#Ø.à[ãùˆùDÓ·/ ËB©8­,;ã1G>º~°f¶23ñ“ek»É2Žîò  ëgù¡’\4T+»jÉ6®°nß+£_©Æ“Ó(“@Åž¶¼øî! oN…yÀ°-.¡U¶Iò.f*åë@±$êVÞ`êä\YKZ±
†ezïø’VŸ8ê ¦È†TLN5^ãY÷øìãV‰X9Ë«L¼I µÐey,D/¿ažeÃ¹FeÄù§E­*¼'VòW¼-
¢þ“Ô$†¯£'r$áT,¹ø®“à”Æ›åÕ†7Q¾ÉÔM–‰¨ÔÀu$hkà@.#PP‡·½õžžñáõh–l°¬dW9'Öö¹%rÇXø_Ï¨žUè=«€M¼2ÜJ’N]™EXIƒ¯‚èŠð§uY‹a¸R¤Ö¸žªa)6f˜hËl£*kA«Úpo]ñ6ù^ñº»Mù„Òb÷ÈŸ­œ}m+šÌBkVú¦´G•z‰¦ªJ6©„~›Èž'œG'eô”‰DÂˆ/ÓÀ©eÉúÇHœàû‚¼{P÷p½Œiß
Ò[ß¼e®¨©ãYßLQ-ïÕÒ6 ·Ç„q{aQÅ&¬‚.1oì_yÂÙ'YeÒ¦ø–ÚÔaÚcC;è³“Ê§õw>p†I,<U#îüI„ÈböËôÙ³ÌI/Gßr:lŸã£°ã†×%¶žFÔ³öe8‰Z  0707010001f083000081a40000000000000000000000016a102a8a000000e4000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.freenas.conf.gz  ‹     ­Q1nÃ0Üõ
žïÚ1c—ŒE°Ò¹êJ)!Éï+7Ž «näÇ»cÓÔ„i¨"f¹^€ÀZK®®»ºÝ™÷%l#>8\>Ììø×s×=«àòˆBn*bnGmœøsÄzmÏ£Î‡HpÊ^àþ3=ç1=¾Å€»¨x‡ÜH`}ï-½ÒÄÛ¿„EAmÙ8,¹hbá$õQ( ÎnØ&Cwœ²KOG
ÑnÌ–ÀÓ‡³J«Zm{ïlU\z‰/Ûâd·Î^ÊZ4æoÞW  0707010001f0fe000081a40000000000000000000000016a102a8900001080000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.sync.btrfs.conf.gz    ‹     í]QsÛ6~Ï¯À$ÓI2'Ë²ÛÞƒ›dš»¶s™kš›Kr÷ÐéX	I“–Õ¹»€e[¢lÉª[ø!vDb	ì÷íXa—ÏžíòçÉ3¶Ãg–E2`Õ²lRé©¹§¸Ýön·º{ò3ö™–E*®~yòúµö“'Øû±\(žµHMŸíð•dUòI&š}ÒµÀZüZK-ÒkJ­.¥‘ªÅŒ.þÀ3CWR1åuVµ}þItAs¡eÅ‹Ä='|ÊÞ°¹à©ï„`0\¡íuSŠDNe÷ÌDmR
<Á$pÇwÂT²àt‚™zr©²:‡öSVÍÑh·
N•ºM£Z$µ6òRT¯ú¶Plse'še,áE*S^	sÖö–ý¯}N¢ŠK¡ƒL”‚GXÞfFÁøÊL& ªEÆ@7-4:a•‚ài´ ÙçÜ´ŠÄ·þ¨jˆ5œG¯å|ÅõLü¹Ét/(øžê’þ¼‘ò™D|‹²Vá°ÚD¬Ô[0™d*¹ %œ—ÊTçZWÚëßy˜Ah¬eY±J1qüŸVB“Þµ0d Áì–ñ:7#3ûÑ}¯µÒèž@‚®Q,Hq¿[YobÝF8PE(ï
e ©r€{7IÌÖÂ?E:YF$wk”¡nÇ÷ÔÉØWUF<wc™ªÜ…aªr+ü`š=‡);b¸c›tz½³=ºö[cé×^Í]£Iš½žv»-¢ÐµJi1Ý¦N·÷BÕÉ¸®ð+ÂºXá×}Q…_[ƒZ—¸'Ž îT«Ú{jElj]ÄXÂž@T{WP}AÕ"F‡¶Et"¦8Áí9<"³š18t?$wòˆl‹`ŒíÇ$ïZÁg+XchhgvyßØÃcøbdhOyÐP€Ë¶PÆÀÐÞÀ¼sd¨ƒÍ¶€Æ¸Ð!½G`hŸ;ÀÃBûCõŽq¡.:Ûb£BûÃôîa¡.:[`ƒBûÃôžQ¡.:ë1Å‹âAb;ÁpøýCd¥aµ çº€V´á³€©¬=%'ô¥„Í	^ó@š[Ìe2 •†x0@G	Ï2fê²Tºb¥P%È›óK(ÝP
öpq/?O]2Ü)Tý«AÖ]ªÂHÐ1 ¬‰NÀüÖã¿Ò *°£ªFÕ+²¥ [H€x"˜œ`ç)‚é©Báƒº$Ê˜y]¥jVÛ<â]eðžª6Hž-øÒàX ‹c>û(*<ûÝ÷?¼ýüã§Ñq1WYHÿLäû8ûë¯§
ÖÛH¨¹‚þ®‰œŒ©Û«[qç{Ì}šã]‘	 ¸˜JÐ4‘µª5ž²NÀV ˆËÓ³±ÊÙ«’Wó7¾{cÐÜnDJ:»XœÝ ‡þN”ií/_…–$íØ’9/f"t†×}²(ìƒ}Ø®lìqpûñ¦ÓÆ¢Hx„ïÄØõà$Çã
ZÇø·µ1à¿l9à‡Û$H®ÄU…ÜÀOÐcB§™¬üí„T§Q:kà>°L R
|¤M–v†ƒ§¢ÌÔã©;CC©\‚N,èy›è“ZYe$ÐN¼58\¯Ôk>»Òr6Np¢8)™ G˜´Ó}E;ËâÂ …Ì\ÕYŠNß9÷Nô:OeBŸ¡+WŒp"7‘@•èRyöèX€vŠkVqsK#Üb˜îz	.ÎSö¼Pvæx>`ä±é®´J@›¦ˆ.C¨ðp™ÕOàöƒ>ã,SÔïI]©^
*ð×‚Óà~­ÁÜÅÐæ´ 9®Iæ}ZÑbÝýLeÛÊÂ:Øs~	µAoØ—¶»;hþ\`ÍèÙ¿ý³0·Ã_gc…ñ]¦Ms
ÕÀëJå¼’	­E½[ÅUà}6bZgÈƒ¥dò&Ù:/ÙTfÂ,M%rC¬W<‡õ1™J£¯×;`Õ-vVÎÎ'Ù…Tç!gó?Ø©[˜
L`¢HM+8Ñu^ÎpJûûüË&…›Rp ‹úîGÙ»0Äxr	¾–´;dÿáYœˆj!DÁNF„áÉh4²
]ˆ,àK3XºŽŒXAˆñõh²IYŸÿZ«ŠGX×Â@‡«Ø&¸“Ô˜—„;_è ¯i?6¯(7Ï9&.ëÙL+˜aÞü¢»vý‚u¸µFZ4MŒÊ0,r‰t@IF4k²ŽÀ@VÉ5ÏE%ôýÐ:ŠºëeQ_±„Z€sN`©˜ÃR 7ó™ÌeÕÝ é8+•‘ÄÎ•þØ†´œ¬ó	,@¹L´2èŸÂ%Êò
<™£î¤‚M×xüõè‹oáZÓø?
ÂX êúÙ]žÚ§«÷Weï÷ÐšØOÏØ`f^ÛDØ…k³NÊwÖFRä(uq &JŸŒN¿bVÿCö®`<Ü”õÑDÕ8ÛÊª¦@°‘ÂÔ.Pxm³Ï€)]…Õ¥ìl“éÈŠs¢ín¦Cè7k‰û[«
Sxr„|^Ø¥„‚nhßm+dàÉézöEÞõö½oQóìÞ‰qeŽ/¼GE¬V‡q]²¡¹…J:Å¨´Æp;c£ÁÉà?¶"˜øÁí`æ"?'_ÝÖ“ù[wÏñ=™¹écç-– €ÒKöB8l_ZCÿPWG¦Gïí}°¿óÅìa·Ëµñ‰„ôáFÉUz+oÐ¶F>¢ŸìP*?Çˆ‡VYäÈ.f›·lšñ{1Bs=y‰„ð1Jƒ¹€¤å @ý dÜ=q·H‚¹hêä¤ ýåÀî«¬|^ÁnÌ~}…{;¬«Ž{qD$z6ßOøyÿÃ¿Ô‘ÔÇË>|x²täýðÞS&Nß9hæçZPŸ •-ÝÀL Õî7%¾g¦žØMå78
¶•ÌÐ€­`'!ËÖ×O6pÜ,xYÊ&R|Ç /g­VÝ’Šöú«»µ ÅköÕÏ±Úß"Ä™EÑÃïµÞŸ“SØvQ‚¸m\”\ÆUÉïbUò´Ç}/MHRûd·[¯…Ùg4jMÝ[ýþ0\ÏŒ½¹—8¸QÛ°Æ‰•URYÅEÚo=ü²M]•XNåaË©ôÂ®G¾L¬¡rÈ*ýQì•%§<dá”žàmÊ‰ÕRQ-¥x½2bb‰”C•Héb<˜XåuQúCÙ+û%C9\1”­ Ü˜ó+ ®J$ûdºÄ²'‡+{ÒÉÞù-±ÖÉajlD±¥“Xàäaœô®O¸&V59`U“Þ ö‹ÖÄR&XÊ¤vƒ5±~Éê—ôÂ®_¬&-9PÑ’Þö	ÕÄJ%¬TÒÉ~‘šXžä`åI¶Ars &Ö$9XM’Þ@öŠÓÄB$+DÒÈ-Â4B/ì÷˜0OI”Yu!Ëîn i¡A¢tÍ¨»31Cö_®­2þÕ´’y™I,¼ s§®J¢²uL°	¥wvOcº(àú°‚«¥išñŠ;þ´Ì!%o¢Î¹Ãóñ­•÷ãs	4*y"ŽŒÀÄ9Ì¶G.§H£TZ €¹°„áI"À’u]ÝcÕœ=oÆó¼ño[1t\ûªÄƒzt¶³“ÓöJËôÍ‹W”uýf0_b1“wS[ªÃU¹¬0An¯Ë©0&Q—ã1dÕV:qÙ	‡ÇŠ¤ò…?L—<)Ö”Äq¹ùŽ6£}fÉßtLû“¯uáË&,‘4N}À‘Æ»¼*Þ°JâÝTž(ë2jÂ“Ÿ>êË!´¼j$yáXƒ£=$:egÝ÷°VÎJMítm²S\‡Ž±–ƒ¦Z•|èï˜jõ›(Ú»š¢:>ßI²÷5Á,Ð0{Ùr“R`}W- L]gsn\±‡á’2ºä×d6¼ÛŠ7ZráîTì›Ñ ;ø°gx¹n§î©¶Å
(×B6í¢SxÇŠ97É\¤u&ÆdÚÔúž‚°þÅDà5¶š©ÑíÐÊÚën`ç©ÈørìzfVÊÐ†Ód”cº¬k	S×Gál‹Ì9æûb2q%“:ãa†L“ì’0' ‡‰éËvd7«BÞ>¯ÉðO-‘ìÿ1CÆžPf§äú\"ê^iêãë£>Ë*&z®Ðs¥µ]‚¬¼c™ÃrYÈ¼Î©­Ã—Ö»xæP¢63<¿ÑÑ¼ ©h1‡æ!SÇXuËÚÏË!>µ›!<b/
Õ©n ýx‚n]½·‘?ìnÇOìÍÐi?{û÷Ý
ëãºå¢Pì\CÏs¯€vO —!Dgs\}LéÎÇ©JŽáá…¹LŽo5nR•üG(¼âWæL^X†^°ëï{Í~~:FG£ø÷Û¿žàtøtÀžŽNéÓ/Ý§Uë¸ºxúË­¼Àþ¥±bÔ¾JŽÝwåëI¹Ú@4sg·´s~Š6`J‰IàDÿÑãy­tÎÀÖÎ¹œ±I-ÁP@ÚÇ·?á¢çÂ4©0MO¨¡­A”ÝXˆÇæa%J[~’žÛŽ’r¬_R1u¶ºÂ·-,¯mãM¯âŽ_	oQšÊVƒ´K‚L…-¨~ƒ[øÕ©-Ê{i‘ÓÖ¬{X5ÔŠEì¯­h:ëp·”3"\ßø&–6õ—Ÿ0iÚ¯Eh½ç±'Ðæ„VËËFázö(,…y_¸R·ü84¥Ò…*Ž©ˆßgN¹ªÞâè&&«ºpe°p=µ¯¦˜Îˆí¨ªk¶ÄžM±
åpÈ.²qÆ¾ƒ½mµ:Ä®1?æ3×½°k×jWß­o°˜÷côbŸÑÒñcˆ\tÉ°&j[ÄHš=“F•½8sSÂ
¿}9sS®EV9PaeÈEÿIs¤C¢#gEw-Ga“¦Õ”U®×Ö~}A”	¬å.¢Eî×"E?
_~ë¬ÓŠÚ?…@Í†@!%6Ñ'çWÜíNÒ/ç<ÏeQ¯l±>’‚ôöížlÈžXe#r|¢°†)÷£H3q‘õnµŠÕÚá´q*äRñØÞûÒÑ8½ caHöm¬%Ha¼,3Ø0¹ŠÂö{ˆ¶~ã3-¾ðÁUÅpA%[!7üòµ	É›ÎS} ”"˜–@²ðÁöneÊàÏ±qËÑæ3¬¢@®#)í£—Û¿—#E??×¥Å&OçNìEíŸFNÕ†H«ÔèA%û+2iÿLÂàÞ#"RHŒM<²'#öÏ#«éGÃ£b¬áQÅgbÞÜTžãU>yê ª y=‘•æx	þï¿¦ t€H¶°›u¡³¼nÃúYÍÃ Hƒ%ºÊE+WÓŒÞæaA×ò²SZu"ðun ®Sšþ¦Wk\*™â{+–îUUtþc…èîu$ZåÁÓì1#wìr<ö¯Æxm¿£ì¾ãÊ?ÕîPèêÄWõ³›ü~äØžµ¤1ÞÎÊÇ|ÄúñžÏO_;‡`Ýï8m÷(öºµÁq.Ûë\hú1Ìe7ƒ¼ÆÿÀã(Ä   0707010001f0f1000081a40000000000000000000000016a102a89000002f4000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.service.hashpolicy.envoy.conf.gz  ‹     íWMoÛ0½çWÈa·À®-ºã°Ã0`½C Èt-D5INæýú‘òGì&)Š!;‹­ÒÒ{”²\^òY,á‚ÀÕ*Öž¬Ñm©õèvÔþ1ÜeWwYîß›]ãJüù}qŸ7ýÐm{±=l°ÝS(ïKÑäêdÈ­"5AãÊxN¼¹à#ÜEM^­-Ž•ß+Q"4&`yñv&òÊŒ{º›EJ¬TcÓaŸÉå€q5“”Ó}%‹ª‚wP£*‡e 0ºxô¨Me4ç<¡ãouæ‰+DÍ˜U 5ðñ¨²ã-§ÝŸâŽù®ˆÎ³Mƒ+§¶x%y ù±FF€*HüÞ‘Ä¯*ÁÞXk„&b	‰€ÖI—ÓÄñBñ-|¬&í9A0%Þ"Ft	”cˆ$p–öC0b*ø¥Cë«MÐ˜ž²ÑXözê½Jf¯R}•ùœÌÂ“&Š‰”"F±@="KÏKÁb¢Ë4øƒe|Ž5Q,§¾F1¶Ç.X>v&øe”¹™š,Ô¨Njh¹½a¦E„ÇÇO£8\%–ÑL;|"ç¼S³Mø*rð€z7Vì‹($žð„L‚x[Ÿs„°†aÕÿ»Nì—ZY‹1õœ½jrOÔêgø)¤¹/Žõ|<+örÏ'[ãÔµã:~MÄHó†ÿZSHÚÝ˜°¦­o/ù–]Árñ<°e·þ;°RÖ®•ÞL©µÙ;\¸2Oýx¹“é FU »ÍBIÝö…Mq
½Ä2Ã±Ò}fM”9Â¼Úñ:’a7ìkìø¬Ætrp•îr7æ<HQâìÉ±Ý²7ÿ=ý•û2Z¥åÿå_!!¦|ë’Î&;ÎŽïß§%ÆÚå»s,X£ëg§‚¤Éôp7š¥‡ØxÏ>ä­XpÇÍ	>rYWþüVØ‚
˜ÇIÆj¥´'s,—‡þtÈr²ê‹ÅoSeÅá  0707010001f0ae000081a40000000000000000000000016a102a8a00000114000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.sysreport.conf.gz    ‹     ­Q1nÃ0Üý
^ëÄSíØd,
ˆ•h[ˆ"¹¤ŒØKß^¹i”]ÅÃ‘8ÞëºdU5¬NVašÇ2teÕ•Í®zËfk¶ÞÐò^mš´ž›îvZôHfv”†MÁÚò&üp”¯½¢“í0}Î–ÉüŸêqvñ&ð«m»¶mÚÇÔ¯¤ÉñeI&Ò¶·^` OlõÓÄ$:m~ýÁ„Œ'ŠÄÐ†8tºAmðÊi)ðÁ\ð8Vw¿NdúèœÂÆ*€Þ@oÞ­ìP
çÒX©ˆî˜fá½ŒÈ´7Aï¯_ÈÚ2 «¸ì’§§Œ=ÿM¥úzT¯y  0707010001f11b000081a40000000000000000000000016a100daf0000019e000000e600010003ffffffffffffffff0000003600000000root/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ý    0707010001f0db000081a40000000000000000000000016a102a89000010ca000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.disk.raw.conf.gz  ‹     í]Qs·~÷¯ÀŒ'c{JÑ”ÛôA±<U›dšIwj'}ÈtDð$1º. N3ýñÝ] w )ŠTMÙUsšÖ‘xw{À~ß.À.øôé1ž<eGüAq¥´W#æÖ`†¯>ZÜq[w\Ý=ù;ûÔHUŠ›=ùŠ:}ŽÝ~òÛ~%Ö+mÊ³¾	…Ü‰ËbÉÍe)®e!,ÜwrÄT™-tÃg•è^üÁ´/ñK+è[ô-¯,]iŒ¾–Vj%Õâ,»RŠ9o+×w"
“j)Œt\áE•àsö†-/c+ƒþã¯ÛFr.¸g!<[àm…V×Â$ògZƒ$E„·Û.½SìoRµ7#†ªcAuŒÁ”vŒ·N×ÜÉ‚WÕ:(¹d«¥P¬×rüÁ‡ ·BÃkE9fßÍ™Ž9M=cÏÝRÄN¿ "/=°º5…H•F‚¶’UÞÉ¸*áÙJÀ¯ð`ÍZúìd\	£DžC¿º…@ ÒðÁ]Ÿ—2ñÂ=óƒVÊ€/cg•´Žéy@Ÿ5Ü--f¯­)Þœ½.­{“_«yÓ@Gì(Áwµ”NØ†ÃMV4Ü ©ÆìÃR[‘1P¯°m¶&œ­0$˜ˆ²…•H!¸4ÕŠ5º’ÅšIÐ4•„çH¢‚<x	ÿ}‰-æ¥½.ÆåäŒ>Ò†•x	¨èÉätû¶Ó]äYÝ6Îá•>	M> \¤P<wÌ.u[ŽPƒôñ® ñ³o$üe Lµ5Ê‚7¥¯ëbÊæÚx½ès¸bwá¡ôec„¨wÉgÚ¸GMwåX¦f#K04{Ö·–ý»ÏA£EïÑ§S¦Ó{×õþ§¿xo”¾m–+ ™ñÖxg¡ë»TÖA—¼»ˆèÿˆá˜½×µð¢7ôM8Y7•¨…rô:ËJMã—m›¾C¨8ø®Z—0	{ó" AÓéU2¢W#ÂØÙÕêlºIª)jiÍ'¯id„žfæ$ŽolÆ­´DàMAÀeÃ6ƒ˜zp.‰sA…Hk	éÞ•DG³ä×àV.˜uƒ#d¢r!bHðÑ…ãU*àÁ¿’?N&;1ðÙ€H@äk1—
‡ìþFP!ÝÒ3/ ZC7îã¶`ãü
ä4Fjhßša ™Žý'•¸Æ0ÞWzÈ?ä¨*0ñ1ø³ylO¼×£KOC;ônxu­!z4«OqØoyàpfWƒæ	tð—}8˜ùhˆíË“tN Ãµ0¸ mÄmŸGÅAÄs@øÓ"ÌR8œÔŸ|Éž	ºâ¦¨`zw-F ¯xÃÁhU­_n¿ßå]ààÌ:€a‚o¥ÏiÅ‚zª0©€˜ óBì›éÇ‹N$MC¶ƒ?©Àà`Véâª›šOÚ†"ò[á¶âïODUÐ5'kÈúç4ç6P<.ðEsà4ÄØ0TtÚ»Eü<7ÓÐQt|vœA±3˜é- †1éL>hv…1½ˆ}æÖÂMÃp™0¨‡™‘äsf4°m¿úŽhD4P!LñZl-Já:B%ÕY|´¬~íj.«­0<ä¢5ÑvñÞE¥gh\ì ¯Õ-JÐ›ÑGtÔŒsúlçB9ÐÖe£­»ìô7Àà¼è&”àñladCŽPÜˆ¢ÅeÄ¹yø™ wVœ ãÓêlÚõdÊüG`‰Æhc¡Á Á´(v)âEDo2wGšùC4\Pþ·P¦ ’*§CÚýa$1÷†þQål˜ Ù(SÝN?Ð ã~¸êa}÷H–©›c¦nîƒ_«†±òÌ2Qík•‰ˆCA5bˆ~î‹èLÀ4A<tø“"s4‡àçã<rô¹/‚Cìó0&ù±ÁÏ>÷‚u}Žf—û<îßù<œQ~dè“£s7¦Ø`¡ÜãÛ®xüþ*ªÆÒ"*ü«à)òj0]‰¸ˆš¤u@Ú¦Ë4wÀ2…·´:ÁT·.o ºyK~M›ÇpC#0]É¯‹ù‰(xëºKžkmKisðêF++AÇ¸NëóÓ<œ€ùÎ|4i•ÿ³Ì•O¾/uÙSÀïGÍ“¥ßäÈ¶pÚ†(c—­+õJ¥;Ôß9‹÷¸ÖRŠYµâkÜDB²€ éT½äÓé˜±÷Âá¢ý×ß|{ñãß>øãÕRW)Yâ;APlã8mo¼^jA»Gl©U¶ú¿ñQ³7Ç›à{,fØ	]‘Í’gjQJÐ4‘ÕµôÑ`+ðˆ«Ë³©®Ùk\¨~›7¥9\O%ÑÖRèô8Þ	ê7ÚlÒ?‡–$}ßŠ%WQŽ²îåoÊ¿¸Ó‡ÿ``¿=>Þ·7%TÁ›!?ì(Æö>ßÆß½å›­hh´_‰’¸q‰zÌ·¾\¼Êž!Jg›5n	–	T*éö¨Ï!åÐc0àR4•^‹2
Ž††R¹xÑûöÑ§´œ6Žå­ÁáF¥nùlgäbœàDpR² Ž0é‡ûŠ~8–êÊ"…úmÖàÜ³7Ài;Ÿ>CW
®)Dî#nÐ¥òêÑ± íÔgdq{¡‘]«Âæñ\—ì™Ò~äx6bä±ù±´I@›†ˆœ!Ôx¹¬Zh#ŽÝQûI›1«4µ{Öº.gNy®˜ïx€_Z0wL¬¬ßšÝ’šŒûT±X±À$‰”pþYLfEu°güê½a[úçÂÝÉãÏ0µbÆðÀ˜ý#¾;€ c;ü†©¨¾á~ÄGÔ²ô¨T%Á­bÔ:8f?Z1o+äÁ­R*yƒl[7~¿{m¨-±FÜpÌ»%Séôu~VíJÌ[\Îª+©/WB.–ÿg«…÷0À„*íÙfùS³À!ígló¿n5)œ”.0'±¨?SÓwï ‹ðä|-iwÌ~âUœ	·B±Ó	ax:™LÆì{*¹I‹vbgé:¥ãåh1¾œÜlÑ´—¿´ÚñÖ;aÝÎ….(3Öpæ‹Y$Å\&( .å˜yÇÂ°>TgÀ¸ùE>îfƒ5RÐ4³ºÂe‘k¤J²¢‹É2YâáµpÂŒÙ·½£¡»¦z3VÐà¦Ó“Skp2_ÉZº|@ ¬ÑV;7Úã¤p²­g>ƒª–…ÑV ýËÔÑN!‚KÔÔ0éšN¿œ|ñ'¸Ö5þ¦R¬@]B;óðÔ?ñjó~·Òþþh ©‰ýôŽ=&`—àµí`Çpmß‡º@ßØ@I+GeXêViàãÓÉ«?0¯ÿ1û./å hNfºÅÑVº–²CÁF”mÃBáÖdÞ)]…èRfÓdzò¤â˜sP?ø•ÂÖ% YKœßz•Ð2E$GÊç•%4e
†vdôO!O_ÝÍ¾wûÞÔ|{ôƒ€ÃLâº2.Ž/ºTï˜ýþÝÈ*îoá6Xpçj!Ø›ŒNG¯ðƒÉÉ«R?Øf-êKò¥¢÷õ$VþšÏ9¾!3·‡Øy%  Íš=—*[‡€	òoèïZwòn~òÖßwó[0ß® øY®_Ÿ(°
BÏÓ9:Œ†[¬€ŽVÞ¡í|B?{Ø¡u}‰+FWGŽ1Ú\°yÅìùÍõô…Ïïök”T#$= } 2ôqöÄCDyÇ^N	Ò±vçU^>wŽJ%¦¹][{6&¢‰žÝþD7ð÷tù—ÒÕ\¿{÷6äÈûîm¤,œ±qðXke(Æ}ðûùNIl™mg~Rùö".ÛJ7b¾Æç4eyÂZàúéŽÛ•ŸƒÁ?Åq—³^«!¤Š5}Ùl E-yâœýag°z¸ $˜œ¯JÌñmò¹¯CºgP‚¸íJ®‡¨ä"*ùÚãC‡&$©s˜­·––ÙÔkªR›û‡i<3^ä:qp¢¶'ÆÊc>KyLXiß™ürŸâ˜¡&æÓÖÄ„ÝI¡C!Ìç,„9ÅƒRA‡ê—OYýr xû@‡’—ÏWòr„§}u.Ÿ§Îe/Š‡W¹Å-Ÿ¶¸åè‰b†Š–ÏXÑr0ˆ‡1CË',c9»½1ÌP»òÙjWAð!Ìc…ðØ23Çšòä(çJ6¹¥â)¸4H”ÜŒr«µcöOn¼2þÞ=…ÇiJÌÕÖ™¶ ¼_úB§baF`¾Y…†¸æ÷cÂ
zX¯m¿:ÉµuÌ¹ëLæî¾Ë€çãÛxÐŸ |Ò ÜÍŒiOÒü¨…'/
–lZ¥ò8ÎžuýyÖy‚‹^íðÝà!š–¶²4¨×F–ož¿¦DÝ7£ñxüë_ð $ü$œã\ãÁe%æzµÍÈ:¯Ûf:Á‰¬ÖKgÉ1áøZš—–“§#ÅUT!ûÑÑfò‰Õ·íì}ˆåÙi}A}À‘Î»¼Vo˜“5h=5à‰ªœQ3^\ÅŒÃ˜Aßóª“…cÙF¿¯0ggyRtZÞ£RWîCÇgõX´óÑŒºªÈH†wÌþU¨þ®®+fmIþ¾î5˜8Xeç"7)k2–½„Ò´€4Û™-ýñ 1¡(½¡Íòœü†Ì†çOñNKá%<l¤¼™ŒòÎ§-Ã[È%p?tÏÏo_wg§‚fµZ^Ì¥-–¢l+1Íò/Kïx•ŠG—%HÛÜÜÏDô²ÇþzèØe)*€2œ"›ƒO!t§KBÆËð$]ïCôßç•ãá¿˜-
×eÑV<MªèXCÞÞä01ŸÓ	Ñ=Ùí¦ÐQ´Ï-Y þ+O$ÿ7&UøM-öÊŸçËÚPü+ò^ñ%çl²ÇgyÅž+õ\eëC[ÎfµT²nkFjËøÒ{—ÈÌ¸åõ­Žæ9Eô-)S§X¨éíçÅßš'•NØs¥³„xhÇ‹tè]ÐS¦f9“=T5âÇË±Ô,”‘‡®ð8âµ÷í%&ßƒëÀ3SÑ¡¿xnUÕŒ|YÍõ‚ÍZYáù¶ìýÅáˆÚ¸KÞµ„Š”}yRukŽO+À"u_™Nïí{I9–6TÌR[lFrþ	®«xßÊÃ²Ø=ªÖ|¡¸wý)ÿF˜%§Â1Ö4êÄ(Ä×kÃœIÔ‚ç°§
x±ˆýÖÈ•Å[aÈ¶Ù'ÄG<mÚ†à}"ÿ–€=i„&y Ô­¬(Ã¸å$Í’Ç”ŒˆÖªx†' RÖºÒê$Šø}T@G}Ï;º§9‹ºUÉ1ËX7ÇL'|Ž|¨ÖØ²9¨Çlf°gìk˜Ã¸Í.æÆ`cŸÏBóÒ¦m•µ%ë‹{¬üû0K}ÈY*éø1ÌPs2Ü1;Å©À@š&nâÌ.¦¤‡Ê™Û˜²5sÜäLB…»(C.ú7š>™ÅZ¹Py,GÓãî9¯)¯\?®Ý¹Líøâ7l‚|ùÛEg…¨*°%3“Îp\(‚¿ctAÌ |ªf,¯§&l|‡GýWCõ­iá«ž:¹RÍ+ªÏ÷E_ó—®7	< 	ÄeÅ¦·Ë_kYb%ú:>CÓóŸˆg·‡/#¤U °*>Æb÷óðUùKx«_S™bHQÄ<]¿ZƒaÍK¿N}ÜmîyìñnŸ¤{[[Û$	Ö‡ívä;ewíw$waÁƒ†‰¦CDy1ÈküÊ-Hµx    0707010001f04d000081a40000000000000000000000016a102a8a000000eb000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.cluster.packages.conf.gz  ‹     ­Q1NÄ0ìóŠ•RçâŠ"J>p%B:³ž$VBì[Û‚kx;Gr ZO±Åîjvf¶®K¢ª© V:¯yÒB!º²êÊfW=of_ªUë„Ë»ÓÝNaÒŒ<l
bÍ%°óúuÆ~íIÏa=D‚s²óbÐë4Ç›ÀO¥:¥u—ëF
Ê ×¥àÁ¶·L4`Xþvš™çã¿üuÑoˆêPA7š£uKwò)Œ~N´8ºvt~ý7iÃ¨­qÜn¹íl{#\–¨?YÅýÞ{øë£ú`NÑ#   0707010001f0fd000081a40000000000000000000000016a102a890000051b000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.subset.conf.gz    ‹     íWMo7½ûW`ˆQIŒä¢ÀEÓ¢AÖõ¡A/A Q»#‰ÐŠÜ\É
úãû†û!*®íPŠÐ„Õ’ç½y3<??æsvNG|Äœ¯fžÃÑÌ×»ãÆîìC}Øs§MÎwÏÄáï¶Öåãý¾™]¯ÙŒõøHl|fK5+¸Ûì*<ËˆãO•vœß)Ýh¯­Ñf1>Éy®ª"ì=—Ú,Ùé LÖìR°šÓ÷´d•·.0áÀìêq_r¦ç:Ãœ¬ÍbX`Ýg˜ñ¥§Ê³Ã/¢æ<lç–LÎ°4ïßèŒIÆ{[¹Œ}¶K-VhOF¦KÖô3Uà_YZ¨d[ÂÞRmpÐ8¡d§v0¤Zû‰)ìº#å˜Œð¯‚­l]Zã5bLsë8Ê›Nº¢³ /•Ãj.þÄß»êx€w#GAÌ|:GÐýxï-ýµß'³fÃ.ñ`fAe(s='Q@8˜N{¤²€ùßqVÎáuzRkk>¬	È8­y=c·'VÄµ[zÕø÷º‹É¬Xi;Ù²^,Ãÿäß­yãg€›sÉ&÷{Ã™c@=)Äñùã?b¬Mà»Œ,l¶¢ë[±PAo˜êèèOUTppÆaËlh4Œš0‡ú•‰iÖ>ÝaãxDõ-€ûzø²YYM>U6¨¬Âš@÷riÍD!°ÚŠ†Ã ï¢2#«l&Ê‹wÑv
9çiálU’žÓ‹CEoüâ¼GŒÓÆ5jæm$§ÐA,IÃ¡{[’ÑkìôÎ:è„Z£$ô¤ü¦MuGY\º2öGÓ)tA)K…^kJ/±%¤STZ¯#;¿ð§^(¾˜*JB°Ö™³žAÿC6µÕ„U¶”Øi›àÃëá‹0Ö9‚ÿbHªšÄ~Â‹ÄN½âòËùakëùmt¤Žì{<‘~	aõ§8†´Õ"Õ:ÛFÇ(o:š®ßÀçÑðòÕñÐ5z•p@ÓŸY´MV’k9b|åø kÊ-û¦7c.•!|Zeá¬J)¬ká æ3Ê+jÓZ£ûiB"¹Ó‘#åóõWš,¸áZ?X¯Ž.gß‰w_­½o%òì­‡n$À
”Š¼hU°­~Â¯´N)º)ÊS¡Ñ9A12¦1{£Þ¥|ö/÷
˜òáa0ÑgM¢–ž}®’xý™ þ9¦¹ÿš<ßc	 ¬ÛÑKm¨g;4äu¢ßV¡;ïßÔóV°ƒÿè›^@Éê®:S^”"½-‰`”Ê{„²Íòí:É‡ñy‚Ö®'8{ÀÝïÄ‘cT›·4/Ô‚^%]GBˆ€!Qðò)×¾~5úR ÐO@Æ=OÝ$äVÛÉaý¢GAù•¯í«x]Æ+œôÕºfcbª!b¤gwÓnë†¼k\ªsc#9xZ;y{“ªlÈ{{ÓR…³uËÚZ‹ð1B¶kÎ€4ª‡wþÖ3¹î<ŽñFNÑD	Ø2R–'¬×GOpÜoUYjƒ„9Qüÿò¤hÕ¦¥¶Þ»¤¨%+®èÕÊtÂêÛ5!MÊ›Ç±Ûè&ùEá¹M‰àödS²9u%ÿ‰®ä;ÉÇoÝšDKû›Ûz&Í
!§vÑ½ÄÀxµOÓ~fÚªÈ&mqä¢Öö8þM•   0707010001f060000081a40000000000000000000000016a102a8a0000011d000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.stonith.conf.gz   ‹     ­Q=SÃ0Ýó+t—ÒfXÊ@˜8Ç~itø#X6´ÿ»)mïX£Å¶Þó“žÔ¶kFÓÒŠQå$ÏiZKnÝîÖ]ó~2ÛFöû¦vü‰ÃOˆæîRX;Sò·+FŒè0«Áâ\¨ˆøÊaþ£Ê6]:Û?)Piqe†æ‘5=Ò‘u¥éà¿¯þËd±?š.Ú¢ÐO(4ç”7”eA=^û—ísÿDŠf vô&YY{¨L1¸ðšýîjç9±åÄ…¢ƒµÐ‰ƒïŽ%dyÐ2wb¡T²^¹âb\îÁÔÒWj§]Ý*¾Ï3âÂvY eLWlÝ—ÅÑmö›TgØür…rp}     0707010001f0ce000081a40000000000000000000000016a102a89000010b0000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.crypt.conf.gz    ‹     í][sÛ6~Ï¯ÀL¦ãxVRlo³n’Ùì¶ílÓìlÒîC§cB$$aM,AZV»ýï{. 	È–%7RR·ôôb‹Äp¾spôøñ>={üAr™¶—#Ñ¬*%ÒzU5ïIn¿£Û/ï}“}\ë2S×?<úŒ&ý‚§ýèŽþR­–¦ÎÎûAdê
>ïñ™dSSÉi®ºŽÞÕ­Âµú±ÕµÊn<¨js¥­6¥.çôðK™[z’©™ló¦ó7¦¤º\¨Z7²L]?¹’3ñR,”Ìü ”€éªšŸÛJ¥z¦Sxg®Jh›S ›ÂïJÌÚ<¯d³f&ø»VÖù
%¦¹I/‘_:Uhøü
Ü}
ÿ{úóìÇ¬üåi~uº‰Ï¥,ÔGetÇÏ{p92•Váàoáób$l;›ék•‰¥ÎIàŽ&‡Âh}Dêº2z ¡Ç/Þwü› 4U­TQ5rjêæÁÁÚ=Ù:B¤²Ìt&eÏûÑŠÿõý¤¦¼Ru0‚©1ÐUAûÕLXÕˆÆˆ$i€@’ŒÄ›J•o¿û; “çÂ1Fe50ÂªúJ60a†MÆÙ¿™š¢€±Á<mSBÝC|@ÿ"†ñÖ€ŒéFÖsÕätQåªPeCÝY‘G[UÐ¤J[‘KÛˆÂdJ<QöúxÒO' ”$3dI’ O™„jFâüryž¬U‚\S%dÚh˜'H'Ì4š¤ªÇÀÓÖ)I¹¶,úk„@‡Ò\ÕðÙƒáCù€ÏÕL—Ê‚˜t/Ÿ·À	pMÌ F´)†I Ï;y	tªZßJP z)€ÕçêJå81è/cHá€J	5õ™ùñøw	knõÆa6Ãk
Ó¨‹zù!0î û#Û©¦^áSÇys766	5La¼„É)Ôo¶§qÐzÜ¶)0Ú,Ìá‹°L‰`,0Ò?OÈ ]uæ­ÕWdÔjV¤–`FL™¯Ž	·?o².ðØ‡zÚ{K¡Wúœ–5Ø“gbºbÔY!ñÅ5Ä' 	6­V7}$Ø“†ÝÇ·Î<±$ŽÛŠÙ-÷XJ‹ï¤r˜Z£é„ø/±3hÂnä8ÅŽf ÓàÒp`È(˜4›Eü< 750Q4|vA±Ñwöà½f@-öŸ›¼fOb«º©´V/æûcà_’‡60H˜ér±0,†ø1/¾™{ ì»èÍ ¯¢…@
â]25¤Œäm%S”/ÉŸ»Èº."¨î1ðÍ 3u;vlÀ8—S•ÿF!öMn"LkÐC!L,A=DutØƒáo0
…j$é=Y@²_àî&âUÇKÿ&«[”@„Öb`X8ì5˜bŽÑƒ¥wüÂOobæË¹º¨¤µÕ¢–V=4ôüƒ}9Â]üÜßV~#¬,[ñZ£m–9¸t¨€J©–XYëžåâÉÙ³¿`Ä%`“H²¶Ç#Šj!R†5•½àÞõ…&> æŒ=™ú‘g,ší¾xŒË¢#§º1‹ÎÓ›•Ôœ‘à`Œûãád¦<BÓP€ÿ¹eL(w7$
÷¤Hî`¾¨Àè]t´†••¶WÝjü·Mk]0êZ¥-rÖ¨x1àxžŽÑ›ò<éf’þ¢º65ˆV	êÉ.”ˆHn@æîuSÜˆ‚ŸÊ_e ±2YÐî#‘¹7„äk¦ÃvÇž•2ämò>€:÷ÃÕTžûÑLSíC1Muü¿;À¸_µXûkµ2 ±+¨µ¢Ÿû":U «C‡?!2÷@s~ÞÉ=G?‘û"8Ä>‡QÉ÷~Öð¹¬Cè³7½|ßØÇáqø†ÈçpJùž¡OŒÎÝ˜â€UÙ<¼Ã·Cà÷ÚÝm­>oØÝÁi/ÎiGb¹Ðé"L¼°¢Ä×@L9NežwI•2Ð[È+Úý‡*UK>E•ž~@
z]Ñ¡µ¶Z+ìº2¥Õ¸[	‚EBààÌ7¦Ìi‹¨üÎÒ~>ø)ë+ÏÈ^øtæ%è9ÙE’mE"cm“™eæ[|ÕX|§i-ŠÌ—r…G¢(,@(IÊ§2I&B¼åó‹Ï¿øòÕ·_¿‘8.&WÑ.4÷	„ü'áxýóÌ(:ã½#<HhØëþÆÙ>³ÞÙ(ó¨P™N“°6mühSÐhäŠì<1…xŽGÃ/ýð:_Æô”ºIOü›0©þØØókP“4Ï-]Èr®²Q4½¸gUrÇ?øƒ› óa«ûxÛI«*SYÉu{Q¶·q>þÎ:§øc:}GÊº¦L¹ ü-&ÏMÜë„TÔ†x{²ÞÍQÊ@ÂÃþ{a˜1(p¦ªÜ¬TÖç°¢!U©',@Ôß6ñ) ­†´r ½Xk0¸ž©7lvSëùItÀHédDhv÷=½;Öå%Ú…i1‰E	gÜ£Ó9(9…>CS
¦˜Žç˜ä6!0šT™?8)@=åüBi/!4²«2µq¼€ç™8*{Ž£‘P@OOy˜h“‹ˆ%„Æ ë¼…1¢ïöÜÆŒ!XnhÜÓ¶éRiqå§pð”ÀüØ‚º+pU¹5œú±N5ðûH¸R«9¦ü„Çm1Ù!Žä”ßp,};÷vÐü• ft&âß¾oøvøóxyàìñµ(Ù/dƒlSÈF§‹z³ŠQ›àD|kÕ¬ÍQn¥’ëKp²mQ‰™Î•]ÙF–¤F]KLZ&UéøõbRµ)Ít~1Í/µ¹X*=_üÎvï¡*àÀT™ÙžpZ+¨‹jŽ.í{ó·ª.JÁ€Æù”‘÷Õ˜brr¶–¸;ßÉ¼…NU³Tª§'„áéÉÉÉDüSÕe”qÓM–žSriŒÆ³“;M«öâÇÖ4r€õNX×R¦`AšR^'æ_âÊÀ×´žõy5Þ0Á¤0¬óÚ€¿ùI¼îÒ¿6RÐ4µ&Çm‘+‡µä¬ˆ`”¯SËB5ªžˆ/{CA	@_ë²½)µ ã$ãSp1ŸëB7ñ:€Ê'De¬&é\7¤p²-¦ 
ÖÆ*ÿl-ºcˆ’éy§,º’äÙÉ'…gÝ@àoÊƒ yiGká)·8[¿Y~ß+@'Ô$ýÔÇ°°ÚvÐ}˜66R~°N`4íen¨Û¥OOÎ>Ìÿ‰ø*®ƒhÆSÓ¢·ÕMK¹Î #¥mÝFáÅ:ôií’Iu´L¦>P^€*úDŸQýŽw
a@^­5®o™%´Má…#”ç%‡†QûqDÈ­POÏî–¾Aîv¶½¯óìÞbÚ Æ}eÜ!¹è
j½ñwøïZŽ|ÿŠÄ`Ë6¸+]ãvˆ8'£ÓÑ~p2>ë!E0ñƒÍ`ª¸ [: z_KbõOñšãRs»‹ž÷X ¦^‰'ºŒö!`|ÌŠþ¦mÆofã×üÞ%¬oA}ç¸ƒÂ«\ÞŸH1'Ô„É«LVz-ïÐf%?¡Ÿ-ÒaLq;µÉÙ‡·y%f¹œ‹''¨®§Ç(~ÒâGnC’åÐG¡€Œ«'é‚$ªOa:P?ñºŠéË¦¡:ÓÆÐÚ®-XÃRîÂ×4ðù„÷ø{¸ýKÉü~Ù›7¯B<H'¼o^{‘Çé7íR½‘}\BÄs 5Tã“?2ÛNyQùÎÂoÛêfÄiþâ4”ò@jAÖO·È¸]ÊªÂRËÁ±íEÄqƒWŠž«.¤òªÑj D-hñB|ºÅ2X.q*Çuo:¾>çªº{%ˆÛÖ äjˆJ~QÉŸPšpñ]×³[­»º»9Íº¦á­Ÿ†ñLâ­ÈUâàBmKŒ3”Ç|”ò·Ó¾1ùå>Å1CMÌ‡­‰Ù	»’B‡B˜Y³;Š;¥‚Õ/²úeGð¶%€%/¯äe'wNûê\>NËVw¯rŠ[>lqË.ÐíÅ-±¢egwb†2–XÆ²v[c˜¡vå£Õ®ì‚à=B˜‡
á¾oËÙgŽ5åÉQ2Î¥®bM.ÙAA‰Õ(ÖZ;ÿ‘53ã_]+¼‹Tc®¾²MÝ¦”wÃ¥/tÇfÆ‡U˜aˆ{~ßRAÍÊö»“²‘þÚÁNrîºí¦{ïÂáùðŽc–Ýð]ec«0×
ÓIy—~†b”i
ä£P,02M^TÖ–e|'ÅQ7Ÿ£Î¼êÉÐ	ß5^dé8 Jƒz^ëìå“ç”¨ûr4™LŽ±þ/Ôkø>'Lt)ð¾s½Újdô×m•$@8 Õ2u¾Ê"B·|©S‹…§Š;ª¨\:÷ƒ›“C&Vßv²÷Î—GDwO:öŒtÖåyùR4º ®;W–(%j*ÓKŸqè3è{¹ê(yâX¶ÑŸ+ÌÄyœ–w WêÊ}èÎå¨‹N>ªQ—àOU‘á³Úü¤Êþ­®Ëgm;Jü^×&æÑ­›(›”5éË^\é j@˜í,|™%pL•”…^Ñay,ü5©Œ[ÉŽK®éR^žŒâÉ‡#ÃWÈ$HvÝ³šóÛWÝMÀ Q­“¹°éBem®’(ÿ2c; ó€¿7:Ë€Úúá~DÚI@O{ÂÏÝÄ.2•KX dîNä|
A`:]2fXº–àºÞºè¿Ï+_È+¾É­ntÚæ2LªèX]ÞÞè5›ÑõÚ½°Ûu¢#¯Ÿ7høg,Hü7&Uð¡–8#ÓçÊÚüY/ßÉq²Åf1cËZ®¬åä–Û´E¡K]´… ¶EòÒ[/9|ÿ£åoK¸ahž+Z. y(©	j²þO°×8©ôD<)M”ã8AgDï‚ž25³¡˜ìPÕˆï,ûR3W6DÚ]I¶=Ãä{0x0ô·ÏU5#.«¹š‹i«sºòí«oÜ…Ëþ”¼	)syR~k§`‘:W¦S¿ý,‰ ÄÒ& ŠYjóõHŽ[ðõµÜxÛÅÃ¶Ø=ªÖ¸PœM(#Ì’+Ý¥ìäu|ân/Î¤*(a/`²ˆýÏÅ[ÎeÛè['|›vŠaGN"¿î±'ŽÐ"ˆ6«
+Ê0n‡Yò˜‚ÑªLð.nÊZ/M9vBEò}îX@×Ï:qsM[—†cYÜ3Ö¾Ze†ê“‰Xºì¹øœ®\]›b¬ÖÏùÜ/Ú²¶`q‹6€}V©‡\¥Â
5†;V§¸„æÀBcªdf“¤„—ì*3·IÊ•ãºÌ¢p—È‰þƒ¦O†‚b­ž—q,GËã®sŠ™Ë~íÎmêFÎÿÀ*x[Â)ŠY¤š^‘U KõT7µÄ"øÛG$ù §júòzÂÚ7ÒxoÌß«Õ
¨¹ïÉêèêr–S}~pg|¸ß¤ð‚& ›ÞV,et†•è+wù-Ï×l†»` ¿E$¼¡wÜ®x’øb÷î‹[â#×+ï©$R¤>O—wk0¬yÊ[á4ÇÍêþOÀîñIx¶uã˜$Àz·ÓŽø¤ì®óŽàÍ!,8hXpú!D”·	YÿKw  0707010001f0cd000081a40000000000000000000000016a102a890000100a000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.disk.amazon.conf.gz   ‹     í]msÛ6þž_™N'ñœ¤ÊnÒj’9__æ:×47—´÷¡Ó1!’0&– -«s?þv 	È/’9®[fúâÄØ}v±ÀîÂŸ|rÈ?O>aüƒä
iÎGÌnjÁxÅÓêÃÈvt‡åÝ“Ÿq²Ÿ4Râò—'_Ò¤_ùi?y‚Ã?›µnŠY?
¥ÏêFˆª¶g|®øYfr]óy)º^ß7­Àøµ•è‡ó-/½©}!ÔJªå,ySˆoKÛÏ {#ÕJ4Òr•ûžJÁì5[	^„a³{oj‘Ë…Ìá›¥PÐ6ÇÏr®
Yp+Ì¬-û_ßO®Õ…h¢Ìµ†®1ÆgrxõÝ‚a™Õ,Ë,È²{[õî§¯ØZ–%óL‡Q	Œ0¢¹à&oíŠñjáË\WŒæi,L‰éãÝK|Aÿ¢'ì®„#my³6"'«º•P–º3¬Ð€G[×Ð”Ù•4¬äÆ²J‚=æòhÒO'"”edI–O	aGlv¾žeÛ Ê«l*˜[	ó0z•NR4cà‚n›\°97ÒL€Ÿ_n8;9Ü€æºgÂ?hu8G üZ,¤`Ò}|ÞZ:à[€íJ° †I$ž÷üèÔÔ0¾Ó  ô-R¹—âB”81è¯pR 8Â7ˆš€ú,ÂxÂ·$k×êŒCß,^]i+ÎšõÇq'°¿²²ÍßzÎ“ÐA=´GÁ›Ô$40…ñ&'P¿ØëEä¶KÑf9`þ¸æ9± Œúãì	2’®¸ÌËÖÈ1~-ÁŠ4ÌˆVåæˆäöùMÖþûÐlb{Ïa•ƒ^é99iØSl¾qKPg…Ø7—@¸“›Öˆ«k$Øë–³y©óó`žÇmMÙ-ÿ€­¹Áï#R%LÍÊŠ{°ÿ9vMÜ²8Î±£`–42
&íÌ">ÈÍ5LŸ™$¢¸qíì5 ¬šµtý¼iÕìIìR·]¶•0ºb†µ`^£G% .Q¢S¶^2˜š#šDÍ~G~˜ÛYxnNØ©Ú°JTs€ƒ^l-‚ÔÆ»C~}æ%{	"zuÁËVŒºŸ^3úÀG‘jD´r´ÄˆÒŽwÐ#)5/AÃœ)±ö#s¾%á?¬¿1±Ì
 Ý ºå†ÖïFÔ%N™TÒÏ¨#ú(<ùØÉ,&ì=öã¼Jø¦CJëºç0Ð<»Ò&þˆ”Þ¬M^ÊØòä',‡ÅÅŠ±Ÿ‰w†gÌÈßÀI]›ã\–|.KðQÆ xa¯ÍJÛQDÍmavBåÍ­i—¨ü4>>ùüù‹/èÇ/^<ÿüäø&"K=«µ±g '´S°qSgòFÖ„yq)òñ¹°"õ;#IÍò1Z<­fY7“Œ¹G°04n(4-’]‰ðzƒdnw1ÓF´N¢ü½¢ŒH¬Ì¶Ö²»‹‘ÈÜY„ðUÌ‡á•2æmö!õ4î&W]ò<ŒfêúŠ©ë»È¯UÃZyOj±ö÷jeDb_¡6bð~î*Ñ¹Xhïß£ûKæÒœŸ“ä½Ÿ ‘»Jpð}îG%?ÔùÙ’ÏÄ:¸>ÓËõ}¼<î ¾Áó¹?¥ü@×'•Îí2Åe_œâ>ä÷OQÖ†µF Ÿ[­Èª9éRøcÐî@Þ-:Aš®æ«øøÐ0…Ÿ´ç¼,»øt-tôVüOÞñƒZàa¬;÷ôãÃ¾†";xÊÞšhm°ëZ+#Çö$xq‚Ìox!JåO–!ñÑR§‘=Ü™õ\0¹T çÝHb7mM1«Öz­âÐôwÖà7¶5^®ù£GþD9ËÔg<Ë&Œ½À¾þæÛÓ¿?"8®WºŒÁúBaŒ“x¼á}¡…ØJÃxoq2öözãm¡3rL‘I’4*QHà4Õ¶ð£ÍAW «ŠY¦+ö£h¯Ãð2
ÅaL!¦D1%?éIø&ÕGØL4?‹š$ÝÜòWKQŒ’é¥=å:îøá\°‹KùÇ»‚RBå¼ò¢lïÒÐ/þìt,²¢f BP )[qIIEq|-&šI>'I%m\Ø§'‹1ÐL€RQ&Ñ¦êƒ¢.õFpP4¤Ê%ðÄˆúÛŸ
¤eI+ ÄZƒÁL½b³m#—KÀ'è€‘’9`„I·Ü÷¢è—c©ÎBÈ¬t‹ñ~Á¼qO\tRŸž¡)õQ:Ort&•—¨§.‹›spÌFu±Ø`¯-žì©Ònåx:bè±Å¡´‹±aŽI!1BhÐ¹,[#®ÝûÑ˜Ñ+5{ÞÚ.ëwNi|ÜM	,À¯-¨»€¥ª4šÀq…j´î#1àJ#–ŽçÚbÒ$²ƒ=¥ ±k|Ã±ôíü×Qó§˜Ó>£o0aÿ	}{!ÀÚ?aÊ£¸[ñQjI^TÌÞZ]q+sòEƒYE¯ÇOpÂ~4bÑ–ˆƒk©”òÙ¶ªÙB–ÂlŒ•!ÔˆKŽù¤*¿^ U7eä-Ïæå¹Ôgk!—«?ÙiáT0¡
Óvé
gõ—´ŸqÌ¿\«R¸)šhÔ?(yé»·0Åpr¶–¸;a?a&È¬¢]¡Øñ”dx<N'ì_¢Q¢ŒðÒM–ÞS^*- Æ‹é-’Íëöì×V[>ˆõV±F¢Cï6¤9¥Àaªî|a0 ø†ö³]bŽ7L0)tëÙ²Ñ°Âºùiºî2u¼6’Ó47ºÄcJBJFt>YB0É¸kx%¬h&ìÛÞPŒÐ\/U{ÉrjÆ!ËÆÇà*Và
àf¾”•´é>€2ÍY­$tnÇ5$w²9P•ÌmÀ¿ØÊí"x¾BÞI›®,{1ýôïð®üR­Àé@^bºOâžº'ÛßÛµvßè@Mè§>v¨€YÕ6ƒÂ´9#ë#éä¨ðç@Ý)<>žž<gŽÿ”ÿ—€hÆsÝâj+mKi¡ #Ê´!Él{³}€GJoÁ»”É6™ú@¼ U\Còé{wR
j-qëXBÇ1ž×Î•Ð0Œ&Œ# k…<>¹}îö¶½§ÈùNìÁ‚æÏ•ñp$É}DY¬¾‚ÿn¥÷Ÿpã“<0çj)ØŒMGÇ£|0Ÿô"Eaâƒ›…Y‰êŒlé Ñ»ZLòLDý©¹ÙGÏ{Y‚ t³aÏ¤JÎ!`ƒ|äýmkÇoã7î»sØß‚ú.ñÅírÝù%ÝêE¼Ç@ƒQsc€•AË;i;%ŸÒŸèÐº:ÃF—F±Úœ²EÉ—ìÙÕõøÎ(>ò’G }\ éGBÆÝ÷N¥ò;:P?¹}•£Ï­¥’<«io‡ÙÊˆÆˆ”"Á³‹O„uŽi ]jôÛ·o"Bn¼oßÈÂÂÍÂZ+}†Ÿ¨	H5”„‘™vî6•_â,Â±­´#æŠ{Žc”G¨¬ïÀ¸YóºÆª´aa;Äñ€—³ž«Þ¥
Å|Én –ZÔâ{¾Ã2²º?'Ä«œ+ŠÔñMôÜ ÝÑ)A¹ítJ.¯äá•üõñ¾]W’Óõìwë­¡cö%Íº¡ámÇc&Vä"vqp£¶ÃÇÊc¤<ÆŸ´ß˜ür—â˜¡&æãÖÄì%»=’B‡B˜‡,„Ù_Š{¥‚Õ/³úeOáíJ J^®äe/îö9Ô¹<LËN)î_å2·|Üâ–}D·3T´<`EËÞBÜÏ‰ÊX>bË~²ÛéÃµ+V»²ïàÂ<VbÄkÊ“£dœsY§š
éEƒ@IÕ(ÕZ3aÿåcÆ¿»Vxm£Ä\}alÓæ”wãJ_è:,ÌLƒU˜aˆg~?F¨ ÆzcúÓIny¸¡­C1ytÎ¼<_hà~L@?ÔøêýP˜ö$  •ð×>å¹ MnZ¥ÒHgO»ù<í,ÁiO†"|—5žíR8 IƒzÙÈâõ³—”¨ûz4™LŽ°þïÃ'.– +¼±¬À\¯¶‹ëu[gK®—juwš"B·îþ3G-OŠ[ª¨|:÷£ƒÍô>«¯‹ì½åÉ5}ž}€‘Îº¼T¯™•xu•_jÀ•)¢æ<?‡!ƒ¾ÇUG)Ç²>®°`³4):.ïÀU©+÷¡ëi“z,Š|Ô£.ÁŸªá‹Fÿ&TÿUW‡²¶=%÷]×&–É…ˆMÊše/¾t 5 Îvf+wïpL(ÊB¯)Xž‚¿!µái+Þq©»½ÍR^OGéäã‘á'd¸[ºËoßt—¦‚&µZŽÌ™ÉW¢hK‘%ù—…³¼Œ…+v‹¨m÷Ò=í‰{ï'vVˆ’Ã ð×Ç¦Â'¦Ó%!c†¥o	K×;ïý÷yå+Ž)¢˜jeÞ–<NªèX}ÞÞè0±XÐMÄ=ØÍ6ÑQÐÏ+´@ø'Hîï˜Tá‚Zì„LŸ/kCò'd½B'¯Øt‡ÍrŒ,Wl¹ŠÖ¹ ×\<Ì*©dÕVŒØ–à¥·.9î:k¼ð:CóŒ–¢õ
šÇHÍ°PÓéÏÑ{M“J§ì™ÒIB<Œã(º“èm¢§LÍb(&»¯jÄu–C©™/"]â=ÄgÛL¾Ó—¥¢Aäy¥ªfäÊj.–lÞÊ/¶eïNðwÓ†(yÑ&&s¸ò¤òÚ—V€Eê®2úígI9–6UÌR[n{r®…»ÐÕ5Þuwòp,v‡ª5W(îLŒ¿fÉ)5­:ÁqõÚ°g¹à©ØãY”ý••+ñ·úk`c¹û&6íÝ0Ž.ˆÖõ {âmò€¨ÝÔXQ†~ËxûŠXôˆ6*Š×SÖºÒjìAEøžyÐß‹îqÎ¢nUt¿2–Å-0Ó	ÛÑ…åG¶ÀõÉ„­ývÆ¾†=ŒÝžbª&Ìyæ‡íJY[t¾¸CÀ¾»ÔûÜ¥Ã5Ã-»SÜ
 ¹gÐèz/ÌÜ„”øò}1sR®ì·1Aá6È‰þ‹¦OÆ@1F.UêËõ—›c;Ç)Ç\·®ÝzLmùò/¬‚×%œŠí[éƒ"«@—š¹´Çƒ"ø{ð.ù —ªÊëi[¿¼#¬ÆîWõ£jþW
ut¥Z”TŸïª‘I±Ä\àM@.)6½®XþBË+Ñ7þòÚžoÙÁ þÂ…¾7w
äOÅ³,»¿ò¿ã"±ø^Ý™J†.EòtÝiº5Ÿ¹£pšãÍêþ˜#`7|Ç¶®„I"YïíH#e·Å;¢/·à^Ý‚ˆÓÁ£¼d5þŠMR-q    0707010001f108000081a40000000000000000000000016a102a8a000010ce000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.sync.nexenta.conf.gz  ‹     í]QsÛ6~Ï¯À4ÓI2'+²ÛÞƒ›dš»¶s™kš›Kr÷Ðé˜	J“K€’•¹»€e[¢l)ª[ú!vDb	ì÷íXa—ïóçÑc¶Ç§WE<bfU
Vˆ+Q~/qûíÝ~u÷èìãJ‰¸úõÑ·4è—~Øaÿ/Åj©ªä¼íF*3QÁ•“=þ ¢t¬J>ÍDó¨U-ðB%~«e%’kÊJ-¤–ªÅŒ.þÈ3MW‘ò:3m¯V]Å\TÒð"vÏÉOÙ+6<ñ,*{]—"–©Œáž™( mLj'èîø0šðš¤ÌÀß?;Êd*æI³·µ6l*˜†¥ªb‚ÇsV¨D°ZCÏ’ ì|Š<4üjOû–4À¤JÝ
öaÀd3&¼6
	ÏbU¤rVWÜ@?IÃtûF—ÜÌ·
F}xJ53Š<ç¼‚Ž}#Æ5ð]lºê`€Ô¤r°Qå•XˆJKÐÎŸZñŒÅ¼HdÂÐçm—Øÿ|d¾©,„fÒS‰2“1Ñœe²¸DIè‰¬fø¦÷à“ º(JQVÎ]UY%x9©ÊP¡ê0"6ø;©czt‘¶-r€0éïˆ§à'/AŸ¥Òæ¢ÑðQñn`ýXÚkðUyðCh\É’àcŠk#OLi­ê*~çñ	'\Î£f$³ÙU¥* JªÅ‚w!¼™ÍÛm¤¯Ì å]¡$U†Æy7IÌÎÂ?E2]Hî×(CÝF÷ÔÉØWUxîÇ2U¹ÃTåNøÁRëfåÃ=Û¤ÓëíÑµßKÜêÍ Iš½ž$agD¡kFUbÀô ˜:ÝÞU'ã.¸Â¯ÖÃÀ
¿î‹*Fxvµ.q{=€z P­jïª±¨u1Äj Ú»‚ˆèj%†èÐ®ˆNEŠÜÃC!2; 9‡î‡äž£C‘]bC‡1Éû‡ÖðÙ	Ö!4´7»¼olÈá±|Cdè@yÐP€Ë®P¡ƒyçÈP›]âB„ô¡5|î ë:ªwŒuÑÙÓ!*t8Lïê¢³¦CPèp˜Þ3*ÔEg3¦ØaQ|–ØAƒÇ^0<~ÿY©Y­è¹. mø,`*kŽ@Âõ…„Í	^ó@ê[Îe< •šx0@'1Ï2¦ë²T•a¥P%È›óNÅJQ¡ý\ÜËïe]1Ü)”þÕ k….Ua†±ˆNÀü6À©ù±ÞïæÊ ïïüšD±Uðè©ðy±fêN‘-ØRÄSÁä¬ ;OLO
Ô%QFÏk“¨%Zmóˆ7Fã=¦ÖHž-ùJã!A Š¢â9¢1£†²`ßÿðãë?}—s•…dñÏA¾ã°¿þz¢`½„š+èï†ÈIDÝ^ßŠ;ß£Çx‚U{W¤C .&4Md5uú¨c°hâòä<R9{gW_ùîE 5¸]J:¿\žGnÐc'J·öŒÏ %I;¶xÎ‹™HFáuŸ,
ûàFöƒë Û£¶îc01Ë˜[ìL1/ÿ`V¶÷Ã¶=­9Wk uáßÖÆ€ÿ²å Z„ÝI‚d#®r7 ?F	fÒøÛ	©N¢t VÃ}`™@¥øHsç±9Œ8e¦V@'ØJåtb	DÏÛFŸÐ2ª´/o×+õšÏ6•œÍ€Ü&’èZÆÀî»…¢Žeq©‘Bz®ê,A§ïœ{'z‰ÄÑ¡Ð!qr¥àŠ‘Nä6¨]*ÏÐNéÈ»áú–F¸ÅÐÝõ\'ìI¡ìÌñdÄÈcé¾´N@›¦ˆ.Cì¡û”Ë¬†>âÜíµô—`™¢~Ok¤"x)¨À_Nƒø­s0UeZ9®Iæ}Z©ÄºúcÛÊB“:Ø¾€ŽÚ† 7ìKÛÎÝ4¢@°ftÆìßþÙ˜Ûá¯óÈcag|ƒi3HB5`þOÎŒi-êÝ*®zÜ Çì£i!n”’ÉK˜dë¼¤$8½ÒFäÚæt]ñÖÇd*¾^îU·¥#Í.¦Ù¥TK!gó?Ø©;˜
L`¢Ht+8®0ê¢œá”ööù×M
7¥3Ìc,êo¸eoÞÁ3àÉ|-iwÌþÃ³:8f‰IM§Âðt2™ŒÙ?EUˆ,àK3XºN¹N]´€ßL6 —õÅoµ2|€u#¬ë)iì	v’”×‡;_è _Ñ~l^Å¸ÊvŽ	…Ëz6«Ìƒ0o~ÙÝ»~Á:ÜZ#-š¦ZeY (åM4k²ŽÀ@VÉ+ž#ª1û±u#t×?É¢¾b1µ çE'§°TÌa)€›ùLæÒt÷H:ÎJ¥%±s­?¶!-'ë|
‹PA.ãJiôO0k+”åBé¨ ;©`ÓEßL¾ü®5ÿ£ Œ .¡ŸÝå©mq¶~¿Y*{¿7€†ÔÄ~zÆÐsðÚz°}¸6ë¤|ga$EŽj¢4ðñéäìkfõ?fo
ÆÃýHYŸLU³­45%‚ºvÂk›ux¬Hé*¬.eg›LÏ@¾€Tœí¤hw30B‡¼YKÜßZ•P˜Â“#äóÒ.%t£òýèÐ¶BžžmfßÀ»Þ¾÷5j¾ÝûAÀa*1®ŒÁâ…÷¨ˆ5Ðêïð/®ËB64·p\liƒQé
Ã!ìœMF§£3ü`rrÖBŠ`â·ƒ™‹ü‚|é€è®žDËOÝ=Çdæº·X ªZ±§²èÄ!`ƒüÌú»Úœ¼KOÞÚû.aæ;ÃŠÝåÚøDÌ5zŠp£ä«(x+oÐ¶F>¡Ÿ-ìP*¿ÀˆG¥²#û˜m^³4ã3öt‚æzú	ác”?rIË#@'€úkièÜ-’`.Jœ¤?Ù}••ÏìÆì×W¸·«sËÆ@”#"Ñ³ù~ÂÏøwþ¥Ž$>^öîÝÛ@í¤#ï»·ž²0qúÎA3?×‚ú0~åÆpcÑ×3]Oí¦ò[Êºwa[i`†l;Y°¸~º…ãzÉËkÛ^(Ž^ÎZ­º%íõ×w!jA‹—ìë-žiÀêp‹grE¿×z|NNa×E	â¶uQ²V%¿‹UÉ_Ð½4!Ií“ÝnÝU…šÑ¨+êÞú÷‡áz&ò^d.qp£¶e3TV9Jei¿õðË.uU†r*Ÿ·œJ/ìzäË5TŽYC¥?Š½²d†Â)Ÿ³pJOð¶åÆÕRŽQ-¥x½2b†)Ç*‘ÒÄy0C]”cÖEée¯ì—¡ÊñŠ¡ìåÖœ—¡Êñ* ôG²O¦ËPöäxeOz!Ù;¿e¨urœZ'[Qì_éd(pòyœô®O¸f¨jrÄª&½Aì­J™|ÆR&ý°Û¬ê—¡~I/ìúÅj†¢%G*ZÒÃ>¡š¡RÉ+•ôF²_¤f(Or´ò$» ¹=P3Ô$9ZM’Þ@öŠÓ…HŽVˆ¤;„i*„^Øï1až’)³êR–ÝÝ ÒBƒDéšQwg¢Çì¿¼²ÊøWÓJæe&±ðÌ¾´j!\z«¦wvOcº(àú°‚«•nšù×^…ÌÙôÖ«æ¾‡çÃ[+Æ,çhTòXœh‰s˜l\¦H£D6¯Ë…%c–\ÕEÑ=VÍÙ“f<OOðºCÇµ¯J<¨Gg;;9m/*™¼zú‚²®_Æãñ3,fò&µ¥:ìÁP•Kƒ	zp{]Ž´Á˜D]FdÕV:qÙ	‡ÇŠØøÂºKž†Jâ¸ÜüG›É!³äo:¦ýÁ×ºðeVH§>àHã]^¯˜‘9hÝM5à‰².£¦<¾ôé£¾BË«F’Ž58ÚC¢);ïf¸‡µ:pVjj· k“â:tŒµ5Õ¨äC‡dxGZ©O¢hïjŠêø|'ÉÞ×<†ÞTˆ²Ü¤X_ÃÄÕ@S×ÙœkW,äax§¤Ì‡.ù+2ÞmÅ-¹‡pw*öÕdÔ|Ø3¼…\·SwZÙb”k!›vÑ)¼cÅ\èx.’:Q'™6±~€g ÷‚D¸ÒÖ35:¢ZÙc{Ýì"_E®gz­¼-A`8MF9¦Ëº–0u½wÎ¶HÀœc¾/&×3dšld—„99L¤)–í
È®×…Ž¼}^“àŸY"Ùÿc†Œ=¡ÌÎÈõ¹E(þÌ½ñÑÇ×'[|–UÌà¹BÏ•¸wÑÞð.O–ËBæuÎHm¾´ÞÅ3‡µ™¶ï¾æhžÒT´œCó©VÝ²öólŒOífOØÓBuª@?ž… [D7Aïmä»Ûñ{3tÚÃÞy1Dëáùz¹¨ö»ö¥®þ	ø"WÑYÀ<¯uõœÒŸ'*~/ô"~Þx«¨IUò¹7Nw˜“½°¢ÝÜ÷’ýòÅdr>™œLNáßïþzŠÓá#öÅäŒ>ýÊ}jjXÇÕÅ¿ÞÊì_2TŒ:TÉ±ûn¢|=)Wˆfî¬‚Á­ìœŸ`…˜R`b8Ñ¿÷x^+3²µs36­%
H{ÿúg\ô\ê&¦é	U"´5ˆ²ñØÜ!¬DiËOÒsÛQ’@Žõ‹@*¦¢ÎÖWø¶…åµm¼¥°Õð•ð.¥©l5H»$ù7ÂTØ‚ê7¸Õˆ_Ú¢Œ°—9mÍº°‡UC­XÄþÚŠ¦³wK9X`†¸»&–6õ—Ÿ0iÚ¯Eh½ç±'Ðæ„šU‰e£p={–ÂÀ¼/\)ƒ[~J©4E¡ŠG*â÷¹SA®ŒÀ[ÝÃÄdU®®§±öUŠéŒØŽªºf+ìYŠU(Çc¶t‘sö=ìmÍú»Æ ý˜Ï]÷Â®]«]|·¾Å`Þ¢‡Œ^ŽBä¢K†QÜ"¤90iTÙ‹3·1%¬ðÛ—371åZDa36Q†\ôŸ4G:$ŠÖrVt×r6iÚYMYåÚymã×´A™ÂZîr°ÈÃZd£èáË¯Ób“uºcA…O!Póƒ!PH‰môÉùÕç	î÷…§ÉWóÏ¼ÈeQ¯m±>’‚ôöížlÈžXe#r|ª°†)÷£H3q‘õnµŠõÚá´q*äRñØÞûÒÑ8½ caHöm¬Ha¼,3Ø0¹ŠÂö{ˆ¶~ã³J|áƒ«Šá‚J¶Bnøåk’×§ú (E0-dáƒíÝÊ”Á-žcQËÑæ3¬¢@n")í/wx/GŠ~0~®K‹mžÎØhtx9U?"­S£•ì¯I‡g÷‘Bblã‘=±8ðèð<²š~0<Z#Æ>ûóæ¦òt¯êðÉSUÈWSi*Ž'‘àÿþk
Bˆd»ù—qP:Ëë6¬¿”f€@,ÑU.Z¹²H3z›‡½’‹NiÕ©À×¹¸Niú›^­±P2Á÷V¬Ü«ªèüÇÑÝëH*•O³ÇŒÜ±Ë(ò¯Æxi¿£ì¾ãÊ?ÕîPèêØWõ³›ü~ä¹=kIc¼•ùˆõÃ=Ÿž¾v7ÀºßqÚîQìMjƒ;‡¹ì sY é‡0—ÝDòÿëC†     0707010001f101000081a40000000000000000000000016a102a8900001031000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.sync.docker.conf.gz   ‹     í]ßsÛ6~Ï_I¦“dN–e·½7É\îÚÎe®in.ÉÝC§cB$$aL,ZV§üí. 
”ˆ²%«já‡Ø‰%°ß·‹ÅX={¶ÍŸ'ÏØPœ^”é€™E%X¦ÒQ?LÜv{·]Ý=ù	û¬–e&®~~òúµö“'Øý±˜«:;[öÂðz*\:Úâ*J§ªâã\´ÏúT7/Ôâ—FÖ"»v¡ªÕ¥ÔR•²œÒÅïy®éJ&&¼ÉÍ²Û?ª’.Èr&jix™ºçä‚OØ6<óF,j{]W"•™Â=SQBÛoKy™ÉŒ¡Ý#J•	Í~cY]ÑŸt“*/Et"—ÚV¡:…Ï¿ÚÈ’‚“ &ÌÌ‘p·~cµ X©·`2Î2PÂy¥´9oÕ²WZ,ö…R ç·€EQ fÐ×²2Ì(&®DÚÁøÄ€™£Þk¡US§"0Ü³ôˆ§ˆÐYÒŽ$aö£!û®®U­¡Ã ¡nP,Hq¿[@'JõƒSL„ò¾P† ’*“ ÜûÁHb6†þ)³ñ""¹]£u›<P'c3\UñÜŽeªj†©ªðƒiö¦ìˆá–mÒéõÞöèÚoŒ¥½"šÛF“4û <m€»)¢Ð5£j1Ý¦N·BÕÉ¸®ð+ÂºXá×CQ…_ƒÚT¸&Ž î T«ÚjEljSÆ\ÂŽ@T{_P}A­EÌmŠèXLp‚Ûqz(Df4crèaHn9;äÙÁ˜ÚI>49´‚ÏF°ÆÔÐÖìò¡¹!‡Ç&ðÅÌÐŽ,ò©¡ —M¡Œ‰¡yïÌP›My¡Bú€ÄÐ
>÷€5¦…v‡ê=óB]t6Å4f…v‡éýÓB]t6À4&…v‡é³B]tîÆ;,ÊGÉ´xlÃ]à÷O‘Wš5Z€ž›ZÑ‚Ï¦rÑnËõ¥„Å	^ó@ê›Ïd: •š•x0@•G)Ïs¦›ªRµa•PÈ›ñK(ÝP‰íàâ^~ 
žº`¸S*ýk@Ö]©RKÐ1 \	œ€ùm€gR#*g³í•AÈ¦=ì&ûmù€köÆ
ÀçåŠ©;E.)Àæ &§%Øy†`zªPú ©ˆ2zÖ˜LÍÑjÛG¼3ï1FÚð|Î#Y@P’”Ç<I†Œ}À¾ýîû·Ÿø4 :Îg*ÉâŸ	‚|‡aýõLA¼„š)èï™“„º½ºw¾GÙ§™ÐÞéÐ €‹™MYMSƒ>šlÚ€¸";KTÁ^UÜÌÞøî% 5¸]J:»˜Ÿ%nÐC'J/í/ŸAK’vléŒ—S‘:Ãë>Y”öÁ­>ì×Ödbîc01Ë˜[ìL”)¯þ`V¶…²÷26d=8É$1Ð:IðokcÀ¹ä Z„]I‚d#®r7 ?E	fÒøÛ	©N¢t VÃ}`™@¥øH/ì,#ÎD•«Æ	ö††R¹XÑóÖÑ§ ´Ye$ÐV¼58\¯Ôk>ÛÔr:Np¢8)™G˜´ÓýŠåt,ËÒ3Õä:}çÜ;ÙKè<9”1}†®\1RÀ‰\GU¡KåùÁ± ícf¸¾€Ð—º/Á%ÐyÆž—ÊÎÏL€<6Ù6ƒV	hÓÑeõ.ó¦Æøe«ý Ï‚åŠú=nŠà¥¤2 -9à—Ì]ÀT•kEä¸&5˜÷Qh¥Sè^ègŒm+KMê`Ïù%tÔ6½a_–íÜÝAóç
4 1£k0dÿñÏv ÀÜ%;ã»Œÿéª7FÜÈ”bQïV1êq²ÏZLšyp£”\^À$Û›È\è…6¢ÐÄqÅˆÉTZ}½Þ«n±³jz>Î/¤:Ÿ9ýÁ^¤n`*0‰2ÓKÁi-€QçÕ§´Ÿ°Ï?ßhR¸(Ú±¨¿ãz”½û CÌ'—àkI»Cö_ž7ÐÁ±0s!Jv2"OF£ÑýKÔ¥È¾´ƒ¥ëÈˆ´€_î@6­šó_ex„õNXè0úiŠ+ÉÏ%áÊ:À×´ž›W)FÙÎ1Á 0¬gÓZÁ<óæÝu°ëÄáÖ)hk•cZäé€’´hc²ŽÀ@VÅk^#ê!û~é(è®esÅRjÎ!IŽN T, ÀÅ|.iºë $g•Ò’Ø¹ÒÛÂÉ¦Cp *(dZ+-€þ\R¡,¯ÁÓêN*Xt%É×£/þ×ÚŽÀÿQæP—ÐÏnxj[œ®ÞoæÊÞï %5±Ÿž±Æô¼¶Ž6°×f”ï¬#Œ¤ÌQæò@m–>>~Å¬þ‡ì]Éx¸©š£±jp¶•¦¡3€`#¥n\¢ðÚbž)]…èRv–ÉôäHÅ9ÑNŠv5Ó!tÈ›µÄõ­U	¥)<9B>Ïm(¡ µïG‡€¶2ðäônöEÞõö½oQó-ìÞc‰yeLŽ/¼GE¬Vÿ€1.ÙÐÞÂ5:Å¬tévÆFƒ“Á)~0::]BŠ`â·ƒYˆâœ|iDtSO¢å¯Ý5Çwdæº/± T½`/dÙÉCÀù¥5ô9ú09zoï»€õ-˜/žv«\›ŸH¹FO®1ÐaT\kP¥·òmkä#úYÃ¥ŠsÌxÔ*ÙÆló–Mr>e/Fh®'/‘>G©ñ#—´<ôqè €Œ«'î‚$˜‹&NNÒ_ìºÊÊçVcöõ®íšÂ²1åˆHôlßOøyÿÓ¿Ô‘ÌçË>|x²täýðÞS&Nß9hæçZPŸ •-ÜÀL Õî›ß3ÝŒí¢ò…OÛJ34`+ØIÈò€µÀõ“5×s^U²ƒ‰ßÅ1ÁËÙR«.¤¢µþêj D-hñš}µÆ3E¬v„8“£,zø^ë}ð99…MƒÄmmPr£’ßETò´Ç]‡&$iùd·Zo4¥Ù§4êšº·úþ0ŒgïE.ÃjkbœXYe/•U\¦ýÖÍ/›ÔU‰åT·œJ/ìzœ—‰5TöYC¥?Š½NÉÄÂ)Y8¥'xëÎÆÄj)û¨–Ò¼^'bb‰”}•Hébs0±.Ê>ë¢ô‡²×é—XeÅP6‚rí™—XePú#Ùç¤K,{²¿²'½ì}¾%Ö:ÙO­“µ(ö¯tœ<n“>ÐõI×Äª&{¬jÒÄ~ÙšXÊäK™ôÃnm²&Ö/ÙCý’^ØõËÕÄ¢%{*ZÒÃ>©šX©d•Jz#Ù/SË“ì­<É&H®OÔÄš${«IÒÈ^yšXˆdo…Hú ¹AšæP!ôÂ~æéÐ#¬ºUw5€,¡A¢tÍ¨»2ÑCö?^[eü»m%‹*—XxæÎºIé•­c‚Mèxgwç1Å\ŸVPcµÐË­fÜpÇŸ%sHÉë¨sîð<¼Xy7.`>“@£Š§âH<8‡gƒí–Ë	Ò(“(àG!,axš
°äº)Ëî¶jÎž·ãyÞz‚·K1´]ûªÂz´·³s¦íU-³7/^Ñ©ë7ƒápø‹™¼›ØRvc¨*¤Ázp{S´ÁœDS%	d5V:qÙo	‡ÇŠÔøÂºKž–w”ÄqgóŽ6£]ž’¿i›ö'_ëÂ—MX iœú€#­wyU¾aF u7Õ€'Ê»ŒóôÂõå–¼j%yáXƒc¹ItÂÎº'ÜÃZ8+µµ[ÐµÉNqÚÆZÚjTò¡C2¼cR«_E¹¼«-ªãà;Iö¾ö1x
4<½l¹IG`}W- <ºÎf\»b Ó;|è’¿&³áÝV¼Õ’{w»bßŒÝÁ‡=Ã[È%p;uOj[¬€ÎZÈÒ»èÞ±bÎu:Y“‹¤s˜6³~€ç ¬1x¤­žÔèˆvXÊÚën`ç™Èù"q=Ó+å(á´'Êñ¸¬k	S×G—á\	˜q<ï‹‡‰L›œ‡'dÚÓÈîæä01™`Ù®€ìzUèÀÛç5Y þ©%’ý?ž±;”Ù)¹>W£ÅŸ’÷òyÍFk|–ULô\¡çÊ‚¬|Ç&2‡²”ES0R[‡/Kïâ™Cµ™æÅŽæMEó4™š`Õ-k?/‡øÔî	á{QªNuèÇËt‹è]Ð{ùÃ®vüÄÞÖƒÁ°7†(ž­–‹B±³zþ«ýîUÿpBt˜ãF×ÇtÜù8Sé1<¼Ô—éqë­’ö¨’ÿ…~ÕaÎè…Å`íö¾×ì§§£ÑÙht4:ÿö×œŸØÓÑ)}ú¥ûÔ4Ç5åÓŸoåö/‹£vUrì¡‹(_OÊÕ¢™;¯ap;çgXa¦˜˜Nô=ž×JçlíœË)7¤}|û#=º=
Óö„*ÚDù…xìÙ!¬DiËOÒs—£$ëT<Š:]ðmËkÛxMa«øJx“ÒT¶¤	Bþð(lIõ\4â£S[”ÖÒ¢ ¥Yö°j¨‹Ø_‹h:q¸å´ãßÄÒ¦cø	“¦}-BñžÇž4B‹j–Âxö(,…ç¾0R·ü84¡Ò¥*©ˆßgN…2oqt&«¦te°0žÆÚW<Îˆí¨ªk¾ÀžM°
åpÈæ.³qÆ¾…µ­Yb×´ó™ë^Øµkµ«‚wëk¬æý˜½Øeö‚t|™‹.îÈZà1’fÇ¤QU/ÎÜÆ”°Âo_ÎÜÄ”k…UÎT¸‹2ä¢ÿ¤g¤C¢h-§e7–£´IÛÎjÊ*×Îkw¾¾ Êb¹‹h‘»µÈVÑáË¯Óâ.ëtÛ‚"…vO!PóÁ(¤Ä:úüêñR‚ÛýB‡“ìËÙc¤ÏY6+K¬Ï¥¤$½½D«'›²'VÙŒ+¬aJÅý(ÓƒÛL\f½[­bµv8-œƒ
¹Tü–÷¾t4N/ÀX’ý6ˆ@Ö¤0^U9,˜\Eaû"‡¥_Îø´¿ðÁUÅpI%[!7|ùÚ¦äuç©>JLK Yúd{·2ep‹çX²ähûVÑ@ ï")­£—Û½—#EŒŸëÒb§s;ö"vO#§êƒ!Ò*5zPÉþŠLÚ=“0¹w@D
‰±ŽGvÇbäÑîyd5}0<Z!Æ<2|ú'æÍMåé0_Õá“§ª
¯ÇÒÔw"Áÿýk
Bˆd»ù/ã .tÂëeZ.Í,L 4ÑU!–re9ÉéÛ<,èµ¼ì”Vü:7×)MÓWk\*™á÷V,ÜWUÑþ¢»¯#©U<Ín3rÛ.“Ä5ÆkûŽ²ûWþ©v…B/¨S_ÕÏ.ZðýÈ±ÝkIc¼•‡¼Åúp÷ç†›§¯íÃ°î·¶»û®µÁq.Ûé\húæ²›ˆA^ãÿwšSòÍ›     0707010001f0d0000081a40000000000000000000000016a102a89000010d2000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.drbd.conf.gz ‹     í]ëoã6ÿ¾E±›;ÛëäÚ/nwq¹>pÅµÝÃí¶÷¡(,Z¢m"’¨ŠT÷ÇßÌ”H'~udÓ*è#ÑcHÎo^$g¨çÏOùóì9;á’Ë¤¾0³®ËêYö¡äNÛ»ÓòîÙÏ8Øçµ,3qóË³ÏiÐ¯iØÏžaç¯Äz¥êlÒõ¡TÓª¢¨Ì”ÏTmà¡á	a:UŸå¢mõ}Ý¼Q‹_Y‹®;ßð\ÓªV×RKUÊr1‰îdbÎ›Üt#hïÈr)jix™º–rÁçì[
žùn£µ½¯+‘Ê¹Lá™…(áÝKy™ÉŒ¡']oÙÿºvRU^‹:èÁL)hª$Cÿt
·¾3-3Š%‰I2`o+Q¾ûéK¶’yÎÓ¡WZ#´¨¯¹Ã]³d<4ÿdªŠúãÔ†ÄÔœñö&Þ Ã{§
aI^/„	ÈÉ¢ÊE!JCÍi–)èGSUð*3K©YÎµa…Ê{)ôÍÙ¨N@(IæÈ’$žZÂØäj5I6…*A®²™`<5Æ) ÷e<HQª©SÁf\K=~~¾I„Ùâ°Eš«®=9þA•§“à@¿sY
bÒ>|Þ€?:à›Œf)˜‡aÀóž_ª–
ú·f
€žE
2Ã\\‹íeGx& R‚D@}æ¾?þYÂÚ¾õú¡¶Ã«
eÄ´^=Æ-`f;eê5Þuœ'ÐA=Á•
^Ç&¡†!W08ú­Áö­ÇmŸƒð=.´^±?õ}¼.9r°ôÈwŠ^Z#ÉµV©$[J.ƒ·ÓÇ;uØÃ*n Y©:ÊÊ\.šÚZƒ¹Ì‚·%ø YFVÿ•0é+ìË({O`Gðÿ$Q')òI¢×eú\Ž“®+O`Ir)¯i|µ¨r™BóåÂŽ¤ëÉšë€Ün#nÏÚ¶ÞH<¬‘à)± üžÃÏØK²Üâ&Í-¯Å øµ GTsðDªÌ×g„Ûß¶9(ø\L½CŽR¯ìuŠò5°'ÏØlmE¨UöõDÑ
H‚[¬Åí0ÔÉØˆh–«ôªÕ(2fÃ¦¢Xˆ\Ÿ»ÀV\ãó©†fdÁ½þsl^±‘tŠÍÁ,BT„CFÁ ­gÅë¹™‚Zˆ Ø~uà¯€Z‚m¼:û,6Ï²ú#¸ü+·Ôí”¬°ç$$ù5‚áÂd4Ù´û2éÔ, Øh2p %Úc„BÔ#ö£D–,¡r1<E(‡’nXDPh‹}à · Šªô¨ˆnõÁ[½OLvÆ»`Šz\@dûÖ¹fUÈ¾Ýªà7S[¬j+Ÿ_’š¦ª)Í_.Î†ç§ôU›®H–F,D}w]ó¼^“dÉR?0X<$Û $«ÁçÔ¡#*uƒ‘‚géH²âÐw­irÃ-²eSÌàoÀçÝ8F«h-† õm®l[l®\=9Ø*ÂñôJÀ´ÑñÉc‹–‹4Â 6bï„]¨ uÌf$.~ $D›ü|gä—ïág5œ¾ÅÝÓÏw}l+t—Ôk)ÍÉ8 $­üU¥PÀŽ	 2÷æöìêÒV·GZj;O3F»Øs|ï·@GÁ0kZ)m¦-ûú¥"‡Ðe»¼º¦ÓZV–¸ia,Ÿ¯ª3>*U9IÚ‘$Ì^‚Ø»®XI4ÚuÝTvâén"Ò[Ù­‰ñKŠ÷Pþ^(C ‰•ÉÆtáx‰ÌÑ¢Ífýúí‰•2ämò!€:ÇáªªÏÓh¦ªN¡˜ª:¿¦ì}å=©eÀÚß«•‰CA­Eý‹èLÀDÜwø"sš}ðóaHž8úñˆ‹`ûÜJ~hð³ÏQ°ö¡ÏÉôòCc‡Çðõ‘Ïý)å†>1:»1Å‹Ò<½­àûÀïŸ"¯4®ÀŸ›Þ"«fSy»ë÷<ñžRØj)Óe˜I¦YévCT9LqqÎg‘UBU@oÉ¯Ý6ô³æ6-„{ú)huMë´¸ßèh­±éJ•Zq	˜„ÀÁ	˜ïØ½BTþ`yŒ¾çé™m$pÌ“‹ôœ6£íñ¦"‘ÑËÆdjU†É'ßÏ˜F£Øð|Å×¸AÂ„’¤|Å1„VöeÉ¾úú›Ë¿{? q\-U
‹où>ŽÂþúû™vOg© ¿;Âƒ„º½éoœí±;HÚ›"¥R"ÃVÓÔÀ&Mq/)²I¢
ön ¾ñÝK(Û7ëCJ´mï=òOÂ º$ŒÏ &¹ýªtÉË…ÈÑðâ–Einùa/ÜXûýKº½gß_”)¯úlá“(Û»8»·:'² fø|”¸¡Ôß0¡
-&tšIã'¤¢wìvKG÷AA3A”2‡0õ$Ø[	U®Ö"ó„½¢!U.KÜmE¢öö‰OhU÷t*k×3õ–Í6µ\àŽ9·Û¢º‘)%öYwßAÑ¹cY^i!½T¦T	æŒ{œ6ÈK›*E×Ð”‚)¦ÝqKrŸ¨
M*ÏŸœ žÚ„®¯ 4ÂdGÇKpxž±¥²žãÅ€‰w³O-A›h“‹ˆ%$µ©\æ”ø€9uŽûAŸ1Ëõ{Ö˜¶6 gNq*·X€_Pw®*×Š„ãÕÀï#1àJ-¸s
œ}×§X°ü:j_¾a_º÷ÜÓÁë/0mbF÷ÂˆýÇ·í@ ß¿aa‚í¸õøˆZ”½²7FÜÈ”bQoV1êq¤T­y“£ÜI%—Wàd›¢¢dV½ÖFš¤FÜp¬Â UiùõúRµ-qa1åWRMWB.–°ÕÂ#T˜(3Ý¶)CÓj.ígìó/§"ýƒòC¿}CÌAN®ÁÖwGì'L(š€U4+!Jv>&ÏÇãñˆýKÔ¥ÈogñMì}Ê}ˆÑÁøl¼Ù´j¦¿6ÊðÖ°næ£Ð'Ì2Æl`œùBg øšæ³>»Ä&†õlQ+ðƒà7?‰çÁm=‹ÓF
šfZå¸,Òæ©iÑÆdÁ(©¹æ…0˜óMg(h®¿“esÃRzŒC’Ï!T, ÀÉ|.iây Õƒ±JiIÒ¹ÑûbœâVÈ´VZ€øg•-C(x'Lº’ä³ñ'‡{mGào*Lƒ y‰‰:Qxjß¸Ø|Þ¬”}Þ+@+Ô$ýÔÆÐKþ@Å#|ÓfT›`Ü&H6%œÖÚU¸|>¾ø”YþØ·qa@3œ©½­4eÞ\öe4‡h'§˜VØ©D—2š&S(/@}¢ÏïoW
¡C^­±zÄ±„–)¼p„ò¼²¡„‚nÔ¾‘ Ú·PÏ/vK_/wÛÞKä|»·ƒ€ÃLâº2.Ž\´‚ÈÞûþ»Q±Ñ=Â1ØÒW¥k\a6œ.ðÂxxÑAŠ`â…í`¢˜’-í=Ö’hù[<çø:H²Þ£ç– €ª×ìeTu6[ÃùÌ*úÛÆßÎ‡ßÛç®`~ê»À;Ëµë))ªy8Ç@ƒQq­•^Ë[´­’égt(ULqÅ£Vy/#§ð6—lžó{9Fu=?Cðk”/¹I+G€>:€ý dœ=q$Qµ”¥“õ³WYúÜ*œ·©Èº)¬4¤œ úJ »?áýþ.ÿRGÚ\ä·o¿SÓ©“Nxß~ïE§ï¼æ}­t…nn &€j¼Sâ{¦›™T~N…BnÙVš³%¸ç¡”R²~¾GÆõŠWÖŽ÷Ží$"Ž¼œu\u!•/ÉŠf!jÁ¯Ù§{,SÕý!Nå|rÛÐ÷Áu[ãydP‚¸íJ®û¨ä£ˆJþŠúxß¡	QêZv³õFÓ2û‚FmëÓ6÷Ãx&ñVä:qp¢¶'ÆéËc¥<Æ­´oM~9¦8¦¯‰yØš˜ƒ°; )´/„yÌB˜ÃQ<(´¯~yÈê—ÁÛ— Ú—¼<^ÉËAœöÙ×¹<NË^¯ré‹[¶¸åè‰búŠ–G¬h9ÄÃ‚˜¾ŒåËXÃnoÓ×®<ZíÊ!Â<U=±1Çšòä(çJV±¦ 4((±ÅZ«Gì¿¼¶Ìøwû®,1W_hS7)åÝØÒ:q3ãÍª‚Ž¼±© —Õº;Íôç¨¶’CLÞ':S‡çÓÛ¸°ZJ£Š§b¨æZa:©]¥Ÿ£eÒòQ+0<MhrÝ”e¼ÇÙ‹v</ZKpÙ‘¡¾›ŠN™Âí€(ê‹Zfo^~A‰ºo£Ñèë_ðxG¼b÷T‡Bf˜ëÕTmÐ_7U’°èä«ÆRwçÓÙ]D{Æ«¯Ñ±ð´B±£ŠÊ¥s?9±?ÆtT„êØ2ÒZ—/Ê7ÌH<Ì¹°Dy,Qxü™Ï8ôô\µ”<q,Ûèöæl'E‡åè•Úr:D>ªÇ¢jÐ&øS•@$døÄ¼V¿‰²{ª­ÃòYÛŽ’}®móèX:˜N+ve/®t 5 ÌvfK{´*pL””…^Ñfy,ü5©ßâ-—\#Üm¤¼âÁ‡=ÃGÈ$pëºçµÍo_·G›ƒFµZ–ÌT§K‘5¹H¢üËÌÚž„üAøYÔ67÷#ÒN:Ú#{ßlš‰œÃ s‡¼ÇàSÃi“1ÃÒ½	®ë‹þ»¼ò%ÇQÌ?52mr&U´	¬.oot˜˜ÏÝ‰n-§7‰ºÝ6høVìß˜Tá]¼ ÓçÊÚü…?öÙÍ=Ç{l–eLo¹BË•¹£½ïø< +d)‹¦`Ä¶H^:ëâ%Ç~tBóâNCó’\Ñj	¯‡’š`¡¦ÕŸ³¶'•ŽñüÌ(!úq‚nÝ=ejf}1Ù}U#~h°ìKÍ\ÙYè¿°¶¶=Ãä{0þ4ÈwÏ[U5[Vs½`³Fæxv8{wùƒ;þÛï’·=¡"e[ž”ßY£cÓ
°HÝV¦S»Ý(‰ ÇÒ& ŠYj‹ÍHÎ¾aÏÌ¶/ï©yë—ÅŽ©Z³…âÖô‡ò7À,¹Ò}e‚¼Nw<¯=K”‹‚Bðöð@K±¿å¹¢xË¹l}FÇ¿bÅ¦™a˜ÆÑ&‘_o¤u§ypbÖV”aÜ2Œ>ƒ ÉbÅ×<«›²Öñ¼o'T$ßÇú"Â¼÷0g îŽ°Ç²¸9f:á{tàC¾ÆžÍ±@}4b+7ƒ°¯`c6‡+ƒöcž¸î…]»UÖ¬/îÑ°ïý,õ>g©Äã§0C…aÇì§½ÐÜ³Ð¨ê ™Ù&)áá‡ÊÌ]’rkæ¸)3(ì2ÑÒôÉPP´–‹2ŽåhzÜ¾g9e™kýÚÎejÃb¼+áÅ,RM¯…È*Ð¥z&MÍq¡þöÑI> aS5}y=uaã[ÞÛv½jîÃ-]YÎsªÏw©ØøÂLàM@.*6½«XþZÉ+Ñ×îðšžoØwÀ ~Ó¦kÍ®¹Uñ$ñÅî¯Ýg„â-×ª]SI0¤H}ž®]­Á°æ•]
§1nW÷§¼öt·OÂ½­[Û$Ö‡ívÄ;e»ö;‚'û°à^Ã‚€ÓO!¢¼K0Èjü9VDµx    0707010001f0e2000081a40000000000000000000000016a102a8900000fb5000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.zvol.conf.gz ‹     í]msÛ6þž_™L'ñœ¤Ø¾ö‹{.wmç2×47—´÷¡Ó1!”0&	– %+s?þv 	H–%5rR·ô´™„/K`Ÿg`~úô˜?Ož²#þ ¸Lê›3«Z°U|¬¸ã¶î¸º{ò3vöi#«LÜþòäkêô%uûÉlüX-U“]ôm¨x)àÆøˆ?¨$ªšOÑ}é}ÓâwX#~me#²u£RKUÉjF7¿ã…¦;™Èy[˜¾Ñ?¨ŠnÈj.ix•ºï‚çìŠÍÏ|#ƒþŠÆÞ×µHe.Sxf&*x7%­Àt
O¼Ÿ–·EAJa*g.|È5ýµpEVt%I^ÖJW/^âsWIÂrÕ”õ5½xÉ I7/Pëg[Õ®®ëFˆ²6×|ªóY!è4} Ý£€ÀXÊ«LfÜ}Ñ·–ý¯ÿNªª…h‚LÁ«Á×9ÓÂ0£ $’dÄÞÖ¢z÷Ó?ØR²NéÐ*-AZ4n Ãp×ÌìÛ?™ª²„¶A?µ.!-xwoÐÿˆá„½S ?‰6¼™	ˆ“e]ˆRT†>§Y¦€ÐŽ¶®áUà•Ô¬àÚ°Re‚=úödÒw'”$9ªX§+B˜»¸Y^$ë¤JP«l*O„~
h}wR4cÐ‚j›T°)×R[¯	J[¶°¹nàÚ££ðC¹‘oD.+¡&Ýƒ gÐ­ø#ÒÖÐwWñ0LxÞóS7RAûVLÐ³(A¦b\ˆ…(°cð½Ì¢@t„g)0jæ“ûöøg	kûÖ%´Cm‡W•Êˆëfù)0î û3û)Ó¬ð®Ó<æ)à}^Ç.¡.Œ—Ð9ö­Á÷­Çm—£Ï²Äþ´ó”T ÎƒµñWì9 +nÓ¢Õr!F ¯x‘†ƒQU±:!ÜþºÍ»ÀàšUèï9ŒrðUºN‘±õ›®ìÔy!öí-ˆí‡"Á§5bsŒbìp6-TzãÝ“eâ¸­i #¿å.°%×ø| ª€®YrGvÐ?ÇÁ+vØ§ø¡8C6¶n¯â¦
:ŠŽOO"(¶Ž½øQ3ŸÛFÍ^Ä.sKÁbÁ.UMJýœþ•ƒûù—cZÛº1éy!n7Âò‹´Ì.ŒÅ­îØø'æÂo00	æ”IHÄú4 ÉÛ<Ÿü  G\î‚€t²† ^¼cNVeµmM ºdg³mê&¼®•6×]—‡ØÑiúU7ß ¥ÓFÖä¸Ä­H[`=ÏˆÃÅÀK]¤ctTªºHºž$Ì^Þ4ªÑÐ`Ð´(v.üM„n2÷G†ñKäÞ(+”!€¤Êdm:Fs0„ðG•M‡	Ý‘2Ômò1€:‡áªêÏãX¦ªa˜ª>¿¶ÆÊ2Ë@µ¿Õ*û‚Úˆ!ú9Ñ©ÈU#:ü	‘9 Í!øù8$ýxDEpˆ}Æ$?6øYÃç X‡Ðçhvù±±Ãã ø†ÈçáŒò#CŸû1Å‹Ê<¾í…‡ÀïŸ¢¨5kµ =·¼E^Í¦Šn™Í¯£ã=¤±å\¦ópkY³
¨jœò¢è¶•k¡j7ç\0ÇjÑp»OÄ½ü@|uEËî¸8Þêd­ðÓµª´ãn¥]aµpæÛ Ï¤FTþ`‰Ÿ|é•WdO»4LÎ*°sÚ”ˆ¶\Úš(£ç­ÉÔ²
w”_Ï˜V#mx±ä+ÜôA²€ $©^ð$™0öNLwùæÛï^ýøýûÑq9WEHÿMäÛ8	ÛëïgJÐn›+hï=áABÍ^oœïÑ\†ÖÞé(·¢™MYMÛ€>ÚlÞq;ªd/qóëÊ7/¡4Ü 
%ÑVëôÄ?	ê7ÆtÐ?ƒ–$mßÒ9¯f"EÝ‹¿,*ûáNöÂ&ÀvÃ]Þµ—$ª”×CúÐQŒí]¼c‹·6oŽúD0Ú_DÉFÜR.P ~ŠÍ¤ñRÑ;Dé@¬†çÀ2Jð!ÜÎ\ÙQzœ‰ºP+‘yÁÞÐP*— K úÞ.ú”€–!«to×+uÃg›FÎfÀ	NÔ'%Sà“v¸ï¡è‡cYÝh¤ž«·ésÎ=
ÑqÃš¶ßéºRpÅH'r	ìF)/ÐNm×7éU•ê8^‚[ óŒ=«”9ž˜ y,?6ƒÖ	hÓ3„Ú —EmÄ±Ûk?h3†`…¢vO[Ó%âÌ)Îí²]ðkæ.`¨*´"rlHÆ}ZiÄ“BÂÙw1×ÕÁžñ4Ô¾zÃ¶ôï¹§ƒ×Ÿa*ÄŒî…	ûÿ¶Ævøf*Ú†ÛQ‹Ò™B5ðÖ¨’™R,êÝ*F=®ƒö£y[ î”RÈdÛ²f¹,„^i#JM¬·Ó2ÉT:}]UÛéf×ÓâFªë¥³ùlµð SLT™¾XOˆ©g8¤ýŒmþåN“ÂI)8ÐÈ¢þN9G¯ßBàÉ|-iwÂ~âEœ
³¢bg§„áÙééé„ýK4•(¾t¥û”>£ÄøêôdÓº½þµU†°ÞëZ^	LHSÊ\Ã3œùBc ø†æ³`ó*¥œ0ë˜ SÖ³Y£`„qó‹xÜ%¸:k¤ iªUË"¤JÒ¢‹É"Q¢\ÃKaD3aßõŽb„îú{Yµ·,¥7À9$ÉøBÅBœÌ²”&žP‚8«•–ÄÎµöØ)œlË) ‚R¦ÒèŸ­¥zv
<£î¤‚IW’|uúÅßà^×ø7eªCÐº„vÆá©}ã|ýy³Töyo ©‰ýô& çàµõ`ÇpmÖIùÆ:ÂHZ9ÊÜ:P·J—ÏNÏ¿dVÿö:ÎôhÆSÕâh+MKÙœ`#•nÝBáÆd¾)Ý…èRFÓdúò¤â˜èsFßÛ•Bh7k‰ó[«Z¦ðäù¼´¡„‚f4¾í[ÈÀ³óûÙ7ðnoßû
5ßÁîý à0•¸®Œ‹#Ä‹®d Õ;aÿ€?×²€ûG8[ÚàªtƒË!ì‚ŽÎFçxát|ÞCŠ`â…í`–¢¼&_: z¨'ÙHßü–Ì\ïcç=– €jVì¹¬¢u˜ ŸXCÛšñÛ|üÆ>wó[0ß® ØY®]ŸH±jAåáFÍµUz+ïÐ¶F~J?;Ø¡Ty+*Žc´yÅò‚ÏØóS4×³$„_£ÔxÉ-HZú8 Dè ãì‰» ‰2ð­œ¤ŸŒì¼ÊÊçÆP%æÃÜSŠ‘(GD¢g·?áÇü{¸üKÉüzÙÛ·oA¶‘Ž¼oßxÊÂÀé¯ù±Vºâ	×0@5Þ)ñ-ÓíÔN*¿Æ^øe[iFÌÖäœ…,X‹‰Ò;8®—¼®±˜lØŽBq\àå¬×ª©|^4QÞ¸d_îðLV„8“³•=9¾	®Ûº¡ƒÄmgP²¢’ßETò´Ç‡MlÕK÷e7[o5-³Ï¨×5o}ÿ0ŒgïEaˆƒµ1ÎPóYÊcÜJûÖä—CŠc†š˜O[³v{$……0Ÿ³f÷Jª_>eõËžàíJ J^>_ÉË^îö9Ô¹|ž:—(î_å2·|Úâ–} Û'Š*Z>cEËÞ îÄe,Ÿ°Œe?ìvÆ0CíÊg«]ÙÁB˜Ç
¡ö{Ì±¦<9JÆ¹‘ul© H%6£Øjõ„ý—7VÿîÞÂÓ%æêmš6¥¼[úBaF`¼Y…†¸æ÷cÀ
zY­t¿:É÷«uÌ!%ï¢ÎµÃóñm<ŒXÎ%Ð¨æ©k¹V˜NjWés¤Q&-PÀRXÂð4`ÉM[UñNgÏºþ<ë<Á«^íðÝÖ¸¶KÛQÔËFfWÏ_R¢îÕh2™œ`ý†Wì^‚*ñ ±s½Úz¤Ž×m$ 8ÕZéöø2·‹ŸµÇ–Yi1y:RÜSEåÒ¹mN2±ú®½÷¾<":]Ï©8Òy——Õ3²­»¡<Q3jÊÓŸqè3è{^u’¼p,Ûè÷rv'E‡å8*uå>tªlTE;õ¨Kð§*ˆdøDÞ¨¢êŸêê°|Ö¶“dŸë>ƒ‰ƒEt® r“²&}Ù‹+@³ÙÜ×e¡×´Y“¿!³áñ[¼Ó’ûw)W§£¸óaËðr	ÜÝycóÛWÝY§`Q­–s­Ó¹ÈÚB$Qþefý /AþdÜ,ië›û‘hÇ€^öÄÞw»ÎDÁa¹S_cð)îtIÈ˜aéÞ„¡ë‹þû¼ò9ÇQÌ?52m&Ut	¬.oo
r˜Ès:@¸'»^:òö¹!À?·D²ÿÆ¤
»©ÅÎÉõ¹²6NÞËä’îðYV1ƒç
=WÖÚäŽó‚Y)+Y¶%#µE|é½‹gŽ=…ZÛ³Û7ÍsŠ–sx=dj‚…šÖ~N&øÕ8©ô”=¯T”í8	A·ˆÞ=ejfC1ÙCU#~l°ìKÍ\Ùyè^Yßžaò=¸<ãú;çFUÍÈ–Õ,flÚÊÏ£eï^ýàŽ”õ»ä]K¨HÙ–'wÖèØ´,R·•éôÝ¾—$ciHÅ,µÙz$gß°‡PÚ—wy<,‹PµfÅ­ëù7Â,¹Ê;M£ŽBl½6Ì™DI!x{x €‹ØoŒ\Q¼å†l«ï_±´i§f€s´‰@4®{ìI#4É¡fUcEÆ-ã0KSB0"ZUé3<m˜²Ö+U©ˆßNt4wÞÑ=ÌYTm‹Œeq9f:á{tàC±Â–åX >™°¥›Á^°o`cÖ»ƒö}¾pÍ›¶QÖ¬/î°ðïÃ,õ!g©¤ãÇ0CÉpÏì§i˜4ªÞ‹3Û˜þ±/gîbÊÆÌq3î£¹è?iúdH­å¬Šc9šwïYMYåÚqíÞejÃgb¼+ái™¦·BTØR3•¦á¸PÿöÑ1€°©š¾¼žš°ö;7ühlsPß*æ~P'WVyAõùÖ ¹ˆŠ%¦hqQ±é]Åò%3¬D_¹Ãghz¾æ3Üø{ú¯ÙU ·*ž$¾ØýÒýjŠx‹Å}Õ®©Ðo·J}ž®]­Á°æ…]
§>n7÷Ç¼öx·OÂ½­m’ ëýv;â²ûö;‚'‡°àAÃ‚@Ó!¢¼‹ä5þ!Ñ3l=r     0707010001f10a000081a40000000000000000000000016a102a8a00001023000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.service.sync.radosclone.conf.gz   ‹     í]ßsÛ6~Ï_I¦“dNVd·½7É4wmç2×47—äî¡Ó1!’0&	– -«süí. ˆ²¥¨né‡Ø‰%°ß·‹ÅX=y²ËŸGOØPœ^•éˆ™U%XÍ3¥Ó\•âîâvÛ»ÝêîÑÏ8Ø'µ,3qùË£ohÐ¯‚a?z„C8«¥ª³Ó®'—µ†+G;üA]éTU|š‹öQëFà…ZüÚÈZdW.TµºZªR–sºøÏ5]ÉÄŒ7¹ézý.Èr!jix™ºçä‚ÏØk¶<ó,j{]W"•3™Â=sQBÛoKUy!ê@~.µ!}Á£u
Ÿ\8
1Ë¡L\ÈTXåáÎoè/PøL©›4=ÍUzc;«”6gíhªûVÅ‡R~ ã7€CQð2ƒ6Ð¸–•aF1q)ÒÆÆgFÔÌ B«¦NE`’§éOtÿ4iG’0ûÑ˜}_×
Ð‘%H¨RÜEïd¶S^›Ê»BHªLpï#‰ÙBø§Ì¦«ÉÝe¨Ûä>€:ÛáªªÏÝX¦ªva˜ªÚ
?lÎ²zÀp×6éôzg{tí·Æ²T0Í= Iš½ž$akD¡kFÕbÀt˜:ÝÞU'ã.¸Â¯ÖýÀ
¿î‹*üÚÔ¦Ê¸lu ZÕÞT+bP›rÈ%ì	Ô@µw5ÑÔZÙ¡mŠNp{N…Èlæº’;ÎyD¶EpÈíÇ$ï›ZÃg+X‡ÔÐÎìò¾¹!‡Ç6ð™¡=Yä=RC.ÛB9$†öæ3C6Û:ä…öé=CkøÜÖ!-´?Tï˜ŠÑÙÓ!+´?LïžŠÑÙÓ!)´?Lï™ŠÑ¹Sì°(?Kî Åc'î¿ˆ¼Ò¬ÑôÜ”ÐŠ|0•ƒ¤ý×is^ó@ê[.dº •š•x0@•G)Ïs¦›ªRµa•PÈ[ð(ÝP‰íàâ^~ 
žºb¸S*ýk@Ö
]©RKÐ1 \	œ€ùM€gR#*ÎfÛ+×‚¾Í^<€M¢³Ó§]7Ùÿº\Ù¬7U >/×LÝ)²£ [J€x*˜œ—`ç‚é©Béƒ¦"ÊèEc2µD«mñÖh¼Ç4iÃó%_iŒdAIR¾àI2fìƒ0  öÝ÷?¼ùôãÇÑq¹PyHÿLäû8ûë¯g
âm$ÔBAoÉœ$Ôíõ¥¸ó=zŒ{µwE:4 àb&AÓDVÓÔ &[6 ®ÈNU°—7‹×¾{	hn×Â„’NÏ—§‰ôØß	ƒÒýã3hIÒŽ-]ðr.²Q4¼øÉ¢´nõa?¸
°Ýˆé>³Œ¹ÁÎD™òêfewØøºcCÖƒ“L­“ÿ¶6ü—Ð2Ð ìJ$qi»ø)zLè4“ÆßNHEmÆv;n+VÃ}`™@¥øH›®ì,#ÎD•«Æ	ö††R¹XÑó6Ñ§ ´Yå@ xkp¸^©W|¶©å|œàDpR2Ž0i§ûŠn:–å¹F
é…jò¾sîQö:OeJŸ¡+WŒp"7‘@UèRyþàX€vŠ1	3\ŸCh„KÇKp	tž±§¥²3ÇÓ ÍvÍ u‚ Ú4EÄ¡>ÀÃeÞ@qîöÚúŒ!X®¨ßÓÆ ©^J*ðW’Óà~mÀÜLU¹VDŽ+Rƒy…Vj1‡î…~ÆØ¶²Ô¤ö”_@GmCÐö¥kçîš?U ˆ]ƒ1û·¶ævøë4ñXØßå`üO¬ÞUp#SŠE½[Å¨ÇpÌ>i1kräÁµRry“lSTl&s¡WÚˆBkÄ%/ >&Siõõj¬ºéˆÊülšŸKu¶r¾øƒ½HÝÂT`e¦;Ái-€QgÕ§´Ÿ±Ï¿\kR¸(YÔßp=ÊÞ¾‡!æÀ“ðµ¤Ý1ûÏèàT˜¥%;ž†Ç“ÉdÌþ)êRä_ÚÁÒu:	£Äøzr²iÕœýÚ(ÃXo…uí0,HS\IÂ²T*\ùâ©¤”b. £lç˜`PÖ³y­`„yó‹xìúq¸µF
š¦Zå˜¹@: $-Ú˜,ÈªxÍaD=f?tŽb„îúGY6—,¥à’äèBÅB\Ìç²&^ é8«”–ÄÎµþØ†N6Å‚PA!ÓZiôÏà’
ey…ž.PwRÁ¢+I¾ž|ñ-\k;ÿGA˜@]B?ãðÔ¶8Y¿ß,•½ß@Kjb?=cƒ	èxm=ØÀ.\›uR¾³Ž0’2G™ËµYøøxrò³ú³·%ãáz¤jŽ¦ªÁÙVšm­)uã…WëðˆHé*D—2Z&Ó3/ çD;)ÚÕL‡Ð!oÖ×·V%”¦ðäù¼´¡„‚nÔ¾m+dàñÉíìx×Û÷¾AÍ·°{?8L%æ•19B¼ð±ZýþÅ¸,dC{×tà³Ò5¦CØ)›ŒŽG'øÁäè¤ƒÁÄn³ÅùÒÑm=‰–¿ÅkŽïÉÌu;ï° T½bÏdå!`üÜúûÆ½Ÿ½³÷ÃúÌwŽ»Êµù‰”kôáFÅµUz+oÑ¶F>¡ŸìPª8ÃŒG­ò#»˜mÞ°YÎçìÙÍõø9Âç(5~ä’–G€>N úÈ¸zâ.H‚¹hæäd ýùÈ®«¬|n`5f__áÚ®),QŽˆDÏöý„Ÿ7ðï0ýKÉ|¾ìýûw ÛIGÞ÷ï<eaâôƒf~®õ	PÙÊÌPß”øžéfj•ßà(|ÚV˜¡[ÁŽC–¬®oà¸^òª’%Ì@ñ]P¼œuZu!­õ×W!jA‹Wì«žiÀjAˆ39Ê¢‡ïµÞŸ“SØ6(AÜ6%CTò»ˆJþ‚ö¸ïÐ„$uOv«õFSš}N£®©{ëïÃx&ñ^ä"qp¡¶!Æ*«¤²ŠË´ß¸ùe›º*C9•Ï[N¥v=ÎË5TYC¥?Š½NÉ…S>gá”žàm:3TK9Dµ”~àõ:3”H9T‰”þ ö83ÔE9d]”þPö:ý2C9\1”­ Üxæe¨€r¸
(ý‘ìsÒe({r¸²'½ì}¾e¨ur˜Z'Qì_éd(pòyœô®Oºf¨jrÀª&½Aì—­J™|ÆR&ý°Û˜¬ê— ~I/ìúåj†¢%*ZÒÃ>©š¡RÉ+•ôF²_¦f(Or°ò$Û ¹9Q3Ô$9XM’Þ@öÊÓ…HVˆ¤[¤i*„^ØïñÀ<z¤“Uç²ŠW# H%6£xe¢Çì¿¼¶ÊøWÛJU.±ðÌu“Ò!*[Ç›ÐñÎxç1Å\ŸVPcµÒÝV3n¸ãOÇRò&êœ9<^¬¼°\H QÅSq¤œÃ³ÁvËåi”Ið£–0<MXrÝ”e¼­š³§íxž¶žàM'†¶k_V¸QövFgÚ^Ö2{ýì%º~=ÇÏ±˜ÉÛ™-Õa7†ªB< ·7ÕHÌI4U’€à@Vc¥—ý–px¬H/ü¡cò´¤¸¥$Ž;›ÿàh3Ùç)ùë¶iôµ.|Ù„’Æ©8Òz——åkfdZwSx¢<fÔ”§çþø¨/‡Ðñª•ä…cŽn“èŒÆ'ÜÃZ8+µµ[ÐµÉ¨¸mc­Fmµ*ù‘ï˜Õê7QvwµEuü|'ÉÞ×>O†§—-7é¬¯aâê@ „G×Ù‚kW,äaz§¢“1ùk2·â­–ÜC¸Ûûz2Šöo!—ÀíÔ=«m±:k!K{ì"*¼cÅœét!²&It˜6³~€ç ¬1x¤­ŸÔˆD;t²ÇöºØY&r¾J\ÏôZy
A`8í‰r<.ëZÂÔõÁe8»"Žç}ñ0±‘i“óð„L{ÙÂœ‚&f3,Û]¯yû¼"À?±D²ÿÇ2v‡2;!×çj¡øò^þ!¯ØdƒÏ²Š<Wè¹²Æ† ‘ëúŽ˜Ã
YÊ¢)©-âKç]<sè 6Ó¼¸ÖÑ<£©h¹€æ!S¬ºeíçùŸŸž°g¥Šª@?ž‡ [DoƒÞÛÈvµã'övè´†½u0Dñðb½\Š]ÔÐóß¸­Öâž .Cˆ(€yÑèúw~‘©ô<¼Ôé‹Ö[%íQ%ÿ
7ü2bÎè…Å`íö¾WìçÇ“Éédr49†¿ýë1N‡Gìñä„>ýÒ}jˆãšòñ/7òû—£öUrì¾‹(_OÊÕ¢™;¯ap+;çgXa¦˜˜Nô<žWJçŒlíœ‹9›6¤}xó=çº=
Óö„*ÚDùµ…xìÙ!¬DiËOÒs»Q’@Žõ‹@*E¯Gø¶…åµm¼¡°ÕðJx›ÒT¶¤	Bþð(lIõ\4â£S[”ÖÒ¢ ¥Y{X5ÔŠEì¯D4QîB90CÜ]K›fŠá'LšöµÅ{{Ò-þA¨YUX6
ãÙ£°žûÂHÜòSàÐŒJS”ª<r¤"~Ÿ:Ê¼ÅÑ=<˜¬šÒ•ÁÂxk_Íð8#¶£ª®ù
{6Ã*”ã1[ºÌÆ)ûÖ¶f}ˆ±1h?æS×½°kWjWïÖ7XÌûCöbŸÙÒñCÈ\Äd¸%kKÄ4{&ªzqæ&¦„~ûræ:¦\É(¬s& Âm”!ý'=#Ek9/ãXŽÒ&m;«)«\;¯Ýúú‚(SˆåÎ‹Ü¯E¶Š~¾ü*-n³N·-h Ðþ)j~0
)±‰>¿ü|)ÁÝ~¡Ãqöåâs¤Ï
Y6kK¬O¥¤$½½D«'›²'VÙŒŸ*¬aJÅý(ÓƒÛL\f=®V±^;œÎA…\*þË{_:§`,É~D kR¯ªL®¢°}‘ÃÒ/g|^_øàªb¸¤’­¾|mSò:zªO€RÓH–>ÙW¦nñK:Ž¶Ÿaò6’Òúpðrû÷r¤èãçbZlòtnÇÞ@£ýÓÈ©úÁi=¨dLÚ?“0¹÷€ˆcìŽÅGûç‘ÕôƒáÑ1ná‘áó?1o®+O‡ùªˆOž:¨*@¾žJSsÜ‰ÿ÷¯) ’-ìæ¿Œƒº…×]Z)Í"L 4ÑU!:¹²œåômôZ^D¥U§¿ÎÄE¥é¯ûj%3üÞŠ•ûª*Úÿ±Ft÷u$µ*‚§ÙmFnÛe’ø¯ÆxeßQÆßqåŸjW(ô‚:õUýì¢ß¼°{-iŒ7³ò!o±~¸ûsÃÍÓWöáX÷ÛNoÅ¾mCmpç0—íu.4ýæ²ëˆA^ãÿç}, §›   0707010001f112000081a40000000000000000000000016a102a8a0000109e000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.sync.symsrdfs.conf.gz ‹     í]ßsÛ6~Ï_I¦“dNVd·½7É\îÚÌe®inšäî¡Ó± ’0&	– -«süí. ˆ²%+jé‡Ø‰%°ß·`…]>y²ÍŸGOØPœ^É€™e)àÏ\WéTßGÜv{·]Ý=úû¤’E*.}ôúU3ìGp çb¹PUzÚö®ÍàÂÑPQ:Q%Ÿd¢yÒ[žiW*ñ[-+ÑöáSUÓ…²RRKUÈbv5IÅ”×™i{ý“*è‚,æ¢’†‰{P&ø”½fsÁSßÁ`À¢²×u)9•	Ü3´Mð¶D¢
äËÂˆ™¨HeðtÀ¥Ÿx¢¦ÌÌûùû·lV©ºd%— ñ}ªU]%‚ñ"e†W3a é…L„‚ ïPÑ€ÇT©›  ¨Ò?97è4“SÉK§Q§ü<Q\¯úÀXŒW®H	R÷ZDdúˆx½É€¼K=™Jx€Â«Š/Ù\iÓ˜êvÈ>Í¥© J#*&u K­kžeKÛ€MáHd!X*§SÀµ0N®L5`TŸ*uFE ÝŽáz{ý:+¡g÷ŠjÞ ëpyŽ–:ÖI%Kƒ`ˆK‘ÔLjŠÈ!¦•°V`xšñÄ@÷OÇÍHÆÌ~4d?T•ª49ÓªªQ,Hq¼ÙNÖnz(ï
e ©r€{7IÌÆÂ?E:YöHn×(CÝŽï¨“±®ªìñÜŽeªr†©ÊðƒÝÄYZõnÛ&^ïl®ýÆXÒª©Gsh’fï…'IØQèšQ•è1Ý¦N·÷BÕÉ¸®ð«‡u7°Â¯û¢
¿6µ.Snz[Ý¨Vµ÷ÕŠØÔºèc	;5Pí]ADtµ}thSD'bª\v‡á¡™ÐìƒC÷CrËÑ!È¦ö±¡Ý˜ä}ƒC+ølkÚš]Þ76äðØ¾>2´#‹¼Gh(ÀeS(ûÀÐÎÀ¼sd(ÂfS@û¸Ð!½G`hŸ;ÀÚ‡…v‡êãB1:›bÚG…v‡éÝÃB1:`Ú…v‡é=£B1:·cŠ…ÙïY¼»`¸üþ)²R³ZÐs]@+ÚðYÀTÖžÑÍÃkH=`‹¹Læ R³o¨â(áYÆt]–ª2¬ªys~AÇÈà†RTh ÷òQðÔ%ê+”iŽ“Á£KUh	:ÆScD'`~à©ÔˆÊÁÙlsåZÐ79°Itvú´í&û_û€+gd'
ÀçÅŠ©;E¶`	O“³ì<E0=U(|P—D=¯MªhµÍ#Þ÷˜Z#mx¶àK‚‘, h<.^ðñxÈØGa@ìûÞ¾ùüã§Ñq1WYHÿLäû8ûë¯§
ÖÛH¨¹‚þÞ9S·W·âÎ÷h<û(´wEá¹G	\L%hšÈjê
ôQ'`+ÐÄåééXåìeÉÍüµïÞ´†G%…	%ž/NÇnÐC'J·öŒÏ %I;¶dÎ‹™HÑðâ'‹Â>¸Ñ‡ýà*Àö ¦ûLÌ2æ;EÂË?˜•Ýá¼ùVŒYNr<6Ðz<Æ¿­ÿeË´4»“ÉF\än ~‚3ÅºÆßNHEmˆÒX÷e•RàC m²´³0Œ8e¦–@'ØJåtb	DÏ[GŸÐ2d•=¶â­Ááz¥^ñÙ¦’³p‚uÀIÉ8Â¤î[(ÚéXç)¤çªÎRtúÎ¹GÑKè<9”	}†®ÔÈw"×‘@•èRyvp,@;¥“ì†ësXáCÇë%¸:OÙÓBÙ™ãé€	Ç¦ÛfÐ*A mš"b†Pàá2«¡8w{í}Æ%X¦¨ß“Ú ©^
*ðW‚Óà~«ÁÜLU™VDŽ+Rƒy…V*1ƒî…~ÆØ¶²Ð¤ö”_@GmCÐö¥mçîš?U X3ºCö³¶ævøëtì±°3¾‹Á4ùS‘xmTÎLh-êÝ*®zÜ ‡ì³Ó:C\+%“ç0ÉÖyÉ¦2z©Èmþƒ¸ä9¬ÉT}½Ú«n°³rv6ÉÎ¥:[9›ÿÁ¾HÝÀT`Eª[ÁI%€Qgå§´_°Ï¿vÎ"û;îGÙ»0Äxr¾–´;dÿáYœ³¢`Ç#Âðx4Ù¿DUˆ,Êirƒ¥ëÈˆ´€ßŽnA6)ë³ßjexë­°Ðáê6¤	î$a[*î|¡3 |EûY°y•à*Û9&.ë]²Ì›_Åû`×/X‡[k¤EÓD«Ã"H”¤E³&‹²š«!{Û:ŠºëeQ_²„Z€sŽa©˜ÃR 7ó™Ì¥‰÷H:ÎJ¥%±s¥?¶!-'ë|‹PA.“JiôOá’
ey…žÌQwRÁ¦k<þvôÕßàZÓø?
ÂX êú/Om‹“ÕûÍBÙû½4¤&öÓ3Ö˜€žƒ×Ö½lÃµY'å;ë#)r”º8P¥G'ß0«ÿ!{W0îGÊúh¢jœm¥©ÑÖ
°‘B×>Ýsu³Ï€)]…Õ¥Œ¶ÉôäHÅ9ÑNŠv7Ó!tÈ›µÄý­U	…)<9B>/ìRBA7*ßˆ€¶2ðøävöõ¼ëì{ß æØ½&ãÊ!^xŠX­þÿâº,dCsÇÅ–6•®0ÂNÙhp<8ÁFG'-¤&~p3˜¹ÈÏÈ—öˆnêI´ü=Þsü@f®»Øy‹%  ª%{&‹(äçÖÐ?ÔæèÃôè½½ïö·`¾˜¢ïv¹6>‘pž"Üc Ã(¹Ö JoåÚÖÈGô³†Jågñ¨TÖsd³Í6ÍøŒ=¡¹?GBø¥f”-®íßÆ¢@„~ 2îž¸[$Á\4urRþ|`÷UV>7°³__áÞ®Î-QŽˆDÏæû	?oàßaø—:’úxÙ‡ïA¶“Ž¼Þ{ÊÂÄé;Íü\ê ²¥˜	 Sâ{¦ë‰ÝT~G)÷.l+ÌÐ€­`Ç!ËÖ××p\/xYÊ¦§ø6(Ž^ÎZ­º%íõWw!jA‹Wì›5ž©Çjw‹gr¾\Eó ÷Áçä6]” nk%ýªä‹X•üíq×K’Ô>ÙíÖkMaöº¢î­~®gÆÞ‹\„KÜ¨­Yãô•UöRYÅEÚo<ü²I]•¾œÊÃ–Sé„]‡|™¾†Ê>k¨tG±S–L_8å!§to]nL_-eÕRº×)#¦/‘²¯)ÝAìÓ×EÙg]”îPvÊ~é‹¡ì¯ÊFP®Íyé+ ì¯Jw$»dºôeOöWö¤’ó[úZ'û©u²Åî•Nú'[à¤t]Â5}U“=V5éb·hM_ÊäK™tÃnm°¦¯_²‡ú%°ë«é‹–ì©hIg»„júJ%{¬TÒÉn‘š¾<ÉÞÊ“l‚äú@M_“do5I:Ù)NÓ"Ù[!’.@n¦9T½°/1až’)³ê\–ñn i¡A¢ÄfïLôý—WVÿnZÉ¼Ì$^€¹³ªJ¢²uL°	¥wÆ'1]p}XAÕR·GÍ¸áŽ?-sHÉë¨sæð<¼µòn\Àb.F%OÄ‘˜8‡¹ÁöÈåi”Jð#–0<IXrUE|¬š³§Íxž6žàM+†Žk_–xPÎvF9m/+™¾~ö’²®_†Ãás,fònjKuØƒ¡*—ôàöºhƒ1‰ºW_¥fOŽ"—ý‘px¬HŒ/ü¡cò4¤¸¥$ŽËÍ?8ÚŒúÅŸ|­_6a‰¤qêŽ4Þåeñš™ƒÖÝTž(‹5áÉ¹OõåZ^5’¼p¬ÁÑ²Ó8Ã=¬Õ³RS»]›ŒŠëÐ1ÖrÐTk ’ÉðŽi¥~E{WSTÇ§à;Iö¾æ1ô¾À@”å&¥Àú&®Z@˜ºÎæ\»b Ã;%e>Ää¯ÈlxÜŠ7ZráîTìëÑ |Ø3¼…\·S÷´²Å
–îí„”vÞ±bÎt2i‰q”L›Z?À³@Ö¿˜¼ÒV35"ÑŽ­ì¡½îv–ŠŒ/Ç®gz¥¼-A`8MF9¦Ëº–0u}tÎ¶HÀœc¾/&™Ô3dšld—„99LL§X¶+ »^:ðöyE€b‰dÿ2ö„2;!×çj¡øûUáãë£5>Ë*¦÷\¡çJk»‰\×÷ö›¹,d^çŒÔñ¥õ.ž9”¨Í4Ï¯u4Ïh*ZÌ¡yÈÔ1VÝ²öó|ˆO3„GìY¡¢êÐç!èÑÛ ÷6ò‡Ýíø‰½:íƒao¼¢õð|µ\ŠWÐóß¹­Öâž .Cˆhó¢ÖÕJw~‘ªä<¼ÐÉ‹Æ[›T%ÿ
7ü2b½Ó5±eMšû^±_F§£ÑÑèþýÛ_q:|<`G'ôé×îSSÃ:®.ÿz#/°i_1jW%Çî»‰òõ¤\m š¹³
·´s~Š6`J‰IàDÿÑãy¥tÎÀÖÎ¹˜±I-ÁP@ÚÇ7?á¢ç\7©0MO¨¡­A”][ˆÇæa%J[~’žÛŽ’r¬_R1u¶ºÂ·-,¯mã5…­ú¯„7)Me«AÚ%AÈ¿¦ÂT¿Á­FüêÔe„½´ÈikÃVµbû++šhî–r:zß¸obiSOpù	“¦ýZ„Ö{{ÒmþA¨Y–X6
×³Ga)ÌûÂ•2¸å§À¡)•¦(TqäHEü>u*È•x‹£{˜˜¬êÂ•ÁÂõ4Ö¾šb:#¶£ª®Ù{6Å*”Ã![¸ÈÆ)ûö¶fuˆ±1h?æS×½°kWjWß­¯±˜÷ûèÅ.£¤ãCˆ\Äd¸%j[Äž4;&*;qæ&¦„~»ræ:¦\‰(¬r& Âm”!ý'Í‘‰¢µœñZŽÂ&M;«)«\;¯ÝúõmP&°–;ï-r·Ù(ú |ùUZÜfîXPO¡ÝSÔ|0
)±Ž>9¿|¸àv_èpœ~=ˆ°àY.‹ze‹õ¹¤·—h÷dCöÄ*‘ã…5L©¸Ezð˜‰‹¬ÇÕ*Vk‡ÓÆ9¨KÅÿa{ïKGãôŒ…!Ù·A²– …ñ²Ì`Ãä*
Ûï!2ØúeŒÏ*!ð…®*†*Ù
¹á—¯MH^GOõPŠ`ZÉÂÛãÊ”Á-žcã–£ÍgXE¼¤´?ì½Üî½)ú`ü\L‹užÎØëi´{9U‘V©ÑJöWÏ¤Ý3	ƒ{D¤ëxdO,ö<Ú=¬¦†G+Ä¸…G†ÏþÄ¼¹®<Æ«">yê ª ùj"MÅñ$üßMAè ‘la7ÿ2êB´¼nÃúiæa ¤Á]å¢•+‹iFoó° Wò"*­:ø:7•¦¿îÕJ¦øÞŠ¥{UÿX!º{I¥òàiö˜‘;v9ûWc¼²ßQÆï¸òOµ;ú‚:ñUýì¦¿yaÏZÒofå!±>Üó¹ááé+çp¬»§bßv 6¸³ŸËv:—š>„¹ì:b×ø?ì1.Ÿ    0707010001f04c000081a40000000000000000000000016a102a8a00000c8c000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.cluster.node.conf.gz  ‹     í\moÛ8þž_A Xì[¢ØéÛ&»-.HÛC×¦—¤{‡–h‹IÔ’Týñ73¤lÊ¶œ´voëPm‰RÏ33G~ð`;ØÿP\¡±FqëÝz±Ûùö;8Î+1)M»ãY¦F"éÂÂ•+öÖø‡Ø˜X•¼Ÿ‰I¯¯xf^Ñâ¯Jj‘Ì_IÄ€W™´Û‰èßþ/¬ûô ê>¡/X÷¿ýB_»OêþƒÇÚÝoJËŒÙs6…Ð2Æf±*®…ºÈ¤±„ônb8‘
:ÉÔ€Å2Ñ¬Ÿ©øÊ0ç9H²ÆÁgX¬·RØ²Ïã+Q$Vxl™,¬ŠØyªª,a}o,ÆŠ„YÅrnã”U¥™‘Vàøà2‡›LÃúu–)öìÞ ´qoŒ°=Q\o*éoU!îÍh@Üqáž(;j â²2)þÆ*ËDl•fp/Ð»m
ÔCËDCwC¥5pž™»€³kžUÂQS	œ¼Ó*©bì±ö¾æEœ~m˜ŸÜ“åz(P¥áöÂ\ÇÎRüCGdS‰™â	4ªÊ¡††d–Ø§ª flÊ§w¾€×¶I¢ŽŽ®FG—Z”ª¼^2™—Ê!æDì²Q*ÁÄ¤!AZÄ*ÏÁRa#iSÆAú< JÔº[ÑÖ¢Ã6B'ªôµqŠd™±±"g¢ÀQv
¬žÿ~ÂN&æóÅàšú«D¢ÞóhEƒKEÀAJ×>Y
%€éH­
`ÃÑD{!`´Œ°Ã»g„#e\¢TÜ”ÀŸDd!R•15 1ò¸h•Âl
ú05å©[xÆRkKs´¿?9yŽ@sîT?ô¯M'j¨bJãõCæÙr
AJÐ /"ï“0Õ‰ˆý7¤9µ¨’[²ØªõZˆã•Uèè±ë±Ÿ;Á&-«LíÃÝlpDá_4ÁÏ }×qSj5¼C'ü½w«†ÓÛoJ5¼©®C5¼¨/¯®ãÕð£¹K5<åwª†•¹P•ýºq¡«UÇ«ØI¥)››cr~#ó*gøìŸ8P„À”ÜÐd0bo|k‘g†<¤@Ñã6ü“~/—E¯mèAÄ\ñlSÍóIgí$ÔÁ`@ÐÖÀ*ÅŽ˜Ý‹.r—ÉhÔ”œ( a®!N¾ÈÆYÞ„áŒ›³ë†`ã¶‚"åÅP˜0
ïq#*A$ti `æa&¯ïiŒƒ½ñO@ªpDXíüºˆ]Ð¸¸\ª2áVl¼Bt?¿B$"ãcÐ0J MÜØÍMö€‘EÎ|Ž›nÈÍÔ´¸]ÂÎß„™‡ÏHwƒä,És†ôLý¦Mµà	LvÂ	mu†.Q2O^kå&8ˆ¼qšÞT&Î9 Tje ³[‡œI©´eÜÀSøéz×ùZB”"Š7¢U–Æ•–õâgBÏ÷ˆ”»É3]Æ~\n™LñMÉ5æŸ')†ÁõËÄ Â˜ÂA$‘cpÊp·¶‡oU€L7•ù:ÊùT#ì+•	Þ´AX¢B)Â	t‹N²ªÿ§À“»ð14åanAÛFÔ2›ŠœÂ(6ÀgÂ‰4JñüÜ\bO$ì2q­ÌÎk
zæYFi¬Ñ÷li§‘xf†Ý€‹šç`·!Ó²ÑÞˆ½<¥(àNZýS}­éÇE>vÝv.éRœ‰.³hõ¸iÞ˜h¬
X€ZQ§gé«•í³NœÄ¿8¿ø;Û“{/áäåÙûÀ^¼ügïàøêôŽ¯ßâù7ïÏáøîôgÇt|AÇáñÝ™;â©ÿ¼?þ7üwv‚÷½Ä;Î/þ	Ç‹7(Æ	Ç÷ÇÍ­`´ØC¨82¤È2hF_«j˜‚r”¸‰À©kÌ¸I”¾Š
èÓòEÛúÎºÚ—üAâ)|V#mEAÛ¤&n‡mQƒLÅ=ž$z»¡³Ú†N# ý”áŒÐÏp@,Ã½_Éeo±_ûKÀÿDÁR²eÈÇÒŽ·¨¯Ž:âˆ;˜\K³oUnŽÛB¾*äJ@} —A>È”Úzõ5 N@ÜÝepk_mÑ^mÄÀ>{t°mE;²[´WEp¤ÄéR´oe¹{u°FŒwºOÛÐÆe§&ázC0Ñ+aè*ù†6–Ž'§±8Ccàã.Ëä•`——ÝüaÇ\^îºj>U#PRÏ€Û]h$U,@j`q¤á
,¦e±€Ì~ðûXXG«z]ýH+«°…[ùÍÇ© ÜÈ˜ðáE3sìò7¥Ú ÚL¶;T‰9¢†¸°c'Øw}?¡á:Å¦cŸ&)sÊ4M;ð²eml¾˜´²XmåÆ±cãÐå®PÊ£H‹NÄg(d¿›ÞÈBÅ<«t¤Šï)ÑAæÅ¯“ÖÀj© &ë}{ƒÃ—a:«äšçÂB{3)…äÉŸ•ñé2Sò"àÂ&îqùµÙÕÒ-½œßô°gð_éF‚2Äª’Ðþpì¥Þ,RW7¢qÉLÕ/µ®¨‚N!‡¡Áp)Te@Á€‘£8OŽ.a²ùÍçTŸ µ<ö›Ka>¿¤ÄŠñ´Mû\º¿‡ûüšË¬—‹¾[…+#oÅ|„ß«GÁÊÊU=@qŠÕÃõŽ ‚†îwwÃkF¼Ü\[ø¢ #÷ƒ—Z>£ñµ \^{.ÙTp±$j•¤,øcöÁK	0~¸Á¼gøPPM¸úø
¿84"vZàåJtýÉ::À{Ýîå?À™:i7¶ÇT=þÒ1ÕGETðMbLŒE¦.  é–*w&9ëz6‡Ðá~yé£Š@Ü1…5. @^at"/ÝÎŒ›Äaè.À†xÈ±÷¦¢Ò<Ccaåž›½,Ë7–vMª2#ÝJHëniÂ¢—)bõ½¤A¡b,+ûÒâO†ýwÎ¿tp~ØÉ?*4w/«ª¯ÆïñœÃQ´ÁéBM¹c°îDSãÑÉáN¡ŒDÝ›VáÆ‰‹+Äó[ëuôãª˜Æª"åäÐvŠ8‡XNÆ©~)®ÊŒÔ~¦”É/*(8•š•h–¬£zbu0`PË“³-úJY¯šs*zØiWÑR}›µÍ·EêyÞ ƒÓž|£ÌhêÂ’
«‡°"/qÁuGp¯×”éº‹†4ígÖ8“À–XsEPôz	ö¾è"Ž¨æND½¼pE<ƒ+<®´8
>;=½˜|ùyoñC6Îïu;3Þ²Ö]ßbFˆKj°½ç‹[‡m=	&ì£ñBG¿Ñ{Ïig ¹ÆwSÊìÁÙæ˜t™}ÌŒò êì×_mð}œ¶GóÊ´möw"è€5o<:ÄMYKÀukr˜Qòb ùÞá²q,j —mÿÖ¹ŒRùÂ€£ýýº ÏF±Òå2Ï$CÞ#¨zkq3%:÷rí–ß(¾"}ºå7…-±õ6So‘u‡É×†§Z6mz?EsVý­ÏR³ŠÐp•¢™f^ªìýæ’ÐG×.ºééBºèvÝæìzjç¨?ÂU¶yÜyÍZæ¥e’æ\÷'yîåŽ»é·Ê‰Q‘%Ý£šKƒ¦lìn¯VN‡>Õœ|ÒCíÊAfj(iÏ†7²ÒT-gL:“n×f\Ä./ŠË!þ‡5óØQ©åµÌÄp’ÓÄ´7Ž~&è(\fâr¥Žã|ŒŽäˆýÄÀZr]»×˜2á°hÒ÷B/0A Š·›È_äq,J¬žü² žþãô,ŠÂòõ€Ñy”#““òuø›ìqjK±èTˆš¸µ5~æ(kÑ5#âÞ­óÛ=Áökî$¿í.|vo ìæþÀg*­6AÝóî¤êÙ˜s|)ÀŠ²ÈRœ
ü™,Œóé74VN˜ò	–wÔ=flÏíKl*ì±æ&]%+KØŸmhÀBþ¦wƒÓ`œ¤Ê¿Žg˜5Ñ"¦bÀ4b/¼+AGƒR£F‚âk9½æbÌâ»Fø{)4ê@Úú'ŒÌ1}×Hª”j$°nË Ô=ÃmÀ°C¨…bè9§–¶˜+º²)’hU¯ß^,…ÜT%¾š°} {,©Èûý»ÿÇªZËGK  0707010001f0d8000081a40000000000000000000000016a102a89000010b5000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.disk.md.conf.gz   ‹     í]moã6þ¾¿‚@Q$ÁÙn’¶À!Ý·}Ã-®Û=\¶½EÑm‘DU¤ì¸¸3CR"ø­ëd7­wÅ®%ŽÈyžÉé“OùçÅ'ì€P\&õí€™e%X‘½¿¸Ãöî°º{ñö“Z–™¸ûõÅW4èKö‹Øõ[±\¨:»èzPòBÀ…áÿ Štª*>ÎEû¤wuƒÏaµø­‘µèºð=Ï5]©j5—ZªR–Ó‹èJ&&¼ÉM×ë—Øk]ñT\èïW#öè¥÷6’åLÔÒð2uÈŸ°+6<ó=”!j{]W"•™Â=SQBÛ”TO×)Üñn&HYLM˜™“21—©Á%©íµ¢Ñ†Íø\°ÏÏY:ã5O¨5+øÝ(`Î×bÁk§3Šia·íy^C÷–ÐyÖhÁÆKÆá&>®Z55<úõÝ~¹]ë`W7U-DQ™>Vµyvh¯gÆR^f2ãFè‹®·ìÝsRUÎEô`¬<ªŒHòzBh¨Ib@@’ØÛJ”×?Ã2Ï™S:ôJKTÔsn`ÀpÕÌ8âïLUQ@ß`œÚÀ~¼½ˆèÿˆáˆ]+ ‰6¼ž
ˆ“E•‹B”†§Y¦ˆ~º©*h
Œ
çØ[¨L°c¡ïNFÝpAI2A•$	èÔŠfÀ.nÉ*©Ô*­S#aœz_ÆƒõÐÓš¹–Ú’{EÝâ°†ÍU¿=;
ÿ¨Jñžê[1‘¥Ð@“öFÐ3èÖ üé@kl0¢?¼KÛÃwüäTµTÐ¿%S` t/J Ï7ÌÅ\ä80x^fQ :Â=”5ó™øþø{	kÛêú¡ÖÃ«
eÄM½x
Œ[ÀþÊ~ÊÔK¼ê4O ƒy
7åéØ%àœ5\ÀàÚ·ßc´·mŒ>Ë³Gøiæ)© œ†ŠÃ/Ù1 +îÒ¼Ñr. ¯)x‘šƒQe¾<!Ü>_ç]ààêeèï9ÌrðTúârêÉ3Œth
j½ûîD‚öC‘àÓjqŽbìt6ÎUzëÝ“eâ°©h"#¿å~`®ñþ@TC3²àŽì Žƒ&vÚ§ø 	p¦4ì*
mÝ"þˆCÔFŽO"(ÖÎøY3ÏŸëfÍNÄ6sk™õÓeØCt:AŒ0î¦`¬ÈxV0®µ(`L>Ó¨iºwã”5–¦àÕGªcßäÉT|T‚à#´wìº&#73”%¹«®;Â×%Ïìu½JØûÐ
ÜÌVÀ$& "ïRð¥mk'-Û ÝÈ:Îø”Ìª©À­	^œ!¹F—Ì÷fÊé¬)o{#;"0-XS
J6­-ØÏ<oÄªWwó›õê·cŠ(kÕÀz–~ù‚0²¹dgç¿]+ú¹¡óË¯‡%V#…æÔ‡@+ÀÇÁ$ÖEw›C P¹ èïíç|©Ó[G¤èšÃÌa5³UËìXá®ÞµÄ5ÛœLç¡pò6NH´TëÁx ZÃwI²ÏÖ.w*ðxÏÎ5>¦gÂ©v*ê‡`*rNjbõ¶——²š<N×Fj¸d€±ßTJ››V}xì@xÕš ]§µ¬Ès‰;‘6 k>1"Þ@
lã"âÒE•I;’„ÙŸ`…W×ªÖ6Èª;þ"…a#³%ðŽÑ‚¯‡òBHªLV¥ûÃHbö†þSfã~‹÷ÀFê6y@ŒýpUUça,SU‡0LUíƒ_Söså#™e Ú?j•ˆ]A­Eýì‹èX@ø/;ü	‘ÙÍ>øy?$ýxDöE°}Ç$ß7øYÁg/XûÐç`vù¾±ÃcøúÈçñŒò=CŸÍ˜b‡Eiž_ÂÁcà÷O‘W·Ó@ÏxÒR“W³€©¼Mzô'ët$ã€Ô¶˜Ét&›iVâmÀ USžçm¢Y%Tòf|ŽGèxC%jn3G¸—ˆ‚§.™O–lt²–øèJ•Zâ¹,ËY8óµÇAöøñO–êøä™%¯¼";
ØŒ’±`rZ‚SšB”„ÑTD=kL¦e˜cöÚh¼Ç4iÃó_b’%IùO’c×Â`Zì·ß}ÿê§ÞìòLEGºþ™ È÷qö×_Ï” ü6SÐßáABÝ^oœïÑ˜ù+´wE:Ê¶,D&AÓDVÓÔ &[6 ®È.U°—x`}å»—PN¦„„’(9Äzäï„Au©2:ŸAK’vléŒ—S‘¢áÅO¥}p«ûÃ}€m‚‰ûy[v‰(S^õ	Å1¶ë8‡ÿnm,N—BË@ƒ Œ#”lÄeÇÇêZB§™4þvB*jC”ÄbX&P)>„™K;ÃˆÁ€3Qåj)2/ØJåtb	DÏÛFŸÐ2d•=â­Ááz¥ÞóÙ¦–Ó)p‚uÀIÉ8Â¤«qh¡è¦cYÞj¤ž©÷sÎ=
Ñ1…òè7t¥àŠ‘Nä6¨
]*ÏŸÐNmN5×·ée™ê8^‚K ó³¢ìÌq4`ä±É¡´J@›¦ˆ˜!Ôx¸Ì<+ÅÌM§ý Ï‚åŠú=nL[>€+§8EË	<Ào˜»€©*×ŠÈqOj0ï£0ÐJ-¦˜Î¶ÅêT;âsè¨mzÃ¾tíÜÝAó#LÎ„˜Ñ5±ÿøg;`n‡¿aí‚í¸ñµ(Á9ToŒ*¸‘)Å¢Þ­bÔã8b?i1iräÁƒRry“lST”“¦—ÚˆBkÄÇB2•V_—`ÕºÔúéÍ8¿•êf!ätö'Û-ÜÃT`e¦;Á6•à¦šâ”ööù×ó¾¦,ä×oaˆ9ðd¾–´ëòÝ.À+š…%;;%ÏNOOGì_¢.)E§K_sƒ¥ë”P£Äøòt²iÕÜüÖ(Ã{X7Âº’rÒ”rÙ1çW¾Ð ¾¦õ,Ø¼J)KÜ:&†õlZ+˜aÞü4^·%/Î)hk•ã¶åù@:E£ÔùšÂˆzÄ¾ïÅ Ýõ²lîXJ-À9$ÉðBÅB\Ìç²&^PÉ«”–ÄÎ•þØ†N¶™7…Lk¥Ð?[)þh"x:CÝI‹®$ùòôÓÀµ¶#ðoª]ƒ u‰Y»Qxj[œ¯ÞoÊÞï %5±Ÿž±Åôì#Î¶zf®Í:)ßYGI;G™Ûjwiàç³Óó/˜Õÿˆ½Žkÿ šáÓzÐ°¡ú°‘R7n£ðÞb“½
»ôèRFËdzò¤âœè«HÞÙBè7kLw*¡m
OŽÏJØÊ[×ˆ€¶2ðì|3ûzÞíì{_¡æ[Ø½Æ÷•qs„xÑ¢zGìøïJyw×”óŒ»Ò5n‡°v:8œã§ÃóRÊn³ÅùÒÑ}=	VDPGf®w±óK @ÕKv,ËhÈ'ÖÐß6føv2|cï»…õ-˜ïwPì*×îO¤XÇ¨&áFÅµUz+oÑ¶F~J¶°C©âw<j•÷9ÄlóŠMr>eÇ§h®g'H¿G©ñ§¨ ÐÇ	 B%O›» ‰jò¬œ¤ŸìºÊÊçÆPm½Q´¶k
ËÆ@”#"Ñ³=Ÿðóþ=Üþ¥Žd~¿ìíÛ7 ÛIGÞ·o<eaâôƒf~®•®œÒÌPOJ|Ït3¶‹Ê¯p~ÛVš«Œ:Y°ëi¶p\/xUayy?±„â×ôòN«.¤òUùÑj D-hqÉ¾Øâ™z¬/q&gk}s|ün+‰÷J·­AÉ¼J>Š¨äohš¤îÉnµŽ¯ð8êšº·z~Æ3‰÷"ó0ÄÁ…Ú–§/ù å1n§}mòË>Å1}MÌÓÖÄì„ÝI¡}!Ì‡,„ÙÅRAûê—§¬~Ù¼m	 }ÉË‡+yÙ	ÂÓ>û:—Sç²ÅÝ«\úâ–§-nÙº]¢˜¾¢åV´ìânAL_Æò„e,»a·5†ékW>XíÊ.îÂ<W½°1Çšòä(çVV±¥ 4H”ØŒb«Õ#ö_^[eü»m…ï_–˜«/´©›”ònlé½×3ãÃ*Ì0Ä=¿ŸVPcµÔÝî$7Ü¿jµe)yunžÏïhàq\Àb†¯Ä©µÀ\+L'µ»ô¤Q&-PÀBXÂð4`ÉuS–ñIgGíxŽZOðªC'|wîíÒq@”õ²–ÙÕñKJÔ½ŒF£¬Á—ˆâ/ö,AøêÑs½šj Î×M•$ 8ÕXéö…¦îk_dj¥ÅäiI±¡ŠÊ¥s?;Ú|·WQyDô¾]§>àHë]^–WÌÈ´î¦ðDyÌ¨1Oo}Æ¡Ï ïxÕJòÂ±l£;W˜°‹8):,ïÀY©-÷¡÷ÌGõXtòQÚªˆH†wLjõ»(»»Ú:,Ÿµí$ÙûÚÇ`â`½i¹IY“¾ìÅ• „ÙÎlf_à%e¡WtX“¿&³áq+ÞjÉ=„»ƒ”«ÓA<ø°gx¹n§îImóÛ—íÛÏÁ£Z-+æF§3‘5¹H¢üËÌúž‚ü»ò3|Ïåêá~$Ú1 “=²×ÝÀn2‘sX dî=ð1ø‚ÀpÚ$dÌ°t-aêºvÑ—WNŸ³à˜jdÚä<LªhX]ÞÞä01™Ð':²ëU¡oŸ÷døç–Höß˜TaµØ9¹>WÖ†âÏÉ{ù‡lxÇ[¤˜Þs…ž+klòÀX!KY4#µE|é¼‹gŽý.…¶_“¹çhŽi*ZÌ yÈÔ5­ýœŒð©qRé);.U”ý8	A·ˆn‚ž25³¾˜ì±ªß7Xö¥f®lˆ<´ûùö“ïñ;8¬èÐ¯=ž÷ªj¶¬f>eãFæø†zvýêG÷’yJÞö„Š”myRþ`ŽM+À"u[™NÏíFI9–6TÌR›®Fr¶…}3»m¼í#ý¶ØUk¶PÜºþÌ’+Ý‡(hÖñQˆ­×†5“((a_(`Å"ö÷f®(ÞrS¶Ž¾´ã›XÚ4c3À9ÚD š×=ö¤ZäP³¬°¢ã–a˜%)!-Ëô¿?@Yë¥*‡ŽTÄï§úXÇ¤¥{˜³¨š2øP–ÅM0Ó	ÛÑò%öl‚ê£[¸ìûÖ0fuˆ±1h?æ×½°k÷ÊÚ‚ýÅ-Ö þ½_¥>æ*•tüV¨16¬Nq)Ð“æ‘I£ª8³Ž)áË?våÌCL¹·r\åL@…M”!ýMŸ‰¢µœ–q,GËã¶Õ”U®×6nS>ý›àC	§Â¬˜¦·BTØR=–¦æ¸QÿöÑ1€°©š¾¼žº°ò.?Ûo	v½iîÛ€­\YNrªÏ·UËyT,1ø‚&›>T,?W2ÃJô¥{ù-ÏW|†{Á ~9©{šÝr»âIâ‹Ý/ÝÇªâ#÷T»§’`H‘ú<]»[ƒaÍgv+œÆ¸ÞÜŸó	Øó=>	Ï¶î“XïvÚŸ”m:ïîìÃ‚GM?‡ˆò!b×ø?MwQÍz     0707010001f01e000081a40000000000000000000000016a100daf00001404000000e600010003ffffffffffffffff0000002500000000root/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

Files: usr/share/opensvc/lib/pyaes/*
Copyright: 2014, Richard Moore
License: MIT

Files: usr/share/opensvc/lib/json_delta/*
Copyright: 2012-2015, Philip J. Roberts
License: MIT

Files: usr/share/opensvc/lib/six.py
Copyright: 2010-2018, Benjamin Peterson
License: MIT

Files: usr/share/opensvc/lib/json_path/*
Copyright: 2013, Kenneth Knowles
           2017, Tomas Aparicio
License: Apache 2.0

Files: usr/share/opensvc/lib/ply/*
Copyright: 2001-2018, David M. Beazley (Dabeaz LLC)
License: BSD-3-clause

Files: usr/share/opensvc/lib/winstats.py
Copyright: 2013-2017, Mike Miller
License: GPL-3+

Files: usr/share/opensvc/lib/colorama/*
Copyright: 2010, Jonathan Hartley
License: BSD

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.

License: BSD-3-clause
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 1. Redistributions of source code must retain the above copyright notice,
    this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
 3. The name of the author may not be used to endorse or promote products
    derived from this software without specific prior written permission.
 .
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

License: Apache 2.0
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 .
     http://www.apache.org/licenses/LICENSE-2.0
 .
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 .
 On Debian systems, the complete text of the Apache License 2.0 can
 be found in "/usr/share/common-licenses/Apache-2.0"


0707010001f094000081a40000000000000000000000016a102a8a000000cd000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.node.network.weave.conf.gz    ‹     ­Í‚0„ï}Š&\µÞ4zô	¼¥´ ¶Eäímù‘vOMf:»ßDQÈ!8>®‚íj]¬¨íÐü‰âÂ^¶;r™`#­*‰×•ìèÃˆMˆ(Ð;‹Ü-wLŸœ¶8¾+#ê†g%>ËN¼4ðŠÆ£UòW‘Èy[Úå¾$fÛ”Å,Þ$é
ê G“i T®=Ò*h%P—d„sœï ¢l…¦*9#3gÜOo×Ò×&òô~Ï÷Ù     0707010001f0ac000081a40000000000000000000000016a102a8a000000dd000000e600010003ffffffffffffffff0000003500000000root/usr/share/doc/opensvc/template.node.sym.conf.gz  ‹     ­Q»nÃ0Üõ<'Þ]¤cÆ.‹Q¨s,Ô‘\RBâ¿¯Ü4N®âÈ#îÅ¦©9¦¡Š³Ðé|©IW×]ÝîÌ{	ûa›Ÿ˜¯Q\÷TSàòˆn*ÎR‰rœìiÄª¶·£.B$øÊ^àþ#½Ícz|‹2Pñ¹ƒ:}ï™^éŒ ñü“°0(—‹Ão.š¬Ø„ú(”PÇËÉÇÐ§¬C)çH!:Ð}»¥ðç¥Y¥ÕÁ
Z¹}ôµ²­C²·mqñ²îvå,óg_5j     0707010001f0ab000081a40000000000000000000000016a102a8a00000205000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.node.switch.brocade.conf.gz   ‹     Ý•MkÜ0†ïþ{Iaë%×¦Î­…^zÊ-V–Æ±ˆ-¹9»†þøÎÈ1M—jh‰.«•F¯æ}F’w»-[¶ƒ›ÈÑÉF]ï!B¼VÿBnÛì¶e—ÝfwÁ:ƒçoÙM2]Ì¶³L<âpòÁ|xN„Gxüý†M@‘ö*\6ú¬B™	ø½·ÍËƒ•ê›øœÛWïpCà´1Œ“Ô¡¶•Õpè0XÌ±iŽ¸«:kˆ¢ôƒ}RÅªõ„òÓø°.Œär¹‘˜²üýAè\ ×b¬½ù_ÙÕ¯F •3Ö0#š"6#üu~a;:_¡ÔÞ9Ôqæ=á”µÀÝÚ÷T¢OW×È!’¨N¥•¥u*©Ó&EÊàB	œjñ-^ñõ{¨peó>€í@è]_*p>
âýŠq¥š¦Túq‘aIëÝ(A}UÙsÂþ3låèt·C®}è.q—zÉÐÛ|8Fo/^Š=à¹Öh@èÌR°ÝrjtZ¢Wì•Ðó$<ö¡í)B‰óÓs<Ò@Ûã1•„:Å*|SÜJ«VO8-˜3ä%\š>ñÃ É†ù`È]›7š<£Iu^ìÐœÒa©å…ZÏ²ÿ¶Öw¡ß¾Ô±?}–À‚ï[k]–ýu+ºaù     0707010001f033000081a40000000000000000000000016a102a8a0000019f000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.cluster.array.nexenta.conf.gz ‹     í˜±jÃ0†w?Å×4I;6MÆŽ²•‚/ò%µ%÷$§ñÛW’±…B…rZb|Òï»ï‹çù”+ËaÂã»ø®!0t&ãñ7qÓv7-»ì5›³6%ß²Uz=Œeq€wê>-——>t.Þ
Å»	W„å”mpWÑø´g¬Å
ÓG«™.}l¹M…’öØVþÒß‹54d„>‰û¢kHé½V°b­Ò€!Á©°c{¤q4ðZGñ§²Ðftn˜œ£ÐÂSƒþ¸	mí‰É¨´¯|;RL~žRûk¨[çaG!|¸[®sžê¢ ƒ5¹C
š²ßwÄ]Åõ†öÂ‘ eÚ_¯¡[„§-’×{(E "PŠ@(E œJ eÿ·òÆÊ½‡årùc{ ÊšñÕym<ˆoÌ_°¬µÑÎ3zmTá2¦%&=Ðp`¦>¾áþ•ýÃ`íæHlÆêØZ/|„ð>ÂGøá#|„ð>Óñù6¦Yt   0707010001f07d000081a40000000000000000000000016a102a8a000001ec000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.compliance.conf.gz   ‹     ÍR»ŽÛ0ìý¸õW¤Pp)RH}éAH“+‰8ŠdÈel5ùö,%ŸÌC …Šc!;ôìpf¶Û5Ïf+žB§ü¬‘Ná*tëª[×»Íóý±ß7EíŽus_*3ù9hIÈø~ÅSÌIÊy¶¸,ü"m*‹ âÏl"ê¿­Ì–î¤°!°~Œ3š*ÓŸ C‡Ñ¨rMy÷cEpöÞ¢t“LŸC_[HH@„ ˜QˆH§Á´@=^Qe2Þ6Â+qÚ(v)¬bOªG-jˆÙínChÔ áp^#Ìö–,ÿfÎ7pÅVu“yÕ‹8À·Þ$VÙu¡GgˆaÒXÝ|‘Xd­mt
.†úévÄ#–ËƒO†|ð/2²§ó««†F0CŠx\ÑQQÂž‘q]£Ï0äDpf!LÑf[fñ&¥Z¦hT++{ðÀ|¬ú·¨ÿQÕW¯ßkOŸšÓiúÀßÿ®kUÉ§Ûû È($ŒÐú8Ó¨=çÁulD•zñNÌ›Ñ]åñ¥7ªŸkÒÚ¥"¥ä­¹‚wv\†­•]Ç%¢p2,Ä¡âzB.qŠÇÔsÒGíÕñ5‹Eá2àÖ‘¼NÙ.³Ç·Þlþ ”×Í$  0707010001f074000081a40000000000000000000000016a102a8a000002a1000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.node.array.symmetrix.conf.gz  ‹     íVMk1½ûWøêz“~\š:P‚†Æ-I„RlYÙ"ZI•´®ýï;ÒîÚJ÷âZ/úx3ï½ÑÈýþ!G¯	ŽyÏ¶ˆ[‡¶e‰Ñ«ÍßÃ6»Ãj×ûžÉö½27?z™ôhO»×KžpûËzñqŸ‰a%ÒÂ›Ž$UàÖ±…Æ]¤k¦C
VÊ£øsE d•Žûä¦Ö`†@y£¯ƒC®¤âp	K4èÏì!pÚq¿ÂÌ¬„HßYš!L$Áy»VÅ $ÓzÁøD›÷äQYÓjÒŽPI©6C
q‘QGpvvvþöÝû/‰êXiê…m¹%Ñª€éGÛ%(3 %[#Õ²òI^Ü8!  €Á'Çâê²£«G‰Ï,©ï1súÊ*DX gæó°Ëù¼ƒ’,	Ž
3¢>±bkl´ÙÎçÉ¦ìáŽ@º	® hE®‘ó“¡ÿ‰¡Ô¹V3bgèÒ›­w$u-×ÝãÍÕ—Éìêët:¾º'ÑÐ¬•·¦DaÍ¼J9ÖýOšÐ&Û»Ý/YØ´NúÔ’E¥Q–•Qœå¦ÉWŒ´ÔTS‰ÚøùnW¹¦Ýb_¥T7Ì©¢®¼Â`äF.)u©4fÃŸ›G¶ß<Îˆèço“ÙÝøöa|ûŠë©nÍòkëyÖ==z¬ašÞº¥ge …C¤×.[5 g•‰Ê,³êI:ÔñHÅ +d%Í£õôx~ÞÍ…()eå4¶aÖègï»8uÐÔ/è&§Ó]ªAXMi¥™¼©Aë ¹ÜYR¥È&úIïCf1‚ÂºXÔS/iäõ_OËíµ^ž•Ûm·6žô:éõoõú7í60     0707010001f030000081a40000000000000000000000016a102a8a0000011e000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.cluster.array.ibmds.conf.gz   ‹     íÐ±NÄ0à½Oa©+œDGNÇ‚„XŽé6ÄKÜkD'ôíIZÝUb€L`/©êÔõÿÕuÉªj(XyœbVÓÄi@°GgÂïÆ•Ý®¬]õ<‡­Ùzƒ/Õv½[bWU^ÿ§wbs»nÑ9}“×+3Mƒ:öxùÓƒêæãÛh×<Îƒ­û¸îöDÏ³ÒÚÈK3¨mk5ÜÁ	=²Õs¸4!ètãÐ!t"xåÒw-Äôb`ëOð¸¿ß¤ÛÛœ;Ùäcã&M<|#Ôü¡€š¼ùbÔ,FÍFc@Î³þ Ó9DÊÏùèéÖÏD—î˜(Šøˆøˆøˆøˆøˆø”óùŽY¨p    0707010001f081000081a40000000000000000000000016a102a8a000000e3000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.emcvnx.conf.gz   ‹     ­Q=OÃ0Ýý+NÊÜf*##KG„Ts~!©îlšþ{BS$Vßxïô¾®ijŽi¨â,t8óW˜«ÑÕuW·;ó²†mÄ‡ùÕ,†?p½DqÝ]Wy€Ë#
¸«8K9Êq²o#6µ';ê"D‚Ïìî?âÐÛ<¦»Áçp#ïÔ	ì{ÏôHïÏ?	ƒr¹8þæ¢ÉŠ=#A¨Bi u¼³œ|ÝiÊ:”ŽN¢­Û=?ÏÍ*­VÐºÈí­¯m[è5$;ï‹‹‡mw(gÑ˜o%   0707010001f0b9000081a40000000000000000000000016a102a89000010c0000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.service.container.amazon.conf.gz  ‹     í]mÛ6þž_A (’à¼îî^ûeó‚ËõW\Ó.iû¡(,Z¢mb%Q%{]Ü¿y!%Ò^¯½7Ûm´hƒD/#ržg†Cr†ûøñ1=GüAq©)©KUD³®”…üÝ”XÜq[w\Ý=ú¥ëìãZ—™ºúõÑsêôK×íG°—j½2uvÑ7%ÍM›Mt7OŽøƒú²©©ä4WÝ×Þ×­Âµú­ÕµÊ¶nTµYj«M©Ë9ÝüVæ–îdj&Û¼éþƒ)é†.ªÖ,S÷\É™x%Jf¾J@ŸUÍ÷m¥R=Ó)<3W /’fà6…'Þ/ëDèLH‹$šéy­…O‰$)M¦Æx1IÆðâs¯?P4ýõñÙ.MÏ[ecïUÑ>ïKÓ@8Yf:“²îm©¯ÄÿÄ
hkVvZËš)ìÚ6ª@¸¾ÔuÓÊ\2] ï	§bÀ…îbalSÊB}ÚHDjÖV”
(Þ1UÂªFèÙuZît'àLÏfªVe8ÇYm
zÑ¿€6Ý›/ÅÌ˜]ØÀ•É½có'sG RãýÑeiV%âÔtŽª¥œÓ(WëÖ6ÎDXºž ÆË‚ððš¾i'äÃýxèx|lÞõfÒíZéÍb«Yž;®kô.PÌ¤ª•*ªf"§¦n@Ý»S°µ0¦tßÅ.U´`j|ªŒðünFž¬(IÀŽT’ŒÄÛJ•ï~ú
F§<NéÐ*«AVÕK™ ¤\éf!ddoüdj
°N`è’™03!»›xƒþGÇâèFÖszU]T¹*€Hô9 –@;Úª‚WX@¶\‚áˆ§Ê^=÷Ý	%ÉU’$ S¡š‘¸¸\]$›¤JP«8È´ÑÐOðÐÓ¨“ª>-˜¶N•˜J«-óyCp›qØÁfc—é¤6¦™T²YƒrÏFtž€àáÁ ÎH%™çèÈ5pI¦—pËEá”H‚%kp)¸–GÄžGú±øºÆƒ½ÌÁlìÎðú¬Ís‡MCaL†·]Óºv‘³cZ0#b„Ÿ›ªÿU	7vÑ (L£&õêcP¢Ã÷SöoM½Æ»Nó„-˜µ‚÷—°Œ]I]8YAçúÀ|‚Ýã¶ÏðÑ×±þ¸Ë”T c®œ|)žºê*Í[«—tÖj®mSK°qSæëg„ÛßÑûƒÇoÐôÃ¡þSÐÐuäzÀèá«t‚"{iA=y&¦kºü ®èJ³_	DZ!kµ=¶òl…üXnÒKÏK™‰'m5öÔ_+Ÿáù@T]kt!ÙAÿ?¯ðp<NïÑ¦
†Š‚N¡ë¸©ŽâGPìs{ð£m -ww¶½ˆ½æ¶€®e]Mæµi«a ÝZña	]	Òád.90I(´Ó$Á#ˆ=#ëõæù”Õ¿ëÛš_¤™=3\V7<:/±Þ©d´ø	¶2íÃ›qzÌesÜÈÚšÜj„ÄÏRÃ$üÓû8#¸€ƒšËÚÇzýÝ9¦™Ôy[ó\6Ò< „}Ù	’©,Fgç£^ñ veD4°‚ÔIÑþ¤SÏà¿"¯»ù= bÓZW4à«+•¶8ZÌpº…ˆøq;Ý/ÒFç"éz’8À ªkSã\$Ô-Š](1ÜÌÍcOü™ê å…2T™l„n·‡‘ÄÜBø£Ì¦ëÉãe¨ÛäC u2n‡«Âó#Y¦©Ža˜¦º~m9Œ•wd–jÿ¨U"µVCôs[D]hzÇáOˆÌ-Ð‚ŸCòÈÑGä¶±ÏÝ˜ä‡?øÜ
Ö!ô9š]~hìãð¸|CäswFù¡OŒÎÍ˜bƒ9¯ëmËÝ~ÿRyeEkè¹-á-òj˜ÉætÐ6“ÛÂ{H;«…NÃsÌ´ÃÇ€¦<Iežwi•2È[È%­ä—ú¨xÿÇÉDÁW×´]…›J­mAÖ?]™ÒjÐ1.rŽ*Ã	˜ï<ÓQù‹%}ôý××^‘=xßuª„ž—¦æÍ¼h«²­ˆ2vÑ6™Y•ã àï‹Ï4­EÚÈ|%×¸YŠdAIR~.“dìÑJñõ7ß¾þñû÷#¢ãjaò,þ›”–Æm‡íõ÷3£h—T,´÷†ð ¡foŽ7Î÷Ø1nîXïŠl”ËT¨Lƒ¦‰¬M[ƒ>ÚlÞqEv‘˜B¼ÀMãW¾y	í<ãÆi(‰¶P]§ÇþIèT¿¡lƒþ5hIšû–.d9WÙ(ê^üeUò‡;}ð…m€yÖ]Þ·«ÊTVCºÞQŒí]œé€g‹“
|jíËÓÞˆº¢Ü» ü=&4ZhŸ;ËHEï¥±žË*eÀ‡0`Ý§EU•›5—„ãD·KÃ¢ïí£Oh5d•Žâ­q§Ù)uËg7µžÏq¨NJ§ÀŸñÛCÑÇº¼´H!»0-¦·(áœ{¢c¢‡K¿ƒkèJÁ#œÈ½ù’ºT™?8 Òf#í%„Fv]¦6Ž—àè<OJÃ#Ç“‘P˜ž8;6ƒ6	hÓ3„÷OÝî©Å±Ûk?h3†`¹¡vOÛ¦KÎÅ™Sœ…ï¶c-(Ì]ÁP•[NsØ’Œû(´R«9&…„ãw)ñÄ‰'r	åAoØ–þ=÷tðúLa‚˜Ñ½0ÿõßv ÀØÃÌ`n8øˆZ”ªA¶)d£SŠE½[Å¨Çup,~´jÖæÈƒk¥äúÙ¶¨ÄLçŠK~,±F]ILƒæŒR¯¯—G`Õ®Ôùdš_j3Y)=_üÅVoa*0€©2³½à´VÀ¨I5Ç!íló¯×šNJÁFõOÊÕûî-t1ž,Á×’vÇâ'L>¾ ¯Ø¬”*ÅÙ)axvzz:ÿVu©ò€/]gé>¥Æh1¾<½Ù´j'¿µ¦‘¬7Âº‘­Ò”2>1ÿg¾Ð ¾ŽÓäœc‚NaXï’é`Üü,ž»vAÎÖHAÓÔš—E8$YÕÅd‘ÀërÛÇâÛÞQŒÐ]¯ËöJ¤ô8‡$99ƒP±€P 'ó¹.tÏ¨ CTÆjbçF{øE
'Ûb
Á¨ Ðim¬úg)ÒB”L¨;m`Ò•$_ž~ö¸×5þM•!t .¡qxÊoœo>ß¬?ï #5±Ÿ¾±Ç(wÑ6p×ÆNÊ7ÖFÓÊQæÖºU¸|vzþgœb¶p\YÐœLM‹£­nZÊõ)më
·&ëðˆHé.D—:š&Ó7/ ÇDŸkýžW
±ºÓ™µÆù-«„–)<9B>¯8”0ÐŒÚ·#" ¿…<;¿™}ïö½¯QóìÞbµ®Æue\!^xŠX­¾‚?7²çûG$[¶ÁUé—CÄ…8ÎñÂéÉy)‚‰vƒY¨bB¾t@ô¶ž„Â¨¿!3·‡Øy% `êµxªËh&ÈÏØÐß¶ÍÉÛÙÉ~îæ·`¾s\AáY.¯O¤XícfáF%­Uz+ïÐf#?¥Ÿ=ì0¦˜àŠGmò#Çm^‹Y.çâé)šëÙ3$„_£´xÉ-H2 } "ôÃ¢}ƒ«$Qå
ËÉ@ú³Ï«X¾lª\mÍíÚ‚ÙˆrD$zvû~ÜÀ¿‡Ë¿ÔÌ¯—½}û&Ätä}ûÆSNß8xÍµÚ¹>€™ ªñN‰o™m§<©|Ž½ðË¶º	®e;Y°3¥÷pÜ®dUéf ø1(Ž¼RôZu!Íõ7g!jÁ/Å{<Ó€ÕÝ!Îä¸".0Ç7Áu®·»eP‚¸íJ–CTò§ˆJþ†öx×¡	Iê¿ìfëXé>Uà!°×55osÿ0ŒgïE–aˆƒµ=1ÎPs/å1n¥}gòËmŠc†š˜[sv$……0÷Ys8Š¥‚Õ/³úå@ðö%€%/÷Wòr„§}u.÷Sç²ÅÃ«\†â–[Ürt‡D1CEË=V´âaAÌPÆòËXÃno3Ô®Ü[íÊ!Þ"„y¨zaÆkÊ“£dœK]Å–
€ôÐ Qb3Š­ÖŽÅÏ²feü§{O7Õ˜«¯lS·)åÝ¸S¿ñô7ÌŒ7«
:|,~XA/›µíW'e#ý„sHÉû¨3qx>¼­»q«…U2U'Va®¦“ò*ýi”i
øQ(&ŒLS–\·eïÄIñ¤ëÏ“Î¼îÅÐßU…k»´¥A½¨uöêéJÔ}5ÇÏ°þÚÃ+¼—`
< /Ã\¯¶ÙÇë¶J‡Ú²t>öÏí"Âgù¸?–“§#ÅUT.ûÁÑæNÏ²ºngï}wjlx*¥Sp¤ó./ÊWª²~¨O”ÇŒšÊôÒgúúžW$/Ë6ú}…™¸ˆ“¢Ãò•ºr:Å9ªÇ¢jÔ%øS•@D2|bV›ßUÙ?ÕÕaù¬m'‰Ÿë>ƒ‰ƒyt'r“²&}Ù‹+@³Å‚¹©’²Ð+Ú,É_“ÙÈø-ÙiÉ}Dº”W§£¸óaËðr	’‡îYÍùí´=¯KÞ©jµXÌÄ¦•µ¹J¢üËŒý€ÌAþ$ê,i››û‘hÇ€^ö˜ï»ŽM2•K˜ pËìFF<… Ð.	3,Ý›0t½sÑŸW¾K:îë´Íe˜TÑ%°º¼½)Èj6£»{²ÛM¡#oŸ[² üs&ÿ“*xSKœ“ësem(þœ¼—ÿÈKqºÇg±bÏµ÷¾¯‰9¢Ð¥.ÚBÚ"¾ôÞÅ3‡O}·ü›¶ÍSŠVx=dj‚…šl?ÏÆøÕ8©ôT<-M”íx‚ÎˆÞ=\:“ÝU5â‡Ë¾ÔÌ•‘‡ÎñØí5ûö“ïÁuàÙÀèÐßy<·ªjF\V³œ‹i«s<ÇY¼{ýƒ;ŠÙï’‡¿#Ä¸ò¤üÚN+À"u®Lçcs»^’@‰¥M ³Ôæ›‘¿œž»÷ìâaYìUk\(Î®?äß³äJw\;:>
ázm˜3©‚Bðöð@K¿ŽisäŠâ­þ·„¸»W˜6íÃpŽœDãºÇž4B“<Ú¬+¬(Ã¸å$Ì’Ç”ŒˆÖeúOé¦¬õÒ”'ŽTÄï§:Ò~ÖÑ=ÌY4m'Žeq3ÌtÂ÷èÀ‡|-›aúx,Vn{!¾†9L³ÙÅØ¬ïó…k^Ø´­²¶`}q5€f©w9K%?„jL†f§tîï@š»%©âÌ.¦„‡Ê™ë˜²5sÜäL@…›(C.úMŸ‰b­ž—q,GÓãî=Ö+—Çµ—©9ÿ„Mðº„S¤YdšÞ
QU`KõT7µÄ…"ø·.ˆù §júòzjÂÆïªñ£1ÿ¦®¾U Íýæ­N®.g9Õç³AÕzKLÐâ¢bÓëŠå—FgX‰¾v‡ÏÐô|Ãg¸ü¯‡ã¯ñ*[O_ìþÒýJ—x‹Å}•×T)RŸ§Ë«5Ö|ÎKáÔÇÝæþwÀîöI¸·µµM`}ØnG¼SvÓ~GðäÜiXhú!D”×ƒ¼ÆÿgÅ•èñ{  0707010001f0e1000081a40000000000000000000000016a102a890000103f000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.zpool.conf.gz    ‹     í]msÛ6þž_™L'ñœ¤È¾ö‹›dÎwmæ2×47—´÷¡Ó1!’0&	– %+s?þv 	H¶^Ù®zÚLB`÷Ù »ðÓ§Çüyò”ñÉeR_˜YU‚}ª”Ê?“ÜqGw\Þ=ù'û´–e&®}ò-Mú•ö“'8ú+±Zª:;ïQ4¹‘s¥¼ñY¥SUñI.Úî>ÖÀµø­‘µèÆñ†çšÞTµZH-U)ËÙyô&Scí†þ£*é…,ç¢–†—©ë(|Ê^³¹à™…`0kQÛ÷º©œÊ¾™‰Ú¦øYªÊ…¨ú¦–Úp#ˆqÐ½NáÝÛ)ÓÂ0£X’˜M’ðfŸ¦š©ÊÀÈáMËÔWªLÏ€Rm˜œ²R™ E<¯a”+$1bçR{Öhx•ÃÇÐ-v6l¢”[J3gœ½&}=È, 'Kè'ÏE6 FŠ©¨k`ÿÊÎ’cëÉŠ™9(<±aÚEÁËl³ü¶6€fJ|¿4%/ÄƒâÅ¿x(¸ˆøüD~05µ¼ ÂCé9ðR©[9©.AR¢¨Ì%Ÿ¨úñiaûæXj`”€UŸw£eÿëúÙÐSPèªÜ©¦ï+Q~øù A YŽé0*-ZÔNÊkõ+Ð)ÿ¥SÒ1˜Š›·/ñý2±
äN¤Aíg"ÔxYT¹(Di¨;Í2…&é¦ª ) l@ÎAÐ}ö\èë“Q7€P’Š¢y©-	aìüjyž¬ƒ*A®¢á©‘20Óh’¢TS§`i¸–šÂ:!Àò›mv¡ªáÙ—íH~'¦²DKÞ}|Þ:à‰šì‡Ã(ÏG~tªZ*ßŠ)P ú)ÈTs±9NúË¬ŽðM@½ÉÔgêÇã¿%YÛV[ð´ 'tY/ïCÆ­À¾d;eê¾uœ'¡ƒz
h‚×±IÀXb¸„É‰6äp¢õrÛ¥Àh³,0{	ß¯„yJ, cQûðöœHW\§y£åB€_3°"53¢Ê|uBrûëmÖþûP¯B{ÏÁËA¯ôœ–HØ“» ‘ÂPg…Ø÷×@¸’›V‹M	öÄXw6ÉUzåÍ“Eâ°©È‘‘ÝrØ’ëµˆ8‡©Ypvà?ÇÎ ‰uÛ€ã;š¦Á¥áÀQ0ikñy@n¢`¢F"QÜê;;ð^3×#ÿy›×ìHìR·OÖóôîÒÒÈMS(MëuZÈ ¥ó/ªPdÈÊxr ¦F\[KH¶ú¸Œ(˜J»”ÓPáø&²ùå×»\ëy.®7eF‚°|#fÉ-„­6ÄæÃoáý¼ZÏñã¹Ä™5†#›èÍ úŸ¡´üå`"9ÅˆŽ»zÀÀÄ.Uƒ¶=2Ù\kk¯Cù‘Äˆþ69‘u‡N/+¥ÍeË«Þ’9!]´‹Ep/:­eE^G\‹´áS#âX?Ëy:Dƒ¦Êó¤IÂì#pÆu­ÀJ°huÝ Ù¹ð/Qt·Hf»É‹‘oîEù{E
X™¬Å‡‹Ñî*Bø£Ì&ýjüÈJò6ù:‡ÉUU½<£™ª:†bªêù5eï+ïH-Öþ^­Hì+ÔZôÑÏ¡ˆ©ªÅ]‡?¡dfü|ž$ýx‰*Á>ö¹•üÜàgM>‰µ}Ž¦—Ÿû8y ¾>ò¹;¥üÌÐ'–ÎŽÍQ°(Íã;ºùýSä•fÆÍé¦„VdÕ¬ÀTÞæƒøC|ç©l9—é<ÌÐ¬ÄÏ ª¦˜ýãs*¡* 7çÜèÃ*Jç±‡Ž~¼ó½¢3<Ùht´VØu¥J-ÇxÔl·f­8Aæ·	<“¥ò'ËJ¹÷CÀÏÈöðo"˜œ• çt¢—5AFÏ“©e¦¼5¿1¦ó|ÉWxb‡`BIR¾àI2bìƒ0À öÝ÷o.~úá£=6YÎU‚Å÷	„üGáxýûL	:ªcsãÝ$4ìuãlÆ,7¡½)ÒQbL!2	œ&°š¦~4)è
´rEvž¨‚½Ä“Ë×~x	R^8"<Çs“ù/aRÝ©¦æ‡)~¤}0·tÎË™ÈÑôâžEi;nùal
Øž~¸Ç»E™òªÏý:Š²}ˆÛñïVÇâ“mÔT›cé	»ðS´˜0h&ÿœ$µ!Hd5|š	PÊ áY´M¾ä0cPàLT¹ZÑ©eä'*—À êo|
–!­ìtk×3uÃf›ZÎf€	NÐ#%SÀæö¢;QtîX–W!¤çîŽ9ã…è˜m@¹ôM©pi¹–ä.øÀG‡ÔS›þÆõ„FzU¦:Ž—àð<cÏJe=Ç³@Ï¥)ßwø:—yƒ¹Ú˜dãÏ_»1c–+÷¤1m¦'®œâÄ<;%° ¿5 î\U®cƒjà÷‘p¥3L~gÛR28cÏøjßp,];÷uÐüæÑ@ÌèŒØ|ßNàÛáo˜fjn=>J-ÊEÙÀ£
ndJ±¨7«õ¸	ŽØOZL›qp#•\^“mŠŠMe.ôJQhB¸æ˜SKªÒòëÕPu[äìr’_Iu¹r6ÿ“í *àÀD™éóõÔ™j†.íó¯7ª.JÁ€FõwJ{û¦˜N`k‰»#ö3ÏàD˜¥%;“OÇãñˆýKÔ¥È¼´“¥÷”ûK€ñÍx‹dÓª¹ü­Q†÷bÝ*Öµ”X¦”vˆé¸òµ‰W5­gAçUJ	}Ö0Á¤0¬g³Z¿ùU¼n³“6RÐ4Ñ*Çm‘Â)iÑÆdÁ(Ç¥æ…0¢±7¡ ¹þA–Í5K©‡$žB¨X@(€‹ù\ÒÄë Êîg•Ò’Ð¹6ÛÂÉ¦˜@p ,(dZ+- þÙZžnËÁÓ9òN*Xt%É7ã¯þïÚÀ¿©Ì ‚ä%æòDá©mq¶þ½Y*û½W€Ô„~êc‡
è9XmÝëÀ1L›5R~°0’vŽ2·ÔîÒÀãÓñÙ×ÌòÄÞÆe šáD5èm¥i(t¤ÔÛ(ÜX¬C‘Ò[ˆ.e´L¦>/@}¢Oøýhw
Ì%³j-q}kYBÛ!ž—6”P0ŒÚ# m…<=ÛŽ¾w{ÛÞä|+vo±`Pâ¾2nŽ.Úzdïˆýþ\Káî>áš’qWºÆívÎÆƒÓÁ>Ï:‘¢0ñÁíÂ,DqI¶´—è¡–DËOñšã{Rs½žw²¨zÅžG™Ñ“,O¬¢¿oÌðýtøÎ~wë[Pßî ØU®ÝŸH±äDMÃ5Ì!Vz-o¥m•|L?;Ð¡Tq‰;µÊ{ŒÃÛ\°iÎgìùÕõôá÷(5>r’G }t ‘ô!ãê‰» ‰’ï-¨ŸìºÊÒçÆP¤Q´¶k
‹Æ€”"Á³=Ÿð~ÿnÿÒ@Úbå÷ïß„ì xß¿óÇéÍ¼¯•®òÅÍÔ¤ºV#àF¦›‰]T~‹³ðÛ¶Ò˜-¨:Q °~ºãzÉ«
+{Çvˆã/gW]Hå(£Õ@(µ Å+öõËÔËêî‚§r¶,+PÇwÁs[ôu`P‚rÛ”,ú¨ä•üõñ®C¢ÔõìVë¦möÍº¦á­Ÿ†ñLâ­È"qp¡¶#ÆéËc¤<Æí´ßšürHqL_s¿51{Én¤Ð¾æ!aö—â^© }õË}V¿ì)¼]	 }ÉËÃ•¼ì%Â½Ó>û:—‡©sÙ)Åý«\úâ–û-nÙGtûD1}EËV´ì-Äý‚˜¾ŒåËXö“ÝÎ¦¯]y°Ú•}$x@óXEè‰ýs¬)OŽ’q®dk*¤%V£Xkõˆý—×–ÿn[áU™sõ…6u“RÞ-}¡›„0#0>¬ÂCÜóû)@5V+ÝíNrÃý­x-rˆÉ» séïæ}tº°œK€QÅS1Ôs­0ÔîÒOF™´‚|Â†§© M®›²ŒOâ8{ÖÎçYk	.:2tÂw]áÞ.DiP/k™½~þ’u_F£Ñ	Ö¿àmaøÄž%¨o‰Ë0×«©Ú ¿nª$Â­ÆR·wÏ¹SDèÖÞ9g©ÅàiA±¥ŠÊ¥s?:ØŒï2±ú¦“½¾<"ºÑ±0ÒZ——åkfd\w®,Q#jÂÓ+Ÿqè3è;\µ”<q,ÛèÎ¦ì<NŠË;Ð+µå>t%pTE'Õ Mð§*døÅ´VŸDÙ}ÕÖaù¬mGÉ~×vƒ‰ƒyt)$b“²&}Ù‹+@³ÙÜÞµ%e¡WtXƒ¿&µáq+ÞrÉuÂÝAÊëñ ž|82ü„L·®{ZÛüöU{Q-h`T«eÉ\êt.²&I”™Y;Àó€¿Ö8Ë€Úúá~DÚ! £=²ïÝÄ.3‘sX dîÊÞXø‚ÀtÚ$dÌ°t-Áu}pÑ—W>ç˜"Šù§F¦MÎÃ¤Š6ÕåíM€Ó)ÝþÜ]¯xýÜ Â?³@²ÿÆ¤
{¨ÅÎÈô¹²6$FÖËwòŠwØ,Ë˜Þr…–+klrÃeÏ¬¥,š‚Û"¼tÖÅ#Ç^!®í]ú†æ9¹¢åš‡HM°PÓêÏÉ{“JÇìy©¢„xÇI(t+Ñm¢§LÍ¬/&»«jÄÏ–}©™+"í~Ùö“ïÁtàµhÐ?xynTÕlYÍbÆ&Ìñ2aöáâGw°?%oGBEÊ¶<)¿±FÇ¦Ðm”T™Nýv³$‚K›€*f©ÍÖ#9ÛÂ^¢kïº¯ºß; jÍŠ[Óâo€Yr¥»3œ¼ŽBl½6¬™DA!x,öðBKe¿á¹¢xË¹lýRßÄÂ¦™`˜¡¦.ˆüº—=q„y@Ô¬*¬(Ã¸efÉcJFD«2}†WESÖz©Ê¡áûÜ±€îUŸ¶psUSwZcYÜ3°]ø¯pdS,PØÒ­`ÏÙw°†1ëSŒ•Aû9Ÿ»á…CÛ(köwhØ÷~•z—«TâñcX¡Æ`Ø²:Å¥@š;ªöÂÌmH	/ÿØ37!ecå¸Ž™ 
Û C&úMŸ¢µœ•q,GËã¶å”e®õk[·©Ÿ}Á*xSÂ©0kªéµYºTO¤©9nÁ¿}tÑ¸«Ímª¦/¯§!¬ýÂïÝ¯UkGÔÜ¯qjéÊršS}¾U¨Z.¢b‰‰Àš€\TlzS±üBÉ+ÑWîòZž¯ÙwÁ þ’‹®7»ävÅ“Ä»¿r¿W$>bq½Ú=•CŠÔçéÚÝk^Ø­pšãíêþ˜OÀïñIx¶µqLÈz¿ÓŽø¤lÛyGðeÜiXpú1D”7ƒ¬Æÿè²¡äv   0707010001f0f9000081a40000000000000000000000016a102a8900000db8000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.ip.route.conf.gz  ‹     í\ßo7~Ï_A (œàdU6Ú5	.wmqÁ5Íá’Þ=…—Ú¥$Â»ävÉµ¬âþø›’»¤üCR,Çq³~0lírHÎ÷ÍpHÎèéÓCþ<yÊøƒâd=bv]ÖèÖŠ;‹;ìè«»'¿Êúi#U!.{òMú¥›ö“'8ös±^é¦˜öCPÂ*OŽøƒJ2¹®ù¬]WšVàƒFüÞÊFWÔ¾Fj%Õ‚þÈKCO
1çmiûQÿ¬=j)i¹Ê}?¥àsöŠ-/Â ƒ	‹Æ=7µÈå\æðÎB(h›“Z “Ã–@atÛä‚É‚é9³ðQ®•å^gV³ºl«},k€Õcñ©tÝ½útr“Æq_´ÂjéB4‘|³,ÅåU(È^ƒna¨#VscDHxÜ[¹®*®
3b+i—ø,2U^Ã\ëFrx@Î™â•05 °„!vÎµ¾	ºY©ósÐÖY­=ëô÷ hv }öó:À m q#k‹@‰K‘#F|nÑ†"#‹0šæÇ<Gˆ§Y7“Œ¹Æì‡¦ÑA{MÓ¢Xâ"v7 ³œÆòÆP~,”1€¤Ê,6À‚‘Äì!üRÅl= yX£Œu›ÝP/c?\u=àyËÔõ!S×ûà×ªa­¼'³ŒTû±V‰ØÔFÑÏ¾ˆÎÄ\7â¾ÃŸ™=Ð‚Ÿ»!yàè' ²/‚Cìs?&y×àgŸ½`BŸƒÙå]cÇð‘ÏýåCŸÛ1ÅõIÈƒ`xøýC”µa­ çVA+òj0]Špj
Ï/$X >@âñÜRæËPi˜Â×€Zç¼,™iëZÃâW]ƒ¼%¿€‰ÒµhðàâA~$
z]3ÜQÚÂøZµÆ®k­ŒÂ‘ÀÃ	˜ßx!¢òèl¶{r-èûÑl² e›i?Lö¿¾ƒ+g¸3àsµaê^‘=ØJÄ3ÁäBf 
­‘mM”1ËÖz…VÛuñÆ|Ç¶iÃË_ŒdAY¦¾æY6fì½°  öý?¾þå§#¢ãj©Ë˜,¡OÆ8ŽÇžZ"ÔRÃxo	2öæzã}ã‰¶	®ÈÄ \,ðhšÈjÛôÑæ`+ÐÄUÅ4Ó{Qs»|†—Öàu#l,iz¾šf~Òãð&LÊôöÍÏ¢%I7·|ÉÕB£dziÏB¹Ž;}¸®lÈÄüÇ`bŽ17Ø™P9¯ÿdVö÷!16d=8É,³Ð:ËðogcÀi¢‹-E¡4´£+qi‘»ø9zL4“6¼NH%mÆ÷eÞË*À‡H¼´
ÃŒÁ€Q—z„ñ‚ƒ¡uWgŽ@Ôß6úT€–%«to7(õŠÏ¶\,€œ¨NJæÀ&ÝrßCÑ/ÇR¤Yê¶,Ðé{çž„è0xr(3ú])¸b¤€¹ºF—ÊËGÇ´SŒI˜åæB#³V¹Iã%x:/Ø‘Ònå81òØüÐÚ$ MKDÊt.ËÆˆkwÐ~4fÁJMãžµHEðÒÎiã^Ö‰3 L0wKUiÜ½î©ÑºÂ@+XÀðb?î„•!u°#~uAo8–¾;j~¤A3úcöïÐ·Övøkš,ÜŠ¨ERR5ðÖêŠ[™S,Ü*F=~‚cö‹ó¶D\+¥”ç°È¶UÍæ²fm¬¨±F\ò
âc2•N_/Àªì¬^œÍÊs©ÏVB.–²ÓÂ=L0¡
ÓÎŒ:«¸¤ýŠcþíZ“ÂM)8ÐÄ¢þ†ûQöæL±ž\€¯%íŽÙxÙÂ gÂ®„PìdBžL&“1û§h”(#¾t“¥çÈˆ´€ßNnA6¯Û³ß[mù ë­°n¤¨À†4ÇdƒÙ)¸ó…Á ðígÁæuŽQ¶wL0)ëÙ¢Ñ°ÂºùUºöã‚8ÜY#M3£K<¹@: $#º˜,ÉªyÃ+aE3f?öŽb„îú'©ÚK–SpYv|¡b¡ næKYI›îtœÕÚHbçÆx\C
'ÛjÁ¨ ’y£ úðHÇ²‚BÏ—¨;©aÓ•eßN¾ú+<ëÿ£ <@]Â8ÓðÔµ8Ý|ß®´{?@Gjb?õ±ÅÌ¼¶là®Í9©0XOI'G…?êNiàã“Éé7ÌéÌÞ(ÆãýHÝÏt‹«­´­ÏÊ´þ ðÊfú€ˆ”žBt)“m2õ|©¸&ºEÑíf`9„³–¸¿u*¡cŠ@Ž˜Ï+JhFÆ‘ÐµBžœÞÎ¾w;ûÞ×¨ùöà‡™Äse<!^ŠX­þ¿1.‹ÙÐ½Â1Ø2O¥<aS6ŒNñƒÉñi)‚‰Üf%ª3ò¥¢ûz#ÿH÷?™›]ì¼Ç ÐÍš=“*9‡€òsgèïZ{ün~üÖ½wû[0ßž ¸]®;ŸÈ¹AOï1Ða`*¨2Xy‡¶3ò	ýla‡ÖÕžx4º8rˆÕæ5›—|ÁžMÐ\Ož#!Â¥Áü¤ã @‚~2îž¸’`-š{9H>rû*'Ÿ[Ø¹ë+ÜÛµ•cc$Ê‘èÙÝO„uÿŽi E8/{÷îm$ÈÒ“÷ÝÛ@YX8Ãà YXkA}T¶ös 3TÓ›’02ÓÎÜ¦ò;œE8¶•VhÀV°“˜åkë'[8nV¼®¥ƒ(~Šã/g½V}HE{ýÍÝ@ŒZÔâ%ûf‹g°º¿ Ä›¢Ç÷Zo£ÏÉ)ì” n[ƒ’‹!*ù,¢’¿ =ÞwhB’úžýn½5tÌ¾ Y74¼ÍûÃ8žÉ‚¹ˆCÜ¨m‰q†ò˜)ñ'í7&¿ìS3ÔÄ|Úš˜°Û!)t(„yÈB˜ÝQÜ)t¨~ù”Õ/;‚·-t(yy¸’— Ü9ís¨sy˜:—­(î^å2·|Úâ–] Û%Š*Z°¢egwb†2–OXÆ²v[c˜¡våÁjWvApæ±B„}Ž9Ö”'GÉ8ç²N- é¡A¢¤f”Z­³ÿòÆ)ã_]+YÕ¥Ä\}alÓæ”wãJ_°	e¦—U˜aˆg~¿D¬ ÆzmúÓIn¹çOÏRò6êœy<ßÕÀý¸€ÕRZ÷ÝAÇF`®¦“ºSú9Ò¨(àG%axž°ä¦U*½‰ãì¨›ÏQç	^÷bè†ï²Æ³]ºHÒ ^4²xõì%ê¾ÇãçXÿòfîª;Ü]‚®¤Åœ.x½­GÆâzÝÖY‚#Y­“N\·ˆÐ­Èm¨1)y:RÜREåÓ¹m&÷™X}ÝÍÞ‡P2í×H¯>àHç]^¨WÌÊ
´î—ðDeÊ¨ÏÏCÆaÈ ïyÕI
Â±l£¿W˜³iš—wàªÔ•û k“I=Ý|Ô£.ÁŸª’áóFÿ!TÿVW‡²¶½$÷^×&Æ	¯Ž›”5Ê^|é Z@œíÌ–Üøú‡¡OM—å)ù2ž¶â–|'Ü_¤¼šŒÒÉÇ#ÃWÈ%p·tÏ—ßN×óR¹›ú¤VË‰93ùRm)²$ÿ²p~€—‘ ,™˜	|Ò6/÷Ñž½ì±{î'vVˆ’ÃÀÌldÄSÓé’1ÃÒ·„¥ë½þû¼ò%ÇQÌ?µ2oK'Ut	¬>oor˜˜Ï±Ò3"»Ù:
öyE€êˆäþÇ¤
w©ÅNÉõù²6JÞ+tò’M¶ø,§˜ÁsÅž«h]’¸®ï‰9¬’JVmÅHm	_zï˜C¹½ÌðêZGóŒ–¢ÕšÇLÍ°PÓÙÏó1öš&•NØ3¥“„xÇót‡èmÐS¦f1“ÝW5â]ƒåPjæË†ÈC—Lní|{É÷à:À	tèïžWªjF®¬æbÁf­´÷¯ÆÅíÜt·äÝH¨HÙ•'•×Öè¸´,Rw•éÔo?KÈ±´	¤b–Úb3’s-Ü·PºÆ[jÞ†c±}ªÖ\¡¸sý1ÿF˜%§(µÛ¯:!
qõÚ°g…à)ìñ
8±ˆý••+‰·ü’DŒ»oâhÓÎ0Ì çèh]Ø“Fh“BíºÆŠ2Œ[Žã,yL	Áˆh­ò#àÐœ²Ö•VÇžTÄï©WA¥­ÀW<ÝãœEÝ*_!‡q–ÅÍ1Ó	ÛÑ>”kÙÔÇc¶ò;Ø)ûö0vsŠ©1˜0ç©^<´+emÑùâk ÿ>ìRïs—J:~;Ô”·ìNq+0æžI£ë8sSâ/ÿØ•3×1åÊÎq“3n£¹è/4}2&Š1r¡ÒXŽ¶Ç];§)§\·®ÝzLmùâ6ÁëN‘f‰i+DU-53iŽEðˆ.ZÿÕè.U3”×Ó’BÌ~5¦oKpi8ëJôr¥š—TŸïª‘I±ÄLà4¸¤Øôºbù-¬D_û/Ÿ¡íù†Ïð_0Ðè*êÍùSñ,Åî/ÝÖ"ýÖšÐ«;SÉ0¤ÈCž®;­Á°ækwNs¼ÙÜóØã½>‰ï¶®\“DXïvÛ‘Þ”Ývß½9„÷Dš~åuÄ ¯ñ5+viúf  0707010001f105000081a40000000000000000000000016a102a8a000010e0000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.sync.ibmdssnap.conf.gz    ‹     í]msÛ6þž_i¦“dNVd'íÍ¸I¦îµË\ÓÜ\’ë‡NÇ„HHÂ˜$X‚´¬ÎýøÛ] $ ÛeKVÕÐb‡/K`ŸgÀ»|üx›?³-þ 8½Èã«…`rœ%Zç¼¸‡¸í¶n»º{ô+vöq)óD\ýöèêôë¶Ûa.Äb®Êä´m/K¾€3G[üAUéX|œŠæQËZà‰Rü^ËR$×N¥º”Zª\æS:ù#O5IÄ„×iÕ¶úg•Ó	™ÏD)+žÇö9©àö†ÍO\#ƒ‹Òœ×…ˆåDÆpÍTäpoLj'è®ø8,çÜ2aüMÊa3•&Ð&:¢U]Æ®¿”±ÐŒç	–%+8vÊ#‰½døÆÈyÍÞ~÷nxròòÅÑñÉÙwÿne<…†Þ¨0ƒreÂ+¡OÛ&±ÿµˆU~)J¯c¥àQy éÛ\V„0ÎÆ<¾˜–ªÌP½d)¯à’TÅ¬Rtâ6HY]¨4†Gð¸ _RÚ-˜¢4ý¹CºŒX*uÀõËLàÅ‘/¹„®B´Œþ&Iñôè}4úú«ÓÑèï/ð¯¯ñ¯—·ÁPŠ@Ï{ëÚ‚u},Á¦˜ÊÓ‹g<ŸX­ih«® YˆZˆs¢sð¨Îî|óÂ;ÛœhƒÜC£gÃeç…ÒÕy£á½âÝhõO0ÈY†#—*áæRj]\‰¸F?9©DI€‡[†è4>âqÍ?šžDÌ²ÊR•ˆ:H(k‹c¨9I~òfd Ï‰RÝàÔ/«Ê»BéHªŒ<pï#‰ÙBø'OÆ‹Éí¥¯Ûè>€Z›áªŠÏíX¦*¶a˜ªØ?˜Îž'eá¶mÒêõÎöhïßË\AGz4w€&iö^x’„…¦Uª=¦;ÀÔêö^¨ZwÁ~õ°îVøu_Tá×Æ Ö.¯{Pw ªQí½@5"6µÎûXÂŽ@õT{WP=]A-EÚÑ±˜à ·ãðÌhöÁ¡û!¹åèCdSûØÐnLò¾Á¡%|6‚µmÍ.ï²xl_Ú‘EÞ#4äá²)”}`hg`Þ92`³) }\h‡Þ#0´„Ï`íÃB»CõŽq¡M1í£B»Ãôîa¡0íƒB»ÃôžQ¡Õ˜bƒEþ ±ƒ­`¸üþ)ÒB³ZÐsÃ]´à3€áÆ-·oR”´Ï9 õ€Íg2žy€JÍr¼ ò£˜§)ÓuQ¨²b…PÈ›ñKÚwüÁ…Œ;ùž(xê‚qàN®*h_²øèBåZ‚Žá’H`áÌo<‘Q98›mÎÜúƒïÿ;sŠl)Àæ &§¹Â=| ¦£
…ê‚(£gu•¨9Zmóˆ·•ÆkªZ#mx:ç‚‘, (Šòç<Š†Œ}(€}ÿÃgŸ~ú8h7z²Ü3AkãÐo¯;Ÿ(˜o#¡f
Ú»"rQ³——âÖ÷è!n×Îiß €‹	nA&²Vu	ú¨c°¸ÄeÉi¤2öªàÕìk^döTjQù’N/æ§‘íôÐ]	Ò­ýyý«Ð’¤é›Ýœ9º>YäæÁ>Ìë ›Ý˜ö0˜˜aÌ-v&ò˜1+Û×f[d=8É(ªàî(Â¿ÿeË´4³’É•¸ª»ø1zLh4“•»œ
î!J{b5\–	TJÒ`_üxaFaè1p"ŠT-€0V°34”Ê%èÄˆž·Ž> U‘UöÚŠ·‡ë”zÍgW¥œNœ¨NJÆÀ&ÍpßBÑÇ2ÇMÞørU§	:}ëÜƒè%4žÊ˜Ž¡+WŒ°"×‘@èRyzp,@;Å9	«¸¾€©.1t8_‚SSÜ5ÿ$Wfäx2`ä±É¶´L@›†ˆ!Ôx¸Lkh#ŽÝNû^›q
–*j÷¸®€T/•økÁið ¿×`î†ªT›ýý×¤zã>
­”bŠÙ>áÌ½˜U€ê`Oø%4ÔÜzÃ¶´÷Ù«½ÛŸ(Ð ÌíCö÷lŒíð×iä°0#¾Á´	$¾x]©ŒW2¦¹¨s«8ë±²OZLêyp£”T^À [g›ÈTè…®D¦‰5âŠg0?&Siôõz¬º-Ciz>N/¤:Ÿ9ýÅ^¤n`*0€‰<Ñ­à¸À¨óbŠCÚ¯Øæßn4)\”‚,ê;Ê({ûº˜O.Á×’v‡ì¿<­¡cQÍ…ÈÙñˆ0<FCö/Qæ"RÏlgé<å>…h1¾­@6.êóßkUñÖ•°.eƒÂ‚4Æ•$,K¥Â•/4€/i=6¯bœe»¤¥Rá´ža’aãæ—á:Ø¶æáÆiÒ4Ö*Å°È%Ò%iÑÌÉž,LŒËD%Ê!û±ut×?É¼¾b1ÝÎ!ŠŽŽaª˜ÁT ó©Ìd®tœJKbçR{Ì4¬³1L@™ŒK¥Ð?SÊ—å"x<CÝI‹®(újôå·p®iüa, u	í§§æŽ“åë«¹2×;hHMì§g¬1=¯­{Ø†k3NÊ5ÖFRä(±q &J‡G'/™Ñÿ½Í÷×#E}4¦Ì\ a¶–ƒäº¶Âk‹uxÌHé,Ì.e°L¦gPÎaQã˜hE³šáäÌZâúÖ¨„ÂŽ>Ÿçf*¡ ¥kG@@s2ðød5ûzÞuö½g¨ùvç‡±Ä¸2GˆÎ£"Ö@«À¿8/óÙÐ\ÂµÍ$.ÁçSÁNÙhp<8Á££“RJ)>Zf&²sò¥=¢›z-ÿ×?™ë.vÞb	 ¨rÁžÊ<ˆCÀù™1ô÷uuô~rôÎ\wë[0ß)FPÌ*×Ä'b®ÑSøkt×Té¬¼AÛùˆ~Ö°C©ì#¥J{Žlc´9c“”OÙÓšëñ3$„‹Qj<d’†G€> úÈ¸zâv’cÑÄÊI@ú³YWù¼‚Õ˜y}…k»:3lôDY"=›÷nÜÀ¿ýð/5$qñ²÷ïßy‚L#-yß¿s”…Ó5nsc-¨O€Ê¶`&¦¢÷¦ÄµL×c³¨ü{áÂ¶²‚°ìØg¹ÇZàúñŽë9/
™ƒÁôßÅ1ÀËY«U;¥¢µþòjÀGÍ»ã5{¹Æ3õXínbMŽ¢èþ{­wÞqr
›NJ·µ“’Ë~Vò§˜•üíq×S’Ô>Ù®ÖkMaö)õº¤æ-¿?ôç3‘ó"—þjkæ8}e•½TV±‘ö[7¿lRW¥/§ò°åT:a×!_¦¯¡²Ï*ÝQì”%ÓNyÈÂ)Á[—ÓWKÙGµ”nàuÊˆéK¤ì«DJw;äÁôuQöY¥;”²_úb(û+†²”ks^ú
(û«€ÒÉ.™.}Ù“ý•=é„dçü–¾ÖÉ~j¬E±{¥“¾ÀÉÃ8é]—pM_ÕdUM:ƒØ-ZÓ—2yÀR&Ý°[¬éë—ì¡~I'ìºÅjú¢%{*ZÒÃ.¡š¾RÉ+•tF²[¤¦/O²·ò$› ¹>PÓ×$Ù[M’Î@vŠÓô…HöVˆ¤„iB'ìÏ˜0OI”Yu!‹p5€´Ð QB3
W&zÈ~á¥QÆ¿›»dV¤/ÀØYÖ1%Q™:&x¥w†;1]7p}òXA7«…n·šñŠ[þ´ÌYùy9wÝ¹ÅóðæÊ»qóU_”Ã6i€~dÂ†Ç± K.ë<·Usö¤éÏ“Æœµbh»öUõhogÓöª”É›§¯(ëúÍ`8>Ãb&o'¦T‡Ùª2Ya‚\^]aL¢.¢{²j#¸ì¶„ÃcE\¹Â:$OCŠ%qlnþÁÑf´Ë,ù›¶itµ.\Ù„’Æª8Òx—WùVÉ´n‡ðDiÈ(ü&¥KuåZ^5’œp¬ÁÑn°Ó0ÃÝ¯Õ£RS»]›ŠëÐ6ÖbÐTk ’ÉðŠI©þy{USTÇ¥à[Iæºæ1˜êg/nR
¬«abë@ ø©ëlÆµ-ò0¼SPæCHþ’Ì†‡wñFKö!ÜîŠ}3„÷[†—Kàfèž”¦XåZÈÜ¤]…wŒ˜sÏDR§"
’iãxê	Âúcç@Úr¦F Ú2 •=4çmÇÎ‘òEd[¦—ÊÐºÓd”cº¬½†®6ÂÙ	˜qÌ÷ÅdâJÆuÊý™&Ù&aŽA“	–íòÈ®—…œ}^“àŸ"™ÿc†ŒÙ¡ÌNÈõ¹H‚øûÁG_­ñYF1½çò=WR›)Hàº¾'æ°Læ2«3FjøÒzÇJÔfšg7:š§4Ígp»ÏÔ«nûy6Ä§†Â#ö4WAuhÇ3tƒè*èüeW;n`oºNëA¯ÛO†h><[.…bg%´ünªµØ'€Ë"˜À<¯uùœÒŸ'*~Ïõeü¼ñVQ“ªä¡ðŠ_Ì™ ½°¢Ý\÷šýúÅht:Žáßo¿>Æáð‹ûbtBG_Ø£Uó¸:ÿâ·[yíKúŠQ»*9vßE”«'ekÑÈ–Ð¹…ó¬°C
LúÏk¥s¦vÎå”k	†Ò>œýŒ“žÝ¤Â4-¡J„¦Qzc!“;„•(MùIznÛKÈ±~HÅTÔéòßÜaxmn^SØª%¼Ii*SÒL	|þ06§úv6âf§¦(#¬¥EFK³v¿j¨‹Ø_›Ñóp;•ƒ	¦»½ÅÐ¦ãôMóZ„æ{{Ò-þAhµ(°lÎgüR˜÷…3epËO€C*M‘«üÈ’Šø}jU©Jà%–î~b²ªs[çÓXûj‚éŒxUuMé{Ü¬B9²¹lœ²ïam[-w14íú|j›ç7íZí*ïÝúk€q¿^ì2zA:>„ÈEH†Q\"ö¤Ù1iTÑ‰3·1Å¯ðÛ•371åZDa™3VQ†\ôgš#íEk9ÍÃ¹…MšûŒ¦ŒrÍ¸¶òõ-PÆ0—»è-r·Ù(ú |ùuZ¬²N»-¨§Ðî)j>ù”XGŸŒ_=\Hp»t8N^Ì",xžÉ¼^Zb}Ê%éÍ)Z=™=±ÊDäøXaS*îG‘Üfb#ëaµŠåÚá´pö*äRñXÞ»ÒÑ8¼ c¡Kækž¬Ha¼(RX0ÙŠÂæ=D
K¿”ñi)~ðÁVÅ°A%S!×ùÚ„äuðT ¥¦!Ì]°=¬Lé]â8µmŽarIi}Ø{¹Ý{9RôÁø¹ë<Ý±×Óh÷4²ª>"-S£•Ì¯žI»g÷ˆH>1ÖñÈìXìy´{M–ˆ±‚GŸ~Æ¼¹©<Æ«>9ê ª ùr,«’ãN$ø¿{MAè ‘La7÷1jB0½nÃúsYÍü Hƒ)ºÊD+Wæ“”¾æa@/åePZu,ðsn .(MÓ§5.•Lð»û©*Úÿ±Dtû9’ReÞÓÌ6#»í2ŠÜ§1^›w”á7®ÜSÍ
…^PÇ®ªŸY´àû‘çf¯%õñvVòëÃÝŸëož¾¶×ÃºÛvÚp+öªµÞ•ýX¶Ó±ÌÓô!Œe7ƒ¼Æÿ5®Ý‹¡  0707010001f0b4000081a40000000000000000000000016a102a8900002670000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.DEFAULT.conf.gz   ‹     í]ëwã¶•ÿÞ¿‚›n×3­,Û“¤g;ö‰3™l³M:Ù§ù“• ’PSK–•Íöoßû H€zù!ÍFæ´ö˜Äïââ¾ïo»Ëÿ~óÛh‡ÿáp_¼þòê»¯¯w5Ünßn·s÷›ìÇþø|Õ¹˜ë"9ož(âX§ŽwøNŠ‰u.F©¬Ÿu]TOò•*dó_ŠÔÐ™¼Ð·Ê(©lrœIäXTiÙ¼w1×x\eSY¨Rd±}ÎTŠÄ=]Fð©²à&—±«8ºŒ&2ƒ›b¼,Y¢QJs^ýš~Îïèßwü¹ÌÓ¼ÁÊx~t«SšZ¸ÂÄpúz*í¤F3À;Œ£ÁeÕLö£á1FÊDoá]£ïáõeô> ‡ç4œóHÊ]õ&KÍEó»¥¾Ù‚ðÏámÞhxQ¿¾>½ßÔD¢‘‘Å­L¢Ñ"§òÎ¾6UðM}øÔ?Ù¯» Ö‘Už¿šª	d'De¬$¬TŠ1ÐÎƒèË£ŠïLj©#•È¬TãE4Ÿj„©&×™QðQÑX@*pÐÎvÏ^ä!8RiŠ@àÑ—n€Ÿ)£ÊÈq•Fc•–ðül‚°ô£Ïe	Â¿e©2Þhfª‹2Š{Æ5ÏT7	ë€³ø«Îä>@¥¥>7QœªHÞÉ¸*	 !ÍE9íG_£L—¨¤GWÀò.‰"|p‘*æ*È2¾½ºþ33ÇÕ6Öz(x /œ=.:ÄÇN®=XÑ\•SšÜ«ïß“Rn~Ýôm_ £TÇÀû'ƒÖÜ þÐnQØI¿†2›!vfâBå%N>¯X/cäFˆ °>]±ôVÀy|,â^ÿ|X	lTt¨½.
]ÀúÉ`„¢Âai«¥“ãd6/ð¦¢ê€|4>|0‘¾ó8a‡€gJQ”|»€¦òé Ò0†~dÉhÑ!¹[ŽêÏíð)€Ú1†«Î;<w³2u¾‹…©óá·ÈâARtîzMÚy}ôz´÷?ËþÍ= I3û$<i„#
¯VêBv˜îS;·OBÕŽñ\áWë~`…_OE~=Ô*G£sê@å©}¨<ÄC@­²Î´'P½©},¨Þ÷µiï¡ˆŽä7¸=Ûö|d€fgÙ{
Ž;5í1À®3ìí½Xö"E°³ëí‡>Õ°×ÂçA°vf½­Ë§Úõ,¯³êíiE>Á¬çáòP(;£ÞÞÀ|´U/Àæ¡€v6½=Bú£^ŸGÀÚ™ôö‡ê#mz!:Å´³èíÓÇ›ôBt€igÐÛ¦O´è…èlÁtžª™*/y§F \v+o|¸L&ƒ›ÜàÎURN#š6DýËçË*_iÄËBdf,Ó¾–âVFr–—‹0 Uf@ ædšÇc Àíãh6:1ë ‹§*M
yxËo'y	m¼ReÊAªç² ûc†‹Ï‡U#ÊhVÁÑ‘Œ†Cq+T%zž‡n†zŽáákîGƒó3Ü‹ªdÎ‰ ¸!g°Øàÿ
$ž~ôýT•Òä"ÆÜ€\À]‚×Áø®E¸ˆÌpQîƒ©þY¦¹ÁÐd`~Uw‘†¹¨NëôžsÜÕ`‚€Š§a¾Hf1ÔÙqŒËÕTyŽaþ¹Ô9Œ7·„=\K„.Œ„ß
žº ŒŒ`¯Lc-Úé
Œ8Ã¹ð|`â©LªPÿçééùééñéáç¾¢Ô¼8S©Â¡#4‘»9ëGï$.çÊ'f
 œ$:>qgƒŒ!N"‘õ­pëRÜ9¤ ¯àËÖb—àõ°/@–…rã¥Ü/ØÕ¦§–ì‘SÊI&xÀR!ùDÞŠ´%.:©:#ËZd½ff™ËkPe†Þ:ÊSà´´ÊVòc\Îl`pùÞ ‚îe€¯Ã»à\÷™þükÜšŸê¶Ü¼Á@üN±ýŸ9|$íQðA’r]f#™$ÒeµxcýÝÀÀHe÷Gï˜OÁ…H¦¢Px–Ÿ€‚!¾Õpxq1bšÛ¿àoo¨áð’O\Ú^ÚßðÐgò¬ÉÏë?ð‡Cÿ±¾íˆe:“%ÐAû~Ï£üó¯þõ{{òä¤ýr¿T&ŠÅÒàÿÆ÷ÿÌ¿þ›nº¦,“šðúVœááàj?©,|DðN°“ðstA÷¾• ÍÉ,¶y€$%Àžƒ–˜L›ï=î*_ÎHÜ¸ëJKÞ‰’pbÉ?*]KÅ\Dÿúì£ÿA2ÎÄLþïGe/úð™¹û@=ßÀ¤ðI²€š'‰*:¥Í2£¯Bùa
 ¥wq‰odš\®XÕÇå"—ÍD6B†¿.H0Š³D”«Xö‚'Àc5©
ÊK´óBÝ²yd	2!fÒð˜UŽƒâ”Ú¸Ì!	z¡HÕO¢Ö—‰HëÄ·'@Gg'xð¤yúZ’*$<tOŽ’Ü`»RÛ)Ò8~ôsóKºŠå=íüWüJö ŸOASÈµ!‰²½jÎôðð<‡/ö7 Ø÷ŠE/2s‘AÆyâ©˜Àø@¶–B@)½‹ýÑ¦;Mbì*2T?a~¦Ñ‘¹Q¹Øò[ZF@J´óÁ[1©9"
£i_CL‰$íl0.ôì²E<UŒ
Rè×(½K¢rjUÞšA±Î‹ˆj„†(
D0I×vîQD
Å›ÚÞº®NÚµß€T¡1ûy\*àÙ‰27-
âsv¼õfã„¨ƒ©Ï¬ä0ïµ\¹‰l4cƒAóQCMX†EAÓîO´€+æfZ•hé{x~U+”ò4LÓ·rÈZ'‚e-Iö1[^ƒ÷ÀùT§r…IrïØ÷ß×O4h-soˆòàªí°k'e)Ü8³©¿s+PÑ3M:<°A˜ŠÊ8ôñ!³ä|Ôú³Ö/Ýë™îa3öG:¿™ŸíG÷Ý•ðQÆ[Í÷•¤!ñ·ÅS‘M`9Ÿ>$?zp=|``»Ôø0¬2¦˜uëŒD«LQLO“ýáÇ½ƒ§©¼Û&5ò,®{kTX =I‰I7K¸·ˆ¬HèQF§½=âÝß^Ùµ¶Á+KZ$2¢d‡¦GÐÔÈ}•Ì@aF®‚wcÍÂ»À©2ÎþéÈ¥}©qÏ³<•=,”Ã ±E5`÷À1Ã:×yy><>V°ëçF¸Ï*äö«bQ&D·K	$ß‹~Qú°Høe‰Ì]W(kµ!áæhŒC\î“ÞWí"_`,žÞ •c¿ï)XC‚HyaJ9;‚³hŽó=×«ÙÀÿB?È?’OÈãX+ü:;þZeÕ}¢YA@n%]<xŽ7œ¼ëüÙy,t…ÖsÍà‰
méj .|9Ã`§¸O!š‹¬\Ã‘›”*¾!—š»¤@ñÖ&E‡XþÈ>Qe
7ô½‚™ê*M!£dBm(‹e]*DáÎêØnjÕøÔGQQFóHe'–ßŸõÿ}3™$|¨tbyØJrIVÓK²‘`Š|€UÅcÍDqx‘à/Çª 0óêºÁ7±å½žÕË½èÊ çëTO¨süp„³xôãj¥yÉI|Ý¼&Þrx1¡÷GG¾s>høQÔ_Ó_!åÄi;PQk¨TÙP%[‹
!ÍHÝßÝ„=S hqy,ÅÞ‹çL_íñ.î;ïëÉ/í	Ö“ÇHÄ7 ˜ÓÏá‰2ÿQÌoÉq¤Ð]üÅÛo#£ÊÊY]ãã”´½„oJò-¶ï­MølkP+‘K–A5‚c½†m7Pö,¯w3;’è€Þ1r…Å3=Ì)’ zŒûª@!ö@ Ø‰·YŽœÞO1(´{XóDæŠMú®$•„b–)e5}˜"³Xä˜¹íÿË ÿÎ)K¨r€øµ-ÉØÆ2Fm‰³µTK@-G±§å V	*D¥»œ
îas3,*cÍþhÖA.à‹j`Mõ¢ö>û^*ë â¡çm³#5/õ„.í‰ÅøVúa3ÑCCäiÖ$cà‚ÈœýÇgˆ¤'3¥ñ@¥˜*%þÔp„è{„"ÜÁ—4áÆ4äŒ=4IS$r8ü¬=³1Õx¬îÚÙÎznh^˜téÓÆŸp¹—šË¯^¿½þóÅë¿áOØS€½y?¿ú+ÿæ»wðóÛ7¯ðçÛ+úùýüOüùí[þ‰‡þë»«¯á×ÛWxßÛ×xÇ»ëÿ€Ÿ×ßà¨×ïðøwW×-ÿYp€š9 5àÎ1*t5™–Z-QðÁËp6zÑ¨*ùïö~GNÎÞ‰wº‡Ð\>[–¢ž÷[±Ž&’ÆñQ¶ÁCK·("/cµ†I¶Žój0U“é œÂ¢™jÒŒ<ÏÖÄã½7UƒÕ'-æ÷Šµ¤ã9ìh lÐñá&tO‹‘¾•±Éµµ39oDlk'ôÙ•sbÙ€™5àœ­á«o^}È€Ü#‰ÜKP;€}ÂQ×!Ôj€ôfœgâî wš¬š0i™Û˜¥ÅË_Úßˆ;5«fQó¶§TãieLk(éGW´âIÔ±w„þ#1™Ðçy‚Ïs"'…HXÞ™‹"ÃÂý§Xª$ñÀ—•Q>…O4@¸#¬œÓ¤£²Ü~d¢²‡ÉçÄ)vE&(ÌÂ8Ûùd‰=3ì}l°l€]2».Õ†¨°ÛŒ¯Q.Ý°µÑu…AöyC9-¯÷5»ÒHl½9À‰z¶äŸí–Ó/pKz“—€Uz_^s]–ÆúO”s)3V/Ü'Ûˆ£æ¸RŒŽ×YSÆ¼ãC]r‚Ì…ã±f ¦êUtÔÐž5_¬…`¹ÁŠJbLN°ÃãgF–ÏžïÅHcdÙRwç+r÷jƒÍêLB«‚XŽÄ'$Ò&†Ò&ý”ÎñM=s€ Íß¾Ö¬T¶ûÀÖš9|[køñøúmY 2ú² Æ¨gQU©dûB€âÛ/_EŸœ½xyÏàK	OßÄ,;ÛÓ$ç)d~`f#ÀAØ#¬•`Š¹7¿:"ÁZ¶
ÊÑaK©FCØx,ãÒÉ1¡cF¡ÎÎÖ2Àÿlg†<<‚øã©ÙgÐZRq®];öÙöR{°àoÐx}6ûøÔ`°2îú3«»Î…‚5“ìRE—KÂþMsÂ7s îñ1ÞŒg‡H§”/YP²u¶v¢Y œª54`ÙKç¥ÛUl¼ž×i¢Kòe¡& P’uø¼©TŒv,ëÉm hv•ÝôÓqtÈæQ°î÷èX"©1_ºÍc‹§Í^6`züu—­jR)ëˆ!.„™Âïq!åO’ªÿý`ÊX`©'%»Ó³‹É™WbeX·=…P¾“-ÚYœrNµTRßg‘æ4/`K6>*Á·¨bÿHúŽmDp°…öÙvÐm×õì»•”~×ØX2 ÂæŒ¦Lcy¬3#}€5{™\ÜMçp‹^'A|ÜN:¼•QH@,°WÝß–í•®’“'Šú$ž ÿ#†@H;ÀâÐµ5§É+ÊSªKÀe `·áÜ9[s„TJš£V¸Ï¤¹{Þo>' Ï:ü½à!ÐCOÖ‚6QÚÄ%é0.)—Åq½0FÂ(DÛmÛ…3˜ìåT›¥s'ÙÇUQ`~Îå^ÔPš¨‘)è ºXr®£daÏù…1xð…Sœ’jMUõì×† ø,¼vÉf±åø‹-_¿ifEzp`S¾ñoanL¬É&,§@ÆK¢£LóÆ{äç€ìScé’ C‚àÍÅ2
R-ž}ïÑŠjzoŒ³plœ•‹É””fs•ZÝuiTÏiBa‰µW%¸Ç{ëº+G¼È7Ù˜Æ³”R|„êHÝýè­{¶á<>†!Oãgëª­º÷Ê…8ªRÏ€½ÆT€ÉÅÊ!Ó³ØÇäzjüÈêÍ(¤&Õ,ÇÖÐÒæÕØ|7ZAõ|]ì€ªÖ­³Æb|pK-ÛOÃù©@‘çŸí3–Í|TQe­Hi\2¬ãÍ\ã±&£åèº°P>pâ3Z[ë'CÑ¤ìOæœä ü,\­q´Q6ÇrÆ;Œ¯ÚVþÈ:ºl¯pª„3éœ´‘‘#mÕ@ní§Ü¸ U¦}éÅf¸·uªL+ÝsÉw±Úéâ-{7½ 
ò Hz‘a2=Á[$CöðúôjN] ¥¬Yc¹Àý®‚PÙD,¤TûÌ¬ŒÂE²†CÎMÿá³—®4ÑåÉ¯,¹Xåk
..•Y­âS-¸hñÞ{šO£ôFéÁ\ªÉôk¤òÇkS¾=¯øÎ÷÷»~NFÒ¯Þ`"Á+™f·ý—ìyíM=;¥óìôô´ýE™L—ƒ<Ïù<á¢¸~zºYÃ‚Z¢ƒu#¬-û	¨ˆ1n ´="+€—à‹ÚÙ#Ÿµâ +\4)4òùqô»°ä¦W*†d rÎŒŒN±{°mý ëŽ\i4Ë #à$ð˜î0T{îÌƒ5!`½V±.µ•Qgë}øFÚTë=l¦âB	äŸ˜P;¯'DŠxŠs§t‚ñhŸžþî38W¿üM–„4¥¹äšq–w¼h__Î5_ï@MÔDýôŒ-K€[¦[»`mÌ¤–ãÏ+ƒÎ­® ,>;}ñ	—AÀâI¡% 9é
uçÞ³ñ ×±.ø‚žÌg‰,RAéz™ò
%+–±úÎºu)í²V˜tÍSB¥qøô<gŽc®œ à ß…xöb3õutwoÞ{EqvÇQÂZ`u¡­š£"Ö@V¯àg+c°¹K’@W ¿|bttÚ;ë½À§Ç/HL<°Ì™œÞ[Åø‹“õShéyí…}mYç–\Þóc‘¸ÄðDZèoªòøÍøø¾ît*X¾0f}™œêƒœÂ—Ò‘aäÂ`å·Êk´y‘ŸÒ[¨CëÙ€J¾é´£‘]ì6WÑ8“èÙ).×³ç¬NqÝ/ÒË¬]ƒéÐÇ @¿UüZX!‰,<N£?ï±5‹ÇeIžŽR“Eë ÎtÀ^,!yÖ¥ÐÝ¾ÿöKªÑ‹Ôq9oÞ|ãÄ/i‰÷Í7Žd±2²}9¸Ííµ
ëEKx.ªj‡Uò›™jÄ¦¼?áW8ë*a‡letæS¹Gµ›BÂíÕX[3W,˜ŽÄwAâ˜+-¢fV­Håü®6à£æÝq}²…3uXíO±Kn©4Ê7Þqb
J·­BÉm'•ü"¤’?PÍá=‹&ÝQ?9ˆÕ_]ÐëµÓ¾<3t\äÖqPQÛ"ã8ÃúáE_ò$BÚ‹Ÿ&CÑÏþcà/›ð9AÎTÑ…crt-à?b(–·¥4ÐÆ‘ëTÅ¸ô÷äÖÉ0"Ó,5TtiŸL<ø/ÚM’j6[ô"=Â‡Ècäd½F·µ£†nuú¬ß.x9.gÚx5[¼Z>µÊÌ.±ÉaCÑe™D¾*Š?ÙMVý0ŒÁƒ0*‡™‹›àÕ`&c™È¬~wšc¸çºÉEb8€Íbƒ0+…­ÈÊ‡ñÆêÎñ.†FÞ±í#>x†pHòôúËU²}õ_r+S|2EÛÌD¶hÍ9‘¶Ð &èÐ!BÁ»›9ð¾<zÆ‘YÈ”jŽ6½pÚòøi:{È®Ø¯üŠ³º»VÊ:> ?$íf§eÊÛ	}^¨	Îûxliñ¹0µé4‰° 8ƒ¡I
ßG—ƒ®áŠqiý¨›û®ï>hãLÖ6!q¹†EÕ¡÷hô|Ì`"7tG¿nØPk;b´¹u˜í3Žy"j,lÜ·&X½ƒo‡ÓŸÛáãQ´#ÜLw îfêüéKPç÷-'EÜ®WŸ×G®<{÷}<Ì ÷C€‹>Dâ~/1ð»ÒÎí ´#< LŽï°Ü–TKø)Pb;ô{"YåÉ!Æ¾’<µO@’¸’UÖ™fö„¤7µCÒ`;’&ùÀ:g> ¶Œ{ë
ö–‘l„&¬a´ª0º8›fªmivÐ%Mß#ô+x cRRº ôjW’Iv. .O«	àk2ª²D{p›ÐÆî/ ,ÎaMþÑ ¬,ã¬†ÒON˜	À…}3]K0…tu	Ö,þåosê¬–pê†í~kãVËª{l+Û-«.yÀº¤{‡8p&’*Z`œÕJpn_ŒÀc!‹D–2.©WqÝÉ`n[!ø‘EM…WÜ‚(bl‘¼ëGcã½¨fºÊJWI\ÀCDVyåŠÀE!ÖA³D]H«ØW§ÜÐWïêö¢îEeîÍOàã²e‚K;/ÁSÀÛ¡›€±ØXç$ØdOö8<î	[ç#Ø·|š“ …Î}°ì\;[OóX4îYç!ØÓÚ{´‹ÀCåžøu‚½!øHA€Ì=Qìü{ÄñÑ‚:÷Ç²sìÊGùBlî	dçØu„Øl²sìÈ'yBl¶Ùùvã°à’;€ªJ9/Án<yÅQëØV{ÚXcOÛ¬¤Âo2z&°õ-¼Ð®³Ío]åb|fmˆ½—Ç ùÂ6‡ÒÅ{êWð~ëíˆxVgÛ
³H#næ0)˜’$A¹tÇžS
—j»ªD5em`åWÜÚúŒ\º¥?Z¨aKÞ‹•5¿P°îÏÄÝ ë³¤©L‡”5\>…²Ç°hÄ-Ræuøà·ÒPL“BC½u8ãË{¡’Ö	¶v*¨%„í5±«‰+ ¯²¿Ë·J0÷ËÎ“Àã¢ª“B["üFûlõ&Ñh[°~¡óŽuû\ÀÉ—ä.t‰Ý¹,Œ2%•òêóÂì­(ÃïšiÀª 	v¥ƒDc¾qyBåºc¢]ÔáoR	[3ªyfÛÜ~ñúË«ï¾¾ÆÜ*s•ÎLfx½fœAÖGÒ]UÛÈ?3¥V¤Íû÷ÚÙOXÎ5Á0£‰ºUQ`ø˜¥¥'YšÄÁ¶ìÜ‡*|¹Á~‰]¾©¨'ñ­•‡vD âF¨BC¸Õ²¬}/
žŒoë»°ˆ´¢î$ÀR+.‹ßXØšÒ¸7‡5f$Jô£ï<yŽnÖ‹¦¾J–j™ïz£3Õ^7°x~ 5Ÿ,¼7=…Ž—{
aµ0ÛQèc&™`DKÐ@ ÌÂ":ª¿ç¨–á¯ša¨0†W€1¨ö²PÉå³—$Ï]öúýþó!7#£#x¢gÈA,‘Vå=S¢7ª1l±²å©Ô¢åÑðXÞ@y´xj¢ØTh±2ÓÃmWðÏÓÓóÓÓãÓ?žïPü[Ó¶ ”\äLqIp[o/Ö©-¾ãn@ dðÉk@-@_ Ö²€
“t†ïUÉ¯˜
³S—7¤Väx’ë•NÅ-J´Â¢Þÿ»aÉÇouMEå‚JsH©F]­¦
Àä+[f=ã€±ÂO¹JèéÕYâÇ”¥]ß„
i‚•âD±­Õ‰2H'!æ@(ôûØ>†©Ê@#<Ñe=Ù•õá²ƒÏþxúÞš—àL÷Üõ‚o£ Pâw­Çì ;Þ»¶~íºÏ¹6R2÷HW¢öevI½ÆŒ³$™†ZbÜwMl¹ XQIÛ+§2ŽÎCµ¢ÝÅÈÖÅbåB•¦ÕÀõ2ª[B^Ê÷O2k®j—ïp#ñuõc0éß¯óË²EÁhK?£ã—Ö·šÆÂx¶!|v`2X®â«±?Óx—¨gÉ>DX–zyÚ?Þ3¼„D:6~`¨#5SXp»@6¶Äj‰}¦^TAãW‘°|µÈ5lI°ÖG»¦Y0t»ßL·6Hd*Cûf¦¥S’ñ>§®½Œ…eí z¼³±%MØ™Ðd	2m©â*¾!©®ÛkË•Ž°V‡©¯MCì¦=hÏ­Ï¥± üLHü7Õ%çRC/8fzŠ•¢hç{áØ—lÚÆ³xb:Îµµc¢µSÏl‹vš¶€^îâ(ÇŠ8 Ï¬b4ÏHX¡‚7>¥•qëçyŸÖÒ=že:ˆSN](p€èFèALFîù«7u<´‡–m5Õ´¿#7ÔMŠC°%ú–Ž‚Ù(Ìðˆ!¼¾QÙm|ñ[×È¨Ù!ˆçNE:>^.óO:&Û~m©k
9z2®M·Ìh†× ‡L<Øæ7|÷w¨#kp¦f|GLfn ÍVPá;{È^í!pð^–uöo´{[BVÙ?–¤•ÐâÂ^Öul\¡ô8>ƒUh=$Px6¨9­*·koú7[Ê¨òW[äë^jï=›ËF¼µ1&“Êà*ÜE&EQ²ñ&R{ìÐv™]ö_RûÚyo@‡$uÂK“Ë¡ßXhyçßÑ-Xv#Î Pã~@ý8÷!#\­ÖZ-7W7æaÁÏE7øº£œÛ©gÚðÐ²`lk?‰7ÙWØµQ}?mT­Ãv>‡½¹\äòøÓèUÔöGìR©neÏEVRáàçÛÇëü½ÚÖ÷z¾”S[Áû†T. ŽÚ†3¢,²t¶’4-¤úÑë;eZtç=¥ Ûò”o'»u.
º»Ûré-7´=EuñÎ¾µT3êŒ3eW=.¹	­.Yj €…‘_ÌyvI¤å+4|9×T^%žû`Ûæ‚fm¬é»®lsãÖå‡›pÒ-½]ú‘k^ºW²ë?nU5²Ù¶~d9K°`/…¿ 6â•Ãs©Af;dÞN¢Q¥Rjuøîê¯vAºÒËõ›q§Ñte»M®ŠÚ "Ó‘×¨$]FÅÖ“¶Ÿ“ï`rå›·’ªžœ
øáXþ(ÕºŽjúªUëaGÜ+³	­ÉÔh¶æV$eòyÙ¿uUæUiñõÛ¢‰s)ó®(ïŠ¢¼p[à,º‚½¶LoX¢—Œ6X¢—ÂË7ëQ$Ý0?ü¸·†››[mbÐn_6qbÄR.p«i\;S±Þ JÍ  |Ò:Œô¸ˆñƒwÏHÐ³ç{AF^kÛŠ–m[†~ËS×y—uâÀ}–Ôíç¯Ž€ÅY–@ÙwFw‡m©«76ç¨9}¤&§Pa’KVPnòa2ñi ¨…gu#Ád¥ê¨fTcË¤4”Óªþò$ÊiSàŽ(Ç'†­äÓÕXI9e¡È¢ÜêÆ3SÃÚ(=T…IgvNV¯Å¶ç8	<!psp*ì]†R–OÚŽÚ ¼À*‘DÚm­_PCÕcÛ@ÜØ{šŠI…AËEŽ&$´±ûé(Øø‰v‘ÅG(lPoÊLgÇVÊ'…ãÜNÁ”i¼ÄrY¿3™­-dÕyÔ§Çn™T@’.ðÍÆðQ¿Ê;ÜžG_PoóÖ'†Ú‰qß|n_Ï5šO[²‰FS¬a£zBîÌÎ‰´O'Íñ!Ô†Ä°Á‘„÷0vÔ¥ƒ£™Oß{\òÃ™¸£ †¹P%…]Ù†ˆ8•µmà
0È;ÊsˆðcÛÙ5_lÂ¥9)¨W"¢D¶âšÏ¨Qy,0COD×o^-×sóã²ì^îLÎØ‡s=xAG‡Oy¶÷xJ–èÂµ¿ãpÊMyvº0Äyzxé»s:m³H-Y5@¿Õ©ž,¨«Ý†kàö~ôãýÍW<éY‰JW„_»q„A*ÄêÊ*g9«(ƒîv+\^–ø9Û,“ðAÝÎ¿ç_ç‡?Â¦}Ÿäì_i§KŸPŒ5Ü<)¤·¾gŠ'—•“Í†J,c1*¤¸éVä~Wd=Ñ!/“Å¦Õi‹…u$´‚i>òIbù¸0ý,Œñâì½+vïl”ØêˆuœPŒDÆÓUi#)ÑlÂÚËi¡«É”Ê¨°_`¨ÅLdÆ=a¦™©než€„˜Š @®ã¸*p¸”zgcGf Âð2.SCL4ëfÄaef­ì“ô˜sL§
•b–»òñ,–JJ&=k¤Ò–»@jÇè™ Ñ¢¸<qç“—uâåÉKt^ò!ø…«ä¿‹«;½DYù£,-=×©$ú&rÆÂ ï-ûbÇôœ|<}=`êèú»Œé¬&Ük¶è—Ä$™ÊÅÔkd ­iÀyÑ7™JŠ®@»¾Jˆè=Ï0DWib½È€á“H#òS1Þ/ò<U±U¢­®Ä¤b‚U2³…[š5„½aÅx›ýd‚§º•»zµ„žÏæGcÃ†åÖÇFÈMDJ6ënÓÞÿ¦M}0ÛvHÛ6n[–²#£ý“‘êƒ!¤6iÜƒ”øWGIû§$t8!ù„±ŽÖpúI]yä³]ªk²ŽHrÚ[ÖQ ¦¬¶>n‚\Xµãûç<ÓÃ	Z„±”bò+¦›{Eh9ÒÁ©ä‹‘*Ì[Ã¿]ðK]uG˜.®ž_!P6bÛ[Õo£GÑ3ÙŒ«²qŠjM)Ô­,V%TÁ0æFÀÊ›®÷—¸Õ*ÁXê…-çJÅZ„Î¯LQ¼ÍÓ¸&‡­17êœËS\Ø 0ÒÔ>•uLr†räZí\Å¨›.,Gß¸*­/ïà(Ó¹÷âuƒG?GãvaLýá³˜"â{)å1}"©æeSY'§(­ñ¨0.|@k$Gêt?é×xÑ€S0‡Ú…a²Xª\Ø¤ Ðð?ÐIPä{ÿKÜýXÍƒ)Å¹u/šÉ]C5‡\?üpKXú•Á—JUz„p¿Š“añM9Þ•´W	È›éC€Ví5ÿ¦‰û\) 0707010001f117000081a40000000000000000000000016a102a8a000018af000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.task.oci.conf.gz  ‹     í]m“Û6’þž_ŠkËv¤ÑL6·Ù‰gjç²ÎÝÖ&ë«µ³ùJI	I¸!	† G£\î¿_w ½-3	S'¦Hì§»ÑÝèn<{vÌ>yÆŽøg¸¾í1³,S‰üàáŽ;»ãÒî“ðcŸU²HÅýŸ|I}…ŸýÉ'8÷[±\¨*½l§0ÉTr+‹Ù¨TÚŒªº€»úGü	¦UòI&š×¾«j?Tâ§ZV¢Ï×<ÓôKY©;©¥*`j—Ñ/©˜ò:3í'üCôƒ,æ¢’†‰{Q&ø”]³¹à©Ÿp@•ŠÊþ®K‘È©Làž™(àÙ„hoÐ	ÜqÃ•ç¼Háx¸’¥aF1q/’ÚÆ§FTÌÌ|…Vu•v™ôyb`Ò—c ä8à{yÀ^W•ª4Lž­jžw?ÂË¿\CÀ›*µ¼JtØŽÝDLU%NžEc7vÉ\$·¿oÀ€ ‘L¹ú²ŸýÂ2îØ¿ýßÑ‚WEòß¦LÂõ¹¿éy@Á¿2|¼&Q©`R³Z‹ï­D©*Ã8-Ê#hÃM­›†§9¬¾C [D5ïL¥.3¾„sol^Ã ­é…2ôžŽÖ/{ÁXø";¦¦nÎ80Ï|©YqÆ‰óˆ—€ÝmüfâwÏqª¸U0¾žgâ>â«w@t¯>¥!‚#!Åã.aX¦
d‹ƒ{êŠ©Òœ•'ÜœMdqÿå™,ø@Ï·TLåLDq'+Uä¢0O¬~||¨s	ú£ä(º¢äh“”e¤Df<~uÇ«ë«W–º¬à¹¸>{Ôg%7óëñxˆÛ¼Bd@*\ã'ÃÔ2‰=
]	ÿ/ÒÓ
¸ "²ŸjïF5kJ¸dTª>€·o}/ðM¶Äåi1W0ÒÊDƒ±6L™ý‹WQ„IÁ:V—¥¨
Îqá#G4»¯Ê9.dOŽãš_Nµ"álajÞ³Æ•øñmK>Þc¨¶Áä`À'd> Õp'"ÊÓÚT[ƒj>äËws`¿iÆgŽÓìb†¦¼˜	`6MÏhàû[PQ lúÀÀŒœ–#ü{®Ü·lã†EÚ™‘Æ¯æH^‚¨„¶,é•H€°KÄ‹–g;ê•X
ËBx#èŸ•¥ÜM…EÙä¥U@€Ð"Ým.¦ÂðdþäñƒjIØ$|ÿ)j—gX¶'<¹‘F°·V,Çã)ÎÙ«a%YHàÕŒ A|3UÌ´£îòvq9‹¬2##s¡j3fd'ÌÉƒ0|v9.”5ÙB~ œ-Œ 5f+Öw2º[ý°úÏ$(-—¯TåÏ"ðñwŒÇ=7Ÿ%J;ÂêG@HèBË[z®ê,es~-þ	Ü	·*‡¸}r×
+tµ,•|‚ß©T/bëwM0¡9¨\gŠ§Þo°›j¦Y^TAk¯,HHlunnKr´ÖÑDßn™wù),ò;žÕ‚¬Ygvx½ŸîÍnYÃm1µÛì´§µ£q‰);nM`Q'¹€žZÀ“¥»)eÐŠ­zdôJƒ’Èœ
à#ÂV=MÐØ_vN³aÝEÚVüšº÷à×,d‘ª…^“aä×@½ºç^å‚(˜šg,34¯EÁ’pÀA·ë(ú²„82“)ÙžDa`¬O$ëOà…ÊÔ2‰ìþîÝü/s>û¸4÷?üJ¬
À#º°²Î²YðÞ÷nÑXH3'BÛ[¯ØŸM/øgizñç/†¸œÔ“º0õe†RevÒ„ï•*“ÉòÉñ¿êŸRáø ‰lL{?\Ì’q ë¸}œ8º^—`.t¯ƒE„œ¶<„¯óH+f<«à–tÖ¨&ß¢ˆªH’m€·ž.Ø¾b/šJ¢o,"”ÿ.D	ð¤°ÂÀ¨ áüá‘1è/ƒÂcïj>¼Iih4¶&=‹Ö¨´17P–»Â_²L
Ý-Qí¥]l#O/ÇNQUöû@©1˜Ë³ãxÅj÷IB›‹\Ïu#ó»YÑ@2‰öW|ÈgªÀÇs°éÃÀÌxÔà¬åíe%ï€©à¡äýnqd
fìÕXGõÂLþv,ú+V³e´í#5÷=ÂX	ê†îˆù÷ÙÐNÕò)Mú*üuŸfjÖ~£fMH©Ú”µÑÞ–÷c3HK"?üwOd.ç÷#tha™ÈžùÏOI{T±3”÷0#ËÔµEáci„B³’%8âAÕ8TRW(‘lI0„D<Î·ñ$‰W¼6Š‰Q‘ëS¹ä^µ–³ÂïUD›Ö­2wÊ»@eê³fgƒcÌ‡¤ Îõ`@¹´ñ·W:'é=nlÒ»£‘ð›q÷)L—nNÎ‰qÌ*Y¶AŽz¸[XRáêü.Ø}‚w<¶«l€V—ª4—c÷¬´«oË 4)ŒŠy00°÷XÚÖ‰ö¦»èat8È-Ò;–R¿€Þ„Û72/aIÐƒcô»‚‹I!Çm^bw¿kXgq³µ¹0M1¨hµ¿ÔQôxÅíCáëâE<kÇrN‚Hù·p°ZÁƒ «tªp–ñðûZ’·ïº$ñÂ@W8®w„
Mte.øùbL Df¦Ûí;'’7"ÿA&F¡0“Mä¥ñ‰ªL—päÜ€ñØÀ ¸åôüž·ÿúÊ²•#:ÌJKäm°Rì¾2qfÈ=þNŸw„Hyó#þ@ÿ"†öVåÂñL„¦°ÌaUF}A¯C]YP—”2GìLio9æ·½úþe&qc³»Zµ^iU¦7±{tÏh§ ¾4úHQõ	Ÿp-#¯´/¯A#Ÿ©×­qŽlä´_I„u®´>„»hqÆc]apŸ»lHÈ÷×tuwö¿¨Ôþï“SÎìÎž>C{päoÜ‘§ô]2Â¨ÿwi;ã=¯=ì*#µ]#(”u'Aðäƒg«›¦=’èPÒÀèÎn¶ÑF*
®ÝL°×ði6þON»“Y§Çðú´Î2‡Mð÷†¿›Z3¯i¥rfeÚŠsŒ°Ï Äxü°-J™væàAæ PjW°fS8m“ñFßi¼}§kkä‡|EOÑÎ&ÄIq6Sj–‰³’S`¼” ÍKö³Ê'X#2×l4øà@ËêŒvÚhe·n°Ì6Íj£ÍLuÙLpE^žk77”	K·ƒlµ®zçñªwœY´µä°Ú®dçñJvö"vHÁê@™‰™èj(ÚÖùOÜm÷F),øõôZ0ÊAà©{ ÂB€mñh4¤ý=û¥Æ&=;ÊÛì\8›ÜHù<í/àãÈ§ ÍW‡³ÇmŸÃF;®.ÉöÉIÜE~JyKëÊUkø}Ï%™/ì•ÿùÚkÍT$¯¼-ÞšQÎµãlÊeV»bî€|É€â¦Öuúð}ø-îcÆ €µG‰pSÐ|.ÍŠk¢+¥,•÷í\Wbo­–£ÈÓyEk»!Ó¨cš@¼Þìk ³ƒ2Â]yð?„:Es™ÌãkÔ‰™šÉ»,Ö§ñÀx·kÓ‹uÃ
•Š~&îDÖ<„›Ð)8&’ƒWƒ=Q
XŒ9®œøß¾¿ÜwþsL«­l“wqÝ÷]yš6~b•=ž7n”Pô5ç·¡¯§kgÌzWa­D®îl 4¨FV¥…:ß·`ú™N¯À)îKŽFèh=]ýõ½©x!Û´äZo@Òy'=[bw§2x‚Ù‚êÖ	¯ ]–ðÉÖƒ÷ tý;Ò²Cœd`Ø3ìüÒþ	fQ¿dŸýiðùŸ:\|~ùÅð‹!ý±vÃÓƒþ4žd[€Ð_/@ öH¥Ýà ”sa…‹vJã“BW’=ÇÆKyÓ€2ë*l‰ÿxŒûà×/^ÑêÝ/ã
cÔ"Æ&P¹4•ÿx\—=mÒÉ’ÕåxÜ,-.)Þ‘-.	~•­a6ÏÀpÛøEc>]ýv“ý>wóéH˜à«ß;Q›b…”Â‚
Áˆ5…"Úü:«uuF>”vÄ>ó ¾h¦“â.1½›á>òŸ¦¢ð‰msßûáÓáðr8ìÏáÏ¿üû9ËUñi}:¼ «Ÿ¹«¦Þ¯‹OÜÎZÚÍÅÎ~\š'Dlç±,Eÿsö‚üäHº“¬ÖòNô¼ÈÁu@'à%áönŠj¸Žëp‡XQölµŒ6uÌœix;’Üj Oæ*qÈê°¦Æ Ciwl‚!IQ­o9[³—vˆ°ëM“JGŽ~¿.~oÊ_`®WŒß>üLîb	¸1HZæ@»àÊXG{ECBÁG30“*º7Qð¡hAG½[vlE·0ÞÐÁ!ÚŽÞ¶	Ý±Ï8³ŽG×Ýã!µ„7V:§nS£Œ›(?÷ý{{¸ÒÂHÍoï¶‚CûÀ‡ôöØÀF;ku¡ÔÏOo©>zôì-*JvÁ³ÕüUâ‘¦Á%Þ„Ûè·…ZP0 ¡M*Gìä”ŠYÅS×ôcNÍ<*b³¦ŸŠ8Žjd<ãU®»Ái9P¬°bï¿b[ýô(Ÿl\õübød«!á1¯ùb+DOS;òOc¹o@'tÐ
åÀ•¡É‘°ùx&ØÀ·cÃáš‡øñ£¼€ »JÚÝýÏ·äŒéêÔ±¼ AëÌ3-êTõt–Ìfo´¼Î-Z]ÜÉ‰"”›"LÃÓd¹¶F0OllB¶7Ã*sq±•ÂZtÉ–qúÊ½7U ë.æ¢X¯qª#JÛ4$uÛ.\1žæ²ØGÑÅ†W=}JÿÅ½æÀd:KÊùtCØ‘®†XK€³Ïû¼²•Ô7—Ofïiø„2Êð§­ŒbtÇ'ÛÓáæ£‘tI€l¬Q¡_BôW«QC>hšK'à“{:ÆÚ=ŽÕßvÍÈŽÒøÈî!
¿ íâ•Êjµ?™-}f®u™ý‹*)*G‘‚ïqñ •âRa%-ÓpÜ^Ô|0èšÖ‘ÅÚX+õ{,˜ù¾Ñ¿…¡(hgŸZkFq×ÎFÜ½aÎ¶xßT×¸¾Î§P6_2~ÏvõÍ ‡6­Ç‡(>ÜAyŒ¬e"å‡Ÿ:@Ã<BÌbH'ËÉã
eHÛñ‡ êÆx®ªkgw$ÉTå1S•Á¯.ºµòDbö}¥2â‡¼tˆ~xÝÈ	ÌŸ™ Ù?Ç« :‚õãy(‚ís‘üPãgŸÁÚ™>G“Ëµ}€¯³|N'”húÄèì9"&üHyRÇÍO<~ÿ%²ÒóáÏž"­æšigTÞ&¯PK[¤îÙº•8@XàmÀªèc_®¦ÍK)6gsÅ0xµ‚6Îl²¤T¥‚ZzÛòxu©
-1‘jê‚88w¤&§R#*¿±‚”GOD½ñ„Lƒ#çpca"˜œ ç”ÕålÖ%±Œž×&U‹"J‰0Ú§.µGÏÙcôžqUœqŒFSš´,Ø__}óÝ7ïl˜˜àÂ]	÷N{8ÍqÎ×ÿÞtÿœ«":]oÍâ£i¯®7N÷hÚAÕ^é¨×Q.RÉ)Ãö¯°ÑiæörTÎè˜žk?=—×§…Ñ«gK¸ø;á£ÚÌZ|f1øÞj	/”ö¢Ï‹ß,
ûâ†öÂ:À6¨î.ïKFEÂË®ìë(Âö6Nù¦~’±8»Ú—R‚2íä‰{³R?” ÆLqo¿©P$¤¢glR@;,î>‚d+¥Ytj‚;iÒ–0é°ÌÔ²=:ÅZ»éìÎº€÷ícŸÐ2O°“Ö¯“@ûa¾¨#êšÎ6•œÍ0¥X””¤$Q—€ÚBÑ.Ç²¸ÕÈBît"<.®l©Šz%ºOxTGenÛ¯Ú!÷öS£?þôÚà¢œÚü&<1¯‡¥7‰Ží%<®;Q</”]9ž÷˜ÀXÓSWžnNt³ùŠ.[QãÚí©ÌM°LÑ¼'µiš÷¡ç§¤¸ôGÄ¤#v“i[î¸6j°îS7öÙ¬3ÜÕNÅ…¬ÏùLÔ>tÃ¹´Ï¹»ƒÇŸc-ØŒîû§·¡=VÍNÜ®øˆÚ†Ó{-â~³^­¢Õã>Ú2Mëù`ã(öÅ:/ƒÚ|›~-î9¶I´MË<½®ŽÀUÛÚ`ÌF“ìVªÑBÈÙü7-|€¨À&ŠT·Ûîì£r†KÚ8çîLýT´ô·7ð‰§c,u±ú"«a‚aBì|Hž‡Ãû»¨
ålø¥ß©yFŒ&úw ›”õëLxëNXWòl°$†Jß01=_LÎJÈæRà ¸´`§˜\Úª?EjÊþûÁAá»ò‰V†El§—ëL§hÀMíìëVQôP]#‹úž%ô„Ærßþ9˜ŠØ>ùLæÒÄ~ 5lÅ®n’¸se>öA2'ë|‚í¼§,—I¥´Àâä•V<Aèl TtÜÂçÃ?ü~k&§&`t -uoÅ<µO\¬ÞoÊÞï ajâ~zÇ°íŠ;8†j³Jjó!.ÔDiàòùðâ®]4õCû64ý	¨lXS8ÈHÑô®XsÖ±Ü)·®6ßŽÜd›ˆõ÷ek¢/:}g#…x¦˜k‰þ­%	…)<s„ü¼°¦„MœuóˆÐuÀ<¿ØÍ}ß¬{ív¯‡‰,lË¢•#‘‘¼öü¹RFœ- ]†dêO[¾dÃÞyï/û-¤&^Øf.òéÒÑ‡j-Ž}Ž×$æú9o± ðdìÑÙ«“%8È/­ ¿©MÿÍ´ÿ­½ïü[ßFP¬—kãÔŽ5><6fRz)oÐ¶B>¤öp‡Rù#•Ê:9Æjsc¥Açá¿D†ð1J—\@Òò @„~ 2zOÜI”-oÇIaô—=w¾=Ï¡ÎöXb¾&&#7C9F$ölö'üºÿ†i"ÍÉ¤oÞ|ÖòÒ$ó¾ùÖ³,6~q“ƒÇšƒ5\÷÷ &q+
ØØ™ézbÊ/ñ+|ØVš³=ÏC.¸v×y8în½ D 0‹ƒÅ1ÀËYKUgRù
¹ÈQž¸bÜ£™:¬Ng„8‘³­Aqü6¸n<Ð(AÜö%wUò«°JþåñÔ¦‰=¶¾y³óÖÝ©ô3úêŠ¦·ºÚ3c¯EîBµ=6NWóQÊcê`ÊKWó¸51aw@RhWó1aGñ TÐ®úå1«_o_hWòòñJ^‚ðà´Ï®ÎåãÔ¹rJÊ ì¬˜Ç+n9ºC¬˜®¢å#V´âaFLWÆòˆe,‡a·×†éjW>ZíÊ!>À„yªúÁ~9Ö”'GÉ8·²Œ%Õ¶§sÐ £ÄbK­°ïye‰ñßÍSxú±Ä\}¡MU'”wcK_¨6fÆ›U˜aˆ1¿ï® ‡ÕR·ÑIn¸?øªá"ò>Öéu8é¡Í÷t´CÜøyÓ!‡íwò[=äaãÑëL±ë€L}I»ìüS•w|¨öñ¹û.{Î±¨
ùZÂJ&@‡`6#°vÇ½ç·Ïµ4eÍŒMj™á	ìíÍ?\“¿íÐÌ„ª¾l¾w¶1éÙîÓ`ÕŸ-õ£÷¶_IrÌ‡QqÛ¶*ö	ÛìØ>¼¯+~çg<¤ß-UÞY?.ä¿¦îœEò|2¾v-D¹ÈI§Å°‡švXÄÞÑV–bŒÊFìÉß!îîË6õ“eÕÔí¬R„Çž(B«&j–%¦è£ì‡i‡¸Ç†‰¹Ë"yŽç?P`¡Š¾c*âïKG:‹rÚ°{˜‚­åÚƒ*°Î`Š[ÇÔs+h³%ÎlŠƒ[8“à’ý³ú‰±0hÿÍ—nzáÔÖê‡m4T¦[öOºìŸÂ’3Ã®åÛÊwLsZ¦QåS8,b…],C*úwš2ŠÖrVÄ¶²Bûœ¥”%®]×vúý†Ï~Ç"¸)ƒG˜ÑôRˆ¤Yª&ÒTs5áïÞºhŽU¥Ü_¯HSX9dÚ¯ÆÔ9ÀFÃYå¢WÓŒ
­@Uàß‡Ù§/`¸¨zgSõá’)–ö-]5?•ñ¯èW±‰MpÛ·‘kàÃã±¯¼r‡…Å1+÷V[I'Æ%>ñÉÖd¢YsfcôÛÅý)‡Ÿn<*®Å¬Å¡Ç]¤àÎÎ,8©YPú)X”›ƒ´Æÿ±‚áÃÁ»   0707010001f0e8000081a40000000000000000000000016a102a8900001332000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext2.conf.gz   ‹     í]msÛ6¶þž_™NÇÎ\I+û6ÓY7Él¶M÷vn³ÞY§ÝŽ‘ „5I°)Y}ùï{^@,Knä$Þ2ÓÆIç<çÀ9à'ŸóÏ“OÄÿ ¹ÄDµ.”P7Õù»“;nïŽË»'?$ö“Rç±ºùñÉ4è4ì'O°ë×j½2e|Ñõ Z¨èzR*Ãíáÿ £ld
9KUû¾¯ejÞ)ÕOµ.U¼}§(ÍR[mrÏ/‚;±JdV]çÛ;:_¨RW2Ü«R%ñR,xXÔ%`àªäû¶P‘NtÏÌUm#|,’y¬cY)ëÞñ¶¬•øµ{Odò¥*½ÌŒWåÄ[èŸàÖ7‰°ª•Ói¦SQ-T.âä2‡‘[S—‘¶’UmY ø<žèTy ³k[©L pø1 ¥­¨­ŠÅ
Ib+lÓ<	73SçÜ‡¡àm“o]¿”O5ÖEÄÛüxìKU¥¢
ä!LâQË‹#HƒXl¬6Þ´ÑýqRéL™ºò¨ý‚Ì˜¸ë¿kÄ0¿dyõÛ	ÅÆŠÄ¤©Y!'#	àÒ©®ÖÁ0ãRƒ”ì¨áô(±£æ8Èéô¶ŽK<J·0‡ÙÍìu‹oš°•ˆN€''?>;x„Nl6ã[ :úùç‚ÎÝOéZFÍõ´†.•øŸN¤Š?Peøä€i<š˜¢BpVRçºWšéôéPùE7n°Œã† VË÷aP¥n5 Í{èÿßóGSOƒ³Ô€ZStä ¬ÆŸªerîn,Œ­¡ºyÏ‚) µ‰¯t’¨RåUCÕl¦È`Ô 	æžðuÍ@Ç@íÝKâjù¼šËAh× ú”7Ü A'Æìó¼4uñAý{,ýCJš8|“c¦NcˆYÁÃ¨ÕÈïåcˆ*S®Gâµ†‹%ôÅQ!q‰]g3“Âï‰)3RB~Ã´3»Ä’‘À{ís2!u…Ñð7˜ÜRµ:Ôú<Ö-â/ÊçNÐ;ÙÃ~‹ÅÀè‰mØHŽäNVæfR”JeE5‘3S>>ž>– q .•_}ÿ¥Xé4ŽéÐ+«1†TåRRT¶k$¤gŠš'#“e9AHP¡ÿc&Û›xƒþGŽÄ•É“®d9W¾óÑY‘ª½B!€~ÔEMA!J¥­ P±§ÊÞ<uÃ	ãOd	¸+0’DBUqq½º˜n‚jÚøE	‘'Œ/i0HUÛHz&­foÚô ™¬·
žU@†hkIÒ[®¯ñ¹T#ñ
Ât7£)9Ažˆ°¹‰`Šà%)á¿A2gŸþùNÁ”p­—Œ“ÌW*Ñ9„€²{x ¯0”ô­pãb~;ÿñ„òV^¢Ôú·,=‹ ¦j©R¼/f)Ü2•ÄÐsv-iúÓ<Ëâ¥Vwz45Üí~Õá=9
ôîÊˆX PÂ	ìð™8Ýœöª›fŸ0… ¿æ€±Rb §ë§$·ÿÝ…=øÐS®}3-Á9Á[é:Î¢¯-®*@ˆ½fÏÑbT¼¾Ñ<òHâKµíÚ m{!ž¥9ðâ²AYë‚ü¡Ú]+iñyT
C«`W)zø/ñeÐ„½­ÁUxQRš<vƒf¥Áë¹™Ò<mˆb§Ëë4 qvµÐíírv‰}nÎæ²˜Xý³ê-j§.„?FoÅ¬Ö©ã9²‡%R3×8±¥IaŽ‡‚ÔÖ7… T0™ë®Áé¿q½yyƒF£§ü"<¡ÁSjœÏ‡&Õ\œ?m¦^ßPv(ˆéŠÚA_¹¢›´žå/67q)¯T„¥wr¨r^Ë*í:†´FŒ¿µºh7‰Õ¹&BoÔ\ÎÖ`¨~¨ö˜zoeïÑÁïÙ1íü¦ë’¬ÜöÔLÞè¬ÎÀjß‘uäÒ%/‹S„H«0y<ÿÂ%IuSà`¨Ú\lÆ¥L5/eâ‡ßÌJ–ù æØ:ZˆLÏø‚Ú¢x{ù¥ÐÉF@×¤¥fÀ…·2<9"žíÂ/{3äIò ËOô‚¢4à wåg_/”F(È««l‰1PI êiüû ¹ã¯U®æEWrQ×rPµÊÑ¥ ¥‘°!¥wÝiCÙ‚OÜbÍG*¨¦É–œ~øñ!­¨]¤ê&ß«8ÖÈ(pÚŽchè
ié'JaÃ§3{›ºt…îç?ŒeJ›)  Þ_	$R„ÁîZ„¼NìG¼
ùÑˆïõt¦é Ã„Ü&¯U:fÞÉñ8âÝÉí£µÛøM<Ù°vÅ9o³¡¨9MçÝÊB6Ä¥ïˆR×7šÑÀØú2M jÀ=k˜„=Ï_~úË×ÿ|ýú×|õë÷ûcŽ"ÏÆãl—,—ó?´$7FæÈ	gÕí&V0kp{ŒmXÌ¤Ý0Ü—Ä°œßí†h¶ãžÆV“–D84Ž§]sÇ8,*uA« êFE8i“I¥Â•9O ÑPRšÅÅ´ÉTð%ˆùÊÒ@ôaÌë‚wÁÜM”ÜÉÜGœ´VÒ‹ò÷ŠÒ ±rº±žs1™{‹þÊãY¿v~d¥ôy;}:÷“«éS6Ž¤™¦8†bšâ>ò«óÞW>Zz¬ý½Zé‘8T¨¥ê£ŸûJt¦`ö :üñ%siöÁÏ»IòÈÑO#‘ûJ°}F%ß5øÙÏ½ÄÚ‡>GÓËw}œ<î!¾>òy8¥|ÇÐ'”Îžýè°z?ÊÇÍÕyùýŸJ*Â>×9´"«Æ3i»,×$¥pñŠÛYw;­~z¥9>0ù6w›ÔÊB™Ëä’ÿáB•¸QÁ	
L?ÜŸ]ÓfšÔ¶Zk·Ml5f& °xÓ‚Å	2ßYÂõ.ÿeÉ½ï=)ëUW8ÔZdJÆš)¡ç9î£0ƒü¥º ÈØE]Åf•û»QßTÖÛÈ—éJ®qcÁ„¦ÓüO+›Ä•ª€â«×_¿úîÛ·¼Q¹Z˜°¶Ì½3¨üòúÛÜ¢Ô)±0Ðß;Âƒ)u{Óß8ÛÃ)%¶1E6È/ÎT¬ÓÖª.uºm€\_LM&žã¦éË¦{SJGÃM8ŸåU¹Aš'aP]–™WÈ…|¤ÑãØ¢…Ìç*ÃßÌI3ÓŽ|a[À¼?å.ïKÌRy$‹>…þ(ÊvµYtiœŽ…™†¨¨\T…Esê†òáýÊ?´˜1&YTÍã$© —ú	1Õ4 üÜÀ5{aq…ù7EjÖ”ø	W¾‡…‹ zß>ø¸œ@Ç²Ö`p»´¦›]•z>W˜‡…0 #¥#ÀˆÐ.I§EçŽu~mB]^3îAˆŽ{¥”ËJ×Ð”ºŒ*Grx[\¦¨§¼¥,í5„F˜„hÃx	nÏcq’ö'¡€žHŽ M€€´ÉE„áBèDê´ÆZdLzvÜ÷úŒ!Xj¨ß³ºjfpæfçñÀüTƒºcIn™SÝÄ&UÏï#1àJ©æ˜ÕéŽÛb½²CœÈ%t”ß°/];÷´_]Œ©©3º#ñÏæÝNàÛá7¬ÖáŽ³ÇG©Ý’qÈlue2YaBøëÆ¬bÔã8ßY•Ô)âàV*©¾'[g…·±m	5êFbi©JË¯G@Õ®’”ùd–^k3Y)Ì“üïšÏÞCUÀ©<¶›ÙiÅ]ÚØçoU)œ”Î1ùÓÓ¨¿Rÿ7—0Ä”2©sw$¾—iœ©j¥T.ÎÆ$Ã³ñx<ÿ¯Ê\¥ÛÙÓ|ŸŠPBiaFêøÉFE=ù©6•ìÅz§X·ËÅ"*ÁDfœùb*cd¸^tÞDT`Á†	…a½ËU¿ùi8nk‰œ6RÐ4³&Åe‘%Â)YÕÆdÁ ê¤”™ªT9_w†b€æú[ÌOµ ã0Ï TÌ ÀÉ|ª3]…ó *’…±šÐ¹ÑnHádÍ8e7ÓQi¬øÇ6¬€l¢d´@Þi“®éôÙøÓ¿À½¶#Ï¸ ×—ÐÏ0<åç›ÏW+ÃÏ7
Ð‚šÐOïØ£vVÛö:pÓÆFª-ì`À¸ã[šŒûf•.ŸÏ?Ìÿ‘ø&¬vÑg¦Fo««šŠ@Gr[»…Â­É:fðg<õèRÓdzâ¨¢Ol
°ÞòJaÍ›¨Öx¸…c	-S4àðñ¼âPÂPNºëG @n…<;¿}=î¶½¯ó­Ø;r˜i\WÆÅÂE[‰ì‰/áï’ºî‰Á–­pUºÄåq!Æƒ³Á9^Ï;‘¢0ñÂnaf*›-í%z_K²•ŸûšÔÜ¢ç,A X¯}‰PéÖSVôËº^&Ã7üÜ5ÌoA}ç¸‚Â³\^Ÿˆ¤Uá±OL¥V6ZÞJ›•|Lö Ã˜l‚+¥I{ŒÃÛ¼I*çâtŒêzö”ŠxÒâ%· É8é£¤ï]DÕf$Q¹KS1x:~:àyÓ—UE§IPe"à4c4z¤	žíþDã7ðwù—:7ëe——o<BÜIÞË7dÁq6ƒf¯Õ®ÙNÚ¨ds=³õŒ'•_à(še[]Ü	ig>Ê=ÔbºúŒÛ•,
¬ÛïÛQ Ž¼Rt\u!UsÜA0ð¥æµx!>Ûc™zY=\âTŽËä=u|ã]ç"ü{%(·½AÉ²J>Š¨äP:4á²¦öÍn¶^[ZfŸÓ¨©è6ßÜ?ôã™icE–~ˆƒµ=1N_óAÊcÜJûÎä—ûÇô51ï·&æ ÙÚÂ|ÈB˜Ã¥xP*h_ýò>«_Þ¾Ð¾äåÃ•¼$ÂƒÓ>û:—Sç²WŠ‡W¹ôÅ-ï·¸åÑÅô-°¢å`!Äôe,ï±Œå0ÙíaúÚ•V»rˆïÂ<V6Ä>ÆkÊ“£dœk]„š
éDƒ@	Õ(ÔZ;ÿ’%3ãm+<q\c®¾²UYG”wÃ¥/tÆf†›U˜aˆk~ßy¨ Æfm»ÕIYI‡Ÿ9Ää}Ð™4ßžytz°Zh€Q!#5´xÄ¥“ò*}‚0â»h' SE
4¹¬ó<Ü‰“â¤ÏIk	^udh‡Ï;A*Hƒz^êøåésJÔ}9ÀÃV±þÏçÃ+¼—`2<µ7Æ\¯ºØ
ýu]Ð—{ü#NÝùTt ’ÛE„×òÀL-OŠ;ª¨\:÷£ƒÍø!«oÛÙ{Û”GGU;öFZëò<I¢ÚÆÕ€%JCDÍ$~NËøIù®ZJq,Ûèöq±ã U—xÝ–ûÐ—‚z,Úù(m‚?U	„gÃIi~Vy÷T[‡Õdm;Jü\ûLLƒCº›‚æ²W:€àg;‹Ÿ}zú´Y‚¿$µ‘a+ÙrÉ½Dº”—ãA8x¿gø™é>ÖRr~ûº=V40¨Õb2-T\§jä_vçÿu„š¯CÄxŽíææ~@Ú! £=âûn`“X¥& ±;`?>… 0œ6	3,]Kp]W.úïòÊñÃ˜-
÷uT§ÒOªhX]ÞÞè•$ôìv“è ÑÏ-Z üsÿ“*xSKœ“ésemHþœ¬Wó’b¼Çf1czËµ÷¼fþ4ƒÈtNG6Û¼tÖ¥A‰ÅÊìVCsÚ}ÞÏGê”Žn&ýy:Â·†I¥cqš› !úñÔ:Kô.ÑS¦fÜ“=T5â»ËM©™+"M§Ñ®Ù¶Ç˜|¦?€ýª‘çVUÍ€Ëj–s:ñ?î ®^ýÝ}Ÿ¡Ù%÷ˆ4®<)½µF‡Ó
èt{ªL§÷v£$‚Íi ˜¥6ßŒä¸Ê÷}Ð _»GÕŠ³é÷ñ7À,9únGãuš(Ä}È3–*£<»  “EÙoy® Þê>tèËÝ5aØÔ33À8úß;t²'ŽÐ$ˆVë‚Ž¨K9ô³ä1%#¢uà¹¦”µž›|è@Eø¾p,ÈLÅÇw3ÜýœE÷	U÷,‹K0Ó	ÛÑé{–`úhD'è£ä/ÄW0‡©6‡*ƒmÆ|áºçwm«¬Í[_Ü£`ßûYêCÎR‰Ça†‚áŽÙ)NzÐ<0hLqfv!Å?üãPÌÜ†”­™ã&f<(Ü2ÑÐôI(ÖêyÆr4=nÛ1§˜¹ì×î\¦®äü¬‚·%œ"ÌÕl´YºTÎtUJ\(‚7Ñ!Á©šMy=u!(Äì¼1=³ëPs_Ãléê<I©>ŸŠ>.î¯7)< 	ÈÅ¦·Ë/Ž±}íŸ¡éù†ÍpàGÇº·ñ*[ŸN›b÷î;oá‹{+¯©L1¤ˆš<]^­Á°æO¼NcÜ­îyìñnŸø{[[Û$ž¬ÛíwÊîÚïðžìÃ‚<N?†ˆò6`Õø‰­êˆø…    0707010001f0b8000081a40000000000000000000000016a102a89000002a4000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.service.certificate.tls.conf.gz   ‹     íWËnÛ0¼ë+ðµÉ$h^Š‚^â[Qj±¡HuIÉÑßwIÉ];©“¨=YÎ’û˜™9«Õ’O±‚Ÿ˜N]i…>@Z‚`üÛÓ-ÛÝ²Øß³aW¬mI?Šë4ôÇ8vQÄhØ:.¯æN²kU£¶›J²Ø¿Xð‰ zåZ¼3ôTÍ]¬L¿:Í47öO‘–]¯½vVÛû«½HIv&Ì³|s6´­‰u@«¦B†°‚OP–».â1î[R9sOVîª–TðJNÜ8…",~ð(1 x×±’4„š`}s›C		ÊKIsý<ÀBLåÜéÄhk´¥Bïý™‰ÛŒà”ïåfãÓ	ò¤˜Â™“‰“µ ž>10Âîî'©0FjçC„8†srÒš]fn»žï7PcOÞÉ´¥åžx#tEÊ¼|ûÙtú[ªÝàùƒR†‚²½ ×˜‘å*µà"Å†tE+yÉlk­j@cÜÖƒŒ–nÙBÈæ¦¹^”YËºg%r¶æ·Zób„7‘pÔÓi8ñ	Fü:Ràs¶XL1I_q	q·ý•c GlZC¾Â¶{4ºLëxvíÅ\»à¤•!#1ñŸH;âx3	—Ï|–+°¨Ü(L/ž««a“ç­Ñ×³á±ù<›œ˜Ôù¢éÏVŸ¥9núYSÇÜÿ@}ùáš†ì‘è“ÞÑè¿ÐçW2­‡Np—Où‡}ˆjˆ4±3™l/I)L£íø‰¥Œ>ˆÕ£Å8{!¯¾k[ÇZrb2QˆIër %Ž2µà.ÿ¾ @–íqAúë$×K·Îz-Ç-ßý#ÂyQüÊ-=  0707010001f06b000081a40000000000000000000000016a102a8a000001d1000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.node.array.freenas.conf.gz    ‹     í—ÁjÃ0†ïy
A¯[ÒîØ­;î¸SocP×Q[³Äöd§[ß~²³´…^Š|‰±äßÒ÷ƒƒ'“1G1G’SDêpñà6„hUøÜ¸ÕË®xËÍNÈØ¿ß‹ÇÜôbh»(RxørTÏOu(oxý~Ä‘8í¼Z7x<èE5S„ð³3„§–Ôå@Õ5ñTÚ«³8h!pÕH}0xÔfc4<Ã-’Ñ¹7Vš3–;ì}ç³BLBGMÉ9i¾€]Œ>Ì«*'•,nÃ^—ÚµÇ«ý¬œ
,%°Ö-Àò*„´tƒÄ†Ö :è¦Oã¶`ìà·g’kà?¾‚'¯âî™ËÚ ¡Õ9UýÐjÂXfÕ~mÇF¬‘Õ òêj!b»ZU-¯XEÙºÏÛ©=žÉõ†òxÛ’Í<V¼€^®âÓzSgb (Šb (Šb (Š#M‹®‹×õï¹0pö0ý³ ÚÙ=ÒÙöº#³—¯j~>§2Ò³úAFø;g‚éda&Ì®ÈŒoJ×Á^®Ck—k¦sŒ.€œ‹ÂGøá#|„ð>ÂGøá3ŸD å±&     0707010001f092000081a40000000000000000000000016a102a8a0000010d000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.node.network.bridge.conf.gz   ‹     í’1S„0…{~ÅÎÐjä®°àæ,ývŽÃAòà2`‚»Áóþ½ÁÓ±”ÂÂmßÛÍûv“¦kV’ÒŠ5Žs'Ïí…sªØš¿·nºuw—<Î°)[gðö”ì&èýŒ$#A‹sô˜üdîŠÚõŠ5.K´ïËªÃçc÷e'ÆË`æ§bP—C.ù6™ÚnU¦²›Íí2À&é¡mm5ÝQ¶z“DGÇÃ¤»A˜ªR·pfAV“È~0Åü7Û×è¬¹lžá‚P8Zùrù¹•¬žòö”l/E.œ/¨ê¼n…d¨¢UTÌ²[zößaþïñÇîñ&{ÌéÍ     0707010001f07e000081a40000000000000000000000016a102a8a000000e8000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.node.dequeue_actions.conf.gz  ‹     ­Q1RÄ0ìý
Í¤>Ò‡’’æJ†áŒ¼!‚“ìáî÷8\’c Äê¤•W»ë¦©Y¦¡Š5Ó932^,'ƒþ—®®ººÙ™§_fñÁáôlfåï8FqÝU€ò —GpW±æ””ãd_Gl×ì¨ó!’"ÑÜ_Ä¡·yLW1`%í¨Ø÷žéžÞ ž¿å²±_|ÑdÅ~ A¨Bi u¼»dÓ–¬hÉê@!ºµ»ùñÓ{€²J«ƒ´.r»æ¶±n=‡dOåµ¹Ýfwe-óðI>](  0707010001f032000081a40000000000000000000000016a102a8a0000012b000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.array.netapp.conf.gz  ‹     íÓ±NÄ0à½Oa©+pbåTFF¦ÛCHÿëE”$8éAßž¸ô®T'{ie7®ý©­ë5£ªiÅv†ÙŒW”ÇòÈ&Æ?µ[wºuíª§iÙšoñù\m§¥›yíª’ù_1~nï–1J¦ä¯WaJ6DóÒãü¢Ó'H…ñ>8Æ2ÂŽ‡©Ðbo†>/£=S/P™ü]LÖí¥{êàÁÎN»•É–'vP4ù@9P–{vG“!›JjHK:rþ¦œÜJ¡¡œÙä°ÅR¬ÅJà#ø½Rl:ÌŠÞÃæ	hÎ6dá3ØÜªÏï|Ê·ÇÞ¼á…N«ýü‹«qY}ÔG}ÔG}ÔG}ÔG}ÔG}Ôg=Ÿ/£€ò   0707010001f08b000081a40000000000000000000000016a102a8a00000131000000e600010003ffffffffffffffff0000003600000000root/usr/share/doc/opensvc/template.node.hook.conf.gz ‹     ÍS1nÃ0Üõ
ž“´HÇŽº¬JT¬FJrÝßW²ãÄ@Ð©ÊÑGÝïà¦©9¢ŠSè:¢SEººîêf'ÞÊ±[¯qzÅî	¿¿ˆõÃMUQßK¯3¶«8%™ h¯bÏÒ,ã9YF}h42¹xó÷BW2„lyÃ€Ê«à	Žè‘­*kŠüˆ¼y:‡Ó|zæ*¯®GC$À	UŠ™ÚC@‡*¢ÑÇ°ŸW¦#Ël “¡Øá¦õÏ@~gˆ{¯ï@Ë(óS:v¢¶~Ÿ<®š0Dâ—6åÿZÆ_Šp6Ä»ÊG s9{[GÎyùU×ØÈ/}ÌrÒÁ(]ÚVÑ¶Ò¹¶-=eÿ!qneÿ"p !~ Ç†eÛ¡     0707010001f0f3000081a40000000000000000000000016a102a8900001073000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.service.ip.cni.conf.gz    ‹     í]ßÛ6~Ï_A (’à¼ÎnÐ¾¸›Åíõ.¸&)š´}‚-Ñ6±©Š”7.úÇßÌ”Hï®ímì¤Û*Ac‰Cj¾o†Cr†}øpŸ<d{üƒâd=bvU–+¹qûÝ~u÷à¬6RâÃû_ÓG?ÇÏ~ð G~)VWº)&ý x)¹'G{üƒ*2¹®ù´]Wo›VàƒFüÖÊFôcøž—†žÔ^J#µ’j>IžbÆÛÒöÃÂ¤ZˆFZ®rßQ)øŒ±…àE…`ðÅ¢qÏM-r9“9¼3
ÚæøZ®ÕR4‘ü©Ö I‘Î w“Ã£Ÿ "9…ÁÇì¥±
3–ç—Ì´u­Ë*ª¬K|ûiÅÍ%«E±F*+šÏÅˆÍ&—W“ŒdfÌ,t[l*˜–YÍf¨ vµŠÕÚ	úÃ˜¾¦÷WRÃ-Àæ‘_^ä¼i$u? ¼àóÜÊ%·‚Ù…`¥T—ÌkÏérÌÞÄ¨Èà[ˆ%“†q6UD0OYÌqhÌ\I›/·“-øÊ\(^‰ÓÎfòÃ½Cð•VûC0F©( %/[Á8*ßi¡Áß¾z_ƒD†ú³·ð£ÿ!o ÃèÙEkØL7·ðEfÙ)6=;:]Cál|Êëþ®¸âóDÐïð½gYFö¹Ö
ži½	è¶.`\÷ãîÉ¾Ì”«B¢&Ì¤-û£ïg';3µ0vÀÀ­y@€Çe¶‘ó¹h0H§ò›Øœˆp½‘k4àX«Jaaê„R¾ùoEÇ7 ‰ûÐ[ˆ">À$pÿHòîý!y)MýÌ—TÃ{dâàá{LÏAœ¤ÏžžÂ'XëòìÝät¡á)ýþ>Ë"´Pf#§È&£ÁÃ[˜™›¥ÌzS=‘ãìÍO¿8€¤­ŒÙ¯Ò."AÔÃõ€“ûˆ<•¬Ç\â$£ÕLÎÛd»0(COZSþÀÐè-¡éÞ1DØÅÉ³CÌhÛ]¸E³ü“TÊ¶\Bú•ÈB:vSús?¤[µìz˜‘QË¯´e­›®œn;N{•’â7ÎJXe…F´7 ÛX+t[)g!Ò©ËvŽÜv1Ï7¯^ A«—¤sÒ'.üB“‡ÇTÍîòý‡Ò?qÖ+>Òn¢z<øto$ÑC)Zã"Áp"8*uÎË°D@yà–`A¡4Ì!0QÁ»±?1pœY`ý	"‹•{Ñ´©ççRnÁúŠK{QÜCK;>dQxÕ&øT…±Ÿ³¼²5BöKˆ"D’ÀõFØ™¬Õz0‹Þ0>C¼ÉX“<Ä°Þ‡~ÜªS‰¶ÂaxÉ¢67ï ŽtëÅ‘BO×›Ñû Å#àCŽH¼+kÇtø„©û‚r‰0Ï>ø¢ø ò"ø±M¹"â>ónõ/”¿ì.DhrXÿ~Ãê‚ÏRä´û‡ïkÍ	½øñüe@:ÅÅ‘E¤fòû^òm¨LÁ]‚ $Å¸44¦àn‘ë
£{ÚÀ5@M[?Á
œ5ÇF™Õ$?â9:”IÖ}IÆÜOcö]Óh°L
K›Å‚ÿ¼™ÍñSÚˆÜÄ åŸ…2T¯ÿŒÎÙßBøKÓÕ€ä~2Ömö1€zwÃU×žû±L]ïÃ0u}üZ5Ì•2ËHµÖ*#»‚Úˆ!ú¹+¢Ñ‚å€áOŒÌÐ‚ŸCrÏÑO@ä®±ÏaLòcƒŸ5|îëúìÍ.?6öñxÜ¾!ò9œQ~dè“¢³S°PŸd‚ìðØ†‡Àï¿¢¬n«ž[­È«9ÀtÙíÿû3_Ú¬@šž3ç‹ôh_IÚþeZå¼,»”°ZhL[ð%ÀµÀsi†úA~$
z]1ÜQt¶Ö‚¬v]kEY`¸ßK$ðpæ·¦—Hƒ¨üÍrK>yRÉyPdOv%â©`r®ÀÎ3P…æÈ¶&Ê˜Ek}…VÛuñÂ|Ç¶†òÇÊ+¾2 É‚²L=åY6¦\<²þö»ïÏþá­K¸Zè29ð}‚ 0Æq<Þð¼ÐÂ¡Æ»!<ÈhØëó÷=†N½LpEña—.x$Bdµmúhs°hâªb’éŠÖÜ.ÎÂð2Ð¼n„%QV¤ÿèqx>Êôö}ŸEK’îÛòWsQŒ’ÏK{ÊuÜéÃýp`wºæÞš£r^\ûÊàB'™eZgþ·³1—ÓÕŸ»#¥¡{áñOÒ0sô˜0h&mxJÚŒ×ŽÜ}N P©(“$?ŸáÀá‹Á€Q—z…	c*™'ú8—·ƒým£OhYÝÚ—·‡”zÍgûT@˜z‘à¤dÁx„±‡¢ŸŽ¥º4H¡>EÛ;÷$DÇYt(î(ÕÖ"¼Èm$Ð5ºT^Þ; ºÓrn.!42+•›4^‚GsL•|¤´›9˜ÀDêÙ¾´N@›¦ˆkÉÞ0_Ï¸,)åO¹½ö£1cVj÷´µ@*‚—VN üµx€ßZ0wSUi\VÀ5©Ñ¼Â@+˜¯e%ûŒ©©ƒ=âK¨kzÃ±ôíüÛQóG4 1£o0f?u©	˜Ûá¿&YÀÂÍøˆZ$%Uo­®¸•9Å¢Á­bÔã?pŒ³¶DÜ(¥”—0É¶UÍf²fe¬¨(ñL¼ª}mC§¯ç{`Õm9	ó‹iy)õÅ•óÅßl·ð¦˜P…™¬¥+]Ô˜ßÄÞá˜ßßhR¸(cBGdQÿÁõ({ñ>±ž,Á×’vÇìLÕŸ€W´WB(vrLžÙÿD£D™¤Üú¥ç”Ú¢Äøêx²yÝ^üÖjËX7Âº–kÒW’”…+_ ßÐzl^çTã|†õlÞh˜aÞüâ–wo4M.q[ÄUn€$#º˜,ÉêÒ÷ÇìûÞQŒÐ]ÿ UûåÔœC–@¨XA(€‹ùRVÒ¦ë $g.E{ym<®!…“m5…à TPÉ¼ÑF ý³–Õ"x¾@ÝI‹®,ûêø‹Ã³n ðo„{¨KgžºÏÖß·WÚ½ #5±ŸúØbf^Û6°×æœT—éé#iç¨ðû@Ý.ü|rüìKæô?f/ãñz¤n¦ºÅÙVÚ–òÁF”iýFáµÅ:&}UnéÑ¥L–ÉÔò¤âœè&E·šéÌZâúÖ©„¶)9b>_¹PBÃ0š®Î!& k…¼=7Þ½:ðngß{Žšï`~ó1%î+ãæñ"xTÄhõüqYÌ†în|½Kî\Í›°ãÑÉèþp|ô¬‡”276Y‰ê‚|é€è]=‰‘¿§kŽïÈÌÍ.vÞc	 èfÅK•ìCÀù‰3ô×­=z=;zéÞ»„õ-˜ïwPÜ*×íOäÜ §ˆ×è0jn¨˜ÙS¢CÛù1ýÙÂ­«Üñht9pd³Í9›•|Î£¹ž<qIÜn’ò»ý†¤ã @‚~Z½1’`.šy9H2rë*'K+w|…k»¶rlŒDy"=»ó‰0oàÇÛ¿4®"èõë—‘ 7HOÞ×/eaâƒƒfa®õ	PÙÊƒ+¤HOJÂÈL;u‹Ê¯ñ+Â¶­´0C¶‚Ä,X\?ÙÂqsEÅx`0Å÷AqÜàå¬×ª©B½E²ˆQ‹Z<g_nñLV‡B¼ÉÑ.z|®õ2úœÂ]ƒÄmkP²¢’¿DTò/´ÇC‡&$©ïÙ¯Ö[CÛìsúê††·~~Ç3Yð"Ë8ÄÁ…Ú–g(ù,å1~§ýÖä—»Ç51Ÿ¶&f'ìvH

a>g!Ìî(î”
:T¿|Êê—ÁÛ– :”¼|¾’— Ü9ís¨sù<u.[QÜ½Êe(nù´Å-»@·K3T´|ÆŠ–AÜ-ˆÊX>aËnØma†Ú•ÏV»²‚waî+„û¾ÌvŸ9Öî2[LÆ¹”uj© H%5£ÔjÍ˜ýÊ§Œ»V²ªK‰¹úÂØ¦Í)ïÆ•¾`Ê¼vÕ&íùý±‚ë•éw'¹åé¥7[îÒíÞ»ðxÞ¿£Ã¸€WebÚ“t@?*áÃó\€%7­RéIgºïyÔy‚ó^ð}¨qo—Ž’4¨ÓFgO)Q÷l4Ÿ`ýË‹™«îpg	º’x+æzµõÈXœ¯Û:ËÒ+×Z'¸N¡[‘ÛP+bRòt¤ØPEåÓ¹‡‹Ó¶ì½å!Ó~…¤ñê‹ï?;UgÌÊ
´î§ðDeÊ(¼T;d†úžW¤ Ë6ús…›¤IÑqyÎJ]¹º6™ÔcÑÉG=êü©J !¾1kôïBõouuX!kÛKrïuÝÐÝ‘(ÇMÊše/¾t - Îvfn|}ÈÃÐ§¦Ãò”ü™O[ñNK¾îRÎŽGéÇÇ#ÃWÈ%p7uÏ—ßNÇóR¹“ú¤VË‰¹0ùBm)²$ÿ²p~€—‘ ‰<·$ÞVæÐË»çþÃ.
QrX ¸‘™µŒx
A–xç¢OBÆKß¦®7>úïóÊSD1ÿÔÊ¼-yœTÑ%°ú¼½)Èab6ÃJÏˆìf]è(¾´2‘à?sDrÿÆ¤
w¨Åž‘ëóem(þy¯ÐÉ†›÷Åžkë•ßsX%•¬ÚŠ‘Ú¾ôÞ%0‡r{™I¯øîï1MEô¿oˆ™ša¡¦³Ÿ'cì5M*=f•NâaObÐ¢› §LÍb(&;T5âÇË¡ÔÌ—‘‡ö÷º’o/0ù\8 ýMÀóZUÍÈ•Õ,çlÚJ H{sþ
'·KÓ’÷—Cb2‡+O*o¬ÑqiX¤î*Ó©ßþ+I ÇÒ&ŠYjóõHÎµ ¶úÆ[jÞ†m±»T­¹Bqçúcþ0KNQj·ŸuBbü-Å\T‚§°Ç
8±t×ýúÌ•Ä[~Ê†@"½š–š8Ú´S3ôÌ'¹Û=ö¤ZäP»ª±¢ã–£8KSB0"Z©ü^oKYëJ«#O*â÷Ä« ÒVà+žîqÎ¢n•¯Ã¸	Ëâf˜é„íèÂ‡r…#›aúxÌ®ü
vÂ¾…5Œ]ÿÄÔLøæ‰^<´kemÑþâk ÿ>¬R¹J%ß‡jJ†«S\
¤90it½gncJ|ùÇ®œ¹‰)×VŽëœ‰¨°‰2ä¢ÿ¡é“1QŒ‘sµ~;Îa¡Ó”S®›×6nS[>ÿ›àM	§býÐ¬U¶ÔL¥m8nÁ¿“[í—ªÊëiI!f?_I»ˆqi8ëJôr¥š•TŸïŠ.V÷›^Ðâ’bÓ›Šå—Zâÿ@­üå3´<_óþ‚FWQonÈïŠgY(vî–é­5¡W·§’aH‘‡<]·[ƒaÍS·Nßx»¹ßç°û{|Ÿm];&‰°Þí´#=)ÛtÞ½9„"Mß‡ˆò&b×ø?@Õ#Âw   0707010001f0d9000081a40000000000000000000000016a102a890000103e000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.pool.conf.gz ‹     í]msÛ6þž_™L'ñœ¤È¾ö‹›dÎwmæ2×47—´÷¡Ó1!’0&	– %+s?þv 	H¶^Ù®zÚLB`÷Ù »ðÓ§Çüyò”ñÉeR_˜YU‚UJåŸKî¸£;.ïžü‚“}ZË2×¿>ù–&ýŠ¦ýä	þJ¬–ªÎÎ»1Mnä\io‡GüANéTU|’‹¶»u#ðE-~kd-ºq¼á¹¦7U­RKUÊrv½ÉÄ”ÃX»¡ÿ¨Jz!Ë¹¨¥áeê:ÊŸ²×l.xæG!ÌZÔö½®D*§2…of¢„¶)~–ªr!ê€¾©¥6Übt¯Sx÷vÊ´0Ì(–$f“$| Ù§©fª20rxÓ2õ•*“„Á3 T&§¬T& Ïkå
IŒØÇ¹Ôž5^åð1t‹M›(eDÆ–ÒÌg/„I_`2ÈÉúÉs‘€‘b*êØ¿²³äØz²bf.Ø'xbÃ´‹‚—Ùfùm;lÀÌ”ø~hJ^ˆÅ‹ñPp	ñø‰ü`jjy@ …†Òsà¥R·rR]‚¤DQ™K>QõãÓÂöÍ±ÔÀ(3 «>ïFËþ×õ³¡§ ÐU¹SMßW¢üðó?@ƒ@³ÓaTZ#´¨œ”×êW SþK§(¤c0%7o_âúe8bÈHƒÚÏD¨ñ²¨rQˆÒPwše
MÓMUAS@Ø€œƒ
 ûì¹Ð×'£n:¡$!EóR[ÂØùÕò<YU‚\EÂS#d`¦Ñ$E=.¨¦NÁÒp-5„uB€å7ÛìBUÃ³/Û‘ üNLe‰–¼ûø¼5 þtÀ505Ù/†Q žü
èTµT0¾S  ô-R©æb!rœô—Y)á›€
z“¨ÏÔÇK²¶­¶+àiNè²^Þ‡Œ[}ÉvÊÔ+|ë8OBõÐ¯c“€±Äp	“mÈáDëå¶KÑfY`ö¾_	ó”X Æƒöá7ì9	2®¸NóFË… ¿f`EjfD•ùê„äö×Û¬üö¡^…öžƒ—ƒ^é9­4°'w"…¡Î
±ï¯$p?$	6­›>ì‰±îl’«ôÊ›'‹ÄaS‘##»å°%×kqS3²àìÀŽAë¶Ç)v4LƒKÃ!£`ÒÖ,âó€ÜDÁD)ŒE¢¸Õwvà½f®Gþó6¯Ù‘Ø¥nŸ¬çéÝ¥¤‘!6š¦PšÖë´AKç%8^T;¡È2•ñä@M¸¶–lõq)Q0•v)§ÿ ÂñM6dóË¯w¹"Öó\\oÊŒaùF
Ì’[[mˆ
Ì‡1ÞÂûxµžã!Çs‰3kG6Ñ›ô>!C#hùËÁDrŠwõ€‰]ªm{d²¹ÖÖ^‡ò#‰ýmr"ë^VJ›Ë–W½%sBºh‹à^tZËŠ¼Ž¸i:Â§FÄ±~ —ótˆM•çI;“„ÙGàŒëZ-”`ÑêºA²sá_¢èn‘Ìv“7"ßÜ‹ò÷Š2 ±2Y‹£Ý;<T„ðG™MúÕø‘•2ämò9u4“«ªzyG3UuÅTÕ!òkÊÞWÞ‘Z¬ý½ZØW¨µè£ŸC%:SU‹»BÉ Í>øù<I9úñ9T‚}ìs7*ù¹ÁÏš|kúM/?7öqò8@|}äswJù™¡O,›£0`QšÇw6tòû§È+Í›ÓM	­ÈªY©¼Íñ‡ øÎRØr.Óy˜ Y‰ŸT9L1ûÇçTBU@oÎ¸Ñ‡T”Îc!ýxç{Eg&x²Ñèh­°ëJ•Zñ¨ÙnÍZq‚Ìox&5JåO–•rï‡€ž‘ìáßD09+AÏéD):/k*‚Œž7&SË2Lxk4~cMçù’¯ðÄÁ„’¤|Á“dÄØa€ì»ïß\üôÃG{l²œ«<‹ïù1ŽÂñú÷™tTÇæ
Æ»%<HhØëþÆÙYnB{S¤£Ä˜Bd8M`5MühRÐhäŠì<Q{‰'—¯ýð:þ¤¼pDxŽç&=ò_Â¤ºSMÌSüHû`néœ—3‘¢éÅ=‹ÒvÜòÃ>Ø°=ýpwŠ2åUŸûueû·ãß­ŽÅ'Û¨¨6ÇÒ"vá§h1aÐLÿ9I*jCÈjø4 ”Â³h›|ÉaÆ À™¨rµ¢SËÈO U.'@Ôß.ø -CZÙè(Ö®gê†Í6µœÍ œ FJ¦€ÌíE1v¢èÜ±,¯4BHÏÝ9sÆ=
Ñ1Û€r'èšRáÒr-É] ð'€¨§6ýë+ôªLu/Á+àyÆž•ÊzŽg&€žKS¾ï,ðt.ósµ1ÉÆŸ¿vcÆ,W4îIcÚLO\9Å‰yvJ`~k@Ý¸ª\+ÇÕÀï#1àJ-f˜üÎ¶¥dp ÇžñÔ6¾áXºvîë ù3Ì£˜Ñ5±ÿø¾À·Ãß0ÍÔÜz|”Z”‹²7FÜÈ”bQoV1êq±Ÿ´˜69âàF*¹¼'Û›Ê\è•6¢Ð„qÍ1§–T¥å×«# ê¶,ÈÙå$¿’êr)älþ'Û-<@UÀ‰2Óçë©3Õ]Ú/8æ_oT)\”‚4êï”0öö=L1œ,ÀÖwGìgž70À‰0K!Jv:&žŽÇãû—¨K‘xi'Kï)÷1– ã›ñÉ¦Usù[£ïÅºU¬k))° M)íÓqåk¯jZÏ‚Î«”ú¬a‚IaXÏfµ?~ó«xÜf';m¤ i¢UŽÛ"„RÒ¢É"‚QŽKÍaD=bo:C1@sýƒ,›k–R0I2<…P±€P ó¹,¤‰×”ÝÏ*¥%¡sm<¶!…“M1à XPÈ´VZ ü³µ<Ý–!‚§säT°èJ’oÆ_ýÞµS™ÈKÌå‰ÂSÛâlý{³Tö{¯ -¨	ýÔÇÐs°Úº×c˜6k¤ü``$íen¨Ý¥Ç§ã³¯™åÿˆ½Ë4@4Ã‰jÐÛJÓP*.èH©·Q¸±X‡> "¥·]Êh™L} ^€*úDŸðûÑî6˜KfÕZâúÖ²„¶)<8B</m(¡`µG@Û
xz¶}=îö¶½ÈùVìÞbÁ Ä}eÜ!\´õÈÞûü¹–ÂÝ}Â5%â®tÛ!ìœ§ƒ3|0žu"EaâƒÛ…Yˆâ’li/ÑC-‰–Ÿâ5Ç÷¤æz=ïd	PõŠ=2£'+X ŸXEß˜áûéðýî
Ö· ¾3ÜA±«\»?‘bÉ‰š†k4˜C
¬ôZÞJÛ*ù˜~v C©âw<j•÷9†·¹`ÓœÏØó1ªëé	ÂïQj|ä6$-Ž@úè "éBÆÕwA%ß[:P?Øu•¥Ï¡2H£hm×)D‚g{>áýþ=Üþ¥´ÅÊïß¿ÙA:ð¾ç!ŽÓšy_+]å‹›¨	Hu­FÀL7»¨ügá·m¥0[Pu¢<@-`ýtÆõ’WVöŽí(Ç^Î:®ºÊPF«PjA‹Wìë–©—ÕÝ!NålYV Žï‚ç¶èëÀ å¶3(YôQÉ"*ùêã]‡&D©ëÙ­ÖMÛì3šuMÃ[??ã™Ä[‘EâàBmGŒÓ—Ç<HyŒÛi¿5ùåâ˜¾&æ~kbö’ÝI¡}!ÌCÂì/Å½RAûê—û¬~ÙSx»@û’—‡+yÙK„{§}öu.Sç²SŠûW¹ôÅ-÷[Ü²èö‰búŠ–¬hÙ[ˆû1}Ë=–±ì'»1L_»ò`µ+ûHð€æ±ŠÐû#æXSž%ã\É*ÖTH'J¬F±Öêû/¯-3þÝ¶Â«2%æêmê&¥¼[úB7	aF`|X…†¸ç÷S€
j¬VºÛä†û[ñZä“wAçÒßÍûè0t7&`9— £Š§b¨æZa:©Ý¥Ÿ"Œ2iø(„OSš\7eŸÄqö¬Ï³Ö\tdè„ïºÂ½]:ˆÒ ^Ö2{ýü%%ê¾ŒF£¬ÁÛÂð‰=KPÞ—a®WS´AÝTI„Z¥nïžs§ˆÐ­½sÎR‹ÁÓ‚bK•Kç~t°ßebõM'{}yDt5¢c`¤µ./Ë×ÌÈ¸î\X¢<FÔ„§W>ãÐgÐw¸j)yâX¶Ñ+LÙyœ–w WjË}èJà¨‹N>ªA›àOUÈð‹i­>‰²ûª­ÃòYÛŽ’ý®íóèRHÄ&eMú²W:€f;³¹½k8&JÊB¯è°<MjÃãV¼å’ë„»ƒ”×ãA<ùpdø	™n]÷´¶ùí«ö¢ZÐÀ¨VË’¹Ôé\dM.’(ÿ2³v€ç!­q–µõÃýˆ´C@G{dß»‰]f"ç° ÈÜ•½±ð)é´IÈ˜aéZ‚ëúà¢ÿ.¯|Î1EóOL›œ‡Im«ËÛ› &¦Sºý¹»^':ðú¹A„fdÿIöP‹‘ésemHþŒ¬—ïäï°Y–1½å
-WÖØä†ËžY!KY4#¶Exé¬‹GŽ½B\Û»ô7ÍsrEË94‘š`¡¦ÕŸ“ö'•ŽÙóRE	ñ0Ž“PèV¢ÛDO™šY_LvWÕˆŸ,ûR3W6DÚý	²í&ßƒéÀjÑ ðòÜ¨ªØ²šÅŒM™ãeÂìÃÅî>`JÞŽ„Š”myR~cŽM+ Û(©2úífI9–6UÌR›­Gr¶…½D×6Þu_u¿-v@Õš-·¦?Äß ³äJwg8y…ØzmX3‰‚BðXìá…–,Ê~ÃsEñ–sÙ:ú¥¾‰…M3Á0CM]"ùu/{â-ò€¨YUXQ†qË0Ì’Ç”ŒˆVeú¯Š¦¬õR•C*Â÷¹cÝ«>máæ,ª¦î´Æ²¸)f:a;ºð!_áÈ¦X >±¥[Áž³ï`cÖ§+ƒös>wÃ‡¶QÖì/îÐ°ïý*õ.W©ÄãÇ°BÁ°euŠK4wUí…™Û^þ±/fnBÊÆÊq3¶A†Lôš>Ek9+ãXŽ–Çm;Ë)Ë\ë×¶nS>û‚Uð¦„SaÖTÓk!²
t©žHSsÜ(‚ûè¢qW›ÛTM_^OCXû…)Þ»_«ÖŽ
¨¹_ãÔÒ•å4§ú|«Pµ\DÅ4¹¨Øô¦bù…’V¢¯Üå3´<_³î‚ü%]ovÈíŠ'‰/vå~¯H|Äâzµ{*	†©ÏÓµ»5Ö¼°[á4ÇÛÕý1Ÿ€=Þã“ðlkã˜$õ~§ñIÙ¶óŽàË>,¸Ó° àôcˆ(oYÿç†v    0707010001f11c000081a40000000000000000000000016a102a8a00000715000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.usr.DEFAULT.conf.gz   ‹     åX[oÛ6~Ï¯ ‡t…ãõ¶óÒ`F’vÝÒ6sœ¾CBK´ÄE"U’²ã"?~ß!%YN¢­kŒÁhü Û¼~ç;7mo¯ó³µÍÖø!q‡G¯‡gÇãu‰[¯vëÅnëê²ÛFªX\ý¹E_ŠÅ\›x°<˜gî\ñ\XÌî®ñCðØH|’‰æ¸±)Mñ©”F,õxÍ3ëgb1åeæ–
¾×JÔ²ƒîÂ„I[ˆHNeÄöY"”02¢e‘V3aZû3i¿:DÛã˜¦}Ü	v*%UÂFÐGXÇ†™Fq'g‚êœKÅÞ6}Hø©AŠ½bóù¼}”EýHç¬õ{«è‰t›Šñ‹'?¾¼ÆV~Ÿ9£ïßÄ‚•¸Ô£Ip HtbÑ·@É/dÞ.•3M\¦†«Ht"Ã¿1hÆ©`ä[LO™‘ŽXæà€gQ:§Yiãã-H[ñxXºTé}–¶–˜— z"¼Úá<Kçy‡.x$òÐÜ.ØÕƒbdžëÿ6Šh©u'8X/”û?j€ø}<¿ˆ¬°D6ƒ'’¨±Ž«Ø“ÇèÌ“ÕI˜™ŒÀHÌaui"a{lžÊ(m±DT´üÕj7âYÆlYÚ8V]@^ÊgdZPCFx-¿%
§.7 ¯vÐ¯„¬]he%‚L0Z0
,×e¶XZÂvC‰ÝÌ|ij‚däì`y&»^Ê¹•»&–äjÅîÃ•¥=Ù\Â^<¥¡;Y¦¶;Xa\YxûÛ´t±ž«~ËZo‘é°Æ•–8À³9_X&ËCÐÅ…úž_\ô;EŒBlªê¶žçÖ<ÕYÛòõ™TëØoë[ÏÇu
±#ÕÐ7¨H
¶E9#'A
”‚‚jøË$hKàèyåJƒÛ–h•låñà•ÎRÖ^Á]º_«qt°Ñ
\|p9\TWê×óPÙ.]¥%gyGô—áQÊU"â^sr}žPá¸ú¶-Iaê¶!C-WÃ/3ºœÃJÇ[ß·.>Ïô\˜UêW°`]‚ÔþþÌÂM;ÚEºÌbòâUx1‘—·Lp8:AièJNœb2Ë
ÓÚ¸|ÆeFHÔô'Rtk‰šÁ¯*JVòsb'Ä3D<¶C“;7M]Ùðb @¥Ÿ= Ô~D÷õàø›L÷ó41BÍ65ÁïùzŽ[ÏRtÿ>yãàh4FÞ8<úHO9äÃÏ·ïiüÝÙ)ž'è9úç¡þJÏ“QxÒÐïgÃc|hßèˆvœŽßà9~GRÇ§4~6ßÈ@
5	hÜ+ï.p¸‰Ñe’"ñ¡> eté›”.ü¿#c€É2Yùõ!²Gä¾«Nû÷Év|¨³ÏJžÉ¥kÀ£Ô|ºÃŒJÐèllÑøµÎ÷†.åkDK@7M†¥ÚŽ £š’ò6jD¦v­(¸ña4¼²h”@•i´v×öŒ‚è|]IÝšns=É¦is©®÷ÌÍr!‘Óf°×(±¬Ãº/fëQ_£øjžï®Hj«”Ê|/åOî…ŠU›^âCL]'7§Þ¡T8)Éô„þfU™Ä€I\áŠZKô<=-iAÛ˜•öò¸U¹9#“„JxÒ‰_u–°ÚgÑ.ˆš¼Á4T7Ð.ó¨YÝut)L-G
çT;.sn/›uþOXâÏžé¬ÌEð)V[ó–þmiš¡q¥WóV³êExàï¼ØÕ[qÜ"Ó¯Fø©µ˜ü+T˜£Ý%Ê[{µp„ð¸ÞN`LeR
G_ûZ×GïÝˆ^Õe™Çá¬Áã®€ ãÍ0¸LŒâ³,e¼ÿ5aaÈF¯Ø‹§Ïž±–¨°Ô»Ödá†cÄÁ0Í‹#¦ðSµâEdú†°ÐU*YÕîÊ:o¡Õî”ÈÓcV·[âªô€S°.F»ãRMÙd:…™}WJ®·RYAõW7@é°éC*®Žuä}ÁCDµU¢Ë™åU'8çNæxo*õ_>¹O¯Wááf“]#Ô`–ÅÏâÍ%š„§ùó'v'Ä¦œ_É¼ÌÙœKÇ'_ë»•º$ô­Jüö×
›CÇ¸»K›i­iá—j¨h$õ.•í”Ò\Ž ^ ´[‡+nrŸ¸—jëê×«äÇQi½"¥÷ïcYëó:¡ß*aãVY±4r%|QG•º ˆ²ÒRF¬¡¬?¤=­mÞ}(zWG¡º¹û—Kv˜M? ÈôÁ$¼î›=t°UM_'>åˆgì5àT¨ŒOº@²îtJoæ<,–"¢–Ý¯ÏÐ%ÊXºÅ†¢óüåñ:ÒÚ¹Óç1_t"ø±‚¡ÙàÑ«Á†^‘­¿ðU®Œ=!     0707010001f06a000081a40000000000000000000000016a102a8a000001a8000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.array.eva.conf.gz    ‹     í”?OÃ0Å÷|Š“º–F¬@‘`dªXR¯Îµhlãs
ùöœ¦Tª˜BBç%ñ¿çw¿gy2³±%9»)ÄÎÐ*7®»qÙÏ¹ØI¨mE/Åu.zžÊ.Šdþ•ºwª«/«ÚÊøÅˆ-1bã<®vt<èwLi&Ð[[ªÎg*Zc»‹_Þ¥AŒ@lSè'Ù“©×µ[Ø¥P›\œ(°‘‹-ÁýÓ4hqCèƒL“ˆZ¦™,¿–Â…Lé|,™¹-¥Ÿ¾uPû[X‹Ðþ.+dœµdb†4ç+tèÌšÎ¸à¿ãä‘9ýCPCi‡k”>;·ÚNåŽù@ÌT2 ÜxŒÛ[±µ¦@Öä¥xòx0™@q–UûhZŽ°"Qƒ(£Ë%w©Y.ÁbCìQTÐVýº-îéD®ß0Ø“-KÎïèx½\)§•ù‘¸Ô 5@PÔ 5@PÔ 5@P)@©,¤#ÿa€CigfXÇÙ9ç¢òQ>ÊGù(å£|”òQ>ÊGùŒÇçÝ‘Ø  0707010001f098000081a40000000000000000000000016a102a8a000000ea000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.patches.conf.gz  ‹     ­Q»nÃ0Üý<;ÖÔÁE;ö2¢PçZ¨k9¤„6K¾½rN‹®âÀGïŽu]²ªš
ÖB7ÛÈ´]Yue³«^¯fßªEêŽ_A\w¿¨tiD›‚µÄ¢f»±^{±£.‡HpH^àþ#½Mc¼<ÓÓ˜‡Üo¤ ìrYÒì{ÏôLï˜ žÏN3“rÞØ^ýå§‹ýD„P„â ê¸±}˜ºÝœt8µ£)8Ðe¾¡-ðëÁI¥ÕÁ
Z¸½%·ò­=NÑ~o²ŽÇuöô×Iõ¹´¾\$    0707010001f0d2000081a40000000000000000000000016a102a8900001108000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.disk.gce.conf.gz  ‹     í]ÿo7–ÿ=b‘'imo‹Ü&¸Ü¶Å»Ý.éî‹…†š¡$®g†Ó!Ç²Šûãï}!gHÙ²äFNêÍm`k†äû¼o$ß£¾øâ”?Ï¾'üAr…¶Wá¶«\}0¹ÓŽî´¼{öœì­®uóÏg_Ó¤_á´Ÿ=Ã±_©íÆ´Åå0x4ÿÅÔ
NOøƒ|²¹iä¢T}oïÛû­ú¹Ó­*n=hZs­­6µ®Wôð{YZzR¨¥ìJ7ü¯<f¡ëµjµ“uîû)•\Š×b­d¡ÌYµüÜ6*×KÃ;+UCÛœ8=ØÞx§œpk–æË³¯€“ªkÞt£¬;Ÿ.ö1µ–•²Ÿ5G…ÈM}­Úˆ~©­ÛËjÛÀ¶g_Ó/Àm{ŸOñÑù^N›yÓ*U5n.¦uŸ”é=oÀõþÉ©Ø.ëBÒ){9ŒVüßÐÏ-\Æ@WuÍKa#²Ì,›ˆ·ªßýíb£ËRx¦Ã¨¬FXÕ^K†§n-dd&Ã›¹©*ÌÓ:˜’0K!û‡ø€þGgâ©“v²])‘ÓUSªJÕŽº³¢0 0Ž®i )È“¶¢”Ö‰ÊJ¼Pöæål˜ND(Ë–È’,ž2	å&âòjs™í
U†\%dî4ÌSÁèët’ªL×‚,/¤ÕvFr¼CDšqØ#ÍMŸ=9~,Sü­Zêì€^>oÀŸpM,F´&†YÏ{ytšVßVP z)è\MKu­JœôW0
$ŽðND¥‰šú,ÃxÂ»„5·zã0ûá5•qjÞn>Æ=`Ÿ³ríŸzÎè žàooS“ÐÂ¦˜œBý¶`{œ‡6àvHÑf±`Ž\„eN, c1ïô+ñ‚€ŒÐU7yÙY}­&À¯X‘V‚1u¹}I¸ýaŸuÿÀ>´ÛØÞKðrÐ+}NÁ‹ö”…XlÙõVH|w$û1I°i­ºí#Áž8vg‹ÒäWÁ<±$N»†Ù-ÿØH‹ïG¤J˜šÓ•ôÂü—Ø4a·rœcGKipi80dLšÍ"~‘[˜(>;K Øë;^3¢–úÏ}^s qHÝPFZÝ K£^34ùhNóàH‘%¬yÕëdó©÷“,´,9X>@…¸¿{¯7CsT¶‘õ~=ã£Kü¥¥7¹´²aÏÄ{!â¥õEÁ×R—8ÙHQ¸P°`¬ºZ\æUq™­òÒtFÒMçÁ)½D´2 ¸òtï"&À0eYSLÁ~Ô…l‹,cÈ’ xôtð`]V#èƒ¾?È5MIÎhŸ~‰¿¯ÁÎ‚‚ÏÐMD ±ÒNØ¼ë_Ht2U²‚‡ã…ß%®r`i„¿àk“>„-î•ÈÁ«;ýÏ{A…ÕŠùø‡\õÉ³DÈ•Ä¥«Ø¬u+\p¦ÌÚV-U«j&)Yð¤¼öö4a, Qm›ö_{ãG€í³fþQ!ó$V9Vua“µ»>¡19…YMà»¯ÄÅÙj/Ó) ™ÛZ6vmFáØÌ‘Y`.ŽPÙœæ	BC‡¾ÅÊ%ÙÁB•
žÇ$=¹teJ†j-ÑB‚Iô’£R2þ} +±._G´rXÀÙIdIïj#JS¯€x¯‘ü
¦ˆ(¢ŽÈæø×á8ÂµÉð;¼#ddl›ãßûä’0¶yc¬›÷1îõöAý9h¥5ÙÊ1<bI‹·„"œ.ó).FL}™õ3Ék¶¶5­……¶kx›Ú?D0÷ s¼œ6¢%Üå¯…2X™í,3#‘y0„,/ÆMÛ+eÌÛìC õ4†«iF<O£™¦9…bšæ!øuõè+I-#ÖþZ­ŒHj«Æèç¡ˆ.,ŠÔc‡?12@s~>ÉG?‘‡"8Æ>£’üìàó XÇÐçdzù¡±Çãð‘Ïã)å†>):÷cŠVµ{z)ßŸTÙXÜz>w5´"«Æ€™²ß¢gåø, i'¼[§YQãk ¦žæ¸RÇe ·–×´/4ª•œ"ýˆôº¥£õš¶F; µÅ®S[<Æ]i'`~ÏQª? üwJ^üè¹"o#èÏdôª=§Äƒ$­¢kHdìºs…ÙÔqÖØÎâ;®³(6²ÜÈ-&v ° ¡,«/³lÆy¬ºß~÷ý›Ÿþòžw7k“è†>Pã,ox^Ebm`¼÷„{×ßxÛcé(ØSd“üÉJ8MÂêº–¶×AW ?f6•ø\^‡áe”%ƒI1%J÷ð“ž…7aRCò‹æçP“4Ï-_Ëz¥ŠI2½´gUsÇ=?øƒÛ Ûp‚Mä‹¨:—Í˜"|e{—feáï¬cij*å!eÌIAÙtÐbÂ …váuB*iÃÙY<hÍQ*@â”¥-{a˜1(p¡šÒlUECªROX€¨¿CâSZŽ´r “Xk0¸©·l¶kõjEG…(`¤t2"4»ûŠÁëúÊ¢Ùµé0O	oÜ““Ò(ÅŽ>CSêê=ÉCB²«žœ žòI¹´WÙmÛ4^‚GÀóB<¯{Žç¡€žXžZ‚vÐ&‘JHÎkR—Œ}wà~4fÁJCã^t®/À•Sš¿ÍSðs‡§ÃàªJË¹:·¨F~‰WZµÂêXà¸-&… ;ÄsJäâ†À7ËÐÎ¿5Žé–3ú3ñ¿¡oøvø«xàìñµ$e9fƒìœ©¤Ó9Å¢Á¬bÔã'8?YµìJ”ƒ;©”ú
œlW5b©Ke·Ö©Ê’Ô¨‰¥¤*=¿^@ªö%Ë¯æ‹òJ›ùFéÕúßl·ðªLÕ…s®Ä¼Y¡KûŽùŸwª.JÁ€&õß”WüÃ[˜b	rr¶–¸;“e\(·Á„Œó3Âðüììl&þ¬ÚZ•qFa˜,=§ù-Œ¯ÎîA6oºùÏqr„õ^XwsÑT›Sv:¦7áÊ‹sŠ¹LÈ{RÁ0Á¤0¬«Ö€¿ù»tÜ±xm¤ iaM‰Û"×(HÉª>&K&Éð­¬”SíL|?Š	šë¿èº»9µ ãeÓs+p1_êJ»t@E`¢1V“tîŒ‡R8ÙU€•Î[cˆ±SÎÑ3DÉ|¼Ó]YöÕÙïþžõ¿)‚ä%Œ3O¹ÅÅîûncøý  ½P“ôSTÀ®ÁjÛQNaÚØH…ÁzÑ´sTø} ~—>>?»øR0ÿ)Ç,NVh¦Ó¡·Õ®“œ¦jÛùÂ[‹uè"Rz
Ñ¥N–ÉÔÊPEŸêBÞóN!¦ç{µÖ¸¾e–Ð6EŽXž7JFÆ‘ ·B	<¿¸_úF¹;Úö¾AÎ÷°;8,4î+ãæÉE_ˆì‰?Â¿;•>Ã+Òú
€Ìy½RâRœMÎ'øÁÙôb€ÁÄöƒY©jN¶tDô¡–äVrñw¤æö=° L»/tìCÀù%+úÛÎMß.§?ò{W°¾õÅ2¿Êåý	LlK¯1Ð`4ÒbŠjÐòmVò3ú9 ÆTsÜñhM9ÊÈ)¼Í±,åJ¼8Cu=‰ö(-~ä7$YŽ }t 	úÈ¸z’>H¢*;¦S õ—^W1}éUËc
6¬íºŠ¥1"å‘Ä³?Ÿ~·i EØ/{ûöÇˆÒïÛƒÈ‚ãƒƒfÁ×j_ éçÀÕéII™í¼¨üšÅý¶­vÁu·ç±”GR²~~@ÆíF6ŒŽí$"þŽªÔ®ú*ÔZ$«µ¨Å+ñåË4bõxAˆW9®ÞÔñÇès®~`P‚¸J®Ç¨ä7•üêãc‡&DièÙ¯Ö}½ÌŠfÝÒðvÏãx&Vä:qp¡v ÆËc>IyŒßiß›üòâ˜±&æãÖÄ…ÝI¡c!Ì§,„9Å£RAÇê—Yýr$x‡@Ç’—OWòr„G§}Žu.Ÿ¦Îå ŠÇW¹ŒÅ-·¸åèŽ‰bÆŠ–OXÑr4ˆÇ1cËG,c9»ƒ1ÌX»òÉjWŽAð!ÌS…0û-æXSž%ã\é&ÕT d€%U£TkíLü]¶ÌŒÿé[áÊxiôéÚ.§¼.}¡›*1#0=¬ÂCÜóû)’
jl¶vØ”N†ËS{É!&¹Çóé<Ž	Ø¬5ˆQ#s5µ
s­04\ˆiOšù¨ŒÌsšÜvužÄIñ¼ŸÏóÞ¼ÈÐ	_¬EÇIÔ7­.^¿ø†u_Of³ÙK¬ÁkAñ>K0^&Z`®W×L¬CÝ5Y&’Ûî:¦î/hâSDè–¯&ej©ðôBqO•Oç~rbsö˜‰Õwì½åÉºž} #½uù¦~-œÆ»ø½«KT¦µùUÈ8ôƒ\õ”q,ÛÎ–â2MŠŽË;Ð+õå>ts|RE'Í¤Oð§*DÈðek~QõðV_‡²¶=%~¯ïËäî`”MÊše/¾t 5 Îv¦;Í¸¾@Õ”…ÞÐay*ü-©L[ÉžK¾éR^ŸMÒÉÇ#ÃWÈ$HvÝË–óÛ·ý}æ I­“™Û|­Š®TY’Y°eD(Ü~_@m÷p?!í%` =ãç~bóB• …¿Ù=ŸB˜NŸ„Œ–¾%¸®w>úòÊ×SD1ÿÔé¼+eœTÑ'°ú¼½Ðj¹¤/	„ÝîÜqjŸKxÁ‚ÄcRj‰2}¾¬É_õ
¼gl3f´\±å*:AîøN QéZW]%ˆm‰¼Ö%HÓ„•Õ†æ¹¢ÞËKj†…š¬?/gØkšTz&^Ô&Iˆ‡q¼ŒAgDïƒž25‹±˜ì±ª?4X¥f¾lˆ,t‰_°eÛ^`ò=˜¼Çú»€ç­ªš	—Õ\¯Ä¢Ó%Þ9/Þ½ùëÎ­’ýH¨H™Ë“Ê;kt8­ ‹Ô¹2úfI%–6UÌR[íFrÜ‚¯¥äÆ‡¾Ö`Ü{@ÕŠ³éåo‚Yrµÿj	ò:!
±þên©*
ÁSØã˜,bËs%ñ–wÙ6ùîœÐ„Å¦[`˜Æ‘øT=q„y@Ôm¬(Ã¸eºs+EDÛ:Žw·RÖzmê©*’ïKÏúúe/îqÎ¢éêè«°,n‰™NØŽ.|(·8²%¨ÏfbãW°—â[XÃ¸Ý)¦Ê`Ãœ/ýðâ¡Ý*k‹öhØ÷q•ú˜«TâñSX¡¦ÂpÏê—£Ð<²Ð˜æ(™Ù')ñåÇÊÌ]’rkå¸+3‘(Ü'2d¢?ÓôÉXP¬Õ«:åhyÜ·cN1sÙ¯Ý»Míäê3VÁ»N•ÛQÍ …È*Ð¥v¡]+q£þÑE¸žS5Cy=aç{µ‚7æoFÔü·ýõtu½,»áööV_'Å…4¹¤Øô®bùk£¬DßúËghy¾c3üáÖyîwü®x–…b÷Wþë§Ò#ß+ï©dRä!O—wk0¬ù=o…Ó÷«ûS>{ºÇ'ñÙÖ­c’ëãN;Ò“²ûÎ;¢7Ç°àQÃ‚ˆÓO!¢¼K0Èjü?gh{  0707010001f0df000081a40000000000000000000000016a102a8900000f63000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.vxdg.conf.gz ‹     í]_o7Ï§ ‰q’jûÚ7	ÎwmqÁ5Íá’öŠÂKíRáÝå–äJVqþf†ä.)ÿ‘ÔÈvÝ®ÑÉîrHÎüf8äÌÐÏŸòçÙsvÀ$WHs9bvÝ¶¼*æŸJî°£;,ïžý„“}®e]ˆ«ŸŸ}E“~MÓ~ö)Ö+¥‹³~5¯¼ð™drÕði)ºž>êûaZüÒJ-Šk/­–ÒHUËzN/¿å¥¡7…˜ñ¶´ý ¿W5½õBhiyû~JÁgì[^„AóÚ½7ÈåLæðÍ\ÔÐ6'®@&‡/>.ñƒ©³ð÷¥*[ø×\«¶¿¢w¯ÙL©[¹©.-DÕØ>UÚ>*g;îÁÚîÍAxËXÎëBÜ
sÖ–ý¯ï'WõRèhS¥ «:ÌÛ3Â2«X–Y e#ö¾õ‡ÿÁV²,™g:ŒÊH`„zÉ-LÞÚã‘Ú†/sUU06˜§±0%9ï^âúe8aÈH[®çÂFädÕ”¢µ¥î+` ÆÑ64IÃJn,«T!ØKa®Ž&ýt"BY6C–dðÔ‘vÄÎ.WgÙ&¨2ä*›
Æs+ažF_§“z\P­Î›r#Í„ð»A°ìäpšÏž„ïË:|-f²`Ò}|ÞZ:à	M6$ˆa‰ç#¿:–
Æ·f
€¾E
2ãR,E‰ƒþ
'‚#|Q©QPŸYOø–díZÝi¬€§•²âB¯BÆÀþÌvÊê5¾õœ'¡ƒz
h‚7©IÐ0…ñ
&'P¿ØëEä¶MÑf9`~X	óœX Æ}°ñ—ì%	2’®¸ÊËÖÈ¥¿æ`E43¢êr}DrûëmÖþû ×±½ç°ÊA¯ôœ^ì)6]»%¨³Bì›+ 	ÜI‚MÓâú	öÄºålZªü2˜'‡ÄqÛÐBFvË?`+nðûˆT	S³²âìÀŽA·lŽsìh˜†%†Œ‚I;³ˆÏ#rSEÃg&‰(n];{«fD-]?o[5{[×Ë¥y\=»Õ—/Ô•.ièW!:Ñ´šÅÚÈœ—Þ¹67ùÚÎÊ-ÍÝË&xÑ(c/º)þŠÁyçã‚V˜\Ë†”E\‰¼uä3+R%ÒŒ³|ŒÊ¡ê³¬›IÆÜ#°!Z+m`À@A·Hv!ÂKÝ-’ÙGœdRQþVQÆ$Vffo1™½EÔÅtØDX)cÞfŸ"POc?¹ªfça4S5‡PLÕì#¿¶ÖÊ{RËˆµ¿U+#»
U‹ÁûÙW¢S1SZÜ·ûKfiÎÏ§IòÀÞOÈ¾|ŸûQÉOu~6ä³—X×ç`zù©¾—Çâ<ŸûSÊOt}RéÜ-S°¨íÓ;Ò¾ùýS”a­Àç¶†VdÕœÀTÙ…²ÃÙ-¾‚4#¶ZÈ|‡3«ñ3@€ªÇ9/Ë.”ÙÕ ½_â!-~ÐÍ]l‚ú)èuMG½x Ûšh­±ëFÕF1BF ðâ™ß&ðB”Ê,˜þà±‹óÀÈ.f1LÎkÐs:OŽùÛ† c­-ÔªŽ£˜o­Áolk6¼\ñ5,@(ËêÏy–Mû ,0€}ýÍ·ç?|÷qDp\-Tƒ%ô	„Â'ñxÃûB	Š0°…‚ñÞád4ìÍõÆÛ3ÁóiL‘Iâù•($pšÀj[ühsÐhäªâ,S{…GÚoÂð2ŠÚ`Ð!¦Dá?éIø&ÕcL4?‹š$ÝÜò¯ç¢%ÓK{µë¸ã‡{p]À.„áo‹_ˆ:çÍ²reûF	ñïNÇÒ€j*Å´²W”	?G‹	ƒfÒ†ÏIRI‚tDÖÀw ™ ¥ð‡ÐÖn†ƒ¢)ÕZpP4¤Ê%ðÄˆúÛŸ
¤eI+ ÄZƒÁL½f³­–ó9`‚tÀHÉ0Â¤[î{QôË±¬/BÈ,T‹¡aÁ¼qO\t’RÈ—ž¡)SŒð$·@5hRyùäP€zê²v¸¹×È¬ëÜ¤þ¼žìE­ÜÊñbÄÐc³C#h  mZ"R„Ð sY¶#©uÇýhÌè‚•ŠÆ=mm— †;§4ŸÈM	,À/-¨»€¥ª4.f{j´î#1àŠs¤Ç€sm1¿ÙÁ^ð%Ô5¾áXúvþë¨ùÿƒÏèLØBß^°¶Ãß0;ÎÜ­ø(µ$…&fo­ª¸Åð3¬×Á¬¢×ã'8a?1kKÄÁTJy	‹l[5l&KaÖÆŠÊjÄÇT@R•Ž_¯€ªÛræÓòRª‹•óÅì´pULÔ…é	çZ ¢.š9.i?á˜¾Q¥pS
4Ñ¨¿SžËÛ÷0Åp²[KÜ°yÙÂ §Â®„¨ÙÉ1ÉðäøøxÂþ%t-Ê/Ýdé=%3¤Ò`|y|‡dó¦½ø¥U–b½S¬	'°!Í)[
³špçƒÁkÚÏ‚Î«œò|"ŠVèÖ»Œ\7?K÷Á]R¥×Frš¦F•x,²D8 %#:Ÿ,!˜$gi^	+ô„}ÛŠšëïdÝ^±œZ€qÈ²ñ	¸Š¸¸™/e%mº ¤dÖ(#	ãqÉl«)8À‚JæZð/6Ò;†ž/wRÁ¦+Ë¾<þìoð®ü›²£Áé@^Â8S÷Ôµ8ÝüÞ®”û>(@jB?õ±EÌ¬¶tà¦Í©0XI'G…?êNiàñÉñéÌñÂÞ¦Ùå šñTµ¸ÚJÛR!èHmZPxm³}€GJoÁ»”É6™ú@¼ U\CžâGwR
j-qëXBÇ1žWÎ•P0Æ‘ ÐµBžœÞ¾w;ÛÞsä|'ö`AS‰çÊx8B¸èÒÔ‘½öøs#ó´ÿ„Ÿ?¨Áœ×sÁÎØñèdtŠŽÇ§½HQ˜øàvaV¢º [:Ht_Kbä¯éžãRs³‹ž÷²(½f/eœCÀùÈ)úûÖŽßÏÆïÜw—°¿õã	ŠÛåºó‰3åÕ,Þc Áh¸1ÀÊ å´’ÓÏt(U]à‰‡Vå€‘C¬6çlVò9{yŒêzr„€g”ùI‡#>. ‰ô#!ãî‰{'‰²¾¨Ü¾ÊÑçÖRõ–U´·ÃtbDcDÊ‘àÙÅ'Âºi E8/{ÿþ]DÈÒƒ÷ý» YX8Ãà YXk¥OØ÷s 5©¦‘’02ÓNÝ¦ò+œE8¶•vÄ\ÈIŒòµ€õ“-7+Þ4XÀ4,l8ðrÖsÕ»T¡î+ÙÄR‹Z¼f_l±Lƒ¬îÏ	ñ*çªI"u|=wµ*{:%(·­NÉrðJ~^É_PïÛ5!J}Ï~·Þ:fŸÓ¬5o3~û3Y°"ËØÅÁÚg(y”òÒ~kòË>Å1CMÌÃÖÄì$»’B‡B˜Ç,„Ù]Š;¥‚Õ/Yý²£ð¶%€%/Wò²“wNûê\§Îe«w¯rŠ[¶¸eÑíâÅ-XÑ²³wsb†2–,cÙMv[}˜¡våÑjWv‘à.ÌSa ö{Ì±¦<9JÆ¹”Mª© ^4”TR­5ö_®3þÝµÂþ$æêcu›SÞ+}¡›“0#0Va†!žùý¡‚«µéO'¹åá2¯9ÄämÐ¹ðò|z¡û1«…5<c#0×
ÓIÃÕC˜ö$  •p€áy.@“u[×i$Ž³Ý|^t–à¼'C¾«Ïv)¤A½Ò²xóò%ê¾M&“#¬Ákªð‰‹%¨
/·*0×«mFÆâzÝ6Y„#Z­£î®ÌòQDèÖ]•å¨¥àé@qG•Oç~r°9¾ÏÄê›"{CyDr£›g`¤³.¯ê7ÌJ¼ÄÊ/5`‰ÊQSž_†ŒÃAßãª£ˆcÙFW˜±³4):.ïÀU©+÷¡›L“z,Š|4£.ÁŸªá3­~uÿUW‡²¶=%÷]×&–É]vˆMÊše/¾t 5 ÎvfwEpLÔ”…ÞP°<¿&µái+ÞqÉwÂ} åÍñ(|<2ü„LwK÷L»üöuw¿&h`R«åÈ\˜|!Š¶Y’Y8;ÀËˆP¸µ(€Úfp?!íÐÓž¸÷~b…(9l 
Óh*|rA`:]2fXú–°t}ðÞŸW¾à˜"Šù§VæmÉã¤Š.ÕçíM³]ZÛƒÝlý¼F„ê€äþI.¨ÅNÉôù²6$JÖ+tòšo±YŽ1ƒåŠ-WÑ:ä†;jY%kYµ#¶%xé­K@Ž»ùØ¸kÀ¯š—´­Ð<Fj†…šNŽ&ØkšTzÌ^Ö*Iˆ‡qÅBw½Kô”©YÅd÷Uø©Îr(5óeCd¡K¼²víl{É÷`:ð^M4è‚<¯UÕŒ\YÍrÎ¦­,ñTöáü{iˆ’w#¡"eWžTÞX£ãÒ
°HÝU¦S¿ý,‰ ÇÒ& ŠYjóMOÎµpwºÆÛ®ÙŽÅö¨Zs…âÎôÇøa–\í¯:¦U'x!®^öL¢"<{|¡€#‹²¿¶r%þ–_²Mr—{hâ`ÓNÑÍ ãèh]²'ŽÐ&ˆÚuCš‚¥ÇYò˜‚ÑºÎ_à·”µ^«zìAEø>ó, ë gÜãœEÕÖÑU¼X7ÃL'lG>”kÙÔ'¶ò;Ø3ö5ìaìæSe0aÎg~xñÐ®•µEç‹[´ìû°K½Ï]*ñø)ìPS0Ü±;Å­À š{jvÂÌmH‰/ÿØ37!åÚÎq3î‚™è?iúdcä¼N}9Úwí§sÝºvç1µåó?±
Þ”pŠ0KT3h!²
tIO¥ÕŠàßÁ» äƒ \ªf(¯§!lüž‡°»ßVÓ
¨ùß>ÓÑ•õ¬¤ú|§PZ.“b‰©Àš€\RlzS±üRÉ+Ñ×þòÚžoØÁ ÞÍß÷æNü©x–…b÷×þ×!¤!ß«;SÉÐ¥ÈCž®;­A·æswNs¼]ÝŸrìé†OâØÖµ0I$ëÝ¢i¤ì®xGôåàÜ«[qú)x”7ƒ¬ÆÿU·_Hˆp   0707010001f028000081a40000000000000000000000016a102a8a0000020e000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.array.dorado.conf.gz  ‹     í˜±nÛ0†w=Å^]ÉîÖ¤ÊX K¦lE ŸÉSLD"’Rì·IY¶,ªÖAp\DèŽ?ï¾" .sŽb3Ž$‡Îáa	á`	¤q(Í_ÉÍ[Ý¼ìŠ?¹Ù…SZÒþ±¸ÍM×Ç¶‹"ÕÿL‡WãäÍ¹´*¾ÿ6ãH˜¼0·-6ú…­§qôÒ+Gç\Ÿ’ìÛp.íÞhš´bÕäÆ ·$T£ÜÁirJäÞ¢‚1ãaG£íq/RƒÐ»¶Œ9·i^Ã.ëoª*'•Q\ûA”ÂtUŒWÃº\1,†Å°ÖW€¥±£ëÒ:EfÅ•úÓ@˜Ð•ð»mXg%I.¡Á¶Ý¢x†`rž'”Ñ“iø¾iÔ>ÓÎª5àêóü?<-zŸ^}Á/zj-áê=¥Gkž@é%ÐÞÆ/Ý“ô€ðÓbØÝÅ²r¤ENÅK¢$…2«ŽsèúxPl)ªe+6ðºÍ&»á-FÔrÌÛápiÐ¸`*/.‰¶d»N×0ÊUq·ñÐY³l È²l È²l ÈÎd`P™>|Ö_ëõ÷Õ‡ýFä.–ËÞaúU~ëcU.#]ûd„Çy$˜vffÌìŠÌâ©ã®ïõO×©µw‡k¦sŠÖàŒ	Ì‡ù0æÃ|˜óa>Ì‡ù0æ3Ÿ7?×šÄP)    0707010001f0c2000081a40000000000000000000000016a102a89000016e8000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.oci.conf.gz ‹     í]ÿÛ6²ÿ½àÏvv·\ÏÝ,nß5}¯¸ËåpI¯?…MK´Í·’¨ŠÔz]ô3CR"å¯›x“îU²ÉêEÍgf83œ=yrÊ?_<a'üƒÃ%ª0\¢0³.S‰ü˜áN;»ÓÒî‹›—}RÉ"w?}ñ5½ô+|í/¾À¸ë•ªÒq;Då9/R87<á¤•NTÉg™hö¾ªž¨ÄÏµ¬D;‹oy¦éLY©[©¥*d±GgR1çufÚ‰ÿCtBKQIÃ‹Ä=(|Î®ØRðÔÏp¯RQÙóº‰œË®Y –Lð2 Ý­¨‚ñõ2wD3x¶NàÄû¥ðÔbF1q'’Ú˜ 3p&UÉ¨Z†cª`U]Œ`ˆ¯ý]¯ØUšFå	7/f²xÿòL|¤—»*ær¡'¢¸••*rQ˜GÖ?}z¨VKi„.y"˜%¯¸)Ë¤6LÍÙtzyË««W—–º¬à¹¸zq	Ôg%7Ë«étˆû5<Bd@^VBã+ÃÔ2¾bR3^ÂÿE:`ZÔ@Dös­àÙ,á›‰` ^©ø ž¾ó¹À7Ùˆ óW0Rg¢ÁX[¦ÌþÍ+‰(Â¤*Áê²UÂµHn0p$@³ƒñRax²|t¼æ{(n›)À…¿ý«.É0ãÉÍ‚À±wÂ ¾˜Nç8gð\á…Òˆ6#èã™*Z¦‚oVã©6¼2#s¡j3e¤‚€ÇÇÉÐðÅxZ(¸ÀÔzŒ'v# M„Ù‰õ­L„îË	ËÀ@‚’TŽ/Tå/"ðñWL§7Ÿ5¼/ÁêG@Hè@Ë[z©ê,eK~é•®„K•CÜÞ¹O¸Aø«u©ä#\LNºòw°ÕI%KƒêVg^­ý*Ÿúe¾AbÄ®«…fyPÍ\@Bbëþ)ÁÒ’\ýw/úýbÿ‹ý-ÏjA¥[Ñ¼>lm÷+:¬ƒáv¬âíö.ÕÇ.Ñx¨Qúwo¼ƒŒÉ0×îu!ïØ¯lnZénACPÛêµ6"÷ÂD75ÏX&È8AáH0à »€@Y¯L™Éè‰V.O¢ûnÎ
ePe‚ƒdý¹7*Ey«½ø«îs¥v‘]æ|ñyiîOüFÖ/ç…]XYgÙ€lEpD;h¬¤Y¡í¥¯ØW_Î/ø—izñç¯ÎPqÕ³º0õ8C©2{é?ÁçLJ•Édýèø_õRáø ‰x¶âk}.fÉ8‚ÃÞî¯KX˜ò†ÁjCîÁ nÂÇù[¤3žUðk:‹â3G‚ç-ú!€,B’ì¼€…‘'FÞŠ>bÔ±LL%ÑÊ¢xRXa`T€ð~xdzf ðØ{…šGRš'­ñˆf‰5_,„-$¨,éõw¡W&…î—¨v‰ÒÎ‹ÎÓñÔÉ"ªÊá(^vµ¨Ñ‹W¬6Ø:s\äªxªýì­AØ¬h ™DûW¼ÈgªÀîÃs°ÃÀðÝÀ-ÈÛ;ÊJÞSÁM	È1ZžÜâÈÌ$8Z“õÂŒËv,úŠF+„Ù1Úî‘ÚPóŒµ’ nèŠ˜ŸœÙ©Z>¥I¿
ÏîâÓGiI]òÚ(â3t>®j…'£È(ÆÁWYø¿xa‡e¢ådÇ¹rb¦}¬]’|§«Ñˆ~¹µnîe…–Yz‡¡Izv4¾s¦tÂ3ðwìœœç¬·.YvAŽLØ«¦#TP*TMdF8¥êf:µ*&ÀV•±*Íxêî•Võ´L kE+`ü	¬§–^¸hÔ¶ ¥•xB9µGxíqÆG’!øÛcÐ'#ºqŠFgp0)ä@Òª®À‚ÂAlüº%¿È2äÂ4EßÝºqðza¦có"ðq±ÆÚ£ËèF)ÿp×˜O°$ÏURÀxø~-ÉÛgI¼0&Žë­ÀB]E{~¾èÒÌt%´s"y#ò§_Õ¤¬„ÈK3á3U=¾Ssæ¡Ìvœ-˜íÍsŽÚ ]«}¬ßÀ Ù}Fß»ÿÕ²•#:ÌJKämQà`Ä‰83ä¥ß9DÞ€WBÎáÍI<AÃ{§ra‡o`!B;@æe&P_ÐãÀJQVÔe	·ZvÎ¸F{$ì™ÐwÏGíëDÜØlbT­IDÚ£ËTÓ&D†¶)äàM£—Õ°‘ð×Ò1rg àéo÷YµJß&“J)3Áxz¿†´ÜˆZÂ3 UTR[5C®à­^âÉ:ŸÝðö€˜"‹W º0,J!oÄÞ†½Gì5¼šŸ‘Ñë`w¢€Ççà_:lÂ€™ßîpSkæ5¯TÎ,[XŽˆöÛèèOÁ‰]lQÊ´·(Ž²(€Rûœmîè¶õŸ¾wýÿ^×ÖNùŠî¢œY“âl¡Ô"/JNŽ1H€›Ài)Ù/*ŸI`ÈžØXæià£•îŒö.óÍâ—nYÜ·Íjë2ÍLÝ/ÙL°#/à^Ú¹¡LXºµÜ#@2Ñgù³ù¥ˆ;#Š´QC=½álY ê‹†Ê•“jõ)ÐhHû{¶»LµÆ³Žò„á¥ºÙbMŒ–WðríŠ¬:œ=n‡
§º\G'qùCÊ[ZWd‚FøýÀ%éVvéO_¡$ã•7Z6%Z“lÎe.¡E( ;`o² „x‚;´½><F¾Á e ,Eh±9h>·‡Ê5Ñ•v¾	Ž†Ê‡ÂÒ•XÀS«õ$òô&ÛF´œ>-€:ÆÛé7q;´ÄPF¸‹‘ŒþOƒp ¡C‹m)“e–F˜©…ÜE´—ÆÛ„ÛBˆÖF,ÀofâVdÍMaNÁj’L.ƒÂîïÆ˜ãÊ‰ÿýá¡3î‡à ïd›¼[|èòÈÓ´1b«|ê p0HÑ…œß„†¨®ñìíX„µ¹ºµ~ãÐæ¥…:?´`úÀc¯Ž;pŠ»’£:ÙÌzz}g*Þxmd›–\ë-HºÒ€eò|t•ÁÌ¦ü7†Yr^–ðÊÖ½ð tÃ[ëoÛ!GN2G0ìì|l‚Y4,Ù—½üóèOç£‹—ã¯Î¾:£»`ÇX•‚õ–ð§µ„yB$€D†/Ù32x£t¸$«58B¯Ð9Ø ¸š?'Ü¾Äè†ãè‡¡LE{ÜÕ:
CŽxœ¥Rßh O–²ÙÚºØ^gŒ€«¥CR†ÜflÔ®_‡Ê€3^/Iï€Å>¬Ë‘€ùlÅug·M‚ÁÈS ôç”Žs p-Øû‰ñ©™À‰!¡à¥è»ŠŽÃÍ¼(.…zA±3fÚJÀtK±@7Ý-m‡8¤e­Ñ’Ü'·ôúÈÌRgm«É¸ŽvÑ?¼ŒÄ¥šFÁøÝÏÝ•€joø˜2’-l´7G5*]xt÷òÑzÃÝ¡—»Rå£Åçüâì#Ô ÂWÙ‘1}¨â¨PÅu¶ .ç°^jQ§j¤#bÃ¿‡âàœV}ôáˆ#$T°c„äÅC@_Ê–ßGß¢wòº–>åqüês0gfç®ÝÑY;v¤£Óv6¶Ùìý~÷ª³Áæv­ì5ŸÐ¾žÚÉ(F÷|²Á'8>ð	îh>1ú[»ÈÖd*:¢ßÍù )!NÀ;ˆl;Á+&”%Û§ž¤Î†…_q©ñJeÕ­Wµ	ÊÌ•²Ú_TI^9y
?,…H“_¦aj1=ÇuiÓ.O1èŠˆ«±:‰¦,˜ù¡Ñ¿¡Èi·wm”ÊFÜµ×‡¡ >)ƒ'ôÆBã&ûD5°Š]mÐ"ƒÏÑÇEÌ| #NµÖñxÚ¼ÉÔÌ#öºªT¥mÝIU—vÊDw ³¿l,¾‰¥Ê…2H9íÄ°î#soq;"­{$O+”!m§¨ã~¸ª²Çó4’©ÊS¦*ïƒ_]ôkå‰e@Ú•Ê`ˆcA­DoýÜQ|`ó'DæhöÆÏÇ!ybëÇ#r_{ÛçaDòcŸ>÷‚µ7}N&—kû8<î_où<œP~¤é£³Sœð'Ê“8m~ÒCà÷¿"+m0~pi5&(—oãq¨ñŒRlj ,ð2à U±€¼©G,…*³&«/ †MÂ&Â¸ñÃV‡K±¦Tn×®È]ªBKL¤ Æj±Ú|…¥FTþÃ2K?y"Úµ'dËvca&˜\ ç”ÕålÕ%±Œ^Ö&U«",‚þÎhf;\R\™:ãÀÀÈ,Û©/8F£©ë”,Ø7¯¿½þþïïm˜˜`Â]	÷LÈÏqÎ×Ÿozt,Ìwy`Ûqv×§{4Uèk¯ŠtT”›‹T¥‰YM]=jfn/Gåì’bï~z.¯G£»½ÝKü•ðRmfÞ7Í}€dÉ‹…HÑëÅO…}pC{``Tw‡%£‰"áeŸ¿}a{§|âÿ­ŒÅÙ•¾v€i'OÜ™N"p‚3Å„´¦Ô€Šî±M'Úaq÷$X)Í¢§”ßéŠ'±5N™©uÛJÓZ»éìzÂó±OhUõt*m
×uCg›J.˜ÄD¬JJ&Ø˜Øu«j¡h—cYÜhd!×­v†I„MÓâ¨©‡«#‡c¨J]Š¡ò`á?íøÑæãâ”SJ 3\ß€i¤×E¢c{	Û7cIéÓBÙ•ãé€	¬³Ÿ?t	Éö¼*›¿æ²×4®ÝžúÁœÑËÍ{V›¦ËzNqJŠK‡Ó@LwL1É´­[Ø5X÷©gÚbQ‰îj‡§âŠ”§ü&joºá\ÚûÜÕÁíO1—lFwÃˆýË?ÛÐ¶Ù¶·+>¢Õ†dˆ#yµŠV{A*þž×òÁÖQ¨Z#­ó2(²ÓÄ5âŽc?ÛÁÓëÕ	¸jW=ëb2Ën¤š¬„\,ÿÃ¢…÷XÀD‘êv`ÛCmR.pIûçüÓV‘B§tI6Dý7-|÷^1ãÔ¼ÏR³¯³&8f%DÁÎÏÃó³³³û›¨
ålø—¥óT£…™¿g{MÊz‚yæ¼‡u/¬<L‰§ÒÌÿEÏ“³²¹8(.Õ)&x)êÏŽ_(qÝüCìÅ”Â÷Rá3­2‹Ø’MlÑ–FnkÒ2bß¶Šb€êúï²¨ïXBwhl:<Sû|¡3ŸÉ\šØ ÎB¬TZwvæco$s²ÎgØwnÎr™TJ`ÿ´SSß„:˜í¤¢¦ˆ/Ïþð8×L~§jW0:–zÐ1OíÝëÍJÙë½ 4LMÜOÏ8 ¶¯V/§PmVImo…éâ@M”ŸŸ]üÑõ5£®+aw€f8£ëÀ†5åÚƒŒMê†³ŽÝbrëú`—¸ÈM¶ùˆØ|­¬qMôEgïm¤;;±–èßZ’P˜Â3GÈÏ+kJØÄY7ˆ]«6àÀó‹ýÜ×óÝÑºüWì^3YØÞ–/šæ†HÞû+üì”M0µË¬@ÁÆìlp>¸ÀgÃ‹Rì3ù„tiè}5‰–¿Ä>Çks}Œœ·X ªZ³gÑ·8fkpŸ[A[›áÛùð½îü[ßFP¬—kãÔô)êädPa`…5ÒKyƒ¶ò3ús€;”Ê'ñ¨TÖóÈ)V›k6Ïø‚=£®õçÏ‘!|ŒRã!´|èã¡€ŒÞwFeËÛqRýùÀúUv|nµ`4Š|;LLFn†rŒHìÙìOøuÿ†i"©—½}û&ÈNÒ1ïÛ7žeaáô“ƒÛš°®úÚ½ˆ	 ï”ø™ézfÊ¯ñ-|ØVš³ÍÎC.¸+Õð¸^Q+˜žÅOÁâàå¬¥ª3©È×ïz!jÁ¯Øh¦«‡3BœÈÙÖ 8¾	ŽÛÆ÷4J·ƒFÉmo•ü&¬’ÿBy|hÓÄ~Æ¬y²óÖÝWÊôÖM¯»Ú3S¯EnCµ6N_óYÊc\¤}gòË}Šcúš˜O[svG$…ö…0Ÿ³æxJí«_>eõË‘àJ íK^>_ÉËQöÙ×¹|ž:—ƒ(_åÒ·|Úâ–c ;ÆŠé+Z>cEËÑ gÄôe,Ÿ°Œå8ìÚ0}íÊg«]9Á{˜0B?Øo1Çšòä(çF–±¤ -4È(±ÅR«Gì^Ybü³¹?Ó%1W_hSÕö“®î[åØ3ãÍªœ¾b>bß\A7«µn£“Üpÿ‹†sˆÈ‡Xgâð||[£ÚæJÃÍæJ˜ö$-PÀ¹°ƒí†@’«º(â8Îž6ïó´Ñ×í0´Ãg;ÅÒv@Üøµ’éÕ³KJÔ½ŒF£çXÿ‚=‡ýª+&ºäØ©8Å\¯ºhƒëu]N§qc¥ÚŽuT‚ÇÚ¾Çv´˜y¦ØSEåÒ¹Û<h/Ñm;{ï›ÏŸ…í¹ù€GírY\1lªýRš(‹9jÆ“Ÿqè3è[¾jFòƒcÙF»¯0gã8):,ïÀU©)÷¡ÏFõX´óQšªˆ˜¯˜WêQ´W5uXÁwÚëšÇt›”YÞ¤¬I_öâJPÂlg¶´ý¾b¢ ,ô’6Ëcæ¯Hlx|o¨äÂÝFÊÕÙ ~ùpfx	©n—îyeóÛi{^v§>ªÕ²ÃL4~¼ÎÄtÔTºÜîv ß
:Å/Su7÷£¡´cÜG^í‹MR‘qp ìÌt'#žLx&	3,Ý°t½sÖ›WîûÂy™Ô“*šV—·7ƒq˜˜ÏéË“-³ëî /Ÿcø–‘ìï˜Ta7µØ©>WÖ†Ã_¸Ïxßóì€Î²„é5×Á.Èßç°\2¯sFd‹ø¥Õ.žsÜ—lx¾UÑ<£¥h…] CNJíåçùŸ'•ž±g…ŠâaÏCÐ-¢û §LÍ´/&{¨jÄ5–}©™+"áç¥ÖV·§˜|ª?’€
ýÇs£ªÆeÁfµÌðƒìÝõ?Ü7)ü.y3*R¶åIÙÖ›V€Eê¶2žÛ¾%È±´	FÅ,µE×’³wØnàöæCqèÃb÷éN…âVõ‡ü7À,¹Â}ßVo…h÷Eu.r2ÁcØÃ†vXÄ~cåŠì­ös¸!îîË6õÍ5w‰@´®{ì‰"ääÁ f]bEÚ-Ã0KSBÐ"ZÉSü\	e­ª:¦"þ;Ð7ç»‡9‹Ø	µý®
–ÅÍ1Ó	ï£†Ùg6ÇõÑˆ­œ;fß€cº¯ƒöï<vÓ§¶QÖÄHè÷ÞK}H/•hü<Ô˜öx§ôÝ…ži–iTyÏìâ”°ùÇ±<³S6<Ç.Ï¬°eHEÿNÓ'CFÑZ.ŠØ–#÷¸¹ÏRÊ×®k{ÃÔ†/~Ç"¸-áT˜Žhz)DR,U3i*Ž"øÝ[Íç<)UÓ—×Ó:7ö«1õïpƒÑÀpV¹hÇ•Å<£ú|+P•¼Š%f4ÁpQ±é¶bù[%S¬D_»æ3äžwt†k0€=ÛÛ§Ù(‹ŠO§¾Øý•û¶]¼Åâžjc*S4)Ÿ§k£5hÖ¼°¡pzÇÝâþ˜wÀïöI¸·µ±M`}ÜnG¼S¶o¿#¸²7Ô,(ý,ÊmŒAZãÿïvëè÷¦  0707010001f0a6000081a40000000000000000000000016a102a8a00000216000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.reboot.conf.gz   ‹     å”=Û0†wÿ
Yó±ç7´@—.wÛ¡¨‰¶…Ø¢KÉ—äß—²ãÄi <­àI¤_‘_i±˜se˜q%9¡=sœMnÞêæe—½Í~ÍR¥:Yìövà¾fsp¾üÖ
iÂjÆ•ÈÃ-îkºžø	ëÒ|ïœý5b©À®Ž·"¿°§QŒ@ë'‚¡%ã
gà”äIœé»T…`4ã7z‘NdºH°§‚….XÃóž%B¬Æ`$Â²V©§)ØÁ¹k kKAK°:g²7-ÊWé~%(?ÿN2ù_Õ„þóç¢o\€@1±Îó"UçK°^©
5üNSÎEåe“+4•øk:³ñl{}!´çtÂEKtôë“è'RX×|$½kHÐ—´†×j¨±k“˜'²4xÃkÚB«³š¿+i	G«”ÑTZ "×F¨˜ª‚!ÅÔ%Q\Y’€*ªç°ikZOÔVâ¡E,ÿXÔÁ$^?ê›Nìì#XKHGØšÆnsnXÁTd»zËïVŽÒôï'·|ñÀÏÿéËð1½:UÝr¥×ˆíýÉK0á_Ã÷2š«EÁ†âÅîÉ»[³BûmŽÝí¢&Oæ0DÖðBSwA6¡RÎËfs5î¨xÝgñÔáº·Ó4Î²!Æ²	q    0707010001f08d000081a40000000000000000000000016a102a8a000000e3000000e600010003ffffffffffffffff0000003700000000root/usr/share/doc/opensvc/template.node.ibmds.conf.gz    ‹     ­Q»nÃ0Üõ<'Þ]¤cÇ.‹Q¨s-Ô‘RB“¿ì4N®âÈ#îÅ¦©9¦¡Š3ÓùãÉi=ººîêvg>–°øàpù4³ßo\¢¸î)«<ÀåÜTœ¹å8ÙãˆUíÍŽ:‘àœ½ÀýGz›Çô4ød âruûÞ3½ÒÄó’°0(—‹ýo.š¬Ø„ú(”PÇËÉÇÐ¦¬ÃÒÒBt û~K{àÏw³J«ƒ´.rûhlå[zÉ^¶ÅÇËºÛ•³hÌp»¤   0707010001f0de000081a40000000000000000000000016a102a8900001005000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.disk.vg.conf.gz   ‹     í]mo7þž_A (ã$Åöµ_Ü$¸\_Ðàšº8'½ŠÂKíRáÝåfÉ•¬â~üÍÉ]R²,©‘ã¸Ý âÝåp8óÌpHÎÐOŸòÏ“§ì€\&õõ€™e%Ø|úñäËÝae÷äWìÓZ–™¸ùíÉ×4è—0ì'Oõk±\¨:;ï8Èôžø%¤SUñq.ÚŽ¾ç¹ø¦Y‹lýMU«¹ÔR•²œžGo21áMn:®ßÕ½åLÔÒð2u=å‚OØ+6<ólµ}¯+‘Ê‰Lá›©(¡mŠŸ¥¼ÌdÆÐç}ö¿ŽT•sQŒ•‚®J’)°§SxõÃÏÃ÷ÿeªÌ—#vb=bE£¦…aF±	’cU³y‹ø±@y ª€‘ÊŠ›™ÈX’<ÏÄüy¦¯ŸF£$®æ2zÄ.à}½¿‹ãÙp¦Ò°]@ÍSk$€÷¯K Is<J^ˆO¯Ñ5xø{ ã'U‚ßÍÉƒ©	1³¹ÊøiZ«¦BiÒ»— `µQšêªª…(*sÅÇª6*Ù?bxí›ÏÊòÞL¼‰%‰I2`•(/ù†-dž3'tàJK„õœ0¼53Æ{ñ_¦ª(€7§60$T9o_âúu8b—
ôN¤¯§ÂädQå¢¥¡î4Ë` øhª
šŠ¤f9/Q¨L°gBßºáDvLÞìÜ‘fÀÎ¯çÉ*¨”*ºž	ã?#	¤ š:lÌµ´Þ`•`Ùêaš«ž=:ß—wøVLd)4À¤ýä²5 þt 5šÐ‡x5Œõ¼ã×@§ª¥þ–LÐ·H\÷0‡)#ÇA™ÕÁ¾	¨”€¨˜ÏÄóã¿%]ÛVw:+i¡Œ¸ª/@x¤~ÊÔK|ë$OJó~ÎŽ]BC.`pí[ƒï1Nµ^oÛ}–f¯áO«až’ÀY`€>üŠ=#EÚ7iÞh9‡ ¯Sð"57‚Ñå1éíï›¼ü‡!å2ô÷b¯ôœVCÄ“gl¼´SPë…Øw7@¤’ŸV‹õ9ü‰±ÓÙ8WéµwO‰Ã¦¢‰Œü–{À\ã÷©†fdÁØAþ;ƒ&vÚ§ØÑ0S2†‚‚A[·ˆÏrcEÇ§G‘*6ÎøY3 ÏŸ›fÍŽÄ6sSIó31}“5Kûõ·CšÙªéY.nÖÂìù4÷†žÍÊÌ¯–š
Tkø{Ò‚ÿî%²»iB›ë‡õs×2$ûl}MôøÃ]t$w4Ìj¶Ô2å¹[óèÛ–@vò™ß­r0ì«JisÕ
¢"^·+ðU:­eE.LÜˆ´sà#âÀ1ðWçé]–*Ï“v$	³À³×µª50êÉÎ„‰šÛ ™»cÄ¸9ú^•T•¡I”ÉÊd´¿‰ÌÞ*„¿ÊlÜ/íl”¡l“Q¨£±Ÿ^UÕëó0–©ªC¦ªöÑ_Söså=™e Ú?j•‰]•Z‹>úÙW£c1Qµ¸ïð'ÔÌÚìƒŸÓä£¯‘}5ØÇ>÷c’ü¬èg/µö¡ÏÁìòcc§=Ô×G>÷g”úÄÚ¹[§È°(Íã;h¸ýý òJã†&È¹)¡y5«0•·	~GßyEê[Ìd:™5+ñ3@€*‡)Ïóö€¹ªz3>Ç­sü 5·'FÜÓHA¯KÚ€ÇmòF7@k‰]WªÔdŒç–§NÐùÆÄ"©Q+²‡O~¢ôÚ²ƒ€=I&§%Ø9OD‡/MEÑ³ÆdjQ†gËoŒÆoL£6<_ð%ÿ X(ß¨|Î“dÄØ¥0  öíwß¿~ÿã»Áq1Syß'ò<ŽB~ýûL	:÷a3üÞ$Äöê|ã|áö´ö®HGY…È$HšÀjšäÑ¤`+ÐÈÙy¢
öw´_yö:KÃ£ 
¹Aü—0¨îˆLã3hIÒŽ-ñr*²A4¼¸gQÚŽ[yØë
vÉYöñ¶S%Q¦¼ê‰bl—ñÙ-þÛÚX|LŠ–A'HÙˆÊ

”Ÿ¢Ç¦™4þsÒTÔ† ÕðX&@)Ë£4>:¨èÃˆÁ€3Qåj)2OØRådbDýmƒOÚ2d•=€â­Ááz¡®ùlSËé0Á	:à¤d
aÒN÷*ºéX–×!¤gªÁ{<em¬à‰ãÑ5ÄÓ3t¥àŠŽän'Ó<t(@;µ¹T\_Ch¤—eªãx	^Ì3vT*;s˜ DÜC#h  mš"b„Ð¹Ì›RËVúÏ‚åŠø7¦MÄ•Sœåe‡àCæ.`ªÊµ=²]£ÌûH¤R‹)¦7„€³m1ëÅÁŽøµAnÈK×Î}4?Â¤ˆ]ƒû·ïÛ)ævøæ,ZÆíŒZ‹›B1ðÆ¨‚<}†ùÚ»UŒzÜ Gì½“&GÜJ%—×0É6EÅ&2z©(4¡FÜpLÐ2@^/€ªMÓ«q~-ÕÕBÈéìO¶[¸‡©À&ÊLw„m¶ÇU5Å)íWäù·[M
¥à@#‹ú'e½¹€!æ€“9øZ’îˆýÂó³¢d§'¤ÃÓ“““û—¨K‘xiKï)—!Ö ã«“;4›VÍÕ‡FÞ«õNµ®ä›À‚4¥6Ì5Ã•/0Š¯i=6¯RÊsy(µÂ°Þ&œà¼ùE¼nS]5RÐ4Ö*Çm‘9Â)iÑÆdÁ(e®æ…0¢±ï;G1@wý£,›–RpI2<…P±€P ó¹,¤‰×”*Î*¥%¡s…ÛÂÉ¦Cp "(dZ+- þÙJÒg+ÁÓÊN*Xt%ÉW'_üÞµŒÀÏ”³AÊøŒÃSÛâlõ{³Pö{o -¨	ýÔÇÐ3ðÚº·C¸6ë¤<³0’vŽ2·ÔîÒÀãÓ“³/™•ÿˆ½‰sþA5Ã±jp¶•¦¡¼N°‘R7n£pm±Žév…]ú@t)£e2õxª8'úìÑwv§òf-q}kEBÛ!ž6”PXÒäùˆ h[!OÏîF_»}ïk”|«vïAc‰ûÊ¸9B¸h‹P¼#öü½’Ü}ÂµK¬Á—SÁÎÙÉàtp†N†gJQ™ø`³2Q\‘/í5º¯'Ñò÷xÍñ™¹ÞÅÎ;]‚T½dÏdíCÀùØúEc†“á[ûÝ5¬oÁ|§¸ƒbW¹v"Åú5	×è0*®µÏÝµmü„þlA‡RÅîxÔ*ï1rˆÙæ5›ä|Êž ¹ž# ü¥ÆGnCÒâ´@¤ý@É¸zâ.H¢\|K'êÇ»®²ô¹1TSg­í0›ÑÄÚ.žíù„Ÿ7ðßáö/1’ùý²‹‹·!Ë¤ïÅ[Y˜8=sÐÌÏµÒ•Q¸1€™€Vã“Ï™nÆvQù5ŽÂoÛJ3`¶:ç4Dy€ZÀúéŒë¯*,+ë'¶ƒ@7x9ë¤êB*_­B­-^²/·x¦^W÷„8“³5>9¾žÛ
¢=ƒÔÛÖ dÞG%ŸETò7´ÇûMˆR×³[­»+¦4êšØ[=?ã™Ä{‘yâàBmKŒÓ—Ç<HyŒÛiß˜ü²OqL_óikbvÒÝI¡}!ÌCÂì®ÅRAûê—OYý²£ò¶%€ö%/Wò²“
wNûìë\¦Îe«w¯ré‹[>mqË.ªÛ%Šé+Z°¢eg%îÄôe,Ÿ°Œe7ÝmaúÚ•«]ÙEƒ{„0U…Ÿå5©—áe¨˜Œs-«ØRA!j(±ÅV«Gì?¼¶Âø¹m…÷.JÌÕÚÔMJy7¶ô…n	ÂŒÀø°
3qÏï}€
j¬–ºÛä†û+ÖZäÜu{jûÝ•Óçã;¸°˜I€QÅS1Ôs­0Ôß<„iO²½ö©0<MXrÝ”e|ÇÙQ;ž£Ö¼îÈÐ	ßM…{»t¥A½¨eöêÙJÔ}5FÇXÿ‚—‡á{– 
¼r,Ã\¯¦hƒóuS%	h5–º½ÈÌ"B·ö3K-OŠ;ª¨\:÷£ƒÍÉ}&Vßv²÷Î—GD÷ì9ñFZïò¢|ÅŒÄ;¬ÜTž(5æéµÏ8ôô®ZJž8–mtç
v'E‡å8+µå>t¿lTE'Õ Mð§*døÅ¤V¿‹²ûª­ÃòYÛŽ’ý®íóè†AÄ&eMú²W:€f;³™½¸$&JÊB¯è°<MfÃãV¼•’ë„»ƒ”W'ƒxð!gø	¹n§îImóÛ—í­§`Q­–%s¥Ó™Èš\$Qþefý ÏBþŽÜ,j«‡ûi‡€ŽöÈ¾w»ÊDÎ—‰ãL¯dÄSÃi“1ÃÒµ„©ëÒEÿ]^ùŒcŠ(æŸ™69“*ÚV—·7:LL&t•pv½Jtàís(ÿÌÉþŒIöP‹‘ësemHþŒ¼—ïä%;Ùâ³¬`zÏz®¬±!È-7³B–²h
Fb‹ðÒy{µ¶—³¯9šg4-fÐ<Dj‚…šÖ~ŽGØkœTzÂž•*Jˆ>ŽC¥[Þ¥zÊÔÌúb²ûªFüØ`Ù—š¹²!òÐ9^$¼´¾=Ãä{pxÛ):ôK¯Ïµªš-«™OÙ¸‘9ÞLË._ÿä.—õ§ä-'T¤lË“ò[ktlZ©ÛÊtê·%äXÚT1KmºÉÙöFVÛxÛåÇý¶ØUk¶PÜºþÌ’+ÝÔ4ëø(ÄÖkÃšI‚Çj/°dQ÷k3Wo¹)[G7ìû&6ÍÃpŽ6ˆæu¯{’-ò€¨YVtŸ)xÊð—oPJFDË2=Â{‡)k½TåÐŠð}îD@—tOZ¸‡9‹ª)ƒ’±,n‚™NØŽ.|È—ÈÙÔG#¶p+Øsö-¬aÌêccÐ~ÌçŽ½µµ²¶`q‹5€ïW©÷¹J%?†j†;V§¸èAsÏ QÕN˜Ù„”ðò]1sRÖVŽ«˜	 pdÈEÿEÓ'C h-§eËÑò¸mg%e…kçµ;·©Ÿþ…Mð¶„SaVLÓ[!Š
l©KSsÜ(‚Ÿ}tAÈEØTM_^O,¬üö?Ûß!ÔqÔÜïjéÊr’S}¾5¨ZÎ£b‰±Àš€\Tlz[±ü\É+Ñ—îòZž¯øwÁ þÆ„®7»ävÅ“Ä»¿t¿¤">bq½Ú=•CŠÔçéÚÝkžÛ­pãfsÌ'`÷ø$<ÛZ;&	t½ÛiG|Rv×yGðeÜkXHú1D”·ƒ¼Æÿ×=ò|;t     0707010001f04f000081a40000000000000000000000016a102a8a00000204000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.cluster.pool.directory.conf.gz    ‹     å•Moœ0†ïüŠ‘öš†{¢ôØc/Í-ŠV^3îë²¤êï¬²%j­T_xÏû¼£ñf³æ*6°âÊé‘¹€Ø9„J{”‘|÷ŽtëV·.»â.‹Ýxm+<Ü×½è›YvQd{ìÉWWs!NÄš7>­¸2© É‰Áé¤/ÂÌ;$.ª:Ý©P‰dâ\ÜÏVø_eÖUÎ:Æä¬ýJ­´„Ïð€½–½ZÎ$GÜÖ*“µ)ˆõq?Ô¢¶ý×|´dRƒa
ÑŽÌ'†ÈÒÃ%ŸsÝ§½ùsµÔUØfƒÎüA…7sÎXº±H È7"Îh+lµd´:Ö=»ãËg.Ðjö;³ß’‹çÊëîþ¯qH²-ú£¿Cmðpò©Í³Ë´ lÄ=ìÉý¤G5YNØ4}ŒË¯!sOC¡ãâÎ£OœèM˜ø²,eÑ6ë?3!k^àûJ{P\¯3µñ£Þúô¯D¿’Å·Î†’²qèf˜¹k‘a2þpæ¦ˆ–À…(b
Û k¬’9ÛAú‚­0©ï²€1?2¬²ŸÁ<ón®ãËj÷/¢ Âðäè¹+OÍÄý¾!r3û2Ô<ÊŠd9åææÎGÙ¦­ÐÙ(½[/+]û	JàN
  0707010001f036000081a40000000000000000000000016a102a8a0000010a000000e600010003ffffffffffffffff0000004400000000root/usr/share/doc/opensvc/template.cluster.array.vioserver.conf.gz   ‹     íÐ±NÄ0à=Oa©+ÐS™nC!ýï.¢$='-ôí‰‹îº0¡ˆ9K+»qýMSó˜†*g™írCyA³	<ƒ?®îvuíÌó¶az|¾˜ÝºÛb#Þ°|Dîï·MJ¥Ôo+‘J.ŽöuÀõGvHã<yÆ¶Âž§µÑã`§!o«=Å€Ë,PÙüÝL#œ?xGtD {·f+’+_ìO ÑæåHYÞÙÏ6C’JiJÇäÃ]¹¹“FG­ÜislED±ëO±Ê]öÿPìíg¢k·#Ž1«ú¨ú¨ú¨ú¨ú¨úÔóùnsµ    0707010001f058000081a40000000000000000000000016a102a8a000002ac000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.cluster.pool.symmetrix.conf.gz    ‹     í—MoÛ0†ïùríÇ½Ý
ºË°CÛŠ¢Ud:Ñ"‹%¹É~ý(Ùq³¦I÷‘uA_›2%>|%2Ãá6¯Á¶x%w5‘=€0¯ü¼ª0°™ý»í®n»ì×)Ø!Wàìfpšƒ~ûö`"˜âüž¸8yXˆbVs±nñJ¨¼¦Z,öS]qÄd`üc±b(°TÑ†‡µ}"‡W²läÖèkÔ¦4Î`ŒÙèœxðZF\Mœªä“‚üÎ!ÀÔÑ½åáDæW'wù6|“ŸÝqy¸£—’MÚFPîäJ3Ž¬‚!w$sžf‚¹$ÚÞÞÞÞÞ=À\”ã—à{®¬poÙá¬­Ñ|`Š5‚èQ80\^¼?ÏpRä›ÙxK¯Ícñ]"7F#|Ä-¼3b….,Ì-ê€\b5^^›A
¦g8
ê]Ù[~•ägÑSÒ’LÖ«­Å!Ñ
ÖñZ"õ¿ò2§šÌë)²è+uài*kIX!4dc…J¦ªÃVoVQéoSç¾«Bš•þw¹•Æ¢ŸË^%DrHU*dˆ™Zi‡z¸7a’Iu „Všs­j:²Ó[ªÃ®òº¾ùi\J[ƒ¼ôµŸXœ­€üÖ¤?µ¶9 å
 AÈ0ÁM;ˆ Û² ©ªò˜:=ú®@,oò¢–­W2‘“Ðó•4H(k³ ÉúÏ’b^Ãwƒ¼;¨-®ÍL]x)¤k›Â¿S˜+Š©ôvÈºƒ4ÁLª•"L‚¸–É3e8¨ý­×,¢Å×ØÚ4ÊÆ¬2!ÝrócØ0¾m!Zî¨G_¤çñ}©*rê¹IÏ„"f>ö9ŽÒÇ½ïÔ`ÊÀ%o½ÉÏ]P³¶Ô=ZQ—µïÚûäz3  0707010001f113000081a40000000000000000000000016a102a8a00001127000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.sync.zfs.conf.gz  ‹     í]QsÛ6~Ï¯À$Óq2'+²ÛÞƒ›d.wiæ2×4&¹{èt,ˆ„$œI‚HËÊÜ¿Ý@²-Q¶dU-üà8"±öûv¬°Ë'O¶ùóè	ÛâŠ3ó"é±j^
öelî-n»½Û®îý‚ƒ}¢e‘Š«_}Gƒ~‰Ã~ôû~!æ3¥Ó³ERSÁçÇ[üA™D•|”‰æAŸt-ð‚¿ÕR‹ôÚ…R«Ki¤*d1¡‹oyfèJ*Æ¼ÎªEŸT]ÅThYñ"qÏÉ³Wl*xê;!Wh{Ý”"‘c™À=Q@Û„”O0	ÜñF˜J¼‚N°”WÜˆŠ©1«¦‚(Ô‡›¿u>ÇJÝ¦OYTBç"•\Ï÷ªØFhÖÛŠfKx‘JÐ¤0gùì‹$ª¸:èÀH)xTÑ‚å£(Rf
^š©ªK´ ‰ð‰„Þ8PêD… X4¨[ÐÒ"©µ‘—"Bµ5¨œé6UªÁikE:ñW	¨FûkP‚vÑG9«ZíosO¨á•î©âsp\ß&Ï—iZl)øÓÔ®P¥Ð{cõmê²T8Ú>KŽAgÃTd|~ŽžiˆzíC[ið¯`‘ ÔŒ RŒn·p9gf}N7ÿ­Á…ÄXiÑ 
K–imY|~]ÌÎ†%˜J9¯´œL„2qÆ„²ú4§C9å—Áž*…<x†½ƒŽM´ªKÆ³lÙËbÏ\§@»s ŠˆmIZäª¬P @ÛehSÍ„(\WA‰­ž‚§	šß<"3öØ|;•õD<Èâ¥aæïÎ3Üà¸	ðÜ©.ýŸ™Jxv#û3iªå5P¢åšÍ¦2™:iÀ‰:Kq¶è­+ä91¬d¬UN¬ýéç7²Ø¤Oi¡/%˜ZD‰$`º.P_}öñõÌTJó	Ü6å `vD>
Äåh"…BJÛÇsÝ] áç7o3y!XÉ F\Ð*Õ´÷J:r„CFåè³Ûx7u^@¿Ï‘µçòçUw5E½šå9¬4C6•êÚ:(á¼2Â{×Ð$Ç<A¿ƒ~Ã<FbÛ÷Z+mì[×(¤¸‹ˆß-È¬žÛp ŠPÞÊ@Rå0œ¿î#‰ÙBøU¤£yDr»Fêvx@ŒÍpUeÄs;–©Êm¦*7Â&Ýs˜p#†[¶I§×;Û£k¿1–v•ÑÜ>š¤Ù{ái×í›"
]ƒu³ˆ˜î S§Û{¡êdÜWø'ÂºXáŸû¢ŠûÑMAµïê@µª½¨VÄ& ÖEŒ%ìÔ@µw5ÑT-bthSDƒøÃC!2 ƒC÷CrËÑ!È¦ÆØÐnLò¾Á¡%|6‚5††¶f—÷9<6/F†vd‘÷¸l
eíÌ;G†ZØl
hŒíÒ{†–ð¹¬1,´;Tïj£³)¦1*´;Lïj£³¦1(´;Lïj£³Sì°(ö|Xm¯G/üþ)²’ŽE‚žép0mø,`*Í¡Vw`ŒŽŠ9 MÏžKkŸ°,ð6`€*Ž:ÆhOk²R¨äá	ÈbB7”B£ý\ÜËENÅœq-èÀXmj5ÇG—ª0tk"ƒsÅáÄTDåàl¶¹r#è~ üµWä‚l&â‘`rR(<ê`zªPøÀ.4ÓºJÕ­¶yÄ»Êà=Um6<›ñ¹ÁH4ÏùpØÇsì(€½ùþíëÏ?|êgS•µÎ.Î3ú>öÃþúë©‚õ6jª ¿+"'t2öÚVÜùCÇzwEá±`™S®C%ˆ¬U­Au¶m@\žžUÎ^”¼š¾òÝ‚Öàv<¦¿|×ºïï„A™…ýã«Ð’¤[2åÅD¤½ÖðÚO…}p£ûÁu€M´±ƒ‰YÆÜbg¢Hxù³²ýeoÐ<:VÐz8Ä¿­Ù¿‚e AØ$H®ÄUµtª;A‰i²ò·R­6ö¤úB¬±§ÇJ)ð!6²Çæ9Œ8e¦æ@'ØJåtb	DÏ[GŸÐªÈ*#¶â­Ááz¥^óÙîp?L½t4ÜÔ2Ž0i§û‹éX)äÎŸƒÓwÎ½½„Îûƒá¸æèÌ‰Nä:¨]*?, âš„UÜ\ÀÒ·¦½^ÂD‰=OogŽ£ ·Í e‚ Ú4E´B}€‡Ë¬†>âÜíµô—`™¢~ê
HEðRP€¿œð[æ.`ªÊŒ"r\“Ìû(´¢Å	CÂÙ¶²0¤vÄ/¡£¶!èû²hçîš)Ð ¬]ƒ>ûÙ?Ûà‡<vÆw1˜E>M¨^W*ç•Lh-êÝ*®zÜ ûì³ã:CÜ(…²"Ò:/ÙXfÂÌM%rC¬W<‡õ1™J£¯—[`Õ-vVNÎGÙ…Tç3!'Ó?Ø©˜
L`¢HÍB°Mi=/'8¥ý‚}þõF“ÂM)8Ð–Eý÷£ìÝb<¹_KÚí³ó¬†úd¬“ax2úì_B"øÒ–®##–Ðb|;XlRÖç¿ÕªâÖ•°.¥Â†4Á¤Æl;ÜùBg xMûY°y•à*Û9&.ë]ÂÌ›_µ÷Á®_°·ÖH‹¦‘Q†E.‘(ÉˆfMÖÈ*¹æ¹¨„î³·GÑCwýƒ,ê+–PpÃáñ	,sX
àf>“¹¬Úû $g¥2’Ø¹ÔÛ–“u>‚Å¨ —‰VF ýSÓÎ5l"x2EÝI›®áðÛÁWƒkMGàÿ(c¨Kèg{yj[œ.ß_Í”½ß@Cjb?=c	Púš‰6°×fT“Êj	ãj]¨‰ÒÀÇ'ƒÓolú l|ßŒ‡û‘²>©g[YÕ”Ù
6R˜z)9±ÙœÂ3`EJWau)[Ûdzò¤âœh'E»›é:äÍZâþÖª„Âž!Ÿgv)¡ Ú÷£E@Û
xrºš}‘w}ïkÔ|»÷ƒ€ÃHb\ƒ#ÄïQk Õ?à7®ËB64·pCù´•ÖaglÐ;éâƒãÓ¤&~p;˜¹ÈÏÉ—FD7õ$F~iï9¾'37]ì|%  ôœ=•E+ägÖÐ?ÔÕñ‡ññ{{ßìoÁ|1{Øírm|"á=E¸Ç@‡Qrc0óÙQ¢AÛù€~Ö°C©ü#Ze‘#Û˜m^³qÆ'ìé ÍõäÂÇ(~ä’–G€>N -ôq÷ÄÝ"	æ¢±““‚ôg=»¯²òy»1ûõîíêÜ²1L{· z6ßOøyÿÃ¿Ô‘ÔÇË>|x²täýðÞS&Nß9hæçZPŸ •ÍÝÀL Õö7%¾g¦ÙMåwTäÂ…me34`+ØIÈò€µÀõ“573^–² ƒ‰ßÅ1ÀËÙB«nIE{ýåÝ@ˆZÐâ%ûfgŠXínâLŽ¢èá÷ZïƒÏÉ)lº(AÜÖ.J.ãªäw±*ùÚã®—&$iñd·[·eŠÀCà¨5uïZ-Ÿ`=3ô^ä2\âàFmÍ'VVÙKei¿õðË&uUb9•‡-§Ò	»ù2±†Ê>k¨tG±S–L,œò…S:‚·.7&VKÙGµ”nàuÊˆ‰%RöU"¥;ˆò`b]”}ÖEée§ì—XeÅP6‚rmÎK¬€²¿
(Ý‘ì’éËžì¯ìI'$;ç·ÄZ'û©u²Åî•Nb“‡-pÒº.ášXÕdUM:ƒØ-ZK™<`)“nØ­ÖÄú%{¨_Ò	»n±šX´dOEK:cØ%T+•ì±RIg$»Ejby’½•'ÙÉõšX“do5I:Ù)N‘ì­I 7Ó*„¿Ó×Þ”ôH™U²lïF 4H”¶µw&¦ÏþÃµUÆOM+™—™ÄÂ0wê:¡$*[Ç›Pzgûä1¦‹â®Ï+¨±š›ÅQ3^qÇŸsV½ƒ±¹ïÜáù'~`ËÌ¦hTòD‰s˜l\Ž‘F©´@?ra	Ã“D€%»÷Ÿ…<`GÍxŽOðz!†Žk_á+õílå´½Ð2}õôe]¿êõûýgXÌäÝØ–ê°CU.+LÐƒÛë²g*ŒIÔåp‚Yµ•N\öGÂá±"©|áÓ&OCŠ%q\nþÁÑf°Ë,ù›Žiòµ.|Ù„9’Æ©8Òx—Å+VÉ\?Õ€'ÊÚŒñäÂ§úr^5’¼p¬Á±8$:fgí÷°VÎJMítm²U\‡Ž±–½¦Z•|h‘ïkõE‹»š¢:>ßI²÷5i^hè«À7)Ö×0qu ÐÂÔu6åÆ‹ yÞ))ó¡M~MfÃÛ­x£%÷îNÅ¾ôÚƒ{†·Kàvêk[¬€r-daÓ.Z…w¬˜s“LEZgbØJ¦M­àY ë_Œ^Ã·í.ej´D;,d÷íu7°szEéÐõÌ,•7 %§É(ÇtY×¦®.Â¹(0å˜ï‹ÉÄ•LêŒ‡2M6²KÂ&Æc,ÛÝ,íyû¼&À?µD²ÿÇ{B™’ës5ŠPü©{ý°¯Öø,«˜è¹BÏ•Öv	²ôúPdËe!ó:w¯¼ù²ð.ž9”¨ÍÏot4Oi*šMýÛd]«!VÝ²öó¬Omg–ÞvKýx‚n]½·‘?ìnÇOìÍÐi?{ó—SëãÚå¢PìTCÏ¿Ø7
û'€Ë¢µ€y^ýœÒŸ§*y/Ìeò¼ñVÃ&UÉ„Â+~ÕbÎè…Å`è}Øþ¾—ì—ÇƒÁÙ`p<8ßûë	N‡{ìñà”>ýÚ}ZÕ°Ž«‹Ç¿ÞÊzAm¬µ«’c÷ÝDùzR®6ÍÜ™†ÁÍíœŸb…˜R`b8Ñôx^+Ó³µs.'lTK0†o*†EÏ…iRašžP%B[ƒ(»±ÍÂJ”¶ü¤}Ñq3JÈ±~HÅTÔÉò
ß¶°¼¶×¶Š_	oRšÊVƒ´K‚=L…-¨~ƒ[øÕ©qïZç"§­Yö°j¨‹Ø_[Ñ´Öán)G/¥_†ÑÑ¦áò&Mûµ­÷<ö¤ÚüƒÐj^bÙ(\Ï‡¥00ïKÚÜ‡ÆTš¢PÅ±#ñûÌ©€Þí>nè&&«ºpe°p=µ¯Æ˜Îˆí¨ªkF/ŒcÊ~ŸÍ\dãŒ½½mµ<Ä¶1?æ3×½°k×jWß­¯±˜÷côb—ÑÒñ!D.ÚdXµÀ-b$ÍŽI£ÊNœ¹)a…ß®œ¹‰)×"
Ëœ	¨°Š2ä¢ÿ¤9Ò!QŒ‘“¢½–£°IÓÎjÊ*×Îk+¿¾ ÊÖrÑ"wk‘¢Â—_§Å*ëtÇ‚"…vO!PóÁ(¤Ä:úäüêáB‚Û}¡ÃIúõô!Â‚ç¹,ê¥-ÖçBRÞ^¢Ý“Ù«lDŽÖ0¥â~éÁc&.²Þ®V±\;œ6ÎA…\*þÛ{_:§`,É¾"5)Œ—e&WQØ~‘ÁÖ/c|¢…À>¸ª.¨d+ä†_¾6!yÓzª€RÓH>ØÞ®LÜâ96\p´ù«h «HJûÃèåvïåHÑãçÚ´XçéÜ‰½H£ÝÓÈ©ú`ˆ´LT²ÿD&ížIÜ; "…ÄXÇ#{b1òh÷<²š>-cù0ëáž„©^;ñ€ÞíàbûÐëª£‹ÁÑkìÔkš>¯q1Èküã&E¤Ò¢   0707010001f03d000081a40000000000000000000000016a102a8a000001ec000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.compliance.conf.gz    ‹     ÍR»ŽÛ0ìý¸õW¤Pp)RH}éAH“+‰8ŠdÈel5ùö,%ŸÌC …Šc!;ôìpf¶Û5Ïf+žB§ü¬‘Ná*tëª[×»Íóý±ß7EíŽus_*3ù9hIÈø~ÅSÌIÊy¶¸,ü"m*‹ âÏl"ê¿­Ì–î¤°!°~Œ3š*ÓŸ C‡Ñ¨rMy÷cEpöÞ¢t“LŸC_[HH@„ ˜QˆH§Á´@=^Qe2Þ6Â+qÚ(v)¬bOªG-jˆÙínChÔ áp^#Ìö–,ÿfÎ7pÅVu“yÕ‹8À·Þ$VÙu¡GgˆaÒXÝ|‘Xd­mt
.†úévÄ#–ËƒO†|ð/2²§ó««†F0CŠx\ÑQQÂž‘q]£Ï0äDpf!LÑf[fñ&¥Z¦hT++{ðÀ|¬ú·¨ÿQÕW¯ßkOŸšÓiúÀßÿ®kUÉ§Ûû È($ŒÐú8Ó¨=çÁulD•zñNÌ›Ñ]åñ¥7ªŸkÒÚ¥"¥ä­¹‚wv\†­•]Ç%¢p2,Ä¡âzB.qŠÇÔsÒGíÕñ5‹Eá2àÖ‘¼NÙ.³Ç·Þlþ ”×Í$  0707010001f076000081a40000000000000000000000016a102a8a000001d2000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.node.array.xtremio.conf.gz    ‹     í—±nÛ0†w=Å^])Y“(c,™²|¦N1‰dy”k¿}d:UE€à¸XÐQ?ï¾‹7›5WµWŽÃñ¼…t§i´þ_âÖín]vÕ2ì&Z×Ñégu_†n—±«*ðFçß>vw×>0XyÿmÅ•9±ñ÷]úŽS®Dú5ÙH×^âT
õ8éÚÚ³w´dH×ç"2¶·á•EkÊl’ÀFv¼hö.gqÊÂ‡ZöÜçç)¾kš²©–pÇGS?6RoŽ·õÂRX
Ka}XGú\Z—Êª¸ò\à{Hºžzp>Aˆþh;ê¶Ðã0ìÑ¼Aòe“IÖ»…É²xê{{*´Kj;Þþi@æüê^Âe´LlbÊ?ƒë¶@§ —“©d@x˜ÒVO‘œ)[ñ=T2‘R]Rçg'¹Û{’´bc·ã3'w»"„J
ºnÞwÀã{GóK{ò‰h)Æ.·0Ç5rZ£U 
T*Pª@¨U 
T«
”Éâçÿ½þ/—Ñ>,°.Õ¢÷Iù(å£|”òQ>ÊGù(å£|ÖãóüëÓeh$    0707010001f09b000081a40000000000000000000000016a102a8a0000030c000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.pool.drbd.conf.gz    ‹     å—ÁnÛ0†ïy
¹¶i±c‹ØeÇ^ÖŠ"P$ÚÖ"‹ž$;IŸ~¤ì8^[]EæK)¢Èï'Ey>?æ3›‹#>l®B´g"î*Ú¯ô{Í×»ã²›Ýs°soœ†íÃì:}“ÂžÍØù5ì6èõÕÁ©µ§‰ó#>)(¬äÊB¿Ó¯'<ü®‡ƒß¥iFC&k¾Ý LÅÒª€¶-2ô"Ò¸CN–°Øo‚âß®(“%¾Šx£Rô´EPW­ád6¢¨ð‡Bç@E!Eàâ'³Yãrž&ßŒR´µ;äAçv2!C³ëdüæ•ŒSÊí’}	¡N/Â?Èsùjô‚ñ6à‹‹ƒ&‹«ËxY[« ¡
H™ÉkOT]‚XB”ZF¹àµ/hÒH[“ÔÖâ&ˆÜã†åÜ~Iõ|I¨Ë1DYV	nÑÁ[€“93Ç•Tkpzî¾.8W“ÑK2¶? ÒÿM î˜	“‰€e›×Év’„êóžLÞ~»£/]@[)ûoh3S¡’±8E	²ÚZŽm/ƒ¦=UD¿†˜Žå#]4hkfj+‘ÛáK«'Ù5ù©'o‹†Ë»®8Í¸Ü•Œð¼Ü(iÙ»Ÿ¡ãÐ&ŸÆùÈOh
rå#}9PkL‚ËÂ’o#ŸÝ6o®b®Æ]ˆP22:3KÀ44F°‰EbÕ Z¼çXó_¯ìz‰Uü¬¼îÞÓýCaaû$çÔ™°Í™Ô‹z±¢\w…ò £AGË2ý§âŸ¡ëWƒº¦ÊCÅè©móßó%(”QH¬ÿLŽy„ïDzwP[\ÓL]ü(¤oxåxÏ	Zbíb¬;J&g-]|ð€k™L !ÊX‡ePèÚÂ)ö öºNYøž‰	Ö“°ûûæ_ÍPù¸úEw¨Ð·.zãòXöÜù”Ìþ"t:\hT½íîÝl`­Ÿ
;å6©õÔ£Nµ?#Æ4Ñ  0707010001f102000081a40000000000000000000000016a102a8a000010db000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.sync.evasnap.conf.gz  ‹     í]QsÛ6~Ï¯À$ÓI2'+²ÛÞƒšdš»¦ÓÌ5Mç’ôÚŽ	‘„3I°hYûñ·» HP¶%Ê–¢ª¥[$–À~ß.€vùèÑ.<b;üAqz™Çf–…`â’ëœ÷·ÛÞíVw~ÆÁ>*ežˆ«_|Eƒ~á‡ýàöÿB,ªLÆM7àòyÎ3OvøƒºÒ±*ø$õÓ¾å©Æ±RüVÉR4ýøPVt¡(Õ¥ÔRå2Ÿ[M1åUjšžÿ rº ó¹(¥áyì”
>e/Ù\ðÄ÷B0´(íu]ˆXNe÷ÌDmcR<AÇpÇ ¦¦ÌÌûîGöú§WŒ—%_²¹ÒºE´ªÊX0ž'•«çÊ@ûK=i_y­‚þ§JÝ¦û‚ËR÷ŠwŠÅþ«U~2UeÆ	K¥6ˆD"c=ã¥FP”B‹¼†ÂªÝêr˜èkÏ©)Ë*3ç—‚î"]ÆQ4€_m¢ˆ@Œ¢Œëø@ÒCöa.IáEvÉÓJ0©¯ûE¿<GrÜÌ_þò<­r™¼„Ûµ[œYBP÷Ö³‰Ô›âm¦X›™šüWÄ†Œ‹k¦…Èá¹Lk]‘¢k-®Wö$UñŒì¼ àÎë±~
Õ{_Ó|­à?„1Æ*ËÐ4T	KYfW"®h~jDI¸€1’/ìeŸp²ØqTÌŒ>²×e©ÀdÊ
Å‚w¼™màÔ†—¦‡ò®P† ’*£ Ü»ÁHb¶†þÉ“É²Gr·Fê6º NÆv¸ª¢Çs7–©Š]¦*¶Â¶çIÙc¸k›tz½³=ºö[c™+HæÐ$ÍÞO’°5¢Ð5£JÑcºLnï…ª“q\á¿ÖýÀ
ÿÝUøokP«"á¦·Õ}€jU{/P­ˆm@­ò>–°'PÕÞÔ@DWPKÑG‡¶Et"¦8Áí9<"³š}pè~Hî8:äÙÁ>6´“¼ophŸ­`íCC;³ËûÆ†ÛÀ×G†öd‘÷¸leÚ˜wŽµ°ÙÐ>.´GHïZÁç°öa¡ý¡zÇ¸Pm1í£BûÃôîa¡6:[`Ú…ö‡é=£BmtÖcŠ¹9ìñ¬»`¸ü¾i¡Y¥è¹Ê¡mø,`*mŽm‰’Žçá5¤°Å\Æó P©YŽ·T~ó4eº*
UVU€¼9¿¤pC!J´?€‹{ù(xê’qàN®ô¯YK|t¡r-AÇ€pI$ppæ·žH¨ÍÖWn½3ÚÀx€M¢³Óã¦›ìÍb•_Š2xôDø<_1u§È†l!â‰`r–ƒ'¦§
…ªÂ®W&Q‹<<ØùÆh¼ÇTšŽa¦¾¤c¡@'9óg<Š†Œ½O~óúÛW¿ÿ0 :.æ*ÉâŸ	‚|‡aýõDÁz	5WÐß5‘{œtu+î|=Yª½+Ò¡ 	š&²šª}T1Ø
´qY2ŽTÆžÓSß½´·kaBIã‹Å8rƒú;aPº±¿`|-IÚ±ÅsžÏD2h¯ýd‘Û×ú°\Øžzuƒ‰YÆÜv=yñ'³²m]ë®ŒYN2Š´Ž"üÝÚð_6@Ë@ƒ°;IlÄ•AîàÇè1¡ÓL;!ÕjC”Äj¸,¨” i“¥…aÄ`À‰(RµÂ8ÁÞÐP*— wŽŸ·‰> eÈ*{íÄ[ƒÃõJ½æ³M)g3à'ê€“’1p„I;Ý7P4Ó±Ì/4RHÏU•&èôsoE/¡óäP&ôºRpÅH'r	TA9éÑ± í×$Ìp}K#Übèöz	.Îö8Wvæx<`ä±é®´J@›¦ˆ6C¨ðp™VÐGœ»½öƒ>ã,UÔïIe€T/•M+½Â	<Ào˜»€©*ÕŠÈqMj0ï£0ÐJ)fÐ½ÐÏÛVæšÔÁóKè¨mzÃ¾4íÜÝAóÇ
4 kF×`ÈþíŸí@€¹~G;ã»Œÿi«WFeÜÈ˜Ö¢Þ­âªÇpÈ>j1­RäÁRRy“l•l*S¡—ÚˆLkÄÏ`}L¦RëëÅXu[ÎÒì|’^Hu¾r6ÿ“}‘º…©À&òD7‚ãR £Î‹Ni?cŸ½Ñ¤pS
´eQÿÀý({ó†˜O.Á×’v‡ì'L7ƒW4Ìt9†§£ÑhÈþ%Ê\¤_êÁÒuJ;j£Äør´Ù¸¨Î«”á=¬ka ÃÕlHcÜIÂ¶T*ÜùBg ø’ö³`ó*ÆU¶sL0(\Ö³Y©`„yó³ö>ØõÖáÖiÑ4Ñ*Å°ˆÍ>IZÔk²–À@VÁKž	#Ê!û¶qt×ßË¼ºb1µÐ˜	wr
KÅ–¸™Oe&M{€¤ã¬PZ;WúcÒr²Ê&°8 d2.•@ÿ.©P–WˆÀ=ÐT°éŠ¢/GŸ}×êŽÀß(c¨Kèg{yj[œ­ÞoÊÞï &5±Ÿž±Áô¼¶îm`®Í:)ßÙ&}²ÒÈpŠÕQøøttö³ú²79ãá~¤¨N&ªÂÙVš
m-Éuå…×6ëðX‘ÒUX]ÊÖ6™ž|©8'ÚIÑîf`:„y³–¸¿µ*¡0…'GÈç…]J(èFéûÑ" m…<=[Ï¾žw}ï+Ô|»÷ƒ€ÃDb\ƒ#ÄïQk Õ?á_\—…l¨oáÚåõ–àÎó™`c6œÎðƒÑÉY)‚‰Üf&²sò¥=¢Ûz-oï9^“™ë.vÞ`	 ¨rÉžÈ¼‡€òSkèï*sònzòÖÞwû[0ßFPì.×Æ'b®ÑS„{t×Té­¼FÛùˆ~6°C©ì#¥J{Žìb¶yÅ¦)Ÿ±'#4×Ó§H£Ôø‘HZú8´Ð@ÆÝw‹$˜‹¦NNÒŸì¾ÊÊÇj™ýú
÷vUfÙˆrD$zÖßOøyÃ¿Ô‘ÄÇËÞ½{²tä}÷ÖS&Nß9hæçZPŸ •-ÝÀL Õö7%¾gºšØMåW8
¶•fhÀV°Óåkë§8®¼(dÓS|Ç /gVÝ’Šöú«»µ ÅöÅÏÔcµ¿Eˆ39Š¢‡ßk½>'§°í¢qÛ¸(¹ìW%ˆUÉßÐ÷½4!IÍ“ÝnËáLxuIÝ[ýþ0\ÏDÞ‹\†KÜ¨mXãô•URYÅEÚo=ü²M]•¾œÊ§-§Ò	»ù2}•CÖPéŽb§,™¾pÊ§,œÒ¼M¹1}µ”CTKé^§Œ˜¾DÊ¡J¤t±CL_åuQºCÙ)û¥/†r¸b([A¹1ç¥¯€r¸
(Ý‘ì’éÒ—=9\Ù“NHvÎoék¦ÖÉF»W:éœ|Ú'] ë®é«š°ªIg»EkúR&Ÿ°”I7ì6kúú%¨_Ò	»n±š¾hÉŠ–tÆ°K¨¦¯TrÀJ%‘ì©éË“¬<É6HnÔô5IV“¤3â4}!’ƒ"éäašc…Ðû#&ÌSÒ#eV]È¢½@h(m3jïLôý‡—V?Ö­dV¤øö2x¦)«˜’¨llBéí“Ç˜.Š¸>¬ Æj©›£fÜpÇŸ†9¤äMÔ9wxßZy?.`1—@£‚ÇâDLœß?‡9lÒüÈ„%c–\VyÞ>VÍÙãz<kOðªCÇµ¯ð5všÎv¶rÚž—2yùä9e]¿‡Ã§XÌäÍÔ–ê°CU&ñy˜¸Wm0&QQ‚Y••î^’g„ÃcEl|áÝ&OMŠ5%q\nþÑÑf´Ï,ù›Žiðµ.|Ù„%’Æ©8R{—çùKfdZwSx¢´Í¨	/|ú¨/‡Ððª–ä…cŽæè”Ûîa­œ•êÚ-èÚd«¸c-uµ*ùÐ"Þ1-Õï"oîª‹êø|'ÉÞW?³@ÃìeËMJõ5L\´€0uÍ¹vÅ"@†w
Ê|h“¿$³áíV¼Ö’{w§b_ŽíÁ‡=Ã[È%p;uOK[¬€r-dnÓ.Z…w¬˜sÏER¥"j%Ó&Öð4„õ/&¯´ÕL–hÇ€FöÐ^w;ODÊ—‘ë™^)o@KNQŽé²®%L]ï]„³)@ïÍä˜Lld\¥<Ì©³‘]æä01bÙ®€ìzUèÀÛç5Y þ™%’ý3dì	evF®ÏÕ(Bñgä½üC^°ÑŸeÓ{®Ðs%•]‚´\×7Ä–É\fUÆHm-¾4ÞÅ3‡µ™¶//½æhžÐT´˜Có©VÝ²öótˆOmgØ“\µª@?ž† [D×AïmäO»Ûñ{=tÚÃÞz1Dëáùj¹(;/¡ç¿s[­Å=\†h¿+÷Y¥Ëg”îü,Qñ3xx®/ãgµ·ŠêT%ÿ
7üªÅœ)Ð‹ÁÐ+]ý}/ØÏG£ñht2:…¿þû)N‡ìáèŒ>ýÜ}j*XÇUùÃ_oåö/é+Fí«äØ}7Q¾ž”«D3wZÂà–vÎO°ÂL)01	œèß{<¯•ÎØÚ9—36©$
H{ÿê\ô\è:¦î	U"´5ˆÒñØÜ!¬DiËOÒs›Q’@Žõ‹@*¦¢ÎVWø¶…åµm¼¡°Uÿ•ð6¥©l5H»$ù7ÀTØœê7¸Õˆ_Ú¢Œ°—mÍÚ°‡UC­XÄþÚŠ¦µwK9X`†¸»&–6Õ—Ÿ0iÚ¯Eh½ç±'Ðæ„šee£p={–ÂÀ¼/\)ƒ[~šRiŠ\å'ŽTÄï±SA¦ŒÀ[ÝÃÄdUå®®§±öÕÓ±UuM—Ø³)V¡ÙÂE6ÆìØÛšÕ!¶Aû1]÷Â®]«]|·¾Á`Þï£ûŒ^Ž!rÑ&Ãš¨n{Òì™4ªèÄ™Û˜VøíÊ™›˜r-¢°Ê™€
ë(C.ú/š#Ek9ËÛk9
›Ôí¬¦¬rí¼¶öëÚ L`-wÑ[ä~-²VôQøòë´XgîXPO¡ýSÔ|4
)±‰>¿út!ÁÝ¾Ðá4ù|þ)Â‚ç™Ì«•-ÖÇ\RÞ^¢Ý“Ù«lDŽOÖ0¥â~éÁc&.²Þ®V±Z;œ6ÎA…\*þÛ{_:§`,É¾"µ)ŒE
&WQØ~‘ÂÖ/e|V
/|pU1\PÉVÈ¿|­CòºõT ¥¦%Ì}°½]™2¸Ås,j8Z†U4Èu$¥ýaïåöïåHÑGãçÚ´ØäéÜ‰½žFû§‘SõÑi•¨dÿë™´&apïˆˆcì‰ÅžGûç‘ÕôÑðh…kxdøì/Ì››ÊÓa¼ªÅ'OT _N¤)9žD‚¿ý×„Évó/ã .´–×MX!Í< 4X¢«L4re>MémôR^¶J«N¾ÎÄµJÓßôjK%|oÅÒ½ªŠÎ¬Ý½Ž¤TYð4{ÌÈ»Œ"ÿjŒö;Êö;®üSí…¾ Ž}U?»iÁïGžÙ³–4ÆÛYyÌG¬÷|nxxúÚ9Ü ënÇiÛG±×¨îìç²½Îe¦a.»‰ä5þ5H§á1Ÿ   0707010001f0a7000081a40000000000000000000000016a102a8a000000e7000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.node.rotate_root_pw.conf.gz   ‹     ­Q1RÃ0ìõŠ›q¸wÊ”4)&Òk0:å$“ß#aâÀP¢-oO{»«¦©	ÕPE9á¤ŽÂœŽaþ·\]wu»SÏ¿Ã¾¨âø×™Åv÷ÃÑ°ÓˆLn*¢´ý:b½¶×c,‡HpžœÀþe,z=énð‰=nb ì²1À¸Þz¤7xˆ3_	³B4yãð‹‚ý¡ž…Ò êÌF›äØw§¥#*Q˜OäÙ‚rûãƒ MQÚ8hAkÙ´·ÚVÑu¯>éK~­vëì!¯±RŸoKYm   0707010001f069000081a40000000000000000000000016a102a8a000001db000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.array.emcvnx.conf.gz ‹     í”QOÛ0Çßó)Nêkñ
+Ò@â!íMÊÅ¹4‰í®•øð;;iË†*AýÒÊgÿïî÷¿x2seqE9dÆÕÂÊP«fù*¹q«—]v›š°6%-f§©éÙÐv–ÅúïhõÛry²-£¥PÛRB#®HÊ+ë°hh“ëO1Âô«ÓLåãHIvMØ–çIUº¡µOÜÇ½#¥+­àædˆµŠÇšR—ÈŸü%÷ $'™ %[‚!é¼’S75v¡Ž1…A[3P`¡ót(§O‡Ù¦¤<z·Þ+Ñ+kžóHëÞ.ñ§±sÐf
ºeM¥ç”1Z:&ï©ô€ðÕa¨Ï|?L1•402e
‡)IÿÚÎ(HÄAœ<÷+¨Íó*[òEELïoÔ¸ áÂºÚ<6%7ˆIîH²¥Ïæxoè'14z¯n~y©•?®.†{b€t|' ¢~s&Ê»x8ü¿4n¸´ã(­Òè|'^hªk¶J†Ø2|ëÑ80ý´àn>Å§ãs>ð)¶|Š]|äáà¨öÈuoO=	×æðØÚ°çµçõ¶¼þ e™ß}   0707010001f01f000081a40000000000000000000000016a102a8a000016a6000000e600010003ffffffffffffffff0000002900000000root/usr/share/doc/opensvc/daemon.events  Daemon Events
=============

Id ``arbitrator_up``
--------------------

arbitrator {arbitrator} is now reachable

Id ``arbitrator_down``
----------------------

arbitrator {arbitrator} is no longer reachable

Id ``blacklist_add``
--------------------

sender {sender} blacklisted

Id ``hb_beating``
-----------------

node {nodename} hb status stale => beating

Id ``hb_stale``
---------------

node {nodename} hb status beating => stale

Id ``node_config_change``
-------------------------

node config change

Id ``node_thaw``
----------------

thaw node

Id ``max_resource_restart``
---------------------------

max restart ({restart}) reached for resource {rid} ({resource.label})

Id ``max_stdby_resource_restart``
---------------------------------

max restart ({restart}) reached for standby resource {rid} ({resource.label})

Id ``monitor_started``
----------------------

monitor started

Id ``resource_toc``
-------------------

toc for resource {rid} ({resource.label}) {resource.status} {resource.log}

Id ``resource_degraded``
------------------------

resource {rid} ({resource.label}) degraded to {resource.status} {resource.log}

Id ``resource_restart``
-----------------------

restart resource {rid} ({resource.label}) {resource.status} {resource.log}, try {try}/{restart}

Id ``stdby_resource_restart``
-----------------------------

start standby resource {rid} ({resource.label}) {resource.status} {resource.log}, try {try}/{restart}

Id ``service_config_installed``
-------------------------------

config fetched from node {from} is now installed

Id ``scale_up``
---------------

misses {delta} instance to reach scale target {instance.scale}

Id ``scale_down``
-----------------

exceeds {delta} instance to reach scale target {instance.scale}

Id ``crash``, Reason ``split``
------------------------------

cluster is split, we don't have quorum: {node_votes}+{arbitrator_votes}/{voting} votes {pro_voters}

Id ``forget_peer``, Reason ``no_rx``
------------------------------------

no rx thread still receive from node {peer} and maintenance grace period expired. flush its data

Id ``instance_abort``, Reason ``target``
----------------------------------------

abort {instance.topology} {instance.avail} instance {instance.monitor.local_expect} action to satisfy the {instance.monitor.global_expect} target

Id ``instance_delete``, Reason ``target``
-----------------------------------------

delete {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_freeze``, Reason ``install``
------------------------------------------

freeze instance on install

Id ``instance_freeze``, Reason ``merge_frozen``
-----------------------------------------------

freeze instance on rejoin because instance on {peer} is frozen

Id ``instance_freeze``, Reason ``target``
-----------------------------------------

freeze instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_provision``, Reason ``target``
--------------------------------------------

provision {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_purge``, Reason ``target``
----------------------------------------

purge {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_start``, Reason ``from_ready``
--------------------------------------------

start {instance.topology} {instance.avail} instance ready for {since} seconds

Id ``instance_start``, Reason ``single_node``
---------------------------------------------

start idle single node {instance.avail} instance

Id ``instance_start``, Reason ``target``
----------------------------------------

start {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_stop``, Reason ``flex_threshold``
-----------------------------------------------

stop {instance.topology} {instance.avail} instance to meet threshold constraints: {up}/{instance.flex_target}

Id ``instance_stop``, Reason ``target``
---------------------------------------

stop {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_thaw``, Reason ``target``
---------------------------------------

thaw instance to satisfy the {instance.monitor.global_expect} target

Id ``instance_unprovision``, Reason ``target``
----------------------------------------------

unprovision {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target

Id ``node_freeze``, Reason ``kern_freeze``
------------------------------------------

freeze node due to kernel cmdline flag.

Id ``node_freeze``, Reason ``merge_frozen``
-------------------------------------------

freeze node, node {peer} was frozen while we were down

Id ``node_freeze``, Reason ``rejoin_expire``
--------------------------------------------

freeze node, the cluster is not complete on rejoin grace period expiration

Id ``node_freeze``, Reason ``target``
-------------------------------------

freeze node

Id ``node_freeze``, Reason ``upgrade``
--------------------------------------

freeze node for upgrade until the cluster is complete

Id ``node_thaw``, Reason ``upgrade``
------------------------------------

thaw node after upgrade, the cluster is complete

Id ``reboot``, Reason ``split``
-------------------------------

cluster is split, we don't have quorum: {node_votes}+{arbitrator_votes}/{voting} votes {pro_voters}

Id ``resource_would_toc``, Reason ``no_candidate``
--------------------------------------------------

would toc for resource {rid} ({resource.label}) {resource.status} {resource.log}, but no node is candidate for takeover.

  0707010001f08f000081a40000000000000000000000016a102a8a000003ef000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.listener.conf.gz ‹     åW]k#7}÷¯ä!-Ä3MvS¨Kú°”–@éB›>•â‘¥;a¤Õ‡½¡ìï½’Ç'õî²uÓB<–tî½çÜÍÅÅ)?“vÂÁi"ð'‚;­w§ånòûìòuëålo’KéqazÂq„u|¡agé®ÐŠ‡7IyÏW$´<é¸wn †>ƒ/ÁP­ì;¶¤À”È‘áé pÇCL¹‹ø]rè­ažoöº÷)Äí³¦Êg wñ‘­¹N0Ò4Œ‡Á5Ö7aÆ¸Öhä%ãFâÿ¯+ôàÛlñŽMŽð,¼>Wškë"þ	kQÇÞÕ¯~»ÿéû_^¿~~ÞT×Ó›¯^\l]s_ð1Ô‚ÏK|Ÿ$Wòšðke–Y2Â¤#<†±¶øE¡„$XÑjPÃYebÉmp<vÌ¶L£Ø¨¹à$S†5ÍŸèù»×›&kH;îNÇÇ‘T&Ìƒ«ùRÉsÍ‰on_|ªœ³3¨™ŒzË(\ˆ*{¬KxÁqË¨H7ÒÒ!MŒ§ØY¯"fÀòqðY©1‡(¹û!¶Ó’íôYÙNb›jAÉù´ž¯:v®”ÿlœªaQ´Ó-Ö´j™|iYØƒ¸ÙrÂœ·k%3£;/î[ŠW¥»ÙO¨Â	ý#	…3IIê…´épëÑ¢Œcù
¶¤þVä~“ ÄP‘…P²½f†÷8"k!Š´ÞöO²áÒyhÁ#“s:B'.±“rÕ³/Z~”F89Ùe^ùò*Â¥ç‡m]~f]æm[ØŠýHOØ¥[ ã™‰+ŠfÓ)Ñ°´Ž»ÁT™Ú9$ë{#šY+NZÐ1;-È,à@CèmNógÉ‹¹ÞÅèÂ¬®1Õ…¶|Um»}%l_SÁÔ¸î±ç—AêjŸuAœdÅ±ÒqÖÇs­–ë›ë—]-Œ˜ÇÖ1:s–àŸUÅüÑ×´{Ã\Âºà"Ó¸±·J	«5ˆh=–•‘ØÑXï¹no¥¯Ñ“±xÏx¤=<I×b#Ð®d°–”«ÇVýÁ_x"EOêg&¦`ÖHÈ§ÀÊá‰%G1ƒ ì·Œ±ÚÌš‹Šˆj0G×LvŒpÓ>n‚ü{O\
ØnšÊèLÈ fÑcÌÌ‹<Ïu’wÝ•¤8’ÌQ‡ùÿäÕCý—_-2ßózA[Î¼Ü~Žò>1*÷é{{ŒÎ¤Î•ÈŸàŸ¹°Vc¦ù+uÄ2¿^fÜ9jo7Ø,âÅ8Üu¸S¬ÎoX™O<pW|šü­¦ç÷   0707010001f052000081a40000000000000000000000016a102a8a0000032a000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.pool.freenas.conf.gz  ‹     í˜KoÛ0€ïùrM[´[÷H·CwYoE‘*2¨‘%O”òÂ~ü(ù·™³­ÉÚ 1Ê4ù‘’hw»»<:]¶Ã#˜ËQ=æ9°ÔhŽ[™Û­w»e×¹	Áv­Ô	Ìo;1èÏUØNð‹™±Iå·–/Hs´Ã#€Bar>TP?êÚz
?¼´¬)H¹WnåÛ7£¡2ŒÜ[(1!S)Ø%+EŽ,  ×c`šgtKÊÉ1Ä›h3ÓŒ
 /Žâ_ý»xê~
ƒ/ï˜Ôq¸6	4Rm,Ê£:Ê‘·ÜI£é™Ñ@€lÌðððððþ*#&(—ð¿r…¿§\kÖ0ŸŸžý5eLÁ6n/Bkàÿ¢”™Lé$wÆ"s†ÍÉø‚Í¤ï÷ÄY;)¤}j,Èñçv#Ûñ>¶ð&Ë- RJö•°ÔcÂçþ…2×‰L¸ì?0Á~Ryj “Z¾¥ßÑRæG§•pV	o*¡s^	ï*á}%|¨„$,Uaü~ø §W+ÈLÁTÌP=å¨Š²%O‰ÄÉÈŸ¿l–žgµ‰Ñ²n(oNS‚ŠØÔ(ŸRcl²ˆ°¦²y9À;À{xR#oaàr±¯ëk­yê6¤—Wàú¿lcM€„ºðµ6æÜ"¼*ÌWBy.©>CV`õ`‘^yýn)Â‡}7Oñ©ó;•
pAÝgVÖWÆ]œìá{œJA“:TdÄU‚ ^á™-´²ÉPM&wûÊëæv«†t¬`¾2X©iQkÅ!´Ek^Bd"bèd¨«Éâ˜<\Æyï±ùF@PÛC…	k™ˆI¨ùR(”Ö,P²^YBÌ-|7”w	µÀµ™©vÏ…´õu¶è6û|f¼v5²rÃ0ãòI0	¸‚Éæ]w(Æxµ·é6§\ùXe.œ¬GaW`*¾EÏÔü0¼á°îª’ØNÕÜÙw *f{‚cZN#NjÛTÜa`ÃZ­Â…v|^ìw<*³ö*³"ë    0707010001f0da000081a40000000000000000000000016a102a8900001047000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.rados.conf.gz    ‹     í]msÛ6þž_™LÇñœ¤Ê¾ö‹›d.×—¹Î5ÍÍ%í}ètDˆ„$ŒI‚%@ÉêÜ¿Ý@’-Éµ×fúâÄØgß ìÂÏŸóÏ³çìˆ\&õõ€™u%XÍ3¥Hî¸£;.ïžý‚“}^Ë27¿>ûŠ&ýÊNûÙ3ýµX¯T]uƒHs)J3‘¼ñ²J§ªâÓ\´Ý}Çs-ðM-~kd-²í7U­–RKUÊr~½ÉÄŒ7¹éÆþ£*é…,¢–†—©ë)|Æ^³…à™†`0mQÛ÷º©œÉ¾™‹Ú¦ÄèA§ðÅ×Ä&3fk´`3U3Þ˜<•)708¶’fÁà‰“)-ê¥¨õè|ÕrX?Sê.ÆË‚Ï…~\®¨›Ge:ˆŸ*qý\j¡ñ¡e2qŒ•¼š-x™å"cÓ5  5[ÎavZ5u*FlµFèŠ§"PX-*^s#2Ér7BðTiÞ+†‡Â1Õ"Wêšôu mF‹´f—Âˆl«Žï`',¹J¯?qL@o@þeâ¬]â&Í-—‚ý—é‡áÂ¶Ó Á€wÈàBe¢…/Ð.( ƒ‚–â. &¶¯‰áóO“LT¢ÌôUÄ  Î~9³\:ûuË²ß¼—i*P“ÐÒå, àEØ<BçwëK©&U-DQ™	ŸªÚ|œ¼c9
Lí›SéŽ´¥ígË)M•‚®Ê½ïg`º‚—$$É€½xÿó×`ìòœ9¦Ã¨´Dçv.0…<0„þËTŒæ©L‰©ãíK|Aÿ"†#ö^Â’6¼ž“E•‹l/u§Y¦@`MUASë/s®µ/„¾9uÓ	%ÉY’$ÀSKB˜»º^]%›B• WÙT0ž¹D7Ë`¦Ñ$E=ô.šM¹–ÖäoY¶8Ü!ÍUÏžœŸÊ##f²„8†wŸ·r(tÀµÎàûH)€ç¿:U-ŒoÍ( }‹d*†¹XŠ'ýeÇØ¥— Q#PŸ™ÿ–°¶­v+ài¡Œ˜Ô«§çNž¨2õß:Îè žÚ#ð:6	5La¸‚É	Ôo¶Ç8h=nûm–Ìá‹0O‰`,pc`ø%{A@è¶!ß ø5+Rs0#ªÌ×ç„Û_ï².ðØ‡zÚ{Ž¿²ÏiVm"ï–lÝzíÛ ‰‘N@lZ-¶}¤ E9ú¢)EWÎ<YI692²[î[qß¤r˜šË	;ðŸcgÐÄºmã;šLƒKÃ!£`ÒÖ,âó€ÜTÁDÑðéQÅ¾³Ó ï5j±ÿ¼Ëkv$ö©…’°ü0ß?«ÂyW»¥o—§ð™›
ž7nSÀñ
øzy§	“¿‹'ÇËSoÜX¦lðÙ*©õ$ô-fðÿâ4ZãÈ V®ñÕ+v1w1ê–E“Ji3içß‡„ŽíoÚeÖ²"{$nDÚ€Åã3#â(00>Wéí*¯’v&	³ÀL×µªAkJ P7Hv!üKïdv|q#²Ú=”Ê@be²áYî#‘¹7„ðŸ2›öë´#+eÈÛä!€:÷ÃUU=žÇÑLUC1Uuüš²÷•'RË€µT+‡‚Z‹>ú¹/¢SÁ¦8uø"s4ûàçaH9úñˆÜÁ>ö9J>4øÙÀç^°ö¡ÏÑôò¡±Ããðõ‘Ïé”ò¡OŒÎnLqÀ¢4OïÔàøýCä•Æ´àsSB+²j0•<:Nïè¤Î©˜ •.ÂcÍJü$@•Ã”çy{Z\	U½_ºÌ'¦QÙíiG? ½®i7÷¼Ý ­5v]©RKà1îöÙ49'`~à™ÔˆÊÿY¾ÂG?zãÙ‰€=š
&ç¥ªíYCt’ÒT$2zÑ˜L­Êð ø{£ñÓhž¯øÏrPX€P’”Ÿó$1ö^ÜìýæÛïÞüôÃ‡‰ãj¡ò8'Ïö	„üGáxýûL	:ÄaãÝ$4ìMãláž´ö¦HG)…È$pš„Õ45ð£IAW +²«Dì%ži½öÃKè`ÏuBJtÂã&=ò_Â¤ºó.ÌÏ &I;·tÁË¹ÈÑôâžEi;nùallO‰Üã}GD¢LyÕgEÙÞÇ±ø³Õ±øÌ5‚Ž‘²7”â€Ÿ¢Å„A3iüç„TÔfd³Z²¾kÚôØð”rm½0Ì8U®Ö"ó„½¢!U.KŸIýíŸÐ2¤•½ ÅZƒÁõLÝ²Ù¦–ó9È'Ñ#%S&­»ï èÜ±,¯5Š^¨Oß1Ñµ±‚'ŽçÐtªNÏÐ”ºDiGrŸ¨
M*ÏŸœ žÚÄ(®¯!4Òë2Õq¼„Iš`˜ÙY©¬ç80‘c]À±%hS@ mr±„Ð s™70FôÝžûÁ˜1Ë{Ú˜6WNqÊ–X€ßPw®*×Š„c‹jà÷‘p¥sÌUÎ¶ÅFd;ãK¨m|Ã±tíÜ×Aó3Ì°€˜Ñ5±û¾àÛá'L@´·Q‹²”B6ðÆ¨‚c&:Æ¢Þ¬bÔã&8b?i1kr”ƒ[©äòœlSTl&s¡×ÚˆB“ÔˆŽÙ–¤*-¿^AªîÊ›O¦ùµT“•óÅÿÙnáCr­ÓZ€DM*J—þÇüë­*…‹R0 ‘Fý²¾SÌAN–`k‰»#ö3ÏàT˜•%»†ãñxÄþ)êRä¼´“¥÷”£‚ñåx²iÕL~k”á=¬;aÝH2iJ	i˜8†+_ _Ózt^¥”êeL
Ãz6¯øAð›ŸÅëà6oÕi#MS­rÜY¢8 %-Ú˜,"å¿Õ¼FÔ#ö]g(h®esÃRjÆ!I†*
àb>—…4ñ:€ò¾Y¥´$éÜmHádSL!8 2­• þÙFgËÁÓòN*Xt%É—ãÏþïÚÀß)‚ä%Œ3Om‹ËÍïÍJÙï½´BMÒO}ìQ*lÐ½Ã´Y#åëFÒÎQæöÚ]x|1¾üÂ›`2cœÀÐ§ªAo+MCIš #¥nÜFáÖb‹Z
»ôèRFËdêå¨¢Oô© ìN!È«µÄõ­e	mSxáåyeC	Ã¨ý8"´­P/.wK_/wÛÞ7Èùvo‡©Ä}eÜ!¹h+½#ö5üw#¹·û„kª Å]é·CØ.—ø`<¼ì E0ñÁÝ`¢˜-í½¯%ÙÊåü–Ô\¢ç– €ª×ì…,£}X Ÿ[E×˜á»Ùð­ýîÖ· ¾sÜA±«\»?‘b1‚š…k4×Xéµ¼EÛ*ù˜þì‘¥Š	îxÔ*ïeäÞæ›å|Î^ŒQ]/ÎQ ü¥ÆGnCÒÊ  B? ™jˆ]D‰õ–NÔÏv]eésc¨@Î(ZÛ5…•Æ€”DÏö|Âûü9Üþ¥d~¿ìÝ»·!;H'¼ïÞz‘ÇéÍ¼¯•®&ÂÍÔÄ“'%~dº™ÚEåW8¿m+Í€ÙR›‹PÊ©ÅTé=2®W¼ª°F¬wlGqÜàå¬ãª©|i]´QZ¼b_ì±L=V§BœÊÙ‚@ßÏm9Ð=ƒÄmoP²ì£’?ETòÔÇS‡&D©ëÙ­ÖMÛìsšuMÃÛ<?ã™Ä[‘eâàBmOŒÓ—Ç<JyŒÛi¿3ùå>Å1}MÌÇ­‰9»’BûB˜Ç,„9ÅƒRAûê—Yýr xû@û’—Ç+y9ÂƒÓ>û:—Ç©sÙ‹âáU.}qËÇ-n9ºC¢˜¾¢å+Zñ° ¦/cùˆe,‡a·7†ékW­våïÂ<U=±?cŽ5åÉQ2Îµ¬bM@:hPPb5ŠµVØxm™ñ¯¶^¢(1W_hS7)åÝØÒºœ
3ãÃ*Ì0Ä=¿Ÿ© Æj­»ÝIn¸¿/­•bò>Ñ™8<ŸÞÑÀiL@wÇó°½ÙÙíÒÏPŒ2iù(„ž¦4¹nÊ2>‰ãì¬ÏYk	Þtdè„Ï^XDÇQÔËZf¯_¼¤DÝ×ƒÑhtŽõ/x>±g	ªÀûÃ2Ìõjª6è¯›*I€p@«±Ôí­dîºµ·‘Yj±ð´B±£ŠÊ¥s?9±Ÿ2±ú¶“½¾<"º4Ï±d¤µ./Ë×ÌH¼…Ü¹°Dy,QSž^ûŒCŸAßÉUKÉÇ²î\aÆ®6.ÊÊ;Ð+µå>tYlTE'Õ Mð§*HÈð‹Y­~e÷U[‡å³¶%û]Û&æÑu(›”5éË^\é j@˜íÌö>à˜()½¢ÃòXøkR·â-—\'Ü¤¼âÉ‡#ÃOÈ$pëºgµÍo_·W˜‚FµZ–ÌD§‘5¹H¢üËÌÚž„ü…·YÔ6÷#ÒN:Ú#ûÞMl’‰œÃ s—¹ÆàSÓi“1ÃÒµ×õÞEÿ]^ù‚cŠ(æŸ™69“*ÚV—·7:LÌft/p'ìz“èÀëç- ÿÒ
’ý;&UØC-vI¦Ï•µ!ùK²^¾“Wl¼ÇfYÆô–+´\YcC[®f…,eÑŒØÉKg]¼äØË¥5/n54/È­Ð<”Ô5­þœ°×8©tÌ^”*Jˆ‡qœ‡ [DwAoo5ï‹ÉNUøÐ`Ù—š¹²!²Ð9Þ
¼¶¶=Ãä{0xu)ô÷Ï­ªš-«YÎÙ´‘9^3ËÞ¿ùÑÝëOÉÛ‘P‘²-OÊo­Ñ±iX¤n+Óíùí,‰ ÇÒ& ŠYjóÍHÎ¶°×PÚÆûn2î·ÅîQµfÅ­éåo€Yr¥»Mš¼ŽBl½6¬™DA!x{x¡€%‹Øoy®(Þr.[G×åû&Vlš)†`m"ùu=q„y@Ô¬+¬(Ã¸efÉcJFDë2=ÃK„)k½TåÐ	É÷•cÝ¸=kÅ=ÌYTMÜvŒeq3ÌtÂvtáC¾Æ‘Í°@}4b+·‚½bßÀÆlN1Víç|å†m«¬-Ø_Ü£`ßûUê)W©Äã§°B…aÇê—½ÐœXhTuÌÜ%)áå‡ÊÌm’²µrÜ”™@v‰™èO4}2­å¼Œc9Z·í,§,s­_Û¹MmøüVÁÛN…ÙPM¯…È*Ð¥z*MÍq£þî£’|üi”ªéËëi¿JÃ{cûºQ5÷~Zº²œåTŸoª–Ë¨Xb*ð‚& ›ÞV,¿T2ÃJôµ»|†–ç6Ã]0€¿þ ëÍî¹]ñ$ñÅî¯ÜoœˆX\¯vO%Á"õyºv·ÃšÏíV8ÍñnuÊ'`O÷ø$<ÛÚ:&	°>ì´#>)ÛuÞ|Ù‡'N?…ˆò6Á «ñ?Â%¹o€x   0707010001f05b000081a40000000000000000000000016a102a8a000001f8000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.pool.zpool.conf.gz    ‹     å•MoÛ0†ïþrÍšûŠî¸ã.ë­(E¦g-’èI´›ô×’?4p°-ÆV`ºXeŠ|^šZ­–Å
É]Cd×ÀÇá5Íot·ltË²+žR‚«`|‰‡çâ>'ýÐ§])ú=_(”OAxåP‰RÔÔ¨Åé¤ÏÊÆtüÑš€§C›%Vªµ|Šíy}!HØzclP›ÊhøßÐc0:''¢–5æ´€*àz”	”µ¤c^Í‹ÙÖa„W™È	P*V9ž	m<Óø¿Ï. "šcYÅmBþoqN–ž‡*þ)ÎÊXŒÇÈèÇŠ‚S|¢Xbg´P|1\gT¡•Îœ¡åö;»ßRÃï•×Óó/ãÐä;g_ÇÚâád*´5ØnÊ—@‚0ÀNªr?@P±!/Ë{šô÷¶t²ÐlTÀ%²_‘AR™UAÄúÏDH9Ïð½RÞÔ×u¦žÿÒ±yþÑ[Úª£Öó„lè¯	fªZ˜‚\Ïäz¬¸Û¨k,[ûné-;eÛ\erÍ¤G‚õ&íÌÈ·¿¤Îþ|Ú}GÍqºÏJ¨¹‰û|E”b›XKwØ”¤7“o)î´ñÌÛdŠGÏêÕzÑ ÚO»^öì	  0707010001f0c9000081a40000000000000000000000016a102a8900001064000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.container.vz.conf.gz  ‹     í]mÛ6þž_A (’ÅÙîî^ûeó‚Ëõ\Ó.iû¡(,Z¢lb%Q){]Ü¿™!)‘ÞõÚn¼›l£E$z‘ó<3’3ÜÇùóè1;âŠKUe¸¬D3bf]¶üãƒÄ·uÇÕÝ£_»Î>nd•‰«ß=£N¿€n?z„í¿ë•j²‹¾óVh£4ÜñU¥SUóY!º½oZ7ñ{+Ñ·â{^hºS7j)µT•¬æÑLä¼-LßðUE7dµ4¼JÝ‡
Ásö’-Ï|+ƒ>‹ÆÞ×µHe.Sxf.@W2ÅÇR^e2ãFh÷‰¶’WìlŠT+MÊƒFèn¿_€ÀZ4Ü@3™^k#Jh3p})Óò‚•<] xñ™S1À€B·±PÚT¼Ÿ7‘š¥f•3ŠÍÓÂ0™ß¤åNwÞÈdž‹FT&0×¼Q%½è_À‡	›îÍ,Wj6¤iç|Ó»–þk7ÒÃp­Yž9njô6PÔ´n„(k3å3Õ˜Pwç®|¶|X÷PìR4AfJÁ§ªÏ×9YØ\’$#ö¶Õ»Ÿ¿oXÌ)Z¥%(B‹f	žPUp×,ÌÎ?™ª²„¶A?ÁUòŒ©œñî&Þ ÿÃ	{§€$Úðf.B+–e]ˆˆDŸb)à ´£­kxˆd+¸6¬T™`O…¾:™ôÝ	%IŽ*IÐ©!Ìˆ]\®.’MR%¨Uô><5ú	ÞzuR4cÐ‚j›T°×R[>on[¶°Yée:m”2Óš›Å0ôlD‡á	ÈçèG g¤/
€c)K<½„[~ƒ> EoÀ¥p-eDAìíÈ2aßA×ìàÂ0»3¼ž·Eá°	ÇlŒð¶kZ×.rv––1ÂÀ‡/UmàQÁm´ JeÄ´YÝ%:|?gÿfš5Þuš'lÁ¬¼¿”€eìJèÂxè {ðavÛ.ÃG_g½è€ðý"ÌSRŒE8KÍžºâ*-Z-—búšKm6®ªb}B¸ý½?x|ƒ¦…ðŸ€†®#×F_¥ëéKê)26[Û¡Ë"àŠ®¤õ+HÍx#®­6:&?V¨ô’áóÀRËÄq[O¼õØŠk|>U@×Œ,¹#;èŸãÇà;ÜSã=ÚL`ÃPQÐi¦ 3t=7SÐÑ
†`=‰ Ø:æöàGÛ@Z<înm{;Íõ0…î
Õ>¼ñü«ÓcšÛ¦5emCd‹Ìé.!tÔžûÛ/"¸€.1-xãGÀ>nGÐ®œË¢ml„i`Â¾lIÕ£³ó‡ŒQ¯x€»²"drý©YÐ{‹Sa¬IÁ¨–ã¬·¯©ÀÕ«É1êÙjïöEþ½àF|\{M~B*n›Â+ÕëˆµÎÎ1]¸Þ3ÝABï^¸Uõ4–BG§5øÓ®ëÃ¬ÌAñª›ÒƒËÑi#kãÅ•H[Ð-Ïq†EÖà†ê`@¿HÇÖõ\$]Oç ôiÕàô$4-Š]ñÛ‚Ì!pÒ84@ùg¡$U&ÑÚá0’˜ƒ!„?ªl¶<®Q†ºM>P'ã0\U=àyËTõ1SÕ‡à£ð0VÞYªý³VˆØÔFÑÏ¡ˆºy×‡?!2 9?†ä‘£È¡±ÏÝ˜ä‡?øëúÍ.?4öqx ßùÜQ~`è£s;¦Ø`›:ôÀvâî¿‰¢Ö¬ÕôÜVðy5·ÊY¿ç·œðžRØj!Óp¯“¹ð1`€ªÆ)/Š.s£ªy¾¤ejå²ë„ÝòqòQðÕ5íPá>R«[µÆO×ªÒtŒ+áD'`¾ðLjDå/–;tï[®¯¼"{
Ø­Ö™`r^Óþ]´;ÙÖD½hM¦VÕ$ øµÑøŒi5Ò†+¾ÆýQ$J’êKž$—{V±o¿ûþÕO?¼WU„dñß¤L4ÛÆIØ^?S‚6FÙBA{o	jöæxã|ž`
›ö®HGéK¥È$hšÈjÚôÑ¦`+ðˆ+³‹D•ì9î¿ôÍKh³÷JCI´kê:=ñOB§ú=dôÏ %IÛ·tÁ«¹ÈFQ÷â/‹Ê~¸Ó‡½p`» î.ïÚvUÊë!Cï(Æö.NnÀ¿[‹ó|6mÅÓvˆ¸¢t» ü=&4šIã'¤¢wˆÒXÜ…Ë*eÀ‡pçÝgBE]¨5mÉDãD·1c	DßÛEŸÐ2d•Žâ­Ááz¥^óÙ¦‘ó9îuÀIÉ8â“|{(úáXV—)¤ªÅŒÁœsBtÌípwp])¸b¤€¹3E²F—Ê‹Ç´SÚ7\_Bh¤×Uªãx	nÎ3ö¤Rväx2b3óc3h“ €61Clr€KÐ8v{ímÆ¬PÔîYkº|\œ9ð×f`à~oÁÜU…¶»´×¤ã>
­4bŽù?!áì»”k	âØ¾„†ÚAoØ–þ=÷tðúÌZ‚˜Ñ½0aÿõßv ÀØÃd`Ûp;â#jQæ_¨ÞUr#SŠE½[Å¨ÇupÂ~Ò"oäÁR
y	ƒl[Ö,—…°U%šX#®8f>Û$R¯¯G`Õ¶œÓùtV\J5]	9_üÅV0ÀD•é^pÚ`Ô´žãö+¶ù·M
'¥sJôè-êŸ”ž÷ú-t± ž,Á×’v'ìgÌ7¾ ¯hVBTìì”0<;==°‹¦EÀ—®³tŸ2Mc´€_ŸÞ‚lZ·Óß[eø ë­°nÔ{Á„4¥$OL®Â™/4€oh>6¯RJŸ´Ž	:…a=›7
ÆA7¿ˆçÁ®]‡[k¤ i¦UË"6ý$iÑÅd‘À›ÒÙ'ìûÞQŒÐ]ÿ «öŠ¥ô8‡$ŸA¨XB(€“ùB–ÒÄó ªÁ`µÒ’Ø¹Ñû"…“m9ƒà TPÊ´QZ ý³¬èN!‚§ÔT0éJ’¯O¿øÜëÿ¦b:P—ÐÎ8<µoœo>oVÊ>ï #5±Ÿ¾±Ãô¼¶là®Í:)ßXGI+G™[êViàòÙéùWÌêÂ^ÇÅ4 Íx¦Zm¥i)‘l¤Ò­[(¼6Y‡o@DJw!º”Ñ4™¾|©8&úôê÷v¥YKœßZ•Ð2…'GÈç•%4£ñíˆhßBžßÎ¾w{ûÞW¨ùvï± Tâº2.Ž/¼GE¬VßÀŸ	óý#ƒ-mpUºÁåvÁNGg£s¼p:>ï!E0ñÂv0KQNÉ—ˆêI´ü#žs|Gf®÷±óK  slŸÊ*Z‡€	ò‰5ô·­¿ÍÇoìs—0¿óã
ŠåÚõ‰|TÎ1ÐaÔ\kP¥·òmkä§ô³ƒJ•S\ñhT1pä£Í+–|Îžž¢¹ž !ü¥ÆKnAÒòÐÇ B?¬W¸Êaƒ$*V±r2~2²ó*+ŸCÅªFÑÜ®--QŽˆDÏnÂø÷pù—’ùõ²·oß‚l#yß¾ñ”…Ó7^óc­tuF®`&€j¼Sâ[¦Û™T>Ã^øe[iFÌ–¯…,X‹e ;8®W¼®e3PüÇ^Îz­ºŠæú›³µàì«žiÀêî‚gr¶.0Ç7Áu[bw`P‚¸íJ–CTòID%C{¼ëÐ„$õ_v³u,nŸ	ðØë†š·¹Æ3‰÷"Ë0ÄÁ‰ÚŽg(ù(å1n¥}kòË!Å1CMÌýÖÄì…ÝI¡C!ÌÇ,„ÙÅ½RA‡ê—û¬~Ù¼]	 CÉËÇ+yÙÂ½Ó>‡:—Sç²Åý«\†â–û-nÙº}¢˜¡¢å#V´ìâ~AÌPÆre,ûa·3†jW>ZíÊ>Â<T½°O1Çšòä(çRÖ±¥ =4H”ØŒb«Õöo¬2þÓ½…šJÌÕÚ4mJy7î`i<ð3ãÍª’Žœž°ŸVÐËj­ûÕIn¸?ƒ°c)yu¦Ï‡·5p7.`µ@£š§b¬æZa:©]¥Ï‘F™´@?Ja	ÃÓT€%7mUÅ;qœ=éúó¤ó¯z1´ÃwUãÚ.mDiPÏ™½|úœu_Ž&“É	Ö¿àézxÅî%¨ÏäË0×«­GÚàxÝÖIÂ¢P[+Ýžôçvá³ö„?+-&OGŠ[ª¨\:÷ƒ£ÍÔvÓÎÞûî Øð J§>àHç]žW/žÃ¦ýPž¨ˆ5ãé¥Ï8ôô=¯:I^8–môû
9»ˆ“¢Ãò•ºr:¸9ªÇ¢zÔ%øS•@D2|"oÔ¢êŸêê°|Ö¶“dŸë>ƒ‰ƒEt'r“²&}Ù‹+@³ÙÂžl	e¡×´Y“¿!³áñ[¼Ó’ûw)/OGqçÃ–á#ä¸ºóÆæ·Óö¼¬ìN}T«eÅLuºY[ˆ$Ê¿Ì¬àE È>e mss?íÐËžØû®cÓL& ¶ez##žBèN—„Œ–îMºÞ¹è¿Ï+_pLÅüS#Ó¶àaRE—Àêòöf ‡‰<§3º{²ëM¡#oŸ×døç–Höß˜Ta7µØ9¹>WÖ†âÏÉ{ù¼`§;|–UÌà¹v1ù-1‡•²’e[2R[Ä—Þ»xæØƒÞµýå	×ÍSŠVx=dj‚…šÖ~N&øÕ8©ô”=­T”í8	A·ˆÞ=ejfC1Ù]U#~h°ìKÍ\ÙyèOÚ^[ßžaò=¸<ú;çµªš‘-«YÎÙ¬•ÝÌÞ½úÑ¾ìwÉ»–P‘²-O*n¬Ñ±iX¤n+Óé»}/I ÇÒ&ŠYjóÍHÎ¾aZµ/ï>®xXÛ»jÍŠ[×òo„Yr•;¡F…Øzm˜3‰’Bðöð@+–~ãÏæÈÅ[ý/qw¯XÚ´33À9ÚD ×=ö¤šäP³®±¢ã–q˜%)!­«ô	ÌMYë•ªÆŽTÄï§:Å>ïèæ,ª¶
NÇ²¸3èU<ð¡XcËr,PŸLØÊÍ`/Ø·0‡1›]ŒAû>_¸æ…M»VÖ¬/î>¼{˜¥Þå,•tüf¨1n™Ò¡Öiî–4ªÞ‹3Û˜þ±/gnbÊµ™ã&g*ÜFrÑŸiúdH­å¼Šc9šwïYMYåÚqíöCÍùü36Á›N‘f‘iz+DU-53iŽEðo]ó›ªéËë©	¿žÆÆö—sõ­iî—mure•TŸoª‘Ë¨Xb&ð€&›ÞT,¿T2ÃJôµ;|†¦ç>Ã0à#œýš]r«âIâ‹Ý_¸ßâo±¸¯Ú5•CŠÔçéÚÕk¾´KáÔÇíæþwÀîöI¸·um›$Àz¿ÝŽx§ì¶ýŽàÉ!,¸Ó° ÐôCˆ(o"yÿOª¥Ëæx  0707010001f054000081a40000000000000000000000016a102a8a000001eb000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.pool.loop.conf.gz ‹     åÔ½nÛ0 à]OAÀkíÒ±c—fƒ¦ŽkŠÇðG±[ôÝ{G)²CA›p,RGÞwô­VKŽj%Î#Ú‘ö„Eôï·ìé–µ«î8ÙU0®Ý}u]’¾)iW~û'Í—Ã¼L-M\.8)*ôrcaÚé›´x&Àc6šÓ™´Ì6÷»—áOÍ)Õ%…1.JÂ°.zPF%¾ŠpŒ*‰R°¨hÅm%E‘P¨ 2HüŠb·AQwEŸ]—•7'ûÎÐé¸fàsÕÛéøV±‚²	:vÓ:™nôFÚ“Ima!HŽ÷œÑê¶»]£OçêuwÿÏ\B(t=„£¯ckawù«çÞcû!]#ƒØXTÛq¸“ìº²ÆóÏÈîy8è8è…àe€“J”"L¾TJe¶
T¬OVÎyÆ÷•ë=¢\¯›ºôQ¤·!ÿ¯èwtðÖ>Ðavi"C]³tRÂ$þ¸Á„Ü4â\L2å¸Žª…&Û³m¤ïì¥Íå–EHü`¬i?Ã<ûRËÈÝñ?7?A¥(¤¥ÎQÜuÀnr¿? è2‡:¶ÔêU=Å¦ËÍ¢MSqï’Ü•j½<ÑXµ¿W–vÚ	   0707010001f09f000081a40000000000000000000000016a102a8a000002c9000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.pool.pure.conf.gz    ‹     í˜MoÛ0†ïùrM›{ºõ8`—]Ö[Q¤ŠD'ZdÑÓG>öëGÉŽc$‹±­YdñÅISâó"Ýáðœ×`g¼R¸ŠÈŒ l+„*:|k¸óîî¼ìÏ)Ù¡ÓVáæeð“þ˜ÓÒæ—¸]“S“ý„sbË–»3^‰’—T‰™Áv©'1~Ú¡:2(,D4a¿·/dq
·®6ú
¥.´„G˜£E§eNŽ#xÉO+J~¥€ÀçG°´´¶ <Lä]~4yÍ·á‡äüø
ÚfwK
;:“i¢Èw²…žG'‚&{Ïk>ä L¸ º¾¾¾¾¾<À
œòâïAù“0þ×˜[Ëç Êùs‚"ÅÎðò”³"Kô>Ñ Ë•Í¶Á¡€l‡q´•£•öŒuž@0Pch­íZ™¸³
Ñ(^R›,Áž/ëS9!„34ÓÊa¡7—*Å[J¾Î,É!”J·¤GNš‹Þë¹Åö©t(’Jû¥Ï»púëù†ñ,+RWI2šLsðÁ(ãIÖ^*õsG±ºF:ôz(u½zi~š¾².•Ô¦ðªÐý–›n™Û¹R„º¿ð7&;®´äþ²Öa‘‰5 ˜VZó­r93Ë)UáRy=¿ü6.H³È
]çm¿0¸9ùc•þaV#–›.#t03$—Äºë¶Z–Ù§J?}â}wâ|°U©©*‘Ehù²œÊIX¬ÿL„”ó	¾=åÝ@­qõ3µá½žœâÿÍYR´¡EÖÌó	fÓy‰ñŸ W3é?@}!ú©—TÑàU¶ab®²fTO°ÒÞÙñ­ûO÷höeàyžgr™¹ŽÊ–û=|Eäbvc¿àÓa¬HŽÛØ\ÜÉ±­5ù­b“Õ:ÜQ£ÚOØ?£_ß     0707010001f0e9000081a40000000000000000000000016a102a8900001332000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.fs.ext3.conf.gz   ‹     í]msÛ6¶þž_™NÇö\I+§ÍtÖM2›Û¦»»Yï¬Óî‡NG‚HPÂ5I°)Y}ùï{^@,Knä$Þ2ÓÆIç<çÀ9à'ŸóÏ“OÄÿ ¹ÄDµ.”P7ÕgïNî¸½;.ïžüØOJÇêæÇ'_Ò _Ð°Ÿ<Á®_«õÊ”ñE×ƒh¡¢ëI©d·‡GüƒŒ²‘)ä,Uíû¾‘©Ux§T?ÕºTñö¢4KmµÉu>¿îÄ*‘uZuoïè|¡J]É<r¯J•LÄK±àaQ?”€«’ïÛBE:Ñ<3W9´ð±Hæ±Že¥¬{ÇÛ²Vâ×î=‘É—ªôz03^•o¡6‚[ß&ÂªJTFL§˜NEµP¹ˆkËFnM]FJØJVµeàðx¢SåÌ®m¥2ÂáÇ€”¶¢¶*+$‰­°Mó$ÜÌLWp†‚·=jL¾ytüR>ÕX[U<oðOà°/-`T•Š*‡0‰G-O,Ž :`±±ÚxÓF÷/ÄI¥3eêÊ£ö2câ®ÿF¬ÃDü’åÕo'4+“¦f…œŒ$€K§ºZ_ÃŒKR²£†Ó£ÄŽ:˜ã §ÓÛ:J,ñ(ÝÂf;4³Ô-¾irÀV"~8žœþøüdà:±ÙŒoèèçŸþ9w?¥k5×ÓºTâ?|:‘*ü@•á“?¦ðhbŠ
ÁYI[è^i¦Ó³ òËnÜ`Ç;A¬–ïÃ JÝj š÷Ðÿ ç¦þž#4f©µ¦èÈAX?UËäÜÝX[!CuóžS@j9_ë$Q¥Ê«†2ªÙL‘Á¨@ Í=áëšŽÚ»— ÄÕò/x5—‚Ð®Aô7(o¸‚NŒÙ%æyiêâƒ
ú÷Xú‡”4qø
&Ç.LÆ(³‚‡Q«‘ßÊÆU¦\ÄkKè‹/¢:Câ»Îf&…ßSf¤„ü†hgv‰%#÷ÚçdBê"
£áo0¹¥ju¨õy¬[Ä_”Ï w&²‡ý‹+ÐÛ°‘É¬ÌÍ¤(•ÊŠj"g¦||<},Aã@\*¿úþ+±Òi*Ó¡WVc©Ê¥¤¨lÖHHÏ5OF&Ë0r‚ BÿÆL¶7ñý2‰+“)&]Ér®|ç£³"U8$z„B0 ý¨‹š‚:B”J[ b%N•½9uÃ	ãOd	¸+0’DBUqq½º˜n‚jÚøE	‘'Œ/i0HUÛHz&­foÚô ™¬·
žU@†hkIÒ[®¯ñ¹T#ñ
Ât7£)9Ažˆ°¹‰`Šà%)á¿A2ç_|ñÅNÁ”p­—Œ“Ì×*Ñ9„€²{x ¯0”ô­pãb~;ÿñ„òV^¢Ôú·,=‹ ¦j©R¼/f)Ü2•ÄÐsv-iúÓ<Ëâ¥Vwz45Üí~Õá=9
ôîÊˆX PÂ	ìð™8Ýœöª›fŸ0… ¿æ€±Rb §ë3’Ûg»°ÿzÊµo¦%8'x+]ÇYôµÅU±×ì9ZŒŠ×7š§QI@|©¶] ­b/Ä³4^\6(«a]ÿ!T»b%->ï‘JahLâ*EOÿ%¾š°·5¸ê/JJ“'ÂŽ!£`Ð¬4xÝ#730Pš§Qìty4ÎÎ£º½]Î®#±ÏÍÙ\«V½EíÔ…ðçÏè­˜Õ:u<G–á°Djæ:'¶4)ÌñPÚú¦€
&sÝ58ý\o^Þàß£ÑèŒ_’'t#xJóùÐ¤º‘‹óñ§Í´ÓëÊ1]Q;ÈâÁ!Wt“Ö³ü¥Ãæ&.å•Š°4ãNUÎkY¥]çÑÖˆñ·Ví&±:×DèšËÙÃ¯ÕSï­ì=:ø=;¦ß4ãq]’•ÛžšÉÕX-à;²Ž\ºäeQÀbŠP i&Gâß¸$©n
ì`U›‹Í¸”©æ¥ŒAüð;YÉ2À[G‘éù_P[ôo/¿:ÙèZ‚´Ô¸0ðV†ƒ'g@Ä³]x âeo†<i#Ctù‰^ðB”à¡üÌãë…Ò"pu•-1*	D= 14wüµªÀ5À¼HâJ.êZªV9º¡46¤ô®;m([ð‰[¬ùHÕ4Ù’Ó?>¤µ‹TÝò{ÇNÛq]!-ýD)løtfo³`âO—®Ð½áü‡ €ñ¡Li3 Àû+d@Š0Ø]‹×‰ýˆW!?ñ½^‚ÎÔ d˜[ÂäµJÇÌ;9þG¼;¹}T£v¿‰'ÖŽ¢8çm6t5§é¼[YÂæ‘¸ôQªóúf@3»A_¡	D¸g“°çùËOùæ_¯_ÿúÏï¯~ýþ¯¿qìÀQäùxœí’årþ‡–ä†ÀÈ9á¬ºÝÄ
fn±+‚™´†›â’–ó»ÝÍ¶aÜ“ÂØjÒ’è‡Æñ´kî‡E¥.h@Ý¨'m2©T¸2ç	ä"JJ³¸˜¶#™
¾1_Yˆ> Œ€Ùc]ð.˜»‰’Û!™ûˆ“ÖJzQþ^Qú$VN7Ösî/F"soÂ_y<ë×Î¬”>o§ï"PGã~r5}ÊÆ‘4ÓÇPLSÜG~uÞûÊRKµ¿W+=‡
µT}ôs_‰ÎÌÔC‡?¾dî!Í>øy7I9úi$r_	ö±ÏÃ¨ä»?ò¹—XûÐçhzù®±“Ç=Ä×G>§”ïú„ÒÙ³ÿ Vï'Cù¸¹:!¿¿©´ "àsC+²j,0“¶ËrMR
¯¸u·Óê§WZ‘ãc€ “is·I­,”)°¼@.)ñ(T‰œ ÀôÃýÙ5íQ`¦Imk µvÛÄVcf ‹7-Xœ óÅ \ïò_–ÜûÞ“²^u…C­E¦d¬™zžãÞ9
3È_ª‚Œ]ÔUlV¹¿õme½|™®ä7&,@h:Íÿ$±²I\©
 ¾~ýÍ«ïþþ–7*WÖ–¹w•_^›û±Q”:%ú{Gx0¥noúg{8¥Ä6¦ÈùÅ™Š5pšÀZÕ%ð£Ž@W Ëâ‹©ÉÄsÜ4}ÙtoJéh¸	çS¢¼*7èQó$ªË2ó
¹4z[´ù\Åƒ`xá›9ifÚñƒ/l˜÷§Üå}‰Y*dÑ§ÐEÙ®6‹.Ó±0Ó5‚‹ª°hNÝP>¼_ù‡3Æ$‹ªyœ$´áB?!¦Z€f”bÀƒŸ¸f/#®0ÿ¦HÍšr?áÊ÷°pDïÛ—ÓèXÖn—Ö´a³«RÏç
ó°`¤tÚ%é´¢èÜ±Î¯-B¨ËëqÆ=Ñq¯”rYéšR—QåHîo‹ËôÑ¡ õ”·”¥½†Ð“m/Á-ày,NrÃžãd ÐÉ±´	6¹ˆ!\HÖX‹ŒIÏŽû^Ÿ1Kõ{VWmÁÎœÂì<X€ŸjPw,©À-sª›Ø¤êù}$\)Õ³:}Àq[¬÷Avˆ¹„ŽrCàö¥kçžö«‹15bF×`$þÕ¼Û	|;ü†Õ:Üqöø(µ[2™²®L&+L( Ý˜UŒzÜ Gâ;«’:EÜJ%Õ×àdë¬ð6¶-¡FÝH,M"Uiùõâ¨ÚU’2ŸÌÒkm&+…y’ÿ]óÙ{¨
80•Çöb3;­˜£Kûûüã­*…“Ò9&zõ¿”Àÿí%1¥LjÁÜ‰ïeZCgªZ)•‹ó1Éð|<Äÿ©2Wévöôß§"”PZ˜‘:¾C²QQO~ªM%{±Þ)Öír±ˆÊ@0‘g¾˜Ê®×7X°a‚AaXïrUÀo~ÎƒÛZ"§4Í¬IqYd‰p@JVµ1Y@0¨:)e¦*UŽÄ7¡ ¹þ;æ'‰ˆZ€q˜N‡ç*f
àd>Õ™®Ây IŠÂXMèÜè7¤p²Îfœ²›é¨4VücV@¶Q2Z ï´I×túlüé_à^Û‘g\€kÈKègžr‹§›ÏW+ÃÏ7
Ð‚šÐOïØ£vVÛö:pÓÆFª-ì`À¸ã[šŒûf•.ŸŸ~.˜ÿ#ñmXí
¢ÎLÞVW5€Žä¶v…[“uÌàÏxêÑ¥¦ÉôÄPEŸØ`½å•Âš6Q­ñpÇZ¦hÀáãyÅ¡„¡œt× €Ü
xþônôõ¸;Øö¾BÎ·boì Èa¦q]Gmu&²w$¾‚¿7JêºG$[¶ÂUé—CÄ…ÎOñÂxø´)
/ìf¦²	ÙÒ^¢÷µ$[ù¹¯IÍí!zÞÉ€õÚ§A‘•n±¢_ÖÕð2¾áç®a~ê;ÇžåòúD$­
}ªÐ``*=°²ÑòVÚ¬äcú³Æd\ñ(MÚcäÞæ•HR9§cT×ó3.(â5J‹—Ü‚$ã¤ ¾tU›qDå.MÅàéølÀó*¦/«ŠN“ ÊDÀiÆhôH9 <Ûý‰Æoàïþò/u$nÖË./ßx„¸“¼—oÈ‚ãl:Í_«]%²´QÉæzfëO*¿ÄQ4Ë¶º¸ÒÎ}”{¨Åtõ=·+YX·ß;¶£@x¥è¸êBªæ¸ƒ`6àKÍkñB|¾Ç2õ²z¸ Ä©—É{êøÆ»ÎEø÷JPn{ƒ’e•|QÉÿ >>thÂeMí›Ýl½¶´Ì>§QSÑm¾¹èÇ3ÓÆŠ,ý'j{bœ¾<æƒ”Ç¸•öÉ/÷)ŽékbÞoMÌA²; )´/„ù…0‡Kñ TÐ¾úå}V¿(¼}	 }ÉË‡+y9H„§}öu.¦Îe¯¯ré‹[ÞoqË!¢;$Šé+Z>`EËÁB<,ˆéËXÞcËa²ÛÃôµ+¬vå	Þ#„y¬"lˆ}Œ9Ö”'GÉ8×º5Ò‰ªQ¨µv$þ-KfÆ?ÛVxâ¸Æ\}e«²Ž(ï†K_èŒ-Ì7«0Ã×ü¾óPAÍÚv«“²’?rˆÉû 3i¾=óè0ô0&`µÐ £BFjhñˆ;J'åUúaÄvÑN@¦02ŠhrYçy¸'ÅI;ž“Ö¼êÈÐŸw‚Tõ¼ÔñËÓç”¨ûr€‡­býžÏ‡Wx/ÁdxjoŒ¹^u1°úëº /÷øGœºó©è@%·‹¯å3€™ZžwTQ¹tîG›ñC&Vß¶³÷¶)ŽªvìŒ´Öåyþ’Dµ«K”†ˆšIüœ–ñ“ò=\µ”âX¶Ñí+$âbÇª.ñº-÷¡/+õX´óQÚªÏ
†'’Òü¬òî©¶«ÉÚv”ø¹ö5˜8˜‡t#6,Ìe/®t 5ÀÏv>û8ô0ô)h³<Ij#ÃV²å’{‰t)/Çƒpð~Ïð2	Ò}¬¥äüöu{¬<h`P«Åd&6Z¨¸NÕ4È¿ìÎÿë5_‡ˆñÛÍÍý€´C@G{Ä÷ÝÀ&±J%L bwÀ~(|
A`8m2fXº–àº®\ôßå•ã‡0[îë¨N¥ŸTÑ&°º¼½Ð*Iè#Øí&ÑA£Ÿ[´@øOHüoLªàM-ñ”LŸ+kCòOÉz5/y!Æ{l3¦·\{ÏkæO3ˆLçtd3±-ÀKg]äð—X¬Ìn54§Ýçý|¤NéèfÒŸ³¾5L*‹ÓÜ	ñÐ3_è,Ñ»DO™šq_LöPÕˆï,7¥f®lˆ,4F»fÛcò=˜ü` ô«Fž[U5.«YÎéÄ{ü¸ƒ¸zõ÷}†f—Ü?"Ò¸ò¤ôÖN+ Óí©2ÞÛ’6§b–Ú|3’ã|(7Þ÷Aƒ~YìUk\(Î¦ßÇß ³äè»×i¢÷!ÏXªŒBðPìþLe¿å¹‚x«ûÐ¡/w×„aSÏ0Ì ãèïÐÉž8B“< Z­:¢,åÐÏ’Ç”ŒˆÖyt‚çšRÖznò¡áûÂ± 3ßÍp÷sÝ'TÝ7F°,.ÁL'lG>¤kìY‚ê£ ’¿_Ã¦Úb¨¶ó…ëžßµ­²6o}q6€}ïg©9K%?†j†;f§8èAóÀ 1ÅA˜Ù…ÿðC1sR¶fŽ›˜ñ pdÈDÿAÓ'} X«çyËÑô¸mÇœbæ²_»s™º’ó?°
Þ–pŠ0T³ÑBdèR9ÓU)q¡þÝD„|§j6åõÔ… ³óÆüõÌ®W@Í}³¥«ó$¥ú|V(ú¸¸¿Þ¤ð€& ›ÞV,¿4:ÆJôµ;|†¦ç6Ã0€ëÞÆ«@nU|:mŠÝ_¸ï¼…[,î­¼¦2Å"jòtyµÃš?ñR8q·º?æ°Ç»}âïmmm“x²>l·#Ü)»k¿Ã{²4,ð8ý"ÊÛ€AVã?9Sp`ø…    0707010001f031000081a40000000000000000000000016a102a8a00000109000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.array.ibmsvc.conf.gz  ‹     í1SÃ0…wÿ
Ýe²Ó##S7ŽÁu^[©d§h³°ácà¤Å>=K~ïkššeªX²Î2Ûù†ò<‚üî”ÎîWëêº«ËÎ</aö¡ÇÇ‹Ù,¡»ïØÆˆÿWÌï‘ûûÕFé”þmÅLÉÅÑî\?z´C‚(Œ·É3V[ž¡ÇÞNC^­=Å€Ë.PqþÓç÷ÞÑÀÞ-ÙÊ†äÊ‹í4Ú|¤)ËýÙfHRiM	rñ@>Ü•Éµ2ÓæØ
…¥°þV™å`Oø‡Ä.Ñ~FtU;â³òQ>ÊGù(å£|”òQ>ÊGùÔãó	²’ÌË²     0707010001f034000081a40000000000000000000000016a102a8a000002d4000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.array.pure.conf.gz    ‹     í˜MoÛ0†ïùr­ã¦uú‘6=;îÒÞ†¡¦%ºÖêHž$§Í¿%Õ®»,ÀÖXÄ—Dýš|DÑJÆã]^£1ÛáååÀX1·n5­Á”Ûmt»e7ú’©>]…¤!íÑÈGÿˆë'mÄü5h$Ù“^’åº¢ÆþAŸ¡¶ègþh¥Á×îL&–ÐÖî5´/Za§…Œ¢F'mƒ\–’³ö€
ä!7R°œ<î*Œ‹NÏ²Î'ÈZSOÈçÊ_°Ê¹ÆÎÓ48MH\ÙŸp½Li>]M'ÇXXXXû ‹×•»—b‘ÅÜ˜ÌiÖZbh™#{žC+òÜÓ`RSëZƒ`ºÃZ?H5xrvO‹˜—E†b&fI	Ùy’N8+.ÌNg³Ë'Å6êRYäþµû_¡÷3Ôû™?ÂNU¤Õ
Í@ ÐºFPo–ä“´>0SgVóGtŒîózà¤öîW"£Ø†ÐÚÍGxmaêÊÎ(ú~óÞ"ŽynT0™ÿ¶‚»PB%¿È.~éâdÙÏ>(n6‰GùK“¨Höá¾3D8Ä3ƒ¬€éÉErÙ,ÉN3Hàø¬LÄÙ´˜– J<åÛ[^¡hëÛÞùö%/*RCæb©M 8ç	p¿÷çyÓÚÊÿÉ™Ò‚Ö!˜'ìqPÇ­5©­À`*4O;`½\o°kåà9,Mo£ž­õVöÈº=¬î˜XWÝôa¦öêiIEÜ–¡õöÁxº\pïëýˆásCKÎ´Y€]7àªR(Ñ âá!ðòÜÉ †Ø²¥sLÝÎÊs»¶—´ß|²
(ý*XÅxK]×úIª‡­¤“@cäŠ‚¾§qžÇu‰/XÔNiHÓm5ÐµÀ=¬‚.µ.gÛâ]G¡^0þà‡£ÑO7›ìMî  0707010001f047000081a40000000000000000000000016a102a8a000003ef000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.cluster.listener.conf.gz  ‹     åW]k#7}÷¯ä!-Ä3MvS¨Kú°”–@éB›>•â‘¥;a¤Õ‡½¡ìï½’Ç'õî²uÓB<–tî½çÜÍÅÅ)?“vÂÁi"ð'‚;­w§ånòûìòuëålo’KéqazÂq„u|¡agé®ÐŠ‡7IyÏW$´<é¸wn †>ƒ/ÁP­ì;¶¤À”È‘áé pÇCL¹‹ø]rè­ažoöº÷)Äí³¦Êg wñ‘­¹N0Ò4Œ‡Á5Ö7aÆ¸Öhä%ãFâÿ¯+ôàÛlñŽMŽð,¼>Wškë"þ	kQÇÞÕ¯~»ÿéû_^¿~~ÞT×Ó›¯^\l]s_ð1Ô‚ÏK|Ÿ$Wòšðke–Y2Â¤#<†±¶øE¡„$XÑjPÃYebÉmp<vÌ¶L£Ø¨¹à$S†5ÍŸèù»×›&kH;îNÇÇ‘T&Ìƒ«ùRÉsÍ‰on_|ªœ³3¨™ŒzË(\ˆ*{¬KxÁqË¨H7ÒÒ!MŒ§ØY¯"fÀòqðY©1‡(¹û!¶Ó’íôYÙNb›jAÉù´ž¯:v®”ÿlœªaQ´Ó-Ö´j™|iYØƒ¸ÙrÂœ·k%3£;/î[ŠW¥»ÙO¨Â	ý#	…3IIê…´épëÑ¢Œcù
¶¤þVä~“ ÄP‘…P²½f†÷8"k!Š´ÞöO²áÒyhÁ#“s:B'.±“rÕ³/Z~”F89Ùe^ùò*Â¥ç‡m]~f]æm[ØŠýHOØ¥[ ã™‰+ŠfÓ)Ñ°´Ž»ÁT™Ú9$ë{#šY+NZÐ1;-È,à@CèmNógÉ‹¹ÞÅèÂ¬®1Õ…¶|Um»}%l_SÁÔ¸î±ç—AêjŸuAœdÅ±ÒqÖÇs­–ë›ë—]-Œ˜ÇÖ1:s–àŸUÅüÑ×´{Ã\Âºà"Ó¸±·J	«5ˆh=–•‘ØÑXï¹no¥¯Ñ“±xÏx¤=<I×b#Ð®d°–”«ÇVýÁ_x"EOêg&¦`ÖHÈ§ÀÊá‰%G1ƒ ì·Œ±ÚÌš‹Šˆj0G×LvŒpÓ>n‚ü{O\
ØnšÊèLÈ fÑcÌÌ‹<Ïu’wÝ•¤8’ÌQ‡ùÿäÕCý—_-2ßózA[Î¼Ü~Žò>1*÷é{{ŒÎ¤Î•ÈŸàŸ¹°Vc¦ù+uÄ2¿^fÜ9jo7Ø,âÅ8Üu¸S¬ÎoX™O<pW|šü­¦ç÷   0707010001f024000081a40000000000000000000000016a102a8a0000047b000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.cfg.DEFAULT.conf.gz   ‹     ÍWMo7½ûWð!- «IZô :´)’Æ‘å^ŠÂ¢¸#-á¹å‡dýñyÃÕJ”µM-Þ%‘ÜáÌ{o†£ããC>GÇâ€›»¸¼êß¾ÊÜa½;,vG¿­‚=vÚôðû{|OË…uEos°²³™€µ“>ŽW¶–ãŠÖ‡]ÉÊ¯8ú#jGÅã•‚&2Vaã_kˆÜ&×,øš”žh%ÎÄ”9­RpxÛ+ìø‰ªÚ‹èÉaDìÎi
JÎV°4Iß±>×Š¯9ò6:E¾#¥VeF®öÂð¶`…5'JV•ð±®­¢&[Ã^)çÚLÓ†šœ8ÁÙÚÏLáÔ¥Ž„±þEØZòÑµ5^)1±®‹P~X‘"Þˆ£=´Ú3¶ÿmC¿”µõÊ¿¢"º r¾·9Süµ±£¬™“ËN[0)Íïý•Ÿb¡Á×˜„žß™™–w¨Â…X'þ}Ca¦›±õ6xÞ¢gÈj!—†™yÌ7r4ê
qCAhÓÖ–NÒÖ¢´UÎ|{&µ>vsÛõÂ’Oê(-üÍôÔ‰TA[Ó%¿GÉí­Y[c2ýò]1,ÉSû3W3„Uh ”¢QAøxæfEodgâ´–¡<kÝ5l÷rK½ûEo´
ºÛîDP~“LY|ÓB7±©Rš)­ð¶O&Ó¼Æ£™xL°Où²šF¾4ŠÙ—4®6by¦Yó%	³›•öá®²rÛ)Qr†±Ku±§ø¹ÈÊP^•UÁ™Âš™K–8D-3Ž.×Âë%kJè¦ˆ¾Xaú¢%WÎ¥®‰VƒéD®z™)@`òEriTé¬Ñ6f¡í)tÆ•Õš!mìí2¾¢òoJ$™ùs½ÕN½œiŸÀaGÏžR,Ï/CË‹Ë_yK(œßþÂóïoo0^8çqÐOãEæñzÐŒ<õñ¶ÿƒs~opÉoÜÄ8|ÏV‡7<Ûî”]ƒ‹‘¬µ$MÒÔ4v6NKÜvus)ò6º#Æ14¿?S&ÙÀxS¡Ó›í!	²¯X›ÛŠü:	.3¶B]ÌeSía½¶€'áúù{¥‹g+(Q |Æ¨‹³ÿÒ3õÅàê\|÷êõk‘™j¶¦b0^&ü$f¶Ü_1hŽ&(%f«Ï9Ù@mtº‡°àÉb·3  #P²ÉÌÜJ8û
\é¡´,žÉ„P«¸óâÎn«<Àõ7; ìá´²êþ.èÁäse÷û—O¹ŠèR‚ìöJ«iA5ÀóøÚÁ5rOhm^Í¾}é¹¹AF‰™|Ð³8©ƒ`¤Rq[¹Ö4	L|O5/7ûÉ	¿Ì«è%ê´ÕÂI§¹‚‡Rç}E-hÆµ•ˆÌé¥@bÏùb?-­,ÞVø*:Çý=;}önSÔwž*d‚uÊrÁéÔ¬e4¯Œ/ÛÔáÞ=SUDzº5”íÃÞóÞukøU›jí5üA}óQÏ†   0707010001f066000081a40000000000000000000000016a102a8a00000331000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.arbitrator.conf.gz   ‹     ÝUKoÓ@¾çWŒ”­ä	©
T*H\¸PN©{ì¬²Þu÷‘4ÿžoÖylš‚8Dð%±½û½fv<žóšLéŒ—À)?×Ñ«èüYàÎ«î¼ÙM~ÌN½¶?ýœˆè%oÖÎ77n«zÆ‹«3^N¨Ý æ†÷LŸ•	BDž“ö|ÐpïS~Ñp«’‰m_åd³_†kÝêšn©cË^×ÙB÷.‹í98³1d]ÃÙñŒîl¹FRVu^UøÂ'kµí(7…¤9YÖ(î½Äcì®M
‘}Þ‹;ô°¤Ðª€Z¹È´^°ÍX»=Ø£#DíAgŠ^sCÚR@hlk®
0hµU½ÈK^±¸Î}±b‹GXo
 L^‘2¦Ø?
à'D¡!:šÃ½=PµÞõYƒq!–hÜõlÅL$Ã
òPFáÅÚ^¼>Ã<E,Á’_é•„€-$f7»{ü`{
‚j\­ÀQ@ÕÐ	CÖ¥n!ÔBKt¾¢à€Ò<pÌ˜Jù*±@z¦Xîul•V(´–’ØLr€/J"$óB¸Û½/Ìèc*ƒÏ±I¸ÂîÍØ(n«´óŽsFß¥•Í¦Ú60¹Ô\ƒg©”
7pl(èÈUfÊ·^ÕKlhpk3Úr˜e­ÒHG“@q¦àÅ­9TÄ«|$ÆÒdÉ.™-Žû]‹ùš¨×ÝBÊBKëÖP¾AùMÔƒÙ«Ün[öë¨|ap6häŽÛiÌ€"1‚ôÝMÝ77¨FžPWWùéûâØÉt¹}@“wò—>`¸Éo†ïhæ?¿Û*á˜%ô£œ6[ûÍ_7œ1<£Â$@»ÛY¯u\<;úE¦wŸ¾½¹~{™SÝBÿ1×¨{véŸ»‹ï$×}â'Á^ÿuª˜ZÎ®Ø››„L´³'‘÷êI÷©ÏÞ%ñµÂÈP<þæoP¤¹=&çS_ÄÎÃø˜8%2¬õHXe ¼[Æ…œ hGídþïˆ
 |°üFHäˆäÊm«‚Ò]O&¿ }§W@.
     0707010001f0fa000081a40000000000000000000000016a102a8900000db8000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.service.ip.rule.conf.gz   ‹     í\ßo7~Ï_A (œàdU6Ú5	.wmqÁ5Íá’Þ=…—Ú¥$Â»ävÉµ¬âþø›’»¤üCR,Çq³~0lírHÎ÷ÍpHÎèéÓCþ<yÊøƒâd=bv]Ö´¥¸»¸ÃŽî°º{ò«¬Ÿ6Râò·'ßÑ¤_Ò´Ÿ<Á¡Ÿ‹õJ7Å´Vxr|ÀÔ‘ÉuÍg¥èºúÐ´4â÷V6¢¸ò nô…4R+©ôðG^zRˆ9oKÛúg­èTKÑHËUîû)Ÿ³Wl)x!LX4î¹©E.ç2‡wBAÛœÔ=˜Þø°Ž£Û&LLÏ™…r­,—ð:³šÕe[ÍècYÃ ¬ƒˆïH êîÕ§“›4Žcø¢ÎPK¢‰ä›e).¯Bæ$p#±š#
ÂC@/åºª¸*Fl%íŸDfÊk˜hÝHn²Ð8gŠWÂÔP6Â€Øà7×ú&Üf¥ÎÏAUgµ6ö¬SÞƒBÙ!öÏë€´Æ¬-Â$.EÞ‚êùÜ¢Ea4ÍyŽ O³n&sÙM£ƒÆ&š¦E± Å?Dìn@f8å üX(c I•Yl€#‰ÙBø¥ŠÙz@ò°Fë6» ^Æ~¸êzÀó0–©ëC¦®÷Á¯UÃZyOf©öc­2±+¨¢Ÿ}‰¹nÄ}‡?12{ 9?wCòÀÑO@d_‡Øç~Lò®ÁÏ>{Á:„>³Ë»Æ>=à"Ÿû3Ê;†>):·cŠê“,Áð>ðû‡(kÃZ#@Ï­‚VäÕ`ºáÈž_H°@|€4#¶ZÊ|*Sø0@«ãœ—%3m]kXüj¡k·ä0Qz¡žá\<ÈDA¯kÆ;J[_²ÖØu­•‘ c@¸!x8ó› /¤ATÍvO®}ŸóY€M l3í‡Éþ×wpå w¦|®6LÝ+²§ [I€x&˜\(°óÁT¡5²­‰2fÙÚB¯Ðj».ÞXƒïØÖ mx¹âk‚‘, (ËÔ×<ËÆŒ½À¾ÿáÇ×¿üôaDt\-u“%ô	‚ÂÇñxÃóBC„Zjï-áAFÃÞ\o¼ï1c<Î6Á™Ø €‹MYmÛ€>ÚlÚ€¸ª˜fºb/jn—¯Âð2Ð¼n„%MÏWÓÌOzÞ„I™Þþ¢ùY´$éæ–/¹Zˆb”L/íY(×q§÷ÁU€™˜ÿLÌ1æ;*çõŸÌÊ>â2ä Æ†¬'™eZgþílø/Mt«¥È ”†vt[%.-r7?G	ƒfÒ†×	©¤Íxã²ÌÀ{`™@¥øIƒ€—Va˜1p!êR¯0^p0´îÞÌˆúÛFŸ
Ð²d•â­Áá¥^ñÙ¶‘‹p‚uÀIÉ8Â¤[î{(úåXªsƒ2KÝ–:}ïÜ“OeFŸ¡+WŒð"·‘@×èRyùèX€vŠ1	³ÜœChdÖ*7i¼@ç;RÚ­G#&@›šA›´i‰HBc€ÎeÙÂqíÚÆŒ!X©iÜ³Ö©^Ú9mÜË:q”	æ.`©*»Õ½"5Z÷Qh¥^ìgÂ°2¤vÄ/` ®!èÇÒ·óoGÍ4h bFß`Ìþúö ÀÚM³€…[ñµHJªÞZ]q+sŠEƒ[Å¨ÇOpÌ~1bÞ–Èƒk¥”òÙ¶ªÙ\–Â¬•!ÖˆK^A|L¦ÒéëåXuƒÕ‹³Yy.õÙJÈÅòOvZ¸‡©À&TazÁy#€Qgõ—´_qÌ¿]kR¸)šXÔßp?ÊÞ¼ƒ)–À“ðµ¤Ý1û/[àLØ•ŠLÃ“Éd2fÿeÄ—n²ô±ãÛÉ-Èæu{ö{«-`½ÖüØæ¸“l07w¾0 ¾¡ý,Ø¼Î1ÊöŽ	&…a=[4ÖAX7¿J÷Á~\‡;k¤ ift‰Ç"H”dD“%#Y5ox%¬hÆìÇÞQŒÐ]ÿ$U{ÉrjÎ!ËŽO T¬ ÀÍ|)+iÓ} ’Ž³ZIìÜkHád[Í 8 T2o´@ÿéXVPˆàùu'5lº²ìÛÉW…gÝ@à„g¨Kgžº§›ïÛ•vïèHMì§>¶˜€Y‚×6ƒÂµ9'ë	#éä¨ðç@Ý)||29ý†9ýÙÅx¼©Ûã™nqµ•¶õy`B™Ö^Ù¬C‘ÒSˆ.e²M¦>/ ×D·(ºÝ,‡0 `Ö÷·N%tLÈóyåB	ÃhÂ8ºVÈÀ“ÓÛÙ7ðngßû5ßÁü à0“x®Œ‡#Ä‹àQk Õßá7Æe1ºW8[Æâ©tƒÇ!lÊ&£“Ñ)~09>í!E0ñƒ›Á¬DuF¾t@t_Obäéžã2s³‹÷X ºY³gR%ç°A~îý]kßÍßº÷Îaæ»À·Ëuç97è)â=:Ì@U+ïÐvF>¡Ÿ-ìÐº:ÃF—G±Ú¼fó’/Ø³	šëÉs$D8£4ø‘?t<ôqHÐ@ÆÝ÷A¬Es/§ éÏGn_åäs»1w}…{»¶rlŒDy"=»û‰°nàßññ/¤çeïÞ½¹Azò¾{(g4k-¨O€ÊÖ~`&€jzSFfÚ™ÛT~‡³Ç¶ÒÂ
Ø
v³<b-pýdÇÍŠ×µT`0ÅAq<àå¬×ª©h¯¿¹ˆQ‹Z¼dßlñLV÷„x“£Sôø^ëmô99…}ƒÄmkPr1D%ŸETò´ÇûMHRß³ß­·†ŽÙ4ë††·yÇ3Yð"qˆƒµ-1ÎPó å1þ¤ýÆä—}Šc†š˜O[³v;$……0Y³;Š;¥‚Õ/Ÿ²úeGð¶%€%/Wò²„;§}u.Sç²ÅÝ«\†â–O[Ü²t»D1CEËV´ìânAÌPÆò	ËXvÃnk3Ô®<XíÊ.îÂ<Vƒ°Ï1Çšòä(ç\Ö©¥ =4H”ÔŒR«5cö_Þ8eü«k%«º”˜«/ŒmÚœòn\é6¡ŒÀô²
3ñÌï—ˆÔX¯M:É-÷üé™CJÞF3çã»¸°ZJë¾;èØÌµÂtRwJ?GÒü¨„#Ïs–Ü´J¥7qœuó9ê<Áë^Ýð]Öx¶K×IÔ‹F¯ž½ DÝW£ñxüë_ÞÌ]u‡»KÐ•´˜Ó¯·õÈX\¯Û:Ë@p$«uÒ‰Ëáº¹µ"&%OGŠ[ª¨|:÷££Íä>«¯»ÙûÊ#B¦ýIãÕé¼ËõŠYYÖýRž¨L5ãùyÈ8ô=¯:IA8–mô÷
s6M“¢ãò\•ºrtm2©Ç¢›zÔ%øS•@B2|cÞè?„êßêê°BÖ¶—äÞëºÁÄÁ8áÕq“²&CÙ‹/@ˆ³Ù’__ ò0ô©é²<%CfÃÓV¼Ó’ï„û‹”W“Q:ùxdø
¹î–îyãòÛéz^*wSŸÔj91g&_Š¢-E–ä_Îð2„%3Ï@Úæå~"Ú3 —=vÏýÄÎ
QrØ ¸‘™Œx
A`:]2fXú–°t½÷ÑŸW¾ä˜"Šù§VæmÉã¤Š.ÕçíÍ@ó9VzFd7›BGÁ>¯ÈðO‘Üÿ˜Tá.µØ)¹>_Ö†âOÉ{…N^²ÉŸå3x®Øs­A×õ=1‡URÉª­©-áKï]s(·—^]ëhžÑR´ZBó˜©j:ûy>Æ^Ó¤Ò	{¦t’ãxƒî½zÊÔ,†b²ûªF¼k°JÍ|Ùyè²É­o/0ù\8 ý}ÀóJUÍÈ•Õ\,Ø¬•@#öþõÏ¸¸›î–¼	)»ò¤òÚ—V€Eê®2úígI9–6TÌR[lFr®…ûJ×xKÍÛp,¶OÕš+w®?æß³ä¥vûU'D!®^öL¢¢<…=þB'±¿²r%ñ–_²!ˆq÷MmÚ†à]"­ë{Òmò@¨]×XQ†qËqœ%)!­U~šSÖºÒêØ“Šø=õ*¨´øŠ§{œ³¨[å+ä0nÂ²¸9f:a;úÂ‡r#›cúxÌV~;eßÃÆnN15æ<õÃ‹‡v¥¬-:_Übàß‡]ê}îRIÇa‡š’á–Ý)nÒÜ3it½gnbJüå»ræ:¦\Ù9nr&¢Âm”!ý…¦OÆD1F.TËÑö¸kç4å”ëÖµ[©-_|Á&x]Â)Ò,1Í`…¨*°¥f&mÃñ þÑEë¿Ý¥j†òzBRˆÙ¯Æômén g]‰^®Tó’êóA5ò")–˜	ü‚&—›^W,¡e•èkÿå3´=ßðþ]E½¹S *že¡Øý¥ÛZ¤ßZzug*†yÈÓu§5Ö|íŽÂiŽ7›ûc¾{¼×'ñÝÖ•k’ëÝn;Ò›²Ûî;¢7‡°à^Ã‚HÓ!¢¼Žä5þgPÞöf  0707010001f05e000081a40000000000000000000000016a102a8a0000014a000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.cluster.stats.conf.gz ‹     ÍR±NÃ0Ýó'u)m31•	1²tD¨uí±šÚæÎnéÂ·c'i„2pƒ#½sÞ»wÏ³Ù”•Í`ÂJtì…çéè¦nÚÝe/­Ù×,ºÇóÉ’*¯zJ³Ø5{ó	+í„¥u‰z{'! |šPýì(¬Dhüu¾gkðB†GGêšìPêJKx€74HZ¦kÒš#ÒèÿF³oGj–ìü‚\Ce	DÿÞÈ7<lBÒÅÃ‘•9ðI¸z…Ç|v‡l(‡]cå¾ÿ¤kPñ&Ü.¢ò}¿`Xº­HöK",kTáÿFòYeQÌ‹»xþ9šQëÞ8Aâ€©ÍÁ×¥œéµ5å6åÓ³cB‡/`8Ê 0-¹„Keåò²¹o øl¼øhó°Õw'Ù¬j>!´    0707010001f05a000081a40000000000000000000000016a102a8a00000318000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.cluster.pool.virtual.conf.gz  ‹     íWMoÛ0½çWÈµM°k¿ŽvÙe½E¬Èt­E65Ivœýú‘Ší:íÒÃí6™¦È÷Ey>?å5›Ã	/qçˆìÄCh²¯rwÚèN‹ÝìN’{SçØÝÏ.SÒ×CÚ³™Ä¿ÁÝ–|~ñ†VN­5Ñ``ƒó^‚WÐÄþ-Ž+~V6 <ñø£1óçOr,Tcãcžü–Ôñ½ü"p*è÷vÁ¡6…ÑpX£7ZÌ4Õ-ú‰+kBLHð*Aóüm‰ vŽæ°ÞA,U#Ø.à[ãùˆùDÓ·/ ËB©8­,;ã1G>º~°f¶23ñ“ek»É2Žîò  ëgù¡’\4T+»jÉ6®°nß+£_©Æ“Ó(“@Åž¶¼øî! oN…yÀ°-.¡U¶Iò.f*åë@±$êVÞ`êä\YKZ±
†ezïø’VŸ8ê ¦È†TLN5^ãY÷øìãV‰X9Ë«L¼I µÐey,D/¿ažeÃ¹FeÄù§E­*¼'VòW¼-
¢þ“Ô$†¯£'r$áT,¹ø®“à”Æ›åÕ†7Q¾ÉÔM–‰¨ÔÀu$hkà@.#PP‡·½õžžñáõh–l°¬dW9'Öö¹%rÇXø_Ï¨žUè=«€M¼2ÜJ’N]™EXIƒ¯‚èŠð§uY‹a¸R¤Ö¸žªa)6f˜hËl£*kA«Úpo]ñ6ù^ñº»Mù„Òb÷ÈŸ­œ}m+šÌBkVú¦´G•z‰¦ªJ6©„~›Èž'œG'eô”‰DÂˆ/ÓÀ©eÉúÇHœàû‚¼{P÷p½Œiß
Ò[ß¼e®¨©ãYßLQ-ïÕÒ6 ·Ç„q{aQÅ&¬‚.1oì_yÂÙ'YeÒ¦ø–ÚÔaÚcC;è³“Ê§õw>p†I,<U#îüI„ÈböËôÙ³ÌI/Gßr:lŸã£°ã†×%¶žFÔ³öe8‰Z  0707010001f038000081a40000000000000000000000016a102a8a00000193000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.cluster.array.xtremio.conf.gz ‹     í•±nƒ0†wžâ¤¬)4kR:vì”­ª„c.l×gÒðö=‘DêX¦è¼€|æ÷Ý÷¬VK®l®§¼WãÂè.ÁcßÚÿÄ-ÛÝ²ì²4ìÊ·¦ÆËg¶KC—óØYøÂñÇúz{ëC¹–÷Ÿ\‘iëÔ¡ÃëEoª#ŒßCëñÖÂÞ©PãQ]¸µönÎYÜ5ú©Hu{l5¼Â	úV§Ù84ŸØ78yç»(Äað]Îgvñ½„&GÛ¢H‡r7tÖ¹¶}Áõâ¼ÉŸ–ÀXë`9E·Ø<a|tö­Y^“$¬A(xq*4¯ÜÖ=Žª»:¡öò”:½C?°ˆrÞ­*)`_U`Tä§(SOçuÆ»¸éƒ¹=þ„µ$™×ŽK˜â
¾m’º"PŠ@(E "PŠÀ…òd>^ù€çÑþL°®Õ¼µAøá#|„ð>ÂGøá#|–ãóÉAAÎ#   0707010001f0bb000081a40000000000000000000000016a102a8900001014000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.esx.conf.gz ‹     í]mÛ6þž_A (’ÅÙ®w¯ý²yÁåú‚®i—´ýP-Q6±’¨ˆ”½.îÇßÌ”Hïzííz“n£E$z‘ó<3’3ÜÇùóè1;âŠKUe¸¬D3bfS&ôå]Ä·uÇÕÝ£_»Î>nd•‰Ëß=£N¿Àn?z„¸›µj²ó¾‹Vh£4Üñu¥SUóy!º½oZ7ñ¡•è[ñ=/4Ý©µ’ZªJV‹óèN&rÞ¦oøª¢²ZŠF^¥îC…à9{É–‚g¾‚AŸEcïëZ¤2—)<³ ,™âc)¯2™q#´ûD[ÉKö?¶Mªµ&åA#t
·ß/A`-n ™Lo´%´ƒ¸¾’iyÁJž.Š	¼øÌ©p@¡»€X*m*^ŠÏ‰HÍR³JˆŒÅæ‚ia˜Ì¯Ór§;od2ÏE#*ØkÞ¨’^ô/àÃ„M÷æ–+µ›‰Ò´s	¾ÇÇ‡é]Kÿµkéa¸Ò¬GÏ×5z(jV7B”µ™ñ¹jÌƒ¨»s_>[>¬û(v%š s¥àSU„çëœ,l.IH’{[‹êÝÏß€7,
æ”­Ò¡E³O¨*¸k–ŒfçŸLUYBÛ Ÿà*yÆTÎxwoÐÿˆá„½SÀmx³¡Ë².D	D¢Ï±p ÚÑÖ5¼
Ä²\VªL°§0èLúî‚’$G•$	èÔŠfÄÎ/ÖçÉ6©Ô*zž	ýo=:)š1hAµM*Øœk©-Ÿ··-;Ø¬ô*5J™YÍÍrz6¢Ãðäô#€3R‰À±’À%ž^À-?‡Q"‹7àR¸–²¢ övd™°ï kvpá˜ƒÝ™^ÏÛ¢pØ„c6Æ xÛ5­k9;KËˆaàÃ—ª6ð¿¨àÆ.Z ¥2bÖ¬?%:|?gÿfšÞuš'lÁ¬¼¿’€eìJèÂxè {ðavÛ>ÃG_g½è€ðÇE˜§¤‹pš4þš=% tÅeZ´Z®ÄôµÚ4l\UÅæ„pû;zðøM?
á?ÝD®Œ¾J×!(ÒÔSdl¾±C—DÀ]JëW‘šñF\[mtL~¬PéÃç¥–‰ã¶žxê/°5×ø| ª€®YrGvÐ?ÇÁ+v¸§Æ{´¹À†¡¢ ÓLAgèz n® £ÁzA±sÌí-À¶´xÜÝ5Úö"öšêaÝª}x!ãÙWÓcšÛ¶5emCd‹Ìé.!tÔžûÛ/"¸€.1-xãGÀ>nGÐ®œË¢ml„i`Â¾ìIÕ£Ó³‡ŒQ¯x€»²"r7 uVC4ëÔ3®‘WÝ¬PÑi#krƒâR¤-8ZžcŠˆxoø¼ótlÑ9Oºž$0šF5ƒ„¦E±Káo"†;¹y…#~‰Lu€òBHªL¶´ÛÃHbn!üQeóÍ€äq2Ômr@ŒÛáªêÏãX¦ªa˜ª¾~m5Œ•÷d–jÿ¨U"µCôs[D]hzÏáOˆÌ-Ð‚Ÿ»!yäèÇ#r[‡Øç~Lò®ÁÏ>·‚u}Žf—w}·€oˆ|îÏ(ïúÄèÜŒ)6ØfW<°ÍŠûÀï_¢¨5kµ =·¼E^Í¦
;Ý´øîVåñžRØz)Óp;ó]ð1`€ªÆ)/Šns»ªyK¾¢•<å„]wòQðÕ-âãR{«[µÁO×ªÒtŒ‹…D'`¾ðLjDå/–^ñÑw¥^yEö°»QsÁä¢;§-Žh§­‰2zÙšL­«I ðk£ñÓj¤/Ö|ƒ[HH”$Õ—<I&.=§bß~÷ý«Ÿ~x?":®—ªÉâ¿IÉ:¶“°½þ~¦í±¥‚öÞ$ÔìíñÆù=Á,í]‘Ž2<J‘IÐ4‘Õ´è£MÁVàWfç‰*ÙsÜJ{é›—Ð~n'…’hcÉuzâŸ„NõÛl:èŸAK’¶oé’W‘¢îÅ_•ýp§{á*ÀvsÊ]Þ·3%ª”×CÓQŒí]¼ÿ‹·6oµú„Ú­¤½qIIø)zLh4“Æ?NHEï¥±žË*eÀ‡pstÓ'‹ E]¨È¼`ohÝ.%}o}J@ËU:Š·‡ë•zÅg›F.¸‰FÔ'%SàˆÏƒì¡è‡cY]h¤^ª7ýsÎ=
ÑqûÛ%%Á5t¥àŠ‘NäÞ,²]*/ÐNiÓp}¡‘ÞT©Žã%¸:ÏØ“JÙ‘ãÉˆ	LÚÊÍ m‚ Ú4DÄ±û§n÷TãØíµ´C°BQ»ç­éRqæÀ_™øÐ‚¹ª
móÎ®HÆ}ZiÄS$BÂÙw)Ä±'|µ/‚Þ°-ý{îéàõ'˜Ø1£{aÂþë¿í@€±þ†ù’¶ávÄGÔ¢ä¨P¼5ªäF¦‹z·ŠQëà„ý¤EÞÈƒk¥òÙ¶¬Y.aï5±F\rLµyv^_/ŽÀª]iy‹Ù¼¸j¶r±ü‹­ÞÂT` U¦{Ái#€Q³zCÚ¯Øæß®5)œ”‚,êŸ”Áôú-t± ž¬À×’v'ìgLÉ<¯hÖBTìtJžN§Ó	û·h*Q|é:K÷)/FˆñõôdÓº}h•á¬7ÂºUÒ”òà0ÿg¾Ð ¾¡ù,Ø¼J)ÃÌ:&è†õlÑ(aÜü"ž»vAn­‘‚¦¹V.‹Ø]¤E“E¯Ëø°ï{G1Bwýƒ¬ÚK–Òà’d|
¡b	¡ NæYJÏ(MÕJKbçV{ì‹N¶å‚PA)ÓFiôÏ¶G;…ž.QwRÁ¤+I¾ž~ñ¸×5þMùòt .¡qxjß8Û~Þ¬•}Þ@Gjb?}c	è%xm=ØÀ1\›uR¾±Ž0’VŽ2·Ô­ÒÀåÓéÙWÌêÂ^Çõ Íx®Zm¥i)×l¤Ò­[(¼2Y‡o@DJw!º”Ñ4™¾|©8&úÔ÷v¥k¬œYKœßZ•Ð2…'GÈçµ%4£ñíˆhßBžžÝÌ¾wûÞW¨ùvï±fNâº2.Ž/¼GE¬VßÀŸ[9Åý#ƒ-mpUºÁåvÎ¦£ÓÑ^˜ŽÏzHL¼°ÌR”3ò¥¢·õ$ZþÏ9¾#3×‡Øy%  š{*«h&È'ÖÐß¶fü6¿±Ï]ÀüÌw+(v–k×'R¬Py8Ç@‡Qs­A•ÞÊ;´­‘Oég;”*g¸âÑ¨bàÈ1F›W,/ø‚=¢¹žž !ü¥ÆKnAÒòÐÇ B?,U¸Êaƒ$Êç·r2~2²ó*+ŸCõ|FÑÜ®--QŽˆDÏnÂø÷pù—’ùõ²·oß‚l#yß¾ñ”…Ó7^óc­t¥®`&€j¼Sâ[¦Û¹T>Ã^øe[iFÌVøœ†,X‹™Ò{8®×¼®e3PüÇ^Îz­ºŠæúÛ³µàì«=žiÀêþ‚gr¶N(0Ç7Áu[…tË qÛ”¬†¨äO•üíñ¾C’ÔÙÍÖ±þw.ÀC`¯jÞöþaÏ$Þ‹¬Â'j{bœ¡<æ“”Ç¸•öÉ/·)Žjb>nMÌAØ:Â|ÊB˜ÃQ<(t¨~ù˜Õ/‚·/t(yùt%/AxpÚçPçòiê\ö¢xx•ËPÜòq‹[î(f¨hù„-ƒxX3”±|Ä2–Ã°ÛÃµ+Ÿ¬våoÂ<T½°?cŽ5åÉQ2Î…¬cK@zh(±ÅV«'ìÞXeü§{Ï|”˜«/´iÚ”ònÜÙ»x&fÆ›U%Ê;a?¬ —ÕF÷«“ÜpL[ÇRò>êÌžokà~\Àz)F5OÅXÌµÂtR»JŸ#2i~”Â†§© KnÚªŠwâ8{ÒõçIç	^õbh‡ï²Æµ]ÚˆÒ ž72{ùô9%ê¾M&“¬ÁÈðŠÝKP%[–a®W[´Áñº­“„E‡D¶Vº=Íí"Âgí!hVZLžŽ7TQ¹tîG›{=Ëêº½÷ÝYšáY}N}À‘Î»<¯^2<ªJû¡<Q3jÎÓŸqè3è{^u’¼p,Ûè÷rv'E‡å8*uå>t¶mTE;õ¨Kð§*ˆdøDÞ¨ßEÕ?ÕÕaù¬m'É>×}‹è”Bä&eMú²W:€f;³¥=ü4&*ÊB¯i³<&CfÃã·x§%÷î6R^NGqçÃ–á#ä¸ºóÆæ·Óö¼¬ìN}T«eÅÌtºY[ˆ$Ê¿Ì¬àE ÈŸÏ›e m{s?íÐËžØû®c³L& ¶ez+#žBèN—„Œ–îMºÞ¹è¿Ï+_rLÅüS#Ó¶àaRE—Àêòöæ ‡‰<§cŒ{²ëm¡#oŸWdøg–Höß˜Ta7µØ¹>WÖ†âÏÈ{ù¼`Ó=>Ë*fð\{Oáû–˜ÃJYÉ²-©-âKï]<sìYØÚž/ÅÑ<¥¡h½„×C¦&X¨iíçd‚_“J§ìi¥¢„xhÇIºEô&è)S3ŠÉî«ñ®Á²/5seCä¡<Œxc}{†É÷à:ðÄTtèï<žWªjF¶¬fµ`óVxº-{÷êGw@­ß%ïZBEÊ¶<©¸¶FÇ¦`‘º­L§ïö½$K›@*f©-¶#9û†=Ò¾¼ÿD×aYìàª5[(n]È¿fÉUîku|bëµaÎ$J
ÁcØÃ¬Xú¥(Û#Woõg«‡¸»W,mÚ9†àm"ë{ÒMò@¨ÙÔXQ†qË8Ì’Ç”Œˆ6UúÏ.¦¬õJUcG*â÷¹SôwtsU[‡,cY\Ž™NøøPl°e9¨O&líf°çì[˜Ã˜í.ÆÆ }ŸÏ]óÂ¦])kÖ÷Ÿo<ÌRïs–J:~3Ô˜7ÌNéÜß4÷KUÄ™]L	ÿ8”3×1åÊÌq›3n¢¹èÏ4}2$ŠÖrQÅ±M»÷¬¦¬rí¸vã2µá‹ÏØ¯K8EšE¦é­U¶ÔÌ¥i8.Á¿}tAÌ lª¦/¯§&lý?Ûß_Ô·
¤¹ßGÔÉ•U^P}¾5¨F®¢b‰¹Àš@\Tlz]±üJÉ+Ñ7îðšžoùwÀ€ÿ¥YökvÈ­Š'‰/vá~ÑE¼Åâ¾j×T)RŸ§kWk0¬ùÒ.…Sw›ûCÞ{¸Û'áÞÖ•m’ ëÃv;â²›ö;‚'‡°à^Ã‚@Ó!¢¼Žä5þDO	b
v  0707010001f05c000081a40000000000000000000000016a102a8a00000216000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.cluster.reboot.conf.gz    ‹     å”=Û0†wÿ
Yó±ç7´@—.wÛ¡¨‰¶…Ø¢KÉ—äß—²ãÄi <­àI¤_‘_i±˜se˜q%9¡=sœMnÞêæe—½Í~ÍR¥:Yìövà¾fsp¾üÖ
iÂjÆ•ÈÃ-îkºžø	ëÒ|ïœý5b©À®Ž·"¿°§QŒ@ë'‚¡%ã
gà”äIœé»T…`4ã7z‘NdºH°§‚….XÃóž%B¬Æ`$Â²V©§)ØÁ¹k kKAK°:g²7-ÊWé~%(?ÿN2ù_Õ„þóç¢o\€@1±Îó"UçK°^©
5üNSÎEåe“+4•øk:³ñl{}!´çtÂEKtôë“è'RX×|$½kHÐ—´†×j¨±k“˜'²4xÃkÚB«³š¿+i	G«”ÑTZ "×F¨˜ª‚!ÅÔ%Q\Y’€*ªç°ikZOÔVâ¡E,ÿXÔÁ$^?ê›Nìì#XKHGØšÆnsnXÁTd»zËïVŽÒôï'·|ñÀÏÿéËð1½:UÝr¥×ˆíýÉK0á_Ã÷2š«EÁ†âÅîÉ»[³BûmŽÝí¢&Oæ0DÖðBSwA6¡RÎËfs5î¨xÝgñÔáº·Ó4Î²!Æ²	q    0707010001f086000081a40000000000000000000000016a102a8a000001dd000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.hb.multicast.conf.gz ‹     Í–ÁnÔ0†ïyŠ‘öZ"º€´[‰OÐâ0±'¯ÆÎ¶ûöÌ8-	*ˆÒúbÅžŒÿùüÛÉf³f«6°bÓt]sùÔŸÁ”ÏJ·®ºuÙU_ºfÃ.XzüZÝ”¢wSÙU¥ú¿Ñé!²ý8É@kY&^­Ø”S2±ÇÆÓÏ•îy `ú>8¦IÂgô©ÌXjQÔNÚ¶Û·õ›zû¡~ýœ’@Ô1©'ãZgàöˆ)5J¢d$â¾›ïºÖI)AŽ(XíQ:ïR¦ 1Ôòî	´ùÂØ\Èí…bÓºæÑ«LâAnµ¾»e9åvø¬ S‚ì‹.pì#çåxýZÚ_Ã01‰g	”ÃžøH­¹ !4„hiîJÿÕ”%|÷$gÙŽÄGôŠòÝ9­˜*»~oH)Ê$Àò#äŽ	m‹ÒôìÒ1t'ZêF¤øÉûQ_}IõÕŠ%-ôÈrQº^H‡½Ú0ËTG2ÚæÂpÜÍµ,Ìî@q¸ØsýÜhÉã	’zAÊ³×¯“!w$;¡„–ãp<öÉ5@³ÿ
Y.9‘T6¡si“~•]xb«‚8ùP]À	     0707010001f0d1000081a40000000000000000000000016a102a8900000fcf000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.disk.gandi.conf.gz    ‹     í]mo7þž_A (ã$Uöµ÷Áƒó]Ó»àšæpI{ŠÂKíRáÝåvÉ•¬â~üÍÉ]R¶,©–’ºÝ â}™%çyf8$gèçÏùçÙsvÀ?(.“úfÀÌªlÆËL>RÜa[wXÝ=û;û¼–e&nzöuúÒvûÙ3lýX-U]t˜Õª©àÎð€PM:UŸä¢ýÔ‡ºx£?7²]¾á¹¦;U­RKUÊrvÝÉÄ”7¹éšý*é†,ç¢–†—©ûP.ø”½bsÁ3ß
Á Ç¢¶÷u%R9•)<3%¼›’^à:…'>Ì…Õ3sn˜ž«&ÏØD,áa¦¦p]ÀÃ™Š{-á§ÁÊ¦@Yð¥€"zULTW§ª.Fð¯¬èK¸£7áQªëª¢¨Ì5Ÿ¨Ú<9hÚ;Á†±Ù›q#ôE×Zö¿î;©*¢Z0Q
>UFÀ¾™2-3Š%‰I2`ï*Q¾ÿáïl)óœ9¥C«´EhQ/¸Ã]3g<€Õ?™ª¢€¶A?µ.!9x{oÐÿˆáˆ½W…°¢¯gÂâdQå¢¥¡Ïi–)à ´£©*xø&5Ë¹6¬P™`/„¾=uÝ	%ÉU’$ S+B˜»¸Y^$ë¤JP«Hkž	ýÐú2î¤¨‡ ÕÔ©`®¥&¯.[6²9OŽÁèr•òœšÏJ^ˆÑ±œMû6­UAÞå4JñJ²JÉÒ ¯R,þ3ôè÷¶p€dÑû÷@å¨©5[çÍ½¯Ÿóxö+¦£3èM…Î 4ø<˜ˆJ H#„ðg@è/ãñF@j¸Ö#âùZLe)4¸ÎöAP!øJ1hGRÂÌ»¦Q€Ç~rªZ*hßŠ)èY”  s±9v¾—YÈEÇ¨¢A`H™úöøg-ºôÖ%´Cm†WÊˆëzù10nû#Ý¦^á]§y†,oÙ:&kèÂp	8æiƒÖã¶mPÃqÜ³Gøã"ÌSR8œÚ¿d/È ]q›æ–1 }ÍÀ‹Ô‡Ð2_nÞä]à?ðõ*Œ8D~ðUºNóHêÁùÀÊ†e­b¯oA¤/Z‘àÓjq7nblˆ7!üÆ»'ËÄaSQpG~Ë]`K®ñù@T]3²àŽì ŽƒWl(<NñCSL6¶n¯â&
:ŠŽO"(6Æ“øH2Ç”›"ÉNÄ6skà¡~¸TÈqæ§$ù’Õ
Ø¶"-ôÿºRÚ\·ér ]µóB°Ö²"c·"mÀ\ùÔˆ8„	 ¹H‡h<ª¼HÚž$Ì^4ëZÕêÅÎ…¿‰àm@æáh%~‰\Nå¯…2T™¬¹Åýa$1{C•Ù¤ŸdØ(CÝ&ÔÉØWÕ/ÓÈ2UuÃTÕ>ø5e?VÉ,ÕþZ«Dì
j-úèg_D'BMqìð'Df4ûàçqH8úñˆì‹`ûÇ$ü¬á³¬}ès0»|lìãðØ¾>ò9žQ>2ô‰ÑySl°(ÍÓ[ò>~ÿy¥i!þ.á-òj0•¿ç×vñžRØr.Óy˜ Y‰T9Lyž·Ûÿ•PÈ›óm@Â•¨¹Ý»à^~ 
¾º¢¥`\°mt²VøéJ•Z‚Žq­HààÌ7žI¨üÎP>úÞÆ•WdG»§1LÎJ°sZ(¶šŠ(£çÉÔ²w9ßÏ˜F#mx¾ä+Üˆ@²€ $)?çI2bì½0¸ðûõëo®¾ÿöÃ€è¸œ«<$‹ÿ&òm…íõ÷3%h‚ÍU­ ß‰ø¨Ùëãó=z„«×Ú»"åÀ"“ i"«ijÐG“‚­À; ®È.U°—¸!óÊ7/¡]Ü”%Ñö„ëôÈ?	ê6ktÐ?ƒ–$mßÒ9/g"DÝ‹¿,JûáVöÂ]€í‡»¼mC”)¯ú4¯ƒÛûxÿmm,Þ°CË0.ûF£d#n)g+ ?E™áö‰ñRÑ;Dé@,f”€e•2àC¸Å¶²£0ô8U®V"ó‚½¡¡T.A'–@ô½mô) -£êž@‡òÖàp½RïølSËÙ8Ámâ•nd
aÒ÷Ýp,Ëê¶êœsBtÜD¥-aº†®\1RÀ‰ÜFU¡Kåù“cÚ©ÍêáúB#½*SÇKptž±“RÙ‘ãdÀÈcÓC3h €61C¨ðq™7ÐF»½öƒ6c–+j÷¤1mR'Îœâ|#Û%ð ?7`î˜z–kEä¸#5÷Qh¥3Üh	gßÅœTT;áh¨}ô†méÞsO¯Ÿ`z ÄŒî…ûÿ¶Ævøf”Ú†ÛQ‹RlB5ðÆ¨‚™R,êÝ*F=®ƒ#ö½Ó&GÜ+%—70È6EÅ¦2z¥(4±FÜrLŸ%Siõuy VmJîš]Oò©®—BÎæ¿³ÕÂ=L0QfºœÖu]ÍpHûÛüÓ½&…“Òæsõ7Êƒyóº˜OàkI»#öÏhàD˜¥%;†gãñxÄþ%êRä_ÚÎÒ}JéŠÑb|9~ Ù´j®n”á=¬Âz7Ÿ6¥l*ÌzÂ™/4€¯i>6¯RÊS²Ž	:…a½+²€qó³xÜ&]:k¤ i¢UŽË"¤JÒ¢É"QòVÍaD=bßtŽb€îú[Y6·,¥7À9$ÉðBÅBœÌç²&žP"?«”–ÄÎµöØ)œlŠ‰ÍÂ)dZ+-€þÙZúa«ÁÓ9êN*˜t%É—ãÏþ
÷Ú†ÀÏTQ AêÚ‡§öóõçÍRÙç½´¤&öÓ7¶˜€žƒ×Ö½ÂµY'åë#iå(së@í*\>ŸÁ¬þGìM\‘Ð'ªÁÑVš†2ÁFJÝ¸…Â;“uøD¤t¢KM“éÈŠc¢Ïcü`W
›ØŽf-q~kUBËž!Ÿ—6”P”mæÚÐ¾…<;˜}=ïvö½W¨ùvï‡‰Äue\!^´iì¨Þû;ü½–™Ú=Â1ØÒW¥k\al<8œã…ñð¼ƒÁÄ›Á,DqM¾´Gt_O¢å/ñœã5™¹ÞÅÎ;, U¯ØYFë0A>µ†þ®1ÃwÓá[ûÜÌoÁ|g¸‚bg¹v}"ÅLz5çè0*®5¨Ò[y‹¶5ò1ýÙÂ¥Šk\ñ¨UÞsä£Í›æ|Æ^ŒÑ\ÏNmŽ°]£ÔxÉ-HZú8 Dè ãì‰» ‰²Â­œ¤Ÿì¼ÊÊçÆPÅ£Q4·k
ËÆ@”#"Ñ³ÝŸðãþ;\þ¥†d~½ìÝ»· ÛHGÞwo=eaàôƒ×üX+]B¿ë˜	 ï”ø–éfb'•_a/ü²­4fëDÎB–¬®Ÿmá¸^òªÂ§~`;Åq—³N«.¤òuaÑl D-xã’}±Å3õX/q&g«Ms|\·µ,{%ˆÛÖ dÑG%¿‰¨äOhÇMHR÷e7[o4-³Ï¨×TGS®ï†ñLâ½È"qp¢¶%ÆéËc>IyŒ[iß˜ü²OqL_óqkbvÂn‡¤Ð¾æSÂìŽâN© }õËÇ¬~Ù¼m	 }ÉË§+yÙ	ÂÓ>û:—OSç²ÅÝ«\úâ–[Ü²t»D1}EË'¬hÙÄÝ‚˜¾Œå#–±ì†ÝÖ¦¯]ùdµ+» ¸GóT!ôÂ~‹9Ö”'GÉ87²Š- é A¢Äf[­±ÿòÚ*ãßí[x*¦Ä\}¡MÝ¤”wcK_èd%ÌŒ7«0Ã×ü¾XA/«•îV'¹áþ°¯–9¤ämÔ¹vx>½­ã¸€å\*žŠ¡˜k…é¤v•~Š4Ê¤
øQKž¦,¹nÊ2Þ‰ãì¤íÏIë	®:1´Ãw‹1jÚˆÒ ^Ö2{õâ%%ê¾ŒF£S¬Ác¬ðŠÝKP~•a®WS´Áñº©’²+Ý¡dwá³ö(-+-&OKŠª¨\:÷“£Íø˜‰Õ÷íì}ðåÑ‰oN}À‘Ö»¼,_1#ÐºjÀå1£&<½ñ‡>ƒ¾ãU+ÉÇ²n_aÊ.â¤è°¼G¥¶Ü‡Nÿê±hç£´	þT%‘Ÿ˜ÖêQvOµuX>kÛI²ÏµŸ¡_£³î›”5éË^\é Z@˜íÌæö9Ð˜()½¢Íò˜ü5™ßâ­–ÜG¸ÛHy5Ä[†KàvèžÖ6¿}Õž¿	ÕjY1×:‹¬ÉEå_fÖð<äO0Î2¶¾¹‰vèdì}×±ëLä& ™;‰4ŸBèN›„Œ–îMºÞ»è¿Ë+Çd1[îË´Éy˜TÑ&°º¼½	Èab:¥ƒž;²ëu¡oŸwdøç–HögLª°›Zìœ\Ÿ+kCñçä½üG.Ùx‹Ï²Šé=Wè¹²Æ† ÷œaË
YÊ¢)©-âKç]<sìiáš÷:š4-çðzÈÔ5­ýœŽð«qRé˜½(U”í8A·ˆ>=ejf}1Ù±ª,ûR3W6D:Ç#mWÖ·g˜|®ÏÝD‡þÞãy§ªf`Ëj36idŽg¤²÷Wß¹cNý.yÛ*R¶åIù½5:6­ ‹Ôme:}·ë%	äXÚR1Km¶ÉÙ7ìÙ öåmÇðöËb{T­ÙBqëúCþ0K®tG!Ó¨ã£[¯s&QPÃ(`Å"öwF®(ÞrC¶Ž~ÿÅÒ¦™`˜ÎÑ&Ñ¸î±'Ð$„šU…e·Ã,yL	ÁˆhU¦'x.e­—ª:R¿/œ
è¸èiK÷0gQ5epT/–ÅM1Ó	ß£ò¶lŠê£[ºìûæ0f½‹±1hßç×¼°iwÊÚ‚õÅ-Ö þ½Ÿ¥s–J:~
3Ô˜ÌNq*Ð“æÈ¤QÕNœÙÄ”ðð]9sSîÌ×9Pá!Ê‹þƒ¦O†DÑZÎÊ8–£éqûžÕ”U®×\¦6|ö6ÁûN‘f‘iz+DU-ÕijŽEð³.ˆù „MÕôåõÔ„µßáGcûžºV4÷›Z¹²œæTŸoª–‹¨Xb"ð€&›ÞW,¿P2ÃJô•;|†¦çk>Ã0àýš]r«âIâ‹Ý/Ý¯Kˆ·XÜWíšJ‚!Eêótíj†5ŸÛ¥pêãfsÊ;`Owû$ÜÛº³M`½ÛnG¼SöÐ~Gðd5,4ý"ÊûˆA^ãÿ“!¥ÿs   0707010001f0c8000081a40000000000000000000000016a102a890000110b000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.service.container.vcloud.conf.gz  ‹     í]mÛ6þž_A (’à¼îî^ûeó‚Ûë®¸¦9\ÒôCQX´DÙÄJ¢*Röº¸3CR"íõÚn¼»ÝVA[¤z‘ó<3’3ôÓ§Çüóä);â—ªÊpY‰fÄÌªl‘ªÍ~·¸ã¶î¸º{òs×Ù§¬2qýË“—Ôé×®ÛOž`®Äj©šì¢o
ÝœÈnžñêK§ªæÓBt_ûÐ´o4â×V6"Û¸Q7j!µT•¬ftó[^hº“‰œ·…éþƒªè†¬æ¢‘†W©ûN!xÎÞ°¹à™o„`ÐgÑØûº©Ìe
ÏÌèK¦¤ø‚Ná‰sauÂdÆ¸FårÖBkáS,I*•‰1^L’1¼øÒëM}z¶MÓ³Vh£ôƒ*ºÓçCiÇ«LfÜí>ÑVòšý-¶j©7À ¡7ÐL¦WÚˆa0p}!Óò‚•<ï	§bÀ…nb®´©x)þÚHDj–šU(n›
¦…a2¿IËî¼‘É<¨LàóF•ô¢&lº7_³\©mØÀ•ÉƒcósG RãýÑU¥–âd:GUòŠÏh”ƒ«M«3‘ –®'€ñ¢$<¼¦oÇãQÚ	ùp?z›÷½™ô_»Ñ@z³ØhÖ“—„›½5©!ÊÚLøT5æÑÔÝ¹«1[cJ÷PìB4A¦JÁ§ªÏïrò|`EIv$’dÄÞÕ¢zÿñ+Š‚9¥C«´EhÑ,`dRÜ5sÆ#{³O¦ªëÄÐ†.ž1•3ÞÝÄô/b8fïð€DÞÌDèUeY¢"Ñç€X
8 íhë^bÙ
†_BtÂž}ýbÜw'”$9ª$I@§V„0#vqµ¼HÖI• Vq4à©‘ÐOðÐÓ¨“¢9-¨¶I›r-µåóš à¶Åa›•^¤“F)3©¹™ƒrÏFtž€àáÁ ÎH%^èÈ%p‰§WpËEá”H‚Åp)\K‰#boGú1ûºf{^€Ù8Ø)àõ¼-
‡MCaL†·]Óºv‘³³´°Œˆ>|®jÿŠ
nl£@P*#&Íò>(ÑáûWöo¦Yá]§yÂÌZÀû	XÆ®¤.œ,¡sý`>ÁîqÛeøèë¬¾_„yJ*€±×N¾dÏ	È ]q­–:1“Ú4l\UÅêáöwôþàñš~8Â?ºŠ\=|•®CP¤¯4¨§ÈØte‡.?ˆ€+º–Ö¯"5ãØ[íl…üX¡Ò+†ÏK-OÚzì=¨¿À–>Ãó¨ºfdÉÙAÿ?¯ØáxœïÑ¦†Š‚N3¡ë¸©‚ŽâGPls{ð£m -w·¶½ˆæ6‡®eYOfjëa ÝXñ±b²f¤!
ÂÉ\
`’Ph§H‚G{‹@¬×ÛçSZþ6¬;lj~Q’fvÌp­ºá9Ð1x‰ÕV%£ÅOÀ°…jßäèü‹Óc,ëãFÖ6äV#$~â&©àŸ^ùÛoÀÁüÓ‚7>Öëg¨èžÈ1å\mcç²‘æ%ìËVTýh1:;ÌõŠˆ°+[ Zðºâz_õÑz£nMô²®£yÐÇ·¤_Ôèõíê#ªo‹j„07âa#ß­úõ7îU½4Ü*·îlUíÕÄ H›ÑX!*íGßî‰[‡]
¡s“ðštÝFb§þËn¥
ü‹NYSè*®EÚbÜ“ãÂââ#Ð N½HO¬Ÿ¹Hºž$Îõ@Dß4ªÁUÐ´(v.üMÄo2‡ÀIƒÎ åï…2T™¬MB‡‘Ä!ü§Ê¦«Éãe¨ÛäS u2ÃUÍ#Y¦ªa˜ª>¿¶ÆÊ;2Ë@µ¿×*û‚Úˆ!ú9Q7Éºãð'Dæ 4‡àçÓ<rôã9Á!ö¹“üÔàgŸƒ`BŸ£Ùå§Æ>à"Ÿ»3ÊO}btnÇl3Ùó]à÷/QÔšµZ€žÛ
Þ"¯fSE·öæwRñžRØr.Ó0sFñ1`€ªNR^]BR-Tòæ|Ak£Ê%ñ
»“éä¢à«+ÚxÅíÑV· k…Ÿ®U¥%è—½m¶µ…0ßx&5¢ò'K‰»÷L‚K¯Èž6ƒ`*˜œUª±ÛÒÑ¦{[eô¼5™ZVã àïŒÆgL«‘6¼Xònû#Y@P’TŸó$»”ÊŠ}ýÍ·—?~ÿaDt\ÎU’Å“,mÇa{ýýL	Úïgsí½%<H¨Ùëãó=zŒkÓÚ»"eå•"“ i"«iÐG›‚­À; ®Ì.U²W˜þðÆ7/¡
L%Q2€ëôØ?	êS#tÐ?Câ¶oéœW3‘¢îÅ_•ýp§{a`›Pà.ïÊ&UÊë!ñô(Æö>ÎÙÁ¿[‹Óc|’e˜Ð.Ÿ¸¦,Ò ü=&4šIŸn‘ŠÞ!Jb5<–	TÊ€aBËªOðŠºP+[ŽÝ~£%}o}J@ËU:Š·Æœ	§ÔŸm9›áv0Qœ”L#>w½‡¢Žeu¥‘Bz®ZLÔÌ9÷(DÇ”%—H
×Ð•‚+F
8‘;3kt©¼xt,@;¥­xÃõ„FzU¥:Ž—àè<cÏ*eGŽg#&0Ñ6?6ƒÖ	hÓ3Äf¸< c·×~ÐfÁ
Eíž¶¦K3Ç™S\Oâ4(Ì]ÀPUh›°³!5÷Qh¥3Lk	gß¥bÇžñ4Ô¾zÃ¶ôï¹§ƒ×Ÿa2ÄŒî…1û¯ÿ¶Ævøæ¸Û†ÛQ‹ZC5ðÖ¨’™R,êÝ*F=®ƒcö£y[ n”RÈ+dÛ²f¹,„-^ÓÄqÍ1¡ßæF{}½>«¶¥RÏ&ÓâJªÉRÈÙüO¶Zx€©À &ªL÷‚ÓF £&õ‡´Ÿ±Í¿ÜhR8)YÔ?)ëô»wÐÅx² _KÚ³˜F^Ñ,…¨ØÙ)axvzz:fÿM%Š€/]gé>%PÇh1¾<½Ù´n'¿¶ÊðÖ[a]K6	iJ¹Ë˜I…3_h ßÄ	ŸÎ1A§0¬wi¡0n~Ïƒ]» ·ÖHAÓT«—ElUHÒ¢‹É"7UiŒÙ·½£¡»þ^Ví5KépIrr¡b	¡ NæYJÏ¨´ˆÕJKbçZ{ì‹N¶å‚PA)ÓFiôÏÖ’ý;…žÎQwRÁ¤+I¾<ýìp¯kü?Õ8AÐº„vÆá©}ã|ýy³Töyo ©‰ýô&@Y¸z°c¸6ë¤|ca$­en¨[¥Ëg§ç_ØÜiÌ{kÄ š“©jq´•¦¥¬E°‘J·n¡pc²ß€ˆ”îBt)£i2}ùRqLôUìJ!æä9³–8¿µ*¡e
OŽÏKJ(hFãÛÐ¾…<;¿}ïöö½—¨ùvï±î\âº2.Ž/¼GE¬V_Á×ê@úG8[ÚàªtƒË!ì‚ŽÎFçxáôä¼‡ÁÄÛÁ,E9!_: z¨'±¥Ôß™ë}ì¼Ç PÍŠ=—U´äÖÐßµæä]~òÖ>wó[0ß® ØY®]ŸH±nMåáFÍµUz+ïÐ¶F~Jv°C©r‚+*Žc´¹dyÁgìù)šëÙ$„_£ÔxÉ-HZú8 Dè‡ÇO(\å°AÕ`Y9H1²ó*+ŸC5ØFÑÜ®--QŽˆDÏnÂø÷pù—’ùõ²wïÞ‚l#yß½õ”…Ó7^óc­tås®`&€j¼Sâ[¦Û©T¾¤¼h·l+ÍˆÙªÌ³åk1çÇõ’×µ¬À`Šƒâ¸ÀËY¯URÑ\}6¢¼ñš}±Ã3XÝ]âLÎÖvæø6¸n+GJ·AÉbˆJþQÉßÐï:4!Iý—ÝlÏl˜
ðØë†š·¾Æ3‰÷"‹0ÄÁ‰ÚŽg(yò·Ò¾5ùåâ˜¡&æ~kböÂn¤Ð¡æ!aöGq¯TÐ¡úå>«_öoWèPòòp%/{A¸wÚçPçò0u.;QÜ¿Êe(n¹ßâ–} Û'Š*Z°¢eo÷b†2–{,cÙ»1ÌP»ò`µ+û x@óX!ôÂþˆ9Ö”'GÉ8W²Ž- é¡A¢Äf[­³Ÿxc•ñŸî-<§Wb®¾Ð¦iSÊ»qç×ã9†˜oV•t²ý˜ý°‚^V+Ý¯NrÃýÑšsHÉ»¨3qx>¾­»qË¹Õ<'Z`®¦“ÚUúi”Ið£–0<MXrÓVU¼ÇÙ³®?Ï:OpÙ‹¡¾ë×vi; JƒzÕÈìÍóW”¨ûf4_`ý‰Wì^‚*ñ¨És½Úz¤Ž×m$,:Ø·µÒí–n>k®´Òbòt¤¸¥ŠÊ¥s?:ÚÜé©l7íì}èÎ?ÏWuêŽtÞåUõ†á¡kÚ5à‰Š˜QSž^ùŒCŸAßóª“ä…cÙF¿¯³‹8):,ïÀQ©+÷¡óÈ£z,Úù¨G]‚?U	D$Ã'òFý&ªþ©®Ëgm;Iö¹î3˜8XD'Ë"7)kÒ—½¸Ò´€0Û™Íí­ 1QQzM›å1ù2¿Å;-¹p·‘òætw>l>B.Û¡;ol~;mÏËÊîÔGµZVÌD§s‘µ…H¢üËÌú^‚ü™êYÒÖ7÷#ÑŽ½ì±½ï:6ÉDÁa`[¦×2â)îtIÈ˜aéÞ„¡ë½‹þû¼ò9_Ðñ¦p_¦mÁÃ¤Š.ÕåíMAyNGÏ÷d×ëBGÞ>7døç–Höÿ1©Ânj±sr}®¬ÅŸ“÷òyÍNwø,«˜Ásí<Oòkb+e%Ë¶d¤¶ˆ/½wñÌ±¿_ ío‚l8šç4-çðzÈÔ5­ý¼ãWã¤ÒSö¼RQB<´ãEºEô6èí¼C1Ù]U#~j°ìKÍ\Ùyè_Yßžaò=¸<åú{çFUÍÈ–Õ,flÚÊO$gï/p‡Šû]òð×n”+O*n¬Ñ±iX¤n+ÓíÐ]/I ÇÒ&ŠYj³õHÎ¾œ½óîaYì€ª5[(n]È¿fÉUî‡hÔñQˆ­×†9“()a°bé‡ÅÖG®(Þê#ÄÝ½biÓN1Ì çhh\÷Ø“Fh’BÍªÆŠ2Œ[NÂ,yL	ÁˆhU¥Ïð¼yÊZ¯TuâHEü¾p* gÈ;º‡9‹ª­‚ƒñ±,.ÇL'||(VØ²ÔÇc¶t3Øö5ÌaÌzccÐ¾Ï®yaÓ6ÊÚ‚õÅÖ þ}˜¥Þå,•tüf¨1n™Ò	Öiî–4ªÞ‹3Û˜þ±/gnbÊÆÌq3n£¹è¿húdH­å¬Šc9šwïYMYåÚqíÖejÃga¼)ái™¦·BTØR3•¦á¸Pÿï£b> aS5}y=5aíW—ühls®oHs¿!×É•U^P}¾5(<8=œ×OÐâ¢bÓ›ŠåJfX‰¾r‡ÏÐô|Íg¸üÚ¯ÙU ·*ž$¾Øýµûq¢x‹Å}Õ®©$R¤>O×®Ö`Xó¹]
§>n7÷Ç¼öx·OÂ½­m’ ëýv;â²Ûö;‚'‡°àNÃ‚@Ó!¢¼‰ä5þ6l™»~   0707010001f08c000081a40000000000000000000000016a102a8a000000e6000000e600010003ffffffffffffffff0000003800000000root/usr/share/doc/opensvc/template.node.hp3par.conf.gz   ‹     ­Q1nÃ0ÛõŠ<'º¹hÆŽ]2¢Jt$Ô•”;	M~_9Nœ]Åñxà‘¼®k	ÕQCÌr.=%ÍÍäÚºkÛz_ÂvìƒÅùCÍ†¿pù‰l‡Ç]1¶L¨ä¦!ærÄÄ¤?'¬×^õ$ó!bœŠgØÿŒÅ¨Ë”ßbÀ]T½ƒRŒ½¡ÀÞ\V1ucËEµý¦12eÌF›ìc©ˆ[j:Pˆ´[Úþ[„{qšÑÛhú{e«à:KÈú¼­Fž×ÙK]‹Jý—    0707010001f0ca000081a40000000000000000000000016a102a890000109e000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.xen.conf.gz ‹     í]msÛ6þž_™L'ñœ¤È¾ö‹ó2çëË\æšææ’¶:"!	c’`	P²:÷ãow€lYR#ÛuCO’qDr	ìóìbìBOŸóçÉSvÄ—ªÒpYŠzÀÌºìJ”Ÿ"î¸­;®îžüÒvöi-ËL\ýúä%uú5vûÉìÀ¥X¯Twí˜7B¥áÚðˆ?¨+ªŠOsÑ¾ìcÝ¼P‹ßY‹®ßñ\Ó•ªVK©¥*e9?®dbÆ›ÜtÿA•tA–QKÃËÔ½(|ÆÞ°…à™o…`ÐgQÛëº©œÉî™P–Lñ¶”—™Ì¸Ú½¢)åû[&ÕJ“ò :…Ë °57ÐL¦×ÚˆÚÁ|¾”µixÎ
ž. Š<øÒ©p@¡Û€X(mJ^ˆÏ‰HÍR³RˆŒÅ¦‚ia˜œÝ¤åVwžÈäl&jQšÀ^gµ*èAÿ ÞLØ´O¾f3¥¶aó(qAš¶.Á÷øø0}è`éÞv# ×šõä¥á¦FoEMªZˆ¢2>Uµyt µWîÊ‡akÁ‡µïÅ.E´`ª¼ªŒð|;#K›K’dÀÞW¢üðÓ×àóœ9¥C«´EhQ/Áª®šãÙù;SUÐ6è'¸Jž15c¼½ˆè/b8bð€D^ÏEhÅ²¨rQ ‘èu@,€v4U±€l9×†*ì¹ÐW'£®; $™¡J’tjE3`ç—«ód“T	j½O„~‚7‚žFõ´ š:lÊµÔ–Ï‚€Û‡-lVz™Nj¥Ì¤âfÑÑaxò9úÀ©ÄóàXJàO/á’„Ã¨H‚Åkp)\KÑ Q{;²ŒØ·Ð5;¸ðÌÆÁîL?Ÿ5yî°	ÇlŒð²kZÛ.rv––1ÂÀ‡ª2ðW”pa- ‚B1©W÷A‰ßÏÙ¿™zWæ	[0kÏ/%`»’º0\AçúÀ|‚Ýá¶ËðÑ×Y/Ú#|¿ó”T cN“†_±çd€®¸JóFË¥€¾æR›šƒ«2_ŸnGïß é‡C!üÐÐuäzÀèá­ô9EúRƒzòŒM×vèòƒ¸¢+iýJ R3^‹ëc«ŽÉå*½dx?°Ô2qØT#ïAýlÅ5ÞˆÊ¡kFÜ‘ôÏñeðˆîÇ©ñm*°a¨(è4SÐú<7UÐÑ†`=Š Ø:ævàGÛ@Z<înm;;Íõ0î
Õ<¾ñìËñ1ÍmÓš²¦&²Eæô3—ºj¯üå7 |€.1ÍyíGÀ.nGÐ®—ySÛ?Ò<À„}Ù
’ª-F§g£Nñ veD%¯þ¬–‡ó—*„›®^ôB™i/£ Ñº~§yD)V¿ÃÍdî‚FwûÚÞ¡f=xLÎaN°„ZÐÊD*ö…f®·‚S8xX8¼Òœ–^äâêNçi‘'¨³¡›7%íZ„NÈK!Æv¡I‹â•};$&LÖ&­Jú¶â¢U9:­eEñš¸ijç3œ-[c±aW`!çéÐ#çIÛ“Ä,ÆÖµªqªêÅ.„¿ˆømAæ8)¦è¡ü£P† ’*“ÈûpIÌÁÂ?e6]÷H×(CÝ&Ÿ¨“q®ªêñ<Žeªê†©ªCðkÊ~¬¼#³TûG­2±/¨µè£ŸCusè;Bd@³~>É#G?‘CìcŸ»1ÉO~6ð9Ö>ô9š]~jìãð8 ¾>ò¹;£üÄÐ'FçvL±Á6ì‘íªÞ~ÿy¥qEôÜ”ðy5˜Ê¦äÐ.¡Û>ÄkH=`«…LÃ¼LÌÃÛ€ª¦<ÏÛ,œJ¨
ä-ø’Y•Ë”vûÎÉDÁ[×´Ûˆ{‚n@Ö_]©RKÐ1îj	œ€ù6À3©•¿XØ½oŸ_xEv°ÛæSÁä¼;§½Øh§¹©ˆ2zÑ˜L­ÊQ ð[£ñÓh¤ÏW|{ÝH”$åž$#—GX²o¾ýîâÇï?ˆŽ«…ÊC²øwRV¡mã(l¯¿ž)A›Ül¡ ½·„	5{s¼q¾Gp•Z{W¤£T´Bd4Md5MúhR°xÆ-k«‚½Â=‡7¾y	%à¾w(‰vÀ]§GþNèT— ƒþ´$iû–.x9Ù ê^üfQÚ·ú°\Øîp¸wm¡‹2åUŸmycû'ªàïÖÆâœŸÙFi´"®(u2 ?E	fÒøÛ	©è¢t VÃ}`™@¥øfq¬»¬6` ¨rµ™ì­Ý±¢÷í¢Oh²Êž@GñÖàp½R¯ùlSËùwû‰:à¤d
ñ	ÛÝp,ËKÒÕ`v’`Î¹G!:æé¸ìIø])¸b¤€¹3ÝµB—ÊóGÇ´SÊ´0\_Bh¤×eªãx	.Î3ö¬Tväx6`³KgÇfÐ&A m"b†ØD—æ¡qìöÚÚŒ!X®¨ÝÓÆ´¹Õ8sà¯ÍÀÀüÖàÎ9U¹¶	²×¤ã>
­ÔbŽ¹\!áì³´ÿ‹ñÏøj½a[ºçÜÝÁãÏ0bF÷Àˆý×¿Û c;ü†‰Ý¶ávÄGÔ¢,ÎP¼1ªàF¦‹z·ŠQëàˆý¨Å¬É‘7JÉå%²MQÙôªÒÄqÅ1‹Ý&{}½>«¶åÏ'ÓüRªÉJÈùâ/¶Zx€©À &ÊLw‚mVÁ¤šãö¶ù×M
'¥sL	,êŸ”jùö=t1ž,Á×’vGì'Ì?¯hVB”ìtLžŽÇãû·¨K‘‡ù%¾³t²†c´€_oA6­šÉo2¼‡õVX7ÒN`BšRÂ.&ÊáÌµRŠ¹LP \Ê7±Ž	:…a=›×
ÆA7¿ˆçÁ®]‡[k¤ iªUŽË"¶” $iÑÆd‘À›JFì»ÎQÐ]/ËæŠ¥ô8‡$žB¨X@(€“ù\ÒÄó ª§a•Ò’Ø¹Ñû …“M1…à TPÈ´VZ ý³÷V!‚§ÔT0éJ’¯Æ_ü3v|CàÿTØAêÚ‡§ö‰³ÍûÍJÙû½´¤&öÓ;v˜€^€×Ö½ÃµY'åë#iå(së@í*||:>û’YýØÛ¸0
 NUƒ£­4%¥‚”ºq…×&ë˜ïUØ©D—2š&Ó;/ ÇDŸ*ÿÑ®b1¨3k‰ó[«Z¦ðäù¼²¡„‚fÔ¾íSÈÀÓ³ÛÙ×ónoß{šoa÷~‹{%®+ãâñÂ{TÄhõ5ü»QüÐÝÂ1ØÒ˜é	î¼œvÎÆƒÓÁ~0žu"˜øÁv0QLÈ—öˆêI´ü=žs|Kf®÷±óK @Õkö\–Ñ:LO¬¡¿oÌðýløÎÞw	ó[0_Lºv³\»>‘b±eDwÓ" ZÅµUz+oÑ¶F>¦ŸìPª˜àŠG­òž#Çm.Ø,çsö|Œæzz‚„ðk”?r’–G€> úa¿ÂU$Qá‘•“ô“WYùÜ*<ÆÔb˜Û5…ec Ê‘èÙîOøq—©!™_/{ÿþ] È6Ò‘÷ý;OY8}ãà1?ÖJW3æú f¨Æ;%¾eº™ÚIåKì…_¶•fÀl)âiÈò€µXÒ±ƒãzÅ«J–`0=ÅAq\àå¬Óª©h®¿9QžxÍ¾Üá™z¬î.q&gs||nË%J·AÉ²JþQÉßÐï:4±ÅKí›Ýl*˜
ðØëšš·¹Æ3‰÷"Ë0ÄÁ‰ÚŽ§/yò·Ò¾5ùåâ˜¾&æ~kböÂn¤Ð¾æ!aöGq¯TÐ¾úå>«_öoWh_òòp%/{A¸wÚg_çò0u.;QÜ¿Ê¥/n¹ßâ–} Û'Šé+Z°¢eo÷bú2–{,cÙ»1L_»ò`µ+û x@óX!ôÂþŒ9Ö”'GÉ8—²Š- é A¢Äf[­±Ÿym•ñŸö)<œVb®¾Ð¦nRÊ»q‡„ãá}˜oVt|øˆý°‚VkÝ­NrÃýy’-sHÉ»¨3qx>¾­»q«…U<C-0×
ÓIí*ýi”Ið£–0<MXrÝ”e¼ÇÙ³¶?ÏZOpÑ‰¡¾«
×vi; JƒzUËìÍóW”¨ûf0N°þOJÄOì^‚*ð|Ås½šj Ž×M•$,:Í¶±Òí©n^kOk´Òbò´¤¸¥ŠÊ¥s?:ÚÜé¡{7íì}lýuêŽ´ÞåUù†á™zÚ5à‰ò˜QSž^úŒCŸAßñª•ä…cÙF·¯0cçqRtXÞ£R[îC‡pGõX´óQÚªˆH†wÌjõ»(»»Ú:,Ÿµí$ÙûÚ×`â`§ŠÜ¤¬I_öâJÐÂlg¶°§”‚ÆDIYèm–Çä¯Élxüoµä^ÂÝFÊ›ñ î|Ø2¼…\·C÷¬¶ùí´=/K»SÕjY1.DÖä"‰ò/3ëxò‰gHÛÜÜD;t²GöºëØ$9‡	€m™ÞÈˆ§ºÓ&!c†¥{†®.úïòÊSD1ÿÔÈ´Éy˜TÑ&°º¼½)Èab6£óÖ;²ëM¡oŸ×døg–Höÿ˜Ta7µØ¹>WÖ†âÏÈ{ù—¼fã>Ë*¦÷\;ý†˜Ã
YÊ¢)©-âKç]<sì¡ýÚ~Æ5Góœ†¢Õ™š`¡¦µŸ“¾5N*³ç¥Šâ¡'!èÑÛ §LÍ¬/&»«jÄO–}©™+"ã©ékëÛ3L¾×G;£Cÿàñ¼VU3°e5Ë9›62Çc¸Ù‡‹ÜIÚ~—¼m	)Ûò¤üÆ›V€Eê¶2ÞÛõ’r,m©˜¥6ßŒäìöhPûðî£§ûe±½«Öl¡¸uý!ÿ˜%WºÓöiÔñQˆ­×†9“((a°béÛ›6G®(Þê¾"ÄÝ=biÓL1Ì çhh\÷Ø“Fh’BÍºÂŠ2Œ[†a–<¦„`D´.ÓgxÈ:e­—ª:R¿Ï
è	f-ÝÃœEÕ”ÁiðX7ÃL'|Ž|È×Ø²¨Flåf°çì˜Ã˜Í.ÆÆ }ŸÏ]óÂ¦]+kÖwÄÞÏRïr–J:~3Ô˜·ÌNé€òž4wKUíÅ™mL	ÿØ—371åÚÌq“3n£¹èÏ4}2$ŠÖr^Æ±MÛç¬¦¬rí¸vë2µáóÏØoJ8EšE¦é­U¶TO¥©9.Áÿ}tAÌ lª¦/¯§&l|Õí­u­iî‹ÓZ¹²œåTŸoª–Ë¨Xb*ð€&›ÞT,¿T2ÃJôµ;|†¦ç>Ã0à¿ÝÏ¾Í®¹Uñ$ñÅî¯Ý7òÄ[,î­vM%Á"õyºvµÃšv)œú¸ÝÜóØãÝ>	÷¶®m“Xï·Ûï”Ý¶ßÜÙ‡wš~åMÄ ¯ñ Ö³z    0707010001f02c000081a40000000000000000000000016a102a8a000001d1000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.cluster.array.freenas.conf.gz ‹     í—ÁjÃ0†ïy
A¯[ÒîØ­;î¸SocP×Q[³Äöd§[ß~²³´…^Š|‰±äßÒ÷ƒƒ'“1G1G’SDêpñà6„hUøÜ¸ÕË®xËÍNÈØ¿ß‹ÇÜôbh»(RxørTÏOu(oxý~Ä‘8í¼Z7x<èE5S„ð³3„§–Ôå@Õ5ñTÚ«³8h!pÕH}0xÔfc4<Ã-’Ñ¹7Vš3–;ì}ç³BLBGMÉ9i¾€]Œ>Ì«*'•,nÃ^—ÚµÇ«ý¬œ
,%°Ö-Àò*„´tƒÄ†Ö :è¦Oã¶`ìà·g’kà?¾‚'¯âî™ËÚ ¡Õ9UýÐjÂXfÕ~mÇF¬‘Õ òêj!b»ZU-¯XEÙºÏÛ©=žÉõ†òxÛ’Í<V¼€^®âÓzSgb (Šb (Šb (Š#M‹®‹×õï¹0pö0ý³ ÚÙ=ÒÙöº#³—¯j~>§2Ò³úAFø;g‚éda&Ì®ÈŒoJ×Á^®Ck—k¦sŒ.€œ‹ÂGøá#|„ð>ÂGøá3ŸD å±&     0707010001f0ba000081a40000000000000000000000016a102a89000016e7000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.service.container.docker.conf.gz  ‹     í]ÿÛ6²ÿ½àÏvv·\ÏÝ,nß5}¯¸ËåpI¯?…MK´Í·’¨ŠÔz]ô3CR"å¯›x“îU²ÉêEÍgf83œ=yrÊ?_<a'üƒÃ%ª0\¢0³.KUr#ªî´³;-í¾ø±yÙ'•,Rq÷Ó_ÓK¿r¯ýÅø7b½RU:n§’¨<çE
ç†'üƒäÒ‰*ù,ÍÃÞWµÀ•ø¹–•hgñ-Ï4)+u+µT…,ãèL*æ¼ÎL;ñ¨‚NÈb)*ix‘¸e‚ÏÙ[
žúYï,*{^—"‘s™À5ô’	^ä»U0¾^fâŽhÏÖ	œx¿žZÌ(&îDR`fÙ0WS«êbC|íïzÅ^¨Ò¼0*O¸y1“Åø—g²à#½ÜP1—=Å­¬T‘‹Â<:°~üéÓCµZJ#tÉÁ´(yÅHY&µajÎ¦ÓË[^]½º´ÔeÏÅÕ‹K >+¹Y^M§£@â¯á"Ë ò²_¦–Áð“šñþ/ÒÓ
¸ "²ŸkÏf	/ØLñJÕÀðôÏ¾ÉÖ@˜¿‚‘:ÆÚ2eöo^ID&U	V—¥¨®Eê¸pƒ‘€#šŒ—
Ã“å£ã5?ØCqÛL.¼ˆøí_uH>h„Onöˆ½õÅt:Ç9{„ç
/”&@´AÏT±Ð2l|³Oµá•™™U›)#<>N††/ÆÓBÁ¦ÖÓ`48A°[i"ÌN¬oe"t¯XN X– ”¤r|Ù *i€¿b:¸ù¬á}	V?BBZÞÒKUg)[òÛH¯$p%\ªâöÎ}ÂÂ_­K%ábrÒ•¿ƒ­N*YT¿°:ójíWùÔ/ó#v]-4Ëk€j†œ`à’ [·ðO	––ähàê¿{Ñïû‡XìoyVZ(ÝŠàõak»_Ñid·cog°w©>v‰^ÀCÒ¿{ãdL¦€¹v¨yÇ~e+ð„ÔJop
‚ÚV¯µ¹n º©yÆrX0AÆ	
Gb€Ýjl4ÈzeêÈLFO´ryØwsV(ƒ*ì$ëÏ5¸Q)Ê[í%À_tŸ+µ‹ì2ç‹ÏKsâ7²~9/”èÂÊ:Ëd+‚#ÚAc%Í’m/}Å¾úr~Á¿LÓ‹?u†Š«žÕ…©ÇJ•ÙKÿ	>gRªL&ëGÇÿ
¨ÿšÇMÄ³_ëÃp1KÆ¬öpèx]ÂÂ$Ðo0Vrp>Îß"­˜ñ¬‚wXÓ1XŸ9<oÑN ’ì¼€…‘'FÞŠ>bÔ±LL%ÑÊ¢xRXa`T€ð~xdzf ðØ{…šGRš'­ñˆf‰5_,„-$¨,éõw¡W&…î—¨v‰ÒÎ‹ÎÓñÔÉ"ªÊá(^vµ¨Ñ‹W¬6Ø:s\äªxªýì­AØ¬h ™DûW¼ÈgªÀîÃs°ÃÀðÝÀ-ÈÛ;ÊJÞSÁM	È1ZžÜâÈÌ$8Z“õÂŒËv,úŠF+„Ù1Úî‘ÚhóŒµ’ nèŠ˜ŸœÙ©Z>¥I¿
ÏîâÓGiI]òÚ(â3t>®j…'£È(ÆÁWYø¿xa‡e¢ådÇ¹rb¦}¬]’|§«Ñˆ~¹µnîe…–Yz‡¡Izv4¾s¦tÂ3ðwìœœç¬·.YvAŽLØ«¦#TP*TMdF8¥êf:µ*&ÀV•±*Íxêî•Võ´L kE+`ü	¬§–^¸hÔ¶ ¥•xB9µGxíqÆG’!øÛcÐ'#ºqŠFgp0)ä@Òª®À‚ÂAlüº%¿È2äÂ4EßÝºqðza¦có"ðq±ÆÚ£ËèF)ÿp×˜O°$ÏURÀxø~-ÉÛgI¼0&Žë­ÀB]E{~¾èÒÌt%´s"y#ò§_Õ¤¬„ÈK3á3U=¾Ssæ¡Ìvœ-˜íÍsŽÚ ]«}¬ßÀ Ù}Fß»ÿÕ²•#:ÌJKämQà`Ä‰83ä¥ß9DÞ€WBÎáÍI<AÃ{§ra‡o`!B;@æe&P_ÐãÀJQVÔe	·ZvÎ¸F{$ì™ÐwÏGíëDÜØlbT­IDÚ£ËTÓ&D†¶)äàM£—Õ°‘ð×Ò1rg àéo÷YµJß&“J)3Áxz¿†´ÜˆZÂ3 UTR[5C®à­^âÉ:ŸÝðö€˜"‹W º0,J!oÄÞ†½Gì5¼šŸ‘Ñë`w¢€Ççà_:lÂ€™ßîpSkæ5¯TÎ,[XŽˆöÛèèOÁ‰]lQÊ´·(Ž²(€Rûœmîè¶õŸ¾wýÿ^×ÖNùŠî¢œY“âl¡Ô"/JNŽ1H€›Ài)Ù/*ŸI`ÈžØXæià£•îŒö.óÍâ—nYÜ·Íjë2ÍLÝ/ÙL°#/à^Ú¹¡LXºµÜ#@2Ñgù³ù¥ˆ;#Š´QC=½álY ê‹†Ê•“jõ)ÐhHû{¶»LµÆ³Žò„á¥ºÙbMŒ–WðríŠ¬:œ=n‡
§º\G'qùCÊ[ZWd‚FøýÀ%éVvéO_¡$ã•7Z6%Z“lÎe.¡E( ;`o² „x‚;´½><F¾Á e ,Eh±9h>·‡Ê5Ñ•v¾	Ž†Ê‡ÂÒ•XÀS«õ$òô&ÛF´œ>-€:ÆÛé7q;´ÄPF¸‹‘ŒþOƒp ¡C‹m)“e–F˜©…ÜE´—ÆÛ„ÛBˆÖF,ÀofâVdÍMaNÁj’L.ƒÂîïÆ˜ãÊ‰ÿýá¡3î‡à ïd›¼[|èòÈÓ´1b«|ê p0HÑ…œß„†¨®ñìíX„µ¹ºµ~ãÐæ¥…:?´`úÀc¯Ž;pŠ»’£:ÙÌzz}g*Þxmd›–\ë-HºÒ€eò|t•ÁÌ¦ü7†Yr^–ðÊÖ½ð tÃ[ëoÛ!GN2G0ìì|l‚Y4,Ù—½üóèOç£‹—ã¯Î¾:£»`ÇX•‚õ–ð§µ„yB$€kD†/Ù32x£t¸$«58B¯Ð9Ø ¸š?'Ü¾Äè†ãè‡¡LE{ÜÕ:
CŽxœ¥Rßh O–²ÙÚºØ^gŒ€«¥CR†ÜflÔ®_‡Ê€3^/Iï€Å>¬Ë‘€ùlÅug·M‚ÁÈS ôç”Žs p-Øû‰ñ©™À‰!¡à¥è»ŠŽÃÍ¼(.…zA±3fÚJÀtK±@7Ý-m‡8¤e­Ñ’Ü'·ôúÈÌRgm«É¸ŽvÑ?¼ŒÄ¥šFÁøÝÏÝ•€joø˜2’-l´7G5*]xt÷òÑzÃÝ¡—»Rå£Åçüâì#Ô ÂWÙ‘1}¨â¨PÅu¶ .ç°^jQ§j¤#bÃ¿‡âàœV}ôáˆ#$T°c„äÅC@_Ê–ßGß¢wòº–>åqüês0gfç®ÝÑY;v¤£Óv6¶Ùìý~÷ª³Áæv­ì5ŸÐ¾žÚÉ(F÷|²Á'8>ð	îh>1ú[»ÈÖd*:¢ßÍù )!NÀ;ˆl;Á+&”%Û§ž¤Î†…_q©ñJeÕ­Wµ	ÊÌ•²Ú_TI^9y
?,…H“_¦aj1=ÇuiÓ.O1èŠˆ«±:‰¦,˜ù¡Ñ¿¡Èi·wm”ÊFÜµ×‡¡ >)ƒ'ôÆBã&ûD5°Š]mÐ"ƒÏÑÇEÌ| #NµÖñxÚ¼ÉÔÌ#öºªT¥mÝIU—vÊDw ³¿l,¾‰¥Ê…2H9íÄ°î#soq;"­{$O+”!m§¨ã~¸ª²Çó4’©ÊS¦*ïƒ_]ôkå‰e@Ú•Ê`ˆcA­DoýÜQ|`ó'DæhöÆÏÇ!ybëÇ#r_{ÛçaDòcŸ>÷‚µ7}N&—kû8<î_où<œP~¤é£³Sœð'Ê“8m~ÒCà÷¿"+m0~pi5&(—oãq¨ñŒRlj ,ð2à U±€¼©G,…*³&«/ †MÂ&Â¸ñÃV‡K±¦Tn×®È]ªBKL¤ Æj±Ú|…¥FTþÃ2K?y"Úµ'dËvca&˜\ ç”ÕålÕ%±Œ^Ö&U«",‚þÎhf;\R\™:ãÀÀÈ,Û©/8F£©ë”,Ø7¯¿½þþïïm˜˜`Â]	÷LÈÏqÎ×Ÿozt,Ìwy`Ûqv×§{4Uèk¯ŠtT”›‹T¥‰YM]=jfn/Gåì’bï~z.¯G£»½ÝKü•ðRmfÞ7Í}€dÉ‹…HÑëÅO…}pC{``Tw‡%£‰"áeŸ¿}a{§|âÿ­ŒÅÙ•¾v€i'OÜ™N"p‚3Å„´¦Ô€Šî±M'Úaq÷$X)Í¢§”ßéŠ'±5N™©uÛJÓZ»éìzÂó±OhUõt*m
×uCg›J.˜ÄD¬JJ&Ø˜Øu«j¡h—cYÜhd!×­v†I„MÓâ¨©‡«#‡c¨J]Š¡ò`á?íøÑæãâ”SJ 3\ß€i¤×E¢c{	Û7cIéÓBÙ•ãé€	¬³Ÿ?t	Éö¼*›¿æ²×4®ÝžúÁœÑËÍ{V›¦ËzNqJŠK‡Ó@LwL1É´­[Ø5X÷©gÚbQ‰îj‡§âŠ”§ü&joºá\ÚûÜÕÁíO1—lFwÃˆýË?ÛÐ¶Ù¶·+>¢Õ†dˆ#yµŠV{A*þž×òÁÖQ¨Z#­ó2(²ÓÄ5âŽc?ÛÁÓëÕ	¸jW=ëb2Ën¤š¬„\,ÿÃ¢…÷XÀD‘êv`ÛCmR.pIûçüÓV‘B§tI6Dý7-|÷^1ãÔ¼ÏR³¯³&8f%DÁÎÏÃó³³³û›¨
ålø—¥óT£…™¿g{MÊz‚yæ¼‡u/¬<L‰§ÒÌÿEÏ“³²¹8(.Õ)&x)êÏŽ_(qÝüCìÅ”Â÷Rá3­2‹Ø’MlÑ–FnkÒ2bß¶Šb€êúï²¨ïXBwhl:<Sû|¡3ŸÉ\šØ ÎB¬TZwvæco$s²ÎgØwnÎr™TJ`ÿ´SSß„:˜í¤¢¦ˆ/Ïþð8×L~§jW0:–zÐ1OíÝëÍJÙë½ 4LMÜOÏ8 ¶¯V/§PmVImo…éâ@M”ŸŸ]üÑõ5£®+aw€f8£ëÀ†5åÚƒŒMê†³ŽÝbrëú`—¸ÈM¶ùˆØ|­¬qMôEgïm¤;;±–èßZ’P˜Â3GÈÏ+kJØÄY7ˆ]«6àÀó‹ýÜ×óÝÑºüWì^3YØÞ–/šæ†HÞû+üì”M0µË¬@ÁÆìlp>¸ÀgÃ‹Rì3ù„tiè}5‰–¿Ä>Çks}Œœ·X ªZ³gÑ·8fkpŸ[A[›áÛùð½îü[ßFP¬—kãÔô)êädPa`…5ÒKyƒ¶ò3ús€;”Ê'ñ¨TÖóÈ)V›k6Ïø‚=£®õçÏ‘!|ŒRã!´|èã¡€ŒÞwFeËÛqRýùÀúUv|nµ`4Š|;LLFn†rŒHìÙìOøuÿ†i"©—½}û&ÈNÒ1ïÛ7žeaáô“ƒÛš°®úÚ½ˆ	 ï”ø™ézfÊ¯ñ-|ØVš³ÍÎC.¸+Õð¸^Q+˜žÅOÁâàå¬¥ª3©È×ïz!jÁ¯Øh¦«‡3BœÈÙÖ 8¾	ŽÛÆ÷4J·ƒFÉmo•ü&¬’ÿBy|hÓÄ~Æ¬y²óÖÝWÊôÖM¯»Ú3S¯EnCµ6N_óYÊc\¤}gòË}Šcúš˜O[svG$…ö…0Ÿ³æxJí«_>eõË‘àJ íK^>_ÉËQöÙ×¹|ž:—ƒ(_åÒ·|Úâ–c ;ÆŠé+Z>cEËÑ gÄôe,Ÿ°Œå8ìÚ0}íÊg«]9Á{˜0B?Øo1Çšòä(çF–±¤ -4È(±ÅR«Gì^Ybü³¹?Ó%1W_hSÕö“®î[åØ3ãÍªœ¾b>bß\A7«µn£“Üpÿ‹†sˆÈ‡Xgâð||[£ÚæJÃÍæJ˜ö$-PÀ¹°ƒí†@’«º(â8Îž6ïó´Ñ×í0´Ãg;ÅÒv@Üøµ’éÕ³KJÔ½ŒF£çXÿ‚=‡ýª+&ºäØ©8Å\¯ºhƒëu]N§qc¥ÚŽuT‚ÇÚ¾Çv´˜y¦ØSEåÒ¹Û<h/Ñm;{ï›ÏŸ…í¹ù€GírY\1lªýRš(‹9jÆ“Ÿqè3è[¾jFòƒcÙF»¯0gã8):,ïÀU©)÷¡ÏFõX´óQšªˆ˜¯˜WêQ´W5uXÁwÚëšÇt›”YÞ¤¬I_öâJPÂlg¶´ý¾b¢ ,ô’6Ëcæ¯Hlx|o¨äÂÝFÊÕÙ ~ùpfx	©n—îyeóÛi{^v§>ªÕ²ÃL4~¼ÎÄtÔTºÜîv ß
:Å/Su7÷£¡´cÜG^í‹MR‘qp ìÌt'#žLx&	3,Ý°t½sÖ›WîûÂy™Ô“*šV—·7ƒq˜˜ÏéË“-³ëî /Ÿcø–‘ìï˜Ta7µØ©>WÖ†Ã_¸Ïxßóì€Î²„é5×Á.Èßç°\2¯sFd‹ø¥Õ.žsÜ—lx¾UÑ<£¥h…] CNJíåçùŸ'•ž±g…ŠâaÏCÐ-¢û §LÍ´/&{¨jÄ5–}©™+"áç¥ÖV·§˜|ª?’€
ýÇs£ªÆeÁfµÌðƒìÝõ?Ü7)ü.y3*R¶åIÙÖ›V€Eê¶2žÛ¾%È±´	FÅ,µE×’³wØnàöæCqèÃb÷éN…âVõ‡ü7À,¹Â}ßVo…h÷Eu.r2ÁcØÃ†vXÄ~cåŠì­ös¸!îîË6õÍ5w‰@´®{ì‰"ääÁ f]bEÚ-Ã0KSBÐ"ZÉSü\	e­ª:¦"þ;Ð7ç»‡9‹Ø	µý®
–ÅÍ1Ó	ï£†Ùg6ÇõÑˆ­œ;fß€cº¯ƒöï<vÓ§¶QÖÄHè÷ÞK}H/•hü<Ô˜öx§ôÝ…ži–iTyÏìâ”°ùÇ±<³S6<Ç.Ï¬°eHEÿNÓ'CFÑZ.ŠØ–#÷¸¹ÏRÊ×®k{ÃÔ†/~Ç"¸-áT˜Žhz)DR,U3i*Ž"øÝ[Íç<)UÓ—×Ó:7ö«1õïpƒÑÀpV¹hÇ•Å<£ú|+P•¼Š%f4ÁpQ±é¶bù[%S¬D_»æ3äžwt†k0€=ÛÛ§Ù(‹ŠO§¾Øý•û¶]¼Åâžjc*S4)Ÿ§k£5hÖ¼°¡pzÇÝâþ˜wÀïöI¸·µ±M`}ÜnG¼S¶o¿#¸²7Ô,(ý,ÊmŒAZãÿ¦¿¬/ú¦   0707010001f0b6000081a40000000000000000000000016a102a89000012e9000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.app.simple.conf.gz    ‹     í]ïÛ¸Ñþž¿‚À¡Ø¯×ñ.®@áÛ,Þíý@í5E“{ûápXÓm+‘:Q²ã¢ü;3$%Òëµå[{sî)‚D?Fä<‡CÎýÅÇüóêvÄ?(ŽÅ€UëB0#ó"ÏwÜÖWw¯~‚Î~QJ•ŠO?¿úŠ:ýÎuûÕ+lýƒX¯t™ŽÛF$‘<ÀË#þA5™D|š‰æSËZàRüRËR´møŽg†î¥^J#µ’j>Žî¤bÆë¬j›ÝÜ‘j!JYq•¸/e‚ÏØ-[žúf]¥½o
‘È™Là™¹PðnBŠO˜ž˜L*hçdÂÄ'‘Ô•`ã$OÇ““”²¨n™©xU›	ÓÊý“‰%Ïj^A³‡=&“6¥š)]- OÛ_‚GoÌ"Ÿà“E)*àv2	dù¦TÁç\¥O}ÿÕW„' n5´ôûJæB×ÕÙÿw­Ž‡=(C«¥(ùi]’>#bü‹ËŠÍtÉnüí[6pAÀIÆK‚!È2^+ÐqiÍx‚Ï3`:ã2«K1dùƒ0Ð{‘ˆT@O˜†Æ°ñÃj<qøL†ìûSB‚ô2=soEØN˜4Ìˆj`›½®Ø
zb9h.fRÉJdkêá£^T€«êÈzÇœd–p
ä$º;“²4Ô6EØož½£1…b`$0>Õ%üÍ*—:,•!CQ¤¶ÕB€ÒTÜ–T¦$Å5¨¡¼ï1PÿêO£'‰¯ÕLÎÍ½PKYj•ƒ>ÎŽþ?ý|Jò“)Š˜ÿè°Z =LÁ.#
 €Î¤©˜ž¡[òòöÝÕ.S<·oo@û¬àÕŒYhïà"ËkMËdU¸@V ;“"0£ÁtÖ EöK­áã	ÇK]ƒ)„Ï?ùa°“@jàôj¡AÒFKY[ÚÌþ5Â‚Q^…(nDjI÷˜I@=Àæ)æ­Òß·¡õõ‚«¹Ï@Kh9ShlRérVo8H3½%C_ÊÀá³®á/‚Wå…å ´J•™ÖOÁÒ‚Þ<Ït5 shdÑ› Ç%ðhpäN¥â0æn½RÀ¤éš.—fø„+køà.˜Ø*ˆ‘bã@o4öÎÎñ/ÕL÷«Ÿ®«Ô­}ðÎ­Fkp$µ«žÇ·­w¶­raB]úÿž…>Ó¯sN¿Î	°y¡eNˆì™®rHi]9aw¯qd–×Á•°.ØÑØöC¦àž˜qøöV•BD”ÿ 2ð=+ho‰4ÈL…ÎÍœR˜ŠÐµÒu‰®O¥4dVf´YCï’³sõ‚wí,GD’Ñh=A9Ú£×p¤g!³äHþ;¯¯'(É\V÷Üô&oÓ©•ÿ&ì_}åU„ci4Êwk2ó×ër.QIÝ´YÔ½2wÌÅB‹õ¹Ø­M°q¼Wçn¢’:qsfßìÕ¹S¤¥NúÌEžé30½´Fž:éTé™Ìz’îS©US7¢ƒÖ+tŸBQKôYšÞõÜ§MÐQ']Â²±·Ÿ{µIZê¤Ï%˜Ú^{Ô‰JÚ£ÍRT‰†ôÙér4®v5NõJbµK”Äéîv|ã’8(ê€¡†»hõ_ÛM+xÙk›"9/
¸æ"e+^ª—B{ÍÃï¢Î>¤ÝÜí#¿ïê,£Ð×o´3g•5dïKÚš™r#;&m<ƒ<î;.´Að¥¢û.B	øÜ¾½Á‰$o’*u—n‡éÛÉ„š06EÒìçY^•x’ˆ‚"#¼œlS“J.‰lv/Ö0‹õdì(+:ÓáÞ*]Ðm]Ø»´·e/Q¦“½H)x•" öþ“èæµ3ÎbD¬ì/‰·ÞuŒ¶ZÝn\Þ…`?'Úêb¬Q†ÛÓ~*Új_xN´uvF]‰á}€ï€ôF0.»ãC|›ÏÿºI±@“¼=á=z¨ï>¾¢ûB¾Û3ðYµuñE=Þã³“ó}¦ç¿sÛ¶…öSsYÿOaÌ¬åAg¦ñå@—)êÛy{AŒ<sôr.AÏƒ0ßH¥ø–(‰Rè~ãCrý}.ðµ1šë/ÑE4{Zm$°Å-2ÚSÔ• ‘$Uk *Qf™x¾8ì·“ºè'Áî“ 8Ãv6ÒÅAS`ôø¯[!d]t·Ÿ_bl°y±I°Eölç@PZ÷)°íïî°§ûÉé¾‹é_ÛU!ò»”)“Zˆ(7öµÛû˜ñ:@¬aëv>Äâ(¡8æó¡,I»Ág¯‹®<îDá:çæ¡ßwöMI#ˆ
Ð">“®À%R‘ŽíÃïØèúúI2²½‚œrÏmÍ¸¦;7§æ%ÜÚT÷Zz”šM¿¦Ä³Y¡À€ð~#Ÿá†]¸P	ˆäÒÚ©ñ¤éÉ¤ñH¿-K]¢q	e]TÍŽ±ó4Ÿ@æ8Ïs·í7e Ý‰¿
F¿v„8é¥Óuäqe¨ÛÉs u2ÃU÷ÕGG™º8ÆÀÔÅ!øÕªŸ+O4,ÕþÚQˆè
j)zïçPDÝšõÄîOˆÌhöÎÏó<²÷ã9ÁÞ÷9Í|®ó³ÏA°ö®ÏÑÆås}‡ÇðõžÏéå3]ŸÝ˜bƒ_(ë«Áã(ž¿¿ˆ¬0´f(É³$«æ6â2{j´÷æ¶¡ñžÒ0-,YÄûè
hu™p¬^­‹7µ¡ñÌ»_Ò¿Æ 2¦‘a¨±	ÛÄÑèuv®M²ÖøéB+#1+ˆåÎ¿É]òÕS€§Ò *ÿeqçç” c3ÙÚtJ]¸óŠl)ÀVX<LÎŒs{œ£
Í‘uasu…™¿aäûÊ4©†ñlÅ×#Y¡¢ÞrLÂcD…I«ß|ûÝÝûhc4”Í·-Jbš6Ãöúû©†µÐ*Ê?}äñQ³7çŸÌJ	Æ›¢(ì	\L%§x‘ÂÀ H(RÀ\t_çŒRoo}ó\’¢Q •¢D®ÓCÿ$tÊ´ã/è_…#IÚ¾%t¨Q:ˆºY(ûáFöÂc€m€Ã]Þý*áEŸt”Á†¬#ÙdˆÀ¿íþË–>[Q–=H®Ä§ŠÅ¹	ZÌTØ¼û8!½cs†Z±r‘	TJ³(â E¦×mZ¹h(•KÐ‰qGÁ÷öÑ'´*]ö:–µƒë•úÈfW¥œÏ1´NÔ#%àˆ¯h¡h§c©RÈ,t¥hôq\tx£A™Ò54¥._Ú‰ÜG?;à8µ‘bnÀ52k•˜Ø_‚[ ó”](mgŽ‹ ÍN‰hÓ3Äf¸L
ƒs·×~ÐftÁ2MížÖ•;DÖæÒ ðV``~©1ã+ˆŒÍd|$5˜÷)ëa>/ÅK
BÂÙw›¼‡¾„†ÚAoØ–ö=÷tðú&„€Ïè^²úo;`n‡';ã#j”X¼®tÎ+™/êÍ*z=®ƒCö£³:Û(ni¥dò&Ù:/¨>Ç¬M%r:ŒLG¦¡ÒèëÝXõÄ8+æ÷ÓìAêû•óÅÙnáC&0¡RÓ
NJŒº/æ8¥ý„mþyëÂEé“:‚õgªœÿþ=t1ž,ÁÖ’v±”$«¡SQ­„PìjD^F£!û«(•‹íšÎÒ}dÄZ@Œ?Žv ›õ=ÍðÖ°n$ý`}®$)1W¾ÐF‡É€y1Ç
o˜üIJîÐ¿ûÃF>™ÏVq£‘œ¦©Ñn‹àIÙÂ§9×)ÈÂj«\T¢²ïZC1@sý7©êO,¡7Å]^«˜ƒ+€‹y* ×H:Î
m$±s£=öEr'ë|
Î¨ —I© ú§p+Ì7n"x²@ÝIb¶ñGø_¸×4þ‚p/ u	íäå[øÆõæóÕJÛçý hHMì§oìfVÛôcà¦Í©¦¨ÀFÒÎQêöš]¸|5ºþ’YýÙ÷qê&@s9¥sR†öÀx#ÊÔn£ðÑb=ËíÒ‡cBXT‡Šß@¾àE˜ÞN“¢]ÍÀtòÃbJh›Â“#äóÊºº¢$kÛŽˆ€ö-dàÕõnöõ¼ël{ïPóìÞSI?0àŽ°kbFõÙ×ð7úe!šG¸qå©%˜s<ãyÌFƒ«Á5^]^·Rmúå.0s‘ß“-í=Ô’øÓ¨¿¥anºŒóK  Ïã~-U´ä7v ¿¯«Ë÷³Ëìsx0!ß9î ØU®ÝŸÀÚÝ#»Ñ`ÜP¥åÚvèÏvhßãŽG©³ž#Ç˜mîØ,ãsöz„ÃõêÂïQ¼ä6$- }œ "ôÃJ»ÖI¢
 +'éov]eåó
Vc6|…k»:·lD9"=›ø„Ÿ7ðßáö/5¤Éj~ÿþ‡³9Ñ¡ûhïxÊÂÄé¯5|Š	PÙÚõ†	 ºQ·çZfê©]T~…½ðÛ¶²‚°ì*dyÀZ,ØÃq³âE!•0ýÄvŠã/g­VKåk¢Õ@ˆZðÆ;öåËÔcu:'Ä9ÚEãZ?×É(ê” n{’eï•ü&¼’ÿÁñxj×„$µ_v«u<zg*ÀB`¯KjÞfü0ôg&ÞŠ,Cj{|œ¾<æ³”Ç¸ö'“_)Žékb^¶&¦v’BûB˜ÏYÓÅN© }õËKV¿to_h_òòùJ^:AØ9í³¯sù<u.{Qì^åÒ·¼lqKèºx1}EËg¬héb7'¦/cyÁ2–nØíõaúÚ•ÏV»ÒÁ\˜s…Ðû-æXSž%ã<È"©ö %Fñ¨5Cö/^Zeü£yKæxž1fJ›ª¬Ê»±¥/t,6fÆÁ*Ì0Ä=¿VÐËzmÚÝI^qÇŸ–9¤ä}Ô¹wxž_hà4& =ÙúòñÉÖ˜ö$-PÀ\XÂ¸óÎËZ©8ÇÙEÓŸ‹ÆÜµb(Âga¤p@|Šu)ÓÛ×t¿¸‡Ã7{d]±±ãAÙ)æzÕÅÀT8_×x`zt8{m¥G?IŸIåkELLž†;ª¨\:÷ùýÊÂ)«·Eö>¶‡ÈÙLûµ=%Î]ÜZ—uKÅ?Õ€%ÊbFMñÇD\Æ¡Ï oyÕHòÂ±l£+ÌØ8NŠË;pVjÊ}Ð´É¨‹"Å Ið§*ˆdøÄ¬Ôÿª}êÑiuN’}®ù&†	¯–›î>[öâJp„ÙÎlÁ«/öˆÒ‚‚åG‚Ñ°áñ[¼Ñ’ûw”ÛÑ î|Ø2|ÄþŽºg¥Ío§ð¼tÇ–EµZî0\“,DZgbå_¦Öð,äÏµOS¶ÜD;´²‡ö¾ëØ}*2 Û2³‘O.t§IBÆK÷&L]œ÷ßæ•/8¦ˆbþi%“:ãaRE“Àêòö¦ ‡‰Ù+=²›M¡?>Éð¯-‘ìÿ1©ÂµØ5™>WÖ†â¯Ý‹øµçhÍ²Šé-×Þã9¿!æ°\*™×9#µE|i­‹gåö2Ãó­†æ5MEtXeÈÔ	jÚñófˆ_“JGìµÒQB<´ãMºEtô”©™öÅd§ªF|®³ìKÍ\ÙYè¬„Î­­mO1ùL ýƒÇóQUÍÀ–Õ,çlZK Hûp÷wœÜL%oZBEÊ¶<)ÛZ£cÓ
ÜIÚÜñ¨í¥ýÅ,m©˜¥6ßôäìöaûòþè·Å:W­ÙBqkúCþ0KN¹ßî YÇ{!ÆˆÏEN.x{x €‹Ø?š¹"ËMÙ&ú	ÿŠ¥M=E7CÏ\"Íë{Ò-òð¨àueè·\†Yò˜‚ÑZ%xæ>e­+­.©ˆßc§‚\Wqtsu­\…úMX7ÃL'||ÈÖØ²¨‡ôˆü˜}k˜j³‹ñ`0¾Ïc×¼°iÊÚ‚ýÅý?¥Ò¯RO¹J%ŸÃ
5&ÃŽÕ)×Þ“æ´¤ÑE'Î<Å”ðð®œÙÆ”G+ÇMÎTØE2Ñ¿ÓôÉ(ÆÈ¹Š}9Z7ïYMYåÚymç6uÅç¿ã!¸-áiM?
QU0–Ê©¬J<ÿï½b> aS5}y=5!*Älgcúy 7Ž³ÎE+WªYFõùv@•rKLÐâ¢bÓmÅòK-S¬D_»Ãghy¾a3Ü¥Îƒ¯Ù] ·+>Ùø!„I|jÿªû	Ht)Ÿ§kwkÐ­yk·Â©O÷sŽ€oø$Œm=
“Xw‹vÄ‘²]ñŽàÉÞ-8©[hú<ÊmÄ «ñÿoÎ—?ž     0707010001f06c000081a40000000000000000000000016a102a8a00000337000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.array.hcs.conf.gz    ‹     í™ßoÚ0Çßù+NâµãGkKG=lªÔ®/Ó4Lr¨IœÙiüÎ‰™U§Òµb—ûr¹û|íØIšÍC&ð°á„Rb}f#,ýÜp‡Íî°ìß\±Mg!®¾7.]ÑC[v£a“¿ÇõƒTáÀç ò˜Úßð°Œt s1K°¾ÐH$mÂŸE¬Ð§p«
×b$ŠÄøÔ¾È«X”5ª²SçÄQÀÌ1C®6Š ò¸]`©9]K[ *i‘Ï¥µ‰†1¹´ÛÎ©EÁ3½ZLÛÔß^v[†Å°Ã:X!&býº¸êž^ÝÎ“i2[¢Ú:;,”0±ÌvPºša†æ1sù8ªÆ`šk¶É¨µÍP)©´#]ž5´IíaiUø‘+¹z³@Ÿ3 ]a Qe0
Iåª® jÛaìhÍcÍ#ÙŒR¤îo+]Råƒ^·þOýÕÕ ÕÃ4•4ŽáöH^da
ƒzâîæÆ½³ü*Í³Nm^ÔæÈ;Œ¼ÃÈ;Œ{}ï[›ãsïëÍñ{o~ð¾uä[Ç§Þìv¶ì¾÷®ìX™B$pc¤s„ëD)uM¾ÞÀÝç¿[ŸjÍIeû+YsâkÖ®>?kNö,;›QÓ¸t»
õ¾a–‰qÎÚº@FåŒt‹-|Š “ÆÎæebx‘H’™î«™«1°ËPÅ¤:tEñÊÍiu¢sÁ<ÿÏ\hm›Žp£X•V-)ô“È9ÄÙ	à*§¤Æ„saW”V„
³À¹Šm¢ÐN¨å¢–6¤í”fHÑœÓ©^kÚ6M§NŠB·åÒo!–Û•'TéÑ)$‹“«Îxe¸6]­ÜËvY@dY@dY@d$ {ÓûV¬{Ïzýgç¨v¹‹t†Ê>t?õÍ·k$ª½½¯{Lœ¢,Ì›ýŽpú2ìÇ—
â¶±íÇ‚SfÆÌ^•Ý¼Õë¿>|‘5ª*mgrtêÞ!()óa>Ì‡ù0æÃ|˜óa>Ì‡ùŽÏow#+?ë0   0707010001f0c5000081a40000000000000000000000016a102a89000016ea000000e600010003ffffffffffffffff0000004500000000root/usr/share/doc/opensvc/template.service.container.podman.conf.gz  ‹     í]ÿÛ6²ÿ½àÏvv·\ÏÝ,nß5}¯¸ËåpI¯?…MK´Í·’¨ŠÔz]ô3CR"å¯›x“îU²ÉêEÍgf83œ=yrÊ?_<a'üƒÃ%ª0\¢0³.+Ušóâƒ‡;íìNK»/~l^öI%‹TÜýôÅ×ôÒ¯Ükñ¾ÃX¯T•ŽÛ©$*‡³)œžð’K'ªä³L4{_ÕOTâçZV¢Å·<Ót¦¬Ô­ÔR²XŒ£3©˜ó:3íÄÿ¡
:!‹¥¨¤áEâ”	>gWl)xêg!¼³¨ìy]ŠDÎe×,ÐK&xïVTÁøz™‰;¢<['pâýRxj1£˜¸ImL€8“ªäFT-Ï1U°ª.F0Ä×þ®Wì…*Í£ò„›3Y¼€y&>ÒËÝ s¹ÐQÜÊJ¹(Ì£ëÇŸ>=T«¥4B—<L‹’WÜˆ”eR¦æl:½¼åÕÕ«KK]Vð\\½¸ê³’›åÕt:
$þ!² /+¡ñ•aj_1©/áÿ"0­€j "û¹Vðl–€¶™‰` ^©ø ž¾ó¹À7Ùˆ óW0Rg¢ÁX[¦ÌþÍ+‰(Â¤*Áê²UÂµHn0p$@³ƒñRax²|t¼æ{(n›)ÀôiÈoÿª‹@òA#Ìxr³ °Gì0¨/¦Ó9ÎÙ#<Wx¡4¢ÍzÀx¦Š…–©`ã›Õxª¯ÌÄÈ\¨ÚL© àñq24|1ž
.0µž£Á	‚ÝÂHavb}+¡{ÅrÅ²0 $•ãËUù‹H|üÓéÀÍgïK°ú:Ðò–^ª:KÙ’ßFz%+áRå·wînþj]*ù““®üluRÉÒ ú…Õ™Wk¿Ê§~™o±ëj¡Y^T3ä—Øº…J°´$GC WÿÝ‹~¿Ø?ÄbË³ZÐBéVô ¯[ÛýŠN#ë`¸«x;ƒ½Kõ±Kôj”þÝï c2Ìµ{D]È;ö+['¤Vzƒ[`PàÔ¶z­È½pÑMÍ3–Ã‚	2NP88è. Pc£AÖ+SGf2z¢•Ë“hÄ¾›³BT™`ç Y®ÁJQÞj/þj û\©]d—9_|^šû¿‘õËy¡DVÖY6 [Ñ+i–Dh{é+öÕ—óþeš^üù«3T\õ¬.L=ÎPªÌ^úOð9“Re2Y?:þW@ý‡ÔD8>h"ž­øZ†‹Y2Ž`Å°·€»@Çë&†|ƒa°Ú{0€›ðqþiÅŒg¼ÃšŽÁ¢øÌ‘ày‹~ p‘dà,Œ<1òVô£Žeb*‰^˜ˆPþ›%À“Â
£„·ðÃ#cÐ3…ÇÞ+Ô|x<’ÒÐ<iG4K¬ùb!l!AeI¯¿½2)t¿DµK”v^tžŽ§NQU‡@)ð²«E&X¼bµÁ¾Ð™ã"WÅSígoÂfEÉ$Ú¿*à]@>SvŽ˜ƒõ† ^€ïnAÞÞQVò˜
nJ@ŽÑòäG¦`&AÀÑš¤0h¨–`\¶cÑ¯T4Z!ÌŽÑvÔF›Ï`¬•uCWÄüûäÌNÕò)MúUxvŸ>JKê’×FŸ¡óqõP+<EF1¾Ê¢ÀÿÅ;,-';Î-C0íc`5àèšàä;]FôËÕ¨us/+´ÌÒ;MÒ³£‘ðs0¥ž¿cçä,8g½uÉ²rdÂ^5¡š€R¡j"3Â)P7Ó©U1F°ªŒUiÆSw¯´ª§eX›(ZãO``=°\ðÂE£¶(­Äc
8Èi¨=zÄkë0>’Áßƒ>ÑS4:ƒƒI!§ ’Vubã×5(øE–!¦)úîÖƒ×ƒ4›	„‹5X0Ö]F7‚Hù§€»ÎÀ|‚%y®*ÆÃ÷kIÞ>kLâ…1‰p\oš8è*Ú{ðóE‡Df¦Û(¡É‘ÿ8ýª&e%D^š	Ÿ©êñ…˜š3e¶ãlÁložsÔ. èZícýÀÈî[0úÞýû¯–­ÑaVZ"o‹
#NÄ™!÷ø+ýÎ!ò¼roNâ	ú‹ŽØ;•;4xÚ2/3ú‚VŠ²* .K¸Õ²sÆ5Ú#©`Ï„¾{>j_'âÆf£jM"Ò]¦š6!2´M) o½¤¨†„Ï¸–Ž‘;O»ÏªUú6™TJ™	ÆÓû5¤åFÔž­¢’Úªro%ðOnÐùì†·Ä!X¼Õ…aQ
y#ö6ì=b¯áÕlüŒŒ^»<>ÿÒaÌüv‡›Z3¯y¥rfÙÂrDŒ°ßFG
Nìb‹R¦½Eq”E”ÚçìlsG·­ÿDð½ëÿ÷º¶vbÈWt]àÌ
œg¥™xQrrŒ)@ÜNKÉ~QùLkDöÄÆ2Oí¨tg´w™ow¸tËâ¾mV[—y ˆhfêxxÉf‚y÷ÒÎeÂÒí¨å’™Xˆ>Ëç¨˜Íÿ`(EÜQ¤ êégËÑP÷X4T®Œ˜T«OFCÚß³Ýeª5žu”'g(ÕÍ{hâ`´t¸‚—h¯PdÕáìq;dP8Õåj<:‰»ÈRÞÒº"4Âï.I·²Kú
8€%¯¼¡Ðêx´)Ñšds.3p	-BÝ#x“ !ÄÜ¡íõá1úð)c `)B{ŒÍAó¹=T®‰®´óMp4T>–®ÄžZ­'	§7Ù6¢}äôiÔ1ÞN×x¼‰Û¡%†2Â]Œdô„ZlK™,ã°4êÄL-ä–(¢5¸<0Þ&ÜB´6b~ã0·"knÂs
V“ä`ræp7ÆWNüwèq?x'Ûä}ÜâC—Gž¦[åSÏ [€@Š.äü&4DuíŒgoÇ"¬•ÈÕ­uð‡0/-Ôù¡Ó{uÜSÜ•ÐÉfÖÓë;SñÆk#Û´äZoAÒÅ,“7à£«î`6å¿±X0Ì’ó²„W¶î…‡ ÞZÛ9r’9‚a_À`çcûÌ¢aÉ¾üÓèåŸG:]¼uöÕýØ;Æªl¬·„?­%Ì",X#2|Éž‘Á¥Ã%Y­Áx…ÎÁÀÕü9áö%Fï4G¿8e*Úã®ÖQèr¬˜Àã,•úFy²”ÍÖÖÅö:c\-m\(’2ä6c£vý¢8TœÉðzIz,öa]Ž|Ì`+®;«¸mFîœ ?§t<˜…kÁÞOŒHÍN	/Í@ßUt<n¦àEq)Ô£Š1ÓV¦[Š¢¸é®hi;Ä!-k-ˆ¾ä>¹¥×Gf–:ël[MÆu´‹þáe$.Õ4
Æï~î®T{ÃÇ”‘la£½9ªQéÂ£c¸—ÖŽè½Ü*->çg¡–ð ¾ÊˆŒéCG…*®3°p9‡õR‹:UC þ=‡ ç´ê£Gl!¡‚#$/úR¶ü>ú½“×µô)ãW÷˜{ €93;wíŽÎÚ±#¶³±Ífï÷»W6·ke¯iø„ö­ðÔNF1ºç“>ÁñOˆpGó‰Ñ§ØÚ%@¶&SÑ™ýnÎhÈM	qÞ	Œ@dÛÁ6X1¡,Ù¾8õ$…p6üƒ(üŠKeˆW*«n½ªMPf®”Õþ¢JòÊÉSøa)\@šüÒ0åS‹é	8®K›vyŠÑ@W@\mŒÕI4eÁÌføEN»½k£T–80â®½>E ðI	<iX 77Ù'ªUìjhƒ|Ž>.bæq"¨µŽÇÓæM¦Î`±×U¥*mëNªº´ÛPî$b¸™ýecñMä(õP~(”!€DÊi'†ui˜{CˆÛélÝ#yZ¡i;ý@Ý÷ÃU•=ž§‘LUžB0Uyüê¢_+H,Ò~¨TCj%zëç¾ˆºÐà›?!2÷@³7~>É[?‘û"ØÛ>#’kütð¹¬½és2¹üXÛÇáqøzËçá„ò#MŸý˜â„?QžÄió“¿ÿYiƒùð³€»H«YÀ0É@¹|—ˆCgz`Pã a—¨bˆäM=b)T™5Y­x5l6Æ¶:\Š5¥*`p»vEFðèRZb"0VÓˆÕæ+ìl„(5¢ò–YúÉÑ®=![°3Áä¢ 9§¬¦(g«.‰eô²6©ZaôwF3Ûá’âÊÔFfØNµxÁ1M]§dÁ¾yýíõ÷oÃÄ” îJ¸gÂ@~Ž£p¾þ|Ó£c©`¾{ÌÛŽ³»Þ8Ý£©B_{U¤£¢Ü\¤(MÌjê
èQÛè4s{9*g—{÷Ósy=ZÝí5è^zä¯„—j3ëtð~¸iî› $K^,D:ˆ^/~²(ìƒzØ› Û º;|(M	/ûüí“Û»8åÿoe,Î®ôµ” H;yâÎtÔ˜)&¤5¥„Ttm:Ñ‹» ™ÀJiµ8¥üNW<‰­qÊL­ÛVš^ÐÚMg×ûžwˆ}r@Ë¨ªg SikP¸ž¨:ÛTr±À$&bPR2ÁÆÄ®[UE»ËâF#¹nµ3L"lšGM=\9CUêRÝÿiÇ¶0 œR™áúL#½.ÛKØ¾KJŸÊ®OL`ýü¡KH¶çUÙü5—½¦qíöÔæŒ&X¦hÞ³Ú4]&ÐsŠSR\:œb‚¸cŠI¦mÝÂÆ¨ÁºO=Ó‹J,pW;d8W¤<å·0Q{#ÐçÒÞç®nŠ¹Ü`3ºFì_þÙ„¶Í¶¸]ñµ¨n4$CÜÉ«U´zÜRñ÷¼Î¶ŽBÕi—A‘&®wûyØÖž^¯NÀU»êY“Yv#Õd%äbù-¼‡¨À&ŠT·Ûj“rKÚ8çŸ¶Š:¥L²	$ê¿©há»·ðŠ§æ}–º˜}Õ0Á™0+!
v~FžŸØßDUˆ(gÃ¿,§*Ø-Ìü=ÛƒlRÖÌ3ç=¬{aíäÙ`J<•¾`þ/z¾˜œ•Í¥ÀAqY¨N1ÁKQvü"@‰ëæb?8(¦¾—
Ÿi•aXÄ–lb[ˆ¶ü3p[“–û¶UT×—E}ÇºCc«Ðá9˜ŠØçùLæÒÄ~ ub¥Ò’¸³3{#™“u>Ã¾ss–Ë¤RZ û§šú† ÔÁh'5E|yö‡¿À¹f"ð;U»‚Ñ´ÔƒŽyjï¸è^oVÊ^ï ajâ~zÆ°}µz8…j³Jj{+Lj¢4pøüìâ®¯u]	»Ã 4ÃuX6¬)×d¤hŠP7œuì“[×»ÄEn²ÍGÄækek¢/:{o#…ØùÛ‰µDÿÖ’„Âž9B~^YSÂ&ÎºyDèZµž_ìç¾žïŽÖ½˜à¿j`÷zp˜ÉÂö°|Ñ47DòŽØ_ág§Œ0h‚©]†dê¼X6fgƒóÁ8^´"˜x`7˜¹È'¤K{Dï«I´ü%ö9^“˜ëcä¼Å PÕš=‹¾Å1[ƒƒüÜ
úÛÚßÎ‡oìu7àß‚ø.0‚b½\Ÿ ¦OQ''ƒ
+¬”^Ê´­ŸÑŸÜ¡T>ÁˆG¥²žGN±Ú\³yÆìu­?Žác”¹€¤å#@€ý dôž¸3’([ÞŽ“ÂèÏÖ¯²ãsc¨£QäÛab2rc0”cDbÏfÂ¯øÿ0üKI}¼ìíÛ7Á@v’Žyß¾ñ,§ŸÜÖt€uÕ×î@L Õx§ÄÏL×3ëT~oáÃ¶Ò˜m~tryÀµX©v€ÇõŠZ	€Àô,~
Ç /g-UIE¾~×QîxÅþx@3õX=œâDÎ¶ÄñMpÜ6¸§Q‚¸4Jn{«ä7a•üÊãC›&ö3fÍ“·î¾R¶ ·®hzÝýÃÐž™z-rš8è¨°qúò˜ÏRã"í;“_îSÓ×Ä|Úš˜£°;")´/„ùœ…0Ç£xT*h_ýò)«_ŽïPh_òòùJ^Ž‚ðè´Ï¾ÎåóÔ¹Dñø*—¾¸åÓ·Ý1VL_Ñò+ZŽñ8#¦/cù„e,ÇawÐ†ékW>[íÊ1ÞÃ„y¬úÁ~‹9Ö”'GÉ87²Œ% i¡AF‰Å(–Z=b?ðÊãŸÍ]ø™.‰¹úB›ª¶Ÿtuß*Ç6¸˜oVåôóû>à
ºY­uä†û/X4œCD>Ä:‡çãÛxÐ6Wn6WÂ´'iþÈ…el7’\ÕEïÄqö´yŸ§&¸n‡¡>Û)–¶âÆ¯•L¯ž]R¢îÕ`4=Çúì9lìW]1Ñ%ÇNÅ)æzÕå@\¯ër:+Õvô¨£<Öö=¶£ÅÌÓ0Åž**—ÎýèØæA{‰nÛÙ{ß|þ,lÏíÈ<Òh—ËâŠa«Pí—ÐDYÌQ3žÜøŒCŸAßòU3’Ë6Ú}…9ÇIÑay®JM¹}Ž0ªÇ¢rÐ$øS•@ÄdxÅ¼R¿ˆ¢½ª©Ã
¾#Ð^×<¦Û¤Ìò&eMú²W:€f;³¥í÷e¡—´Y3EbÃã»xC%÷î6R®ÎñË‡3ÃKH%p»tÏ+›ßNÛó²°;õQ­–f¢ñ+àu&¦ƒø£¦Òåv·ùVÐ)~™ª»¹í8 {ä>òj_l’ŠŒƒ`g¦;ñd‚Àë4IÈ˜aéî„¥ë³þÛ¼rß/ÎË¤Îx˜TÑ$°º¼½ŒÃÄ|N_žl™]wxùÜÀ¿°ŒdÇ¤
»©Å.Hõ¹²6þÂ}nÄûžgt–%L¯¹vAþ†8‡å²y3"[Ä/­vñœã¾dÃó­Šæ-E+ìrêTj/?ÏGøÔ8©ôŒ=+T”óx‚nÝ=ej¦}1ÙCU#~¬±ìKÍ\Ùiè?/µ¶º=Åä{Pø‘Tèï<žU5þ#(6«e†´`ï®ÿá¾IáwÉ›™P‘²-OÊ¶ÖèØ´,R·•éôÜö-i@Ž¥M0*f©-º–œ½Ãv·7úˆC»O7p*·ª?ä¿fÉîû~´êx+D»/ªs‘“	Ã6°Ã"ö+WdoµŸÃqw·X¶©ghf¨¹K¢uÝcO!'5ë+ÊÐn†Yò˜‚ÑºHžâçJ(k½PÅÐ1ñ÷Ø‘€¾8oØ=ÌYÄN¨íwU°,nŽ™Nx5|ÈÖ8³9¨Flå<Ø1û|Ó}ÅX´ç±›^8µ²¶ ¾x@@¿÷^êCz©DãÇà¡ÆÌ°Ç;¥ï.ôLó°L£Ê£xf§„Í?Žå™mœ²á9vy&`…},C*úwš>2ŠÖrQÄ¶¹ÇÍ}–R–¸v]Û¦6|ñ;Ám	§ÂtDÓK!’
d©šISqÁïÞºh>çI©š¾¼ž¦Ðù¸±_©€Œ†³ÊE;®,æÕç[ªämT,1Ø 	†‹ŠM·Ëß*™b%úÚ5Ÿ!÷¼£3\ƒìÙÞ>ÍF\T|:õÅî¯Ü·íâ-÷TS™¢I‘ø<]­A³æ……Ó;î÷Ç¼öx·OÂ½­m’ ëãv;â²}ûÁ•½Yð fA@éÇ`QncÒÿN¼#ú¦    0707010001f116000081a40000000000000000000000016a102a8a00001118000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.task.host.conf.gz ‹     í][sÛ6~Ï¯À4ÓI3+Ër¶Ý'öÔÛËlg›fgÓn2"!	c`PŠöòß÷œ€eK¢kÙ‰[æ!q$âÂóp.~úôž<eüƒÓ9n¯Ì­*ÁæÚº»NwØÝ–vOÞáË>5Råâý/O^ÒKŸÑk?y‚›¿«¥6ùéz“BgWRÍ.+xèÒÔ
ž::à¤˜ÍtÅ'…h–ýÑÔ¿0â×Z±ÞÏ·¼°ôMeôBZ©lí´õM.¦¼.Üú~ÐŠ¾j.Œt\ea¡Bð);gsÁó¸Áàí…ñßÛJdr*3xf&ŒÍˆF°‚Íà‰–é²ä*‡10ØÈÊ1§™x/²Ú	Æ§NææÞÂêÚd‚fG<s°éÓ1rœ0‹ÿxÈ¾1F[…±¦Æ	a|øy@oªõ~ðŒè±ëŽÝDLµ÷žGc7vÙ\dWlÀ€ ‘Ì¹öt=?û/+x`ÿõ—KnTäï¦Ì
ÂõY|èÙ€@Áÿ2n„Ët.˜´¬¶"Çg¨´qŒ“]HPnÁ:îj;¼izÚÃæÙ¢5Q³f.mUð,Ì-¬Ø,Ó ­i¥­óY õóA2.ä÷Ãô4ì'æÅ’¯,SÇœ8x	ØÐÙÆo^ >(ÇÅ/>ñ#P6êˆD3hÅž!¹fø5€€Ây] $ý^”p®°ñæI}CpYÄk<þr4{¬Â¢gìØšÅñ/ÅÿŽAø±WTöxÂ³«ºÚùv,ÕTÎì¥Pi´*…rN“¼ûåzD«…0Éìv^ˆ÷× _Î%¨šŠ£”‹ŠP<9+$2H×xüjÁÍùÙ+O]†Àœ¿ê³Š»ù9á×,pKˆ¢ Æmoñ•ak…DŸ å³‚ŸE>`V³\×@Dök­amÔx`~Rëbtì «o]¸²X¡%[Î5Ì´±Ñd®¶ÌþÅDaS`òêª&ã 3^c$`L€fã™’£l<:Žk¾¹/ã…»‹Õ¬s+'ðãÛ¬0Ôð¨;€OH× "ZˆåÉŒåAë€*¦­‹€ý¦ŸNóv'Ì¹š	`6ÔR!¯@mÞ9và­9"®sÞe7,óÞã0~5Gò$@%tÁú‘aWˆYr?êy
Ke¬úgÃêG{ÂŽ¢ìÊê<e¾Û³ì­BoîdºZƒl²ê5ÀZ‘£àN¤â ò ·^*`¤ÉÊŸ35øù ‹Í€T·tx$h)…1"l¢h4Aã¿Ù)þ…,¥»äö¶¼&ýòßiöäe$òd4*wS2ÓØÓr-‘HÝ¨YÕ=17ˆ™×&xÔ	A«é9ßMMtßzrîáM$R'Þœú‘=9w’“¨Ô‰ž¥(ñJ´§èŠ:u¢©ÒSYôLº¤žLÝ(
¯‘õÝGP¤R'zÛ»žû¨	4êDKëx¯?÷R“¨Ô‰žPµ=9÷‰´šzöèÈ'»/2Þt¹û½ž5·´L×®ªýÁ_˜…ÌÒ’Žöð/Pv¹…ê%‰÷SE!ŠGGþ“û¤=Þ”Ï„iç …^âennB	»’øN–xó¦UV#”+VCJd”‚m`hu#¿ýýW;ç¢OÝH¬ˆ‘U9e§WËÓq4nGÐkƒwg<D×	˜Hð]‘SDø2>¸#€Ú';=\²S®)ÇakÚL·T§>Ãéá2œö"Ö%¿	Óeßyà~[šŽ®šÀåIlÇRø˜0Ï2Q¹hÅRÈZy*Câ‡0ÆB ÊgÇ¯ŒÌÏ?{…©Dâ|0Ÿ·ãV„Æï|8Hƒè0N5×ÕÀº|²bu57iT!tÈ–ÌËŠßÊÏFÜ’2°Ì6~‰‰3¿[	ÝæÕ‘0É[ßš¡Þ
/™CþäÛ PÆ‡ð}QkkŽí>Îuv‹)»ÈŽã À—r–Ð5ILv¥ß2ÍSÎRæ“šçÎØ»OF£ÓÑèhtù—VjõÉ€}2zAŸþ9|êjàýZ}²5¦hEæ¿OmºMû¢cÛÓöÆxðE‚ñ]BØ!pÝâ»íënaûw	aßÀF;CÙViýïþG–Z+"Të |Í“ñˆôAñè«H›€z¥ô’r™2V,Zœ0d¼æbfx2žæ”ÉdˆÍrØ~‹è”iý¾xÁMiE_R¬Àj-xQÕåŸ?na	<Â‘½¿.êÂ?séˆê¯â×ç‘/ - ¥uG;lÙ”Ë¢¦ì³ÔQ:€jàóY×|†’¼„ÕðÞ]*PM D"âYÝ­#©ô9qÞ3öÅÖ®ºôóô‡˜%EA:¢(Ej“9©l  ƒA$öƒp½x±•ÂVôw'-£zúÊ,™Õ
l8ØE¡®_a¦¦‰ç	ÏÓD 4MÒ}pÆx^JÕ­¬!IÐo½ I IÞÍ›ŒcMX3A×Ê0Õ¸ÊCÜu)ï^ÚGÓÜBøKÁa¼Gò°B™Òv|@Ã·ÃU÷)´’L]B0uuüjÕÛÊ{Ë„´¿U*“)nQIÝ#z÷hÃ=¸?)2·@³w~7:€÷¹-‚½ïs?"yWçgŸ[ÁÚ»>“Ë»ú>[À×{>÷'”wt}ÚèìéC~  [ƒÇG£ÿ›(*Šqk£H«…¾B`4-|àwH;À(\6O •–)|8@«£Œ³uEíG*¡1mÎt³T£vŒ®ÄÃüí’àÅ¹•Ö0×
—®´²râÛM4í%|°kà¹´ˆÊï¬|ÿÁëö/"!ó¤¯‹ˆ'‚É™9Ïžš˜–quE,cçµËõRµBÎÆ¸×º¿‹ïU#°XszRÐ_*öõ7ß^üôý>(BÑÓ4ÖôhhÃt¿ñû\K5×ªÕÂæšÇGÛÞ´7A÷Xº‡¶Q¥>	¼˜KNŸÔQ” )óÓ±.¸ŸÇí… °.‰RÃKã“ðRv-Éûaò	I¼[F…ùù õzí•…ò7ôð\Ø7ÂÇûº#•ñªo’qa{ë³¡Æc£Çc
“Œùä—F CjµÒ¹Oíuâ½c¼•¡ÆÌ1Bââã„TkŒ­¬§Å|'L`¥¼hUj‡ên
KbÄº*ôŠºµìÎÊ%ÐÄ†úzXoû”€–{„éÅ'öÃdƒ@Ôk:Û9›OpbPR’2BöÂŠµ9–
;;9Ðåº.rj´â•{ËEÇlT(úUiÈO	Sîc]¡Jå/ÛåtÝk€‰d™mûKðÐ<gÏ”ö–ãÙ€	˜MÍA›h“‰hsˆÏGÙmw¤~²gtÁ
MûžÔ.fÊÒÉ	€¿vðkMérì¢°¾ÌµY»“UŒ˜aWÊp~,¶„@r°g|õn¸—õ¸ðt2ü™
€ÏÙ?ãÚ°íðÓé8bá->¢vC‹<O^;}‚2òE£ZE¯'¼àýdÅ´.nœÅw ªËŠa±¦]Y'BîŽxÏKð}Þ¤×Ù¸j[vùìrR\I}¹r6ÿÝÞBTÀ€	•ÛõÄ™ÀQ—ÕMÚ;Üó/pþJEÍß½W,85±òÔÅÔ½¢†N„[
¡ØÉˆ0<FCöwa•75ç•ø²ô=åŸ·ÑÂDžÑd³ª¾Ä$EÞÃºÖ|Ì§Ä“$¥sáÉ[sRŸÌK/¨…•ˆŠ)$ÿÄÎ5SöiûÜt¹	ÒHNÓÄê¯E(+/¦×©5a2&·–Â	3dß®Å Õõ÷RÕïYF#,&¯€«X‚+€‡yªmlŸ¨¹#ÖIâÎýøäNÖåœ A)3£­ÀT{ÛÎ?l"x6GÚI‡®ñø‹Ñ§_ÂwÍFàÿÔÊœ¤%ì“o¤Ëâˆ›Ï»¥öÏGh˜š¸ŸÖØ#”ùm{8„jóJªIé÷ª#Â=PsKŸŒ^|Î<ý‡ì;Åxz©ê£	5û6¬)•dDÙÚ´³l›Ã)æÊ–þèƒ=[ÇdZƒªIªm¢7Šþ4æ6Å»#’Ð5EdŽ”Ÿ—Þ•Ð°÷Ñb@?
9ðäÅnîëù®³îõu«ö¨‡‰Ä{e¼i7DòÙWð7úe)74pª¨sìSxÊFƒ“Áü`tôb)‚‰l³å%éÒÑÛj’Xhß@ý‰¹í"çk, ì)ù™T­{8 ?÷‚þ¦vGo¦G¯ýsWp¾ñáŠ?åúû	¬•Øh;‰
£âÖbkêÀÚ^ÈGôgwh]^â‡ÑEÏ#‡°6¾¡ëg#×“çÈñŽÒâGáBÒó  …~2µ–Nõ÷óä0ûóAèKós§1¾Â³]]znL¦
ŒHìÙÄ'¢ÝÀŸÓë_ÚHÓñÍ›×i!m20ï›×‘e±Œ1l†E[ä@²Ux“va$]ØøÙzâ•/ñ-âµ­t`¡[ÁNR.O¸vWÙxÚ.yUI%loØÂâxÁËÙšªÁ¥Šu­Ó@ŠZ2âŒ}¾G3õXÝŸDŽnÑÓ¸ÖëäsR
·uJ·½NÉ¢÷J>
¯äO(÷íšÐLë•Ãi½¶tÍ>£·6´½ÍøaêÏŒ£Y¤.Ôöø8}yÌ)éÔE¤cÊK_ó°51°ëÚÂ|ÈB˜î(vJí«_²ú¥#xû@û’—WòÒ	ÂÎiŸ}Ë‡©séÒ[ëö^ÌÃ·t®‹ÓW´|ÀŠ–Î vsbú2–,cé†Ý^¦¯]ù`µ+]¼…óX!<t+êCæXSž%ã\Éª-©¾ÉO€¥-Fm©µCö³ÿ5·§ìÍ(Ybÿ8Ì”¶ÎÔþWúÒê|‰í`fâßO	WÐ`½²ëÛIîxàŸ5çìê¤Ý<×·(½×¥ÍûtjTÚîxSËÒîJÛý6[–ÞØ¨ô:SìjWŠ©/yŸ_åwÕ>1w?äaÏñp^n–L€ÁlF`Î*±ç·ÏkiÊŸ§¼˜±I-u`¶·?`8ùÊ6a‡f'Tõåó½‹“ž}œ«þ|©­»~Kšc®8ÌŠaÿÙ¦hø¾‰¡¼§ˆ ?gÜ¦ÀWÞùs\ÊL;P”+N1ß†Fl\”¤ÓÚ°§š~ZÄ>Î±®,ÅØ*™`ð-më‡x¶©'˜,«§!²JY{¢YM˜Ô­*LÑG-x”¦bŒsW*{†¿!˜Ò •VG©ˆ¿O	Jà´a÷4	D×*” g‡uSSÃN¬ -V¸³)Vü‡l\‚Sö5·ùŠma°ñOÃöÒ­]«Hl{¤Á¸Þìß«Ù'?“ßf†]æŽ•=ÓÜ3Óèê1ô³o±Â.–!ýÍGIÅZ9Sm_Ya=ÎSÊ×Ûµç~Çg`¼)ƒÙ¬%šQ
‘T Kf"ÁßJÿÞq> ás_b½"m¡UÙ²¶ÆKéæ)n08Îºëy¥šTðèÊÀù>Í>ìxÓµªwnª>\h™ciß*TóSÿ†Î›F—Éjt4ˆ×ãq¬<óG‹v€¸ª/‹¤ßÅÄ'_“‰nÍ±¿[ wÜ.îùJññÞG¥—…×î¬»]µ¯w] %OönÁ½º	¥ƒGycÖø?;:˜'“  0707010001f0af000081a40000000000000000000000016a102a8a000000e2000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.vioserver.conf.gz    ‹     ­Q1nÃ0Üõ
žï.Ú±c—ŒE(Ò¹êŠ.)%Íï+7 «8òÈãÝ±ëZ–é¨a­tÇÀ
9BÚÐµU×6;óZÍ¾™UìÎ'?Ünª›àóŒnÖŒ:^ìaF½ölg]‘à+ÿxŒ6Ïé&ð…#®d ¢rucpôDïˆà~ueb÷ç‹+ö©¼{d¡4·±.ŽÃ~É:ÕˆöÙƒ.Øöîµ;€²J¯“ôž]M­rÖ†žc²ßeÛ<ÔÞccc~ èéÍ    0707010001f0a5000081a40000000000000000000000016a102a8a000001f8000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.pool.zpool.conf.gz   ‹     å•MoÛ0†ïþrÍšûŠî¸ã.ë­(E¦g-’èI´›ô×’?4p°-ÆV`ºXeŠ|^šZ­–Å
É]Cd×ÀÇá5Íot·ltË²+žR‚«`|‰‡çâ>'ýÐ§])ú=_(”OAxåP‰RÔÔ¨Åé¤ÏÊÆtüÑš€§C›%Vªµ|Šíy}!HØzclP›ÊhøßÐc0:''¢–5æ´€*àz”	”µ¤c^Í‹ÙÖa„W™È	P*V9ž	m<Óø¿Ï. "šcYÅmBþoqN–ž‡*þ)ÎÊXŒÇÈèÇŠ‚S|¢Xbg´P|1\gT¡•Îœ¡åö;»ßRÃï•×Óó/ãÐä;g_ÇÚâád*´5ØnÊ—@‚0ÀNªr?@P±!/Ë{šô÷¶t²ÐlTÀ%²_‘AR™UAÄúÏDH9Ïð½RÞÔ×u¦žÿÒ±yþÑ[Úª£Öó„lè¯	fªZ˜‚\Ïäz¬¸Û¨k,[ûné-;eÛ\erÍ¤G‚õ&íÌÈ·¿¤Îþ|Ú}GÍqºÏJ¨¹‰û|E”b›XKwØ”¤7“o)î´ñÌÛdŠGÏêÕzÑ ÚO»^öì	  0707010001f057000081a40000000000000000000000016a102a8a000001c3000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.cluster.pool.shm.conf.gz  ‹     å”ÁnÛ0†ï~
¹vÍ}ÅvÜq—õV"S³IôDÉMúô%Ç¸ØV£(0^›4E~?ÅÕjI«V° iºŽÈ_A>tÜ„7§[¶ºeÙUwÚì*µ±Æý}u34ýEÛ®*­}‡‡Jõçço4H|Ÿ4ÅÄ–:³õ8öÍxÖƒ áïÒ&¬/=5:S|~®oïø”A*Çtôq‡¶u­…¯ð#¦ÖýI¶qÛ ¸Ö#8c€Là(“!‹CI`ßZdxhss-ÿÞŒ „–ž9C+ì¶~·¡.T^w÷ŒÀRì1ýÍÇýÈÇ^ïï¯ÀÄH&Øz²»"Ø„&·%aCL§¯¬ÜË±ÐÑäC—°3	/”D˜øŠÒÊ¬
"Ö&‚ö<Ã÷•ñ¡q½Î4æ÷Bz›ÊßýNÿu*1OÈÈ¦N-
LÁ?îÈD¸9¢9pœM.¼aÛ`]ü‡]¤o!Ø_†)cÌúPX/Ú>9ñ••QÂùÍ§í/´™ÁxÙw—(LÜ¯á¢sZs#Ûa]“]O¹e¸5ð,ÛäâCÌf?¨õ²¢Qµ''®±ž   0707010001f10c000081a40000000000000000000000016a102a8a000012b3000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.service.sync.rsync.conf.gz    ‹     í]QsÛ6~Ï¯À´Óq2'+²Û»·ÍœÛ4s¶—›:½{èt,ˆ%œI€%@ËêÜ¿Ý@²-Q¶dÕ-ó("±ñ}»X¬°‹?ÞåŸ³þAqf¡’³‹R°
??RÜn{·Û±{ñ3¾àÇ•T©¸ùåÅçôÒ_º×~ñ{%s]¥gËNLæ¹,¤…kÇ;üƒe]òI.š‡½ã¹x¥¿Ö²éí+e¥¯¥‘ZI5=k]IEÆëÜ.{þO­è‚T3QIËUâŸ”ž±7l&xº!¼´¨ÜuSŠDf2{¦BAÛoK´ºU$_*+¦¢¢aƒ§›.}ÅU:—©14¸‡}÷ãe™K‘2«™Iãif+®L&ª!û^ðkÁDQÚÜqH¨LW‰`J;yCö_	£ ‘
x#¦¡Sììj~6ö@™øàHÌÏo¿ywþÓ÷~Bg?÷÷î™Ö÷¡žš#þ¡ª
xê[a¬TÜB'˜Î BA6cÈ¾s^‰ð]!8v2«sÀ-…OìèõÑý(2$IÓK"A05W¬äSPTð8Ëen$ÿ eæO®”FßfD{\-´<t­,+5èé€½/…ºø÷×l.óœÊÈTmÆ-bãŸºµf¨²‚çù‚Í4Ra
¡ëpß»PJTê œÄ²j—¹{Ï¨¶rTÚ’Öf¹žJ <åÝ{Yða•ÖöUàAfÖ3A—ÈÌ'áBPÊPaŸ¶ÙÌrqÓ"È9›Ï¤¦ä‰86Æœ#|¹EÆÿ`X¹AÔj•Ì¸š:“Mš:Œ€ý°(eÂs 5 ?ÿú{4ÉFT×ÎJðÄJ÷‘@ôŒ÷aˆíåsE²¹²/8'Zƒ$Õô'#õAËŽ©Œ…!œ0ãzn }¿ÙŒWi¢S‘F˜úÛ†ìkRc§¼ØF7 ä†[[Aßá#OrC[Üð¢ÌÜ‚ vƒrÞFñ²‡œ%™‚š³eoÙÿ–ÏéÄƒ¥å-W¬½­Ð«¢!73MŸe²"'Æ/ZÛä0×ë}°hægÐñF,þ?«tOñxH$^à‡t¨’ƒ²áÐÎÖP£aná|q†FYùzÛžÉ|f;a)àŒ½3¡ïäÕE¢È¿ëèÕÎëçrË§ýDÞšÈE›‚
‚¹u„0uYêÊÿ³ä†ðlœŠœ/.QõÆ8¦ÞEƒO0]¯ºh43ÀíÚ’•HÀã£¹ü¿5°h" J±„x ëXÏlX‡•à#¢˜K[É),Ç0Mˆ¤&o€úæ¡‰˜ñk	oÁ^ªxÕ‡$xÅ'§•®K¾gÓ3³ìšïí‰	¦”·×ÞV0Ù×ehcçB(ßUÅVOÁ°EÍï~b2öq?cy5òä3\Z•ô±›åƒõgRÉ	´3—Ì¼àB§#òZ¸À1 ™‘ˆ®ÿúñm„(6)Âs=V™‹,ƒY­ªŽÓ]œÿ“«+4c<&P”#zðQ$®@ÝÀõÌÄk%0Öß~|ûî8—W8Ÿ"0¸¨U<‡öapŽ<ÑI@57`÷©r\A¿/‘­—âÏÎ”îky|4+
f$dSI“3LÂ[/²nÞ¬ÆV 9ÆõŒVh/ü›Œ™ûjÈ¾©*]
†UU]:÷Ê_¤hÓÝÈ¬ŸÛpÀöP>Ê@Êq<o=F³5„ð—J'‹ÉÝ*e<¶ãÇ êel‡«.{<w£™ºÜ…bêr+ü`Ò½„	·ÇpÇ:éÇõÁúèÛoep+{4w&ì£ðt~û¶ˆB×Ào=¦{ÀÔí£Põ2‚+þêÞÃºXáŸÇ¢ŠëÑmA­K\î÷ îT7´Õ‰ØÔZõ±„=íCADtµ}th[Dãà÷þÂC12[ Ù‡‡äŽ£C‘mìcCûQÉÇ‡VðÙ
Ö>4´3½|llÈã±|}dhOùˆÐP„Ë¶Pö¡½ùàÈP›míãB{„ô¡| kÚªŒµÑÙÓ>*´?Lj£³¦}Ph˜>2*ÔFg=¦Øa¡žabÛ>ðû‡ÈKƒû!aœk­hÁç ÓùrÛ­ß0F[Å<fàö¥E€JÃÞÐê8¡í‹n›&+….Aî|TSº¡¸]—aÚDßÞK½`¸7ŒÕ¦¦4xt©•‘¸YˆE$ðp®Ù”è÷òÿÁöÝ?ù†ûó0K
¸öÁäTiÜê`ªPøÀï.4³Ú¦z®âýÓßZƒ÷ØÚ mx>çL·B²€ ñX½æãñ±Êjd>•q@tœÏtÞÚC¸ÜÏú8Œû®§üm$ÔL«Ö¾ð[Á0êöêRÜÛCÛyM0Eñv`	\L%Œ4‘ÕÖŒG€®@W¤gc]°/JngoB÷Æ0jp»Ö¬îÁõ/=wÂK™¥þEïgQ“¤{7ŸÂ4h½^ûÉB¹7ãá¾¸°OJôÉ0²„Júì–)ÛE;µ?;ó™ÄAA3P!ÜJ$[qcWvs'h1¡ÓÌå%ãí„T«Û¡¾kÜ®q Rš·¦&n¿<‡7NE™ëÆŠ†R¹„1q¢çm¢OhYÒÊž@;±Ö`pÃ Þ²Ù~S?L½´5ÜÔ2Ž0é“_(–Ó±TW)ä÷Ÿc®œ3î­è%fÃúá¸æhÌ‰^ä&¸<:ž?; žRÞåæ
\#\b˜¶¿„	S·ŸÞÍG&@ËöG	hÓÑfõ.óºÂD)ÕŒ~ÔgtÁrMýžÔHEðRP¹Bí^	,À¯5(€©*7.9ê–ÔhÞGa0*•˜bêÖítyÌíÄá`GüÓ©¨!ŒöeÙÎß5?ÂLkð}LÇ÷Ïö „Œ¡€…›ñ}&JÏ†×VÜb0Ì×Á¬¢×ã_pˆÉ©˜1–µtg)…²"Òº(]nÚÂXQÜÎ(mÆëË°ê=+§—“üJêË¹ÓÙì‡Ô-T&0¡R³LIVâ²œâ”ö3öù—îU@p=Ê¾}¯˜O®ÁÖÒèÙ¿y^CCÖÉˆ0<FCö¨”Èã$Åð²t±‚ã¯£5È&e}ùk­-ïa]ëJ~!,H\IV˜e‡+_è _Ñzt^'èe{Ã/…n½OÔƒyó“ö:¸©(áµ‘œ¦‰Ñ9†E®‘”E,Ÿ¬%0’E
„Åz1ï–†b€æú{©ê–P0ãññ	¸Š˜¹Š‹yªöÒ^ é8+µ‘ÄÎ•þ¸†äNÖÅœ‚B&•6èŸšvŽa3 ‚'3;©aÑ5ÿuôÉßáZÓø?eæƒÓc	ýl»§®Åéêýv®ÝýARûéT€Ò×L¯»0mÎH5)¬Ž0’"G©5Qøúdtú™K„…ï·Šñx=RÖÇ]ãl+mM­ #ÊÔ+É‰Íâž)]ïR¶–ÉôäHÅ9ÑMŠn5Ó!t(¨µÄõ­
SrÄ|ž;WBC7ªÐ]+dàÉézöõ¼ël{ÏqäØƒ&ãÊ!^45zpx‡ìkøý²;ËøpãË T`ÎÕT°36œNñ‹ÑñéR¿¸ÌB—OV
íeIŒü­½æø†ÔÜtÑó¨$“€oìe«¶ÙdäWNÑß×öø}vüƒ»ï
Ö· ¾˜=ìW¹.>‘p¬“Åk4X3Ÿ=%´’èÏvh]\bÄ£ÒyÏ‘]Ì6ç,Ëù”½¡ºž¼r»\Œz„8¥ã @ýd\=qï$±jŒ““‚ôW·®rò¹µXŠ®íêÂ±1N{w z6¿O„y?Çá_êHâeïßÿ	rôä}ÿC ,Lœ¡sÐ,Ìµ0|†láßÔP]©:ã{fê‰[T~×0’fhÀV°“˜åkë'8næ¼,¥…é)¾Šc€—³å¨z—ŠÖú««µ¨Å—ì³–©ÇjNˆW9Š¢Ç¿ký}OFa[§qÛè”\÷^ÉïÂ+ùêã¾]Wý¨y²_­»úD`!ð­+êÞ­>‘?3Vä:vqp¡¶ÁÇé+«¤²Š´ß»ùe›º*}9•§-§Ò	»ù2}•CÖPéŽb§,™¾pÊSNéÞ¦Ü˜¾ZÊ!ª¥t¯SFL_"åP%RºƒØ!¦¯‹rÈº(Ý¡ì”ýÒC9\1”­ Ü˜óÒW@9\”îHvÉtéËž®ìI'$;ç·ôµNSëd#ŠÝ+ôNž¶ÀIèº„kúª&¬jÒÄnÑš¾”É–2é†ÝÆ`M_¿ä õK:a×-VÓ-9PÑ’Îv	Õô•JX©¤3’Ý"5}y’ƒ•'ÙÉÍš¾&ÉÁj’t²Sœ¦/Dr°B$]€Ü"Ló\!Â~	ó”ôH™UW²l¯F %4H”¶µW&fÈþÃ+7ÿjZÉôÆ´wc«:¡$*WÇ„ÿÄôÎöÎcLÅ\?E¬ Æza–[Í¸åž?KæÐ o¢Î¥ÇóO|v`÷£~1‡M6‡ÂÂ$4ÙŸó€5ïsÔX‚ó¥Ú®}ƒGé¹Ó$[9m_T2}óòÊº~3‡¯°˜	žYŠß¸¡ºôàöº‹1‰ºï8«»Í‰aK8<V$6þ0mò4¤Xîð³/Žö™%×6í¡ÖEëLY?|tæ¸·._¨7ÌÊB˜0Õ€%ÊÛŒšðä*¤†rK^5’‚p¬Á±Ü$š±³v†{\«g¥¦vš6Ù*®CÛXËAS­J>´H†wd•þM¨å]MQ‚ï%¹ûšÇ`hœ½ì¸I)°¡†‰¯§®³7¾XÈÃðNI™mòW¤6¼ÝŠ7£ÔœþîvÅ¾Ú/÷o!“ÀýiÀ•+V@¹R¹´‹Vá'æÒ$3‘Ö¹·’iSgx	JüÉ´iŠÇ‰¯dj´D{,eÝuÿb—t6éØ÷Ì¬”7 ^§É(ÇtYß¦®á\	˜qÌ÷Ådb+“:çq†L“ì“0' ‡‰,Ã²]ÙÍªÐAÐÏ[² üSG$÷Ìq;”Ù)™¾è˜õÓpŠ¶¯6Ø,70½åŠ-WZ;dåøPd+¤’E]ø³nc¾,­K`%j3Ã‹;ÍKšŠæ³pŠ¬o5Æª[N^ñ©íáÑÊ)·ÔW1èÑuÐùÃ®vÂÄÞ¼:­£×ÞÚ"x¶Z.
ÅÎ*èùoî$áð0B´˜×µ©^SºóëT'¯ááÊ\'¯k5nR•ÂW(Üò›s2 ƒ¡C°Ã}_²Ÿ?ÎF£ãÑ	üý÷¿àtøÑ€}4:¥o?õßÚü¸Z}ôË½¼ jûŠQû*9öØET¨'åkÑÌWðr7ç§Xa¦˜˜NôÏ[¥s®vÎõ”Mj	ŠÒð¤bpz®L“
Óô„*ºDù…x\îV¢tå'ÝAÇÍ[’@Žõ‹@*¦¢NW=|×ÂñÚ5ÞPØªÿIx›ÒT®¤s	bþ0VQýïïÔø3Ö¹(hiÖ†=®êÄ"ö·<š–î]9:‰~FO›z‚î'Lšîgò÷ö4"´ø¡vQbÙ(ôgãR˜÷…ž2˜å#àPF¥)”VÇžTÄï3?t¦{ÖÐ=NLÖµòe°ÐŸÆÚW¦3b;ªêšÓAñV¡ÙÜG6ÎØ[XÛÚÕWl+ƒ	ï|æ»wíVíªè·õÚ ó~½Øgô‚Æø9D.ÚdXµÀ%bOš=“F—8sSâ
¿]9sSnEV9QaeÈDÿIs¤c¢#§ªíËQØ¤içFÊ®›×Öþ|A”	ørW½FîW#›~¶ü6-Öi§ßÔShÿ‚a~6Š)±‰>¿yºànt8I?=EXð²ª^Ybý¤$éÝ%Z=¹=±ÊEäøDcS*îG‘Üfâ#ëíj«µÃiáUÈ¥âÿ°¼¥£qzÆÂ+¹Ó "YÂxYæ°`ò…Ýï9,ýrÆ§•xàƒ¯ŠáƒJ®Bnüãk’7­§† (E0¤
ÁöveÊè–À±ñ’£ÍwXE\GRZöVnÿVŽúÙØ¹6-6Y:¿c¯§Ñþiä‡úÙi•¨äþé™´&apï)&Æ&¹‹=öÏ#7ÒÏ†G+ÄXÃ£ç¼™õùî„Œ·©ÞÚñÞmãb{Óëº­‹Ñ½ÕØ«ÕˆFú9X»ˆAVãÿcZfGª   0707010001f072000081a40000000000000000000000016a102a8a0000019f000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.node.array.nexenta.conf.gz    ‹     í˜±jÃ0†w?Å×4I;6MÆŽ²•‚/ò%µ%÷$§ñÛW’±…B…rZb|Òï»ï‹çù”+ËaÂã»ø®!0t&ãñ7qÓv7-»ì5›³6%ß²Uz=Œeq€wê>-——>t.Þ
Å»	W„å”mpWÑø´g¬Å
ÓG«™.}l¹M…’öØVþÒß‹54d„>‰û¢kHé½V°b­Ò€!Á©°c{¤q4ðZGñ§²Ðftn˜œ£ÐÂSƒþ¸	mí‰É¨´¯|;RL~žRûk¨[çaG!|¸[®sžê¢ ƒ5¹C
š²ßwÄ]Åõ†öÂ‘ eÚ_¯¡[„§-’×{(E "PŠ@(E œJ eÿ·òÆÊ½‡årùc{ ÊšñÕym<ˆoÌ_°¬µÑÎ3zmTá2¦%&=Ðp`¦>¾áþ•ýÃ`íæHlÆêØZ/|„ð>ÂGøá#|„ð>Óñù6¦Yt   0707010001f073000081a40000000000000000000000016a102a8a000002d4000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.node.array.pure.conf.gz   ‹     í˜MoÛ0†ïùr­ã¦uú‘6=;îÒÞ†¡¦%ºÖêHž$§Í¿%Õ®»,ÀÖXÄ—Dýš|DÑJÆã]^£1ÛáååÀX1·n5­Á”Ûmt»e7ú’©>]…¤!íÑÈGÿˆë'mÄü5h$Ù“^’åº¢ÆþAŸ¡¶ègþh¥Á×îL&–ÐÖî5´/Za§…Œ¢F'mƒ\–’³ö€
ä!7R°œ<î*Œ‹NÏ²Î'ÈZSOÈçÊ_°Ê¹ÆÎÓ48MH\ÙŸp½Li>]M'ÇXXXXû ‹×•»—b‘ÅÜ˜ÌiÖZbh™#{žC+òÜÓ`RSëZƒ`ºÃZ?H5xrvO‹˜—E†b&fI	Ùy’N8+.ÌNg³Ë'Å6êRYäþµû_¡÷3Ôû™?ÂNU¤Õ
Í@ ÐºFPo–ä“´>0SgVóGtŒîózà¤öîW"£Ø†ÐÚÍGxmaêÊÎ(ú~óÞ"ŽynT0™ÿ¶‚»PB%¿È.~éâdÙÏ>(n6‰GùK“¨Höá¾3D8Ä3ƒ¬€éÉErÙ,ÉN3Hàø¬LÄÙ´˜– J<åÛ[^¡hëÛÞùö%/*RCæb©M 8ç	p¿÷çyÓÚÊÿÉ™Ò‚Ö!˜'ìqPÇ­5©­À`*4O;`½\o°kåà9,Mo£ž­õVöÈº=¬î˜XWÝôa¦öêiIEÜ–¡õöÁxº\pïëýˆásCKÎ´Y€]7àªR(Ñ âá!ðòÜÉ †Ø²¥sLÝÎÊs»¶—´ß|²
(ý*XÅxK]×úIª‡­¤“@cäŠ‚¾§qžÇu‰/XÔNiHÓm5ÐµÀ=¬‚.µ.gÛâ]G¡^0þà‡£ÑO7›ìMî  0707010001f0b5000081a40000000000000000000000016a102a8900001280000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.app.forking.conf.gz   ‹     í]mã¶þ~¿‚@Pìêõy)P8{‹nó‚mzEsi?Áš–h›X‰TDÉ>ýñ’i{m9kïeåC°§—9óÌp8/ôgŸò¿WŸ±þ‡äxQXµ.›éòAªùÓÈvt§åÝ«a²Ÿ•R¥âãO¯¾ I¿óÓ~õ
‡ÿ Ö+]¦ãvÉB$pçò„ÿ!ŸL¢>ÍDó©e-ðF)~®e)Ú1|Ã3CwŠR/¥‘ZÁpÇÑTÌxUí°›;R-D)+®÷¥Lð»eÁS?Á`Ê¢´÷M!9“	<3
ÞMˆ1ð	“À“IãœL˜ø(’ºlœäéxrc’RÕ-3¯j3aZ¹?™Xò¬æ{àc2™áPª™ÒÕ‘·ó%xôÆ,2ñ>Y”Â n'“€–Jµ,ÑyÎUúØ÷_}Aò±[íú}%s¡ëêÅ	ÿïZNöÀ­–¢è§uIüŒ€ño.+Ô%vãoß²©€žH2^¢€QB ƒ,ãµ—–ÑŒ'ø<ãLg\fu)†ìf/‘
˜	Ó06~X'N>“!ûvÆ”@½HDÏ ÆÜ[‘l'LfD5°CƒYWl31´3©d%²5Ípk•ÁUu	`½cŽ2K¸t]™HÙ
j‡¢œ7ÏÞ‘N!ÐÆ§º„ÿÃ°Ê¥„	KeH†!)bÛj!€i*K*S¢âÔ@ÞÏ õÇÑ£À×j&çæ^¨¥,µÊ/þ?þtNð“)Šÿà°Z <LÁA\F IS1=C¶äåí»Ë]¦x.nßÞ ÷YÁ«³Ð2ÞÁ'D–Ö†–ÉªáXAØ™é€¦³.²Ÿkß/u¦>ÿè‡ÁN¨Ó«…J#hí3û(5ŠZ^…(nDjA·$€Èæ1ä­Òß¶¡õå‚«¹]ÏVÎ)Ka°I¥Ë5Z	¼áH ÌtBö–| ŸuGñ¢ðª¼°˜)­RrôcbéAožfº€9²èM€Ãx4¨¹S©8è<ˆ[¯ iº¦Ë¥†¾aÀÊÜ[…@bÄØÀ8ÐÛ${g¯þK5Óýî§ëî¹E{üÃ¹µÃh¯jR»ëÙx|×~g×.' ¶A¥Kÿ>°ÑÁgú}Îù÷9lži›Jö…îrˆi]79á|÷ïq2™Ëêž›ï›ü@ž½úÂ³9åû9™ ö{^à%2©7‹ºgæCÜ0´¨‘Ÿ‹ýÜLyÅ{vÀ&2©6göÍž{ÙI\êÄÏ\ä°yè9z€£ŽOxªôLf=H±Ô²©GaIÏÐCE.uâgiz×ó7Gx	{†Þ~ä&q©?—`j{v`'2é 7acšèT¼<Eë‚]S½Rç u	‘;ÞÝŽo\ŸBÎg¾Ë² <PÛˆ¼ì¹Máçœ\si’‹/Õ…« @Aóð»h²ÒFöú°´“ß7u–QÜßó7
ËXfÙû’ÉÊ°)7eÇ¤f“Ç!Ã ÓB¾Ttß¥§@>·ooðE	&¤JÝ¥Ûaúv2¡!M5ûy–× %ž$¢ °8/ç#;Ô¤’K›Äfe=™ :Ê
¨ÎtX£«º­{—áŸö•¹Ø‹”_Ç«þµ×ðO‚›ÔÞ »	 ²Ïº“l»ë˜j³¼Ý™¶º…ý”T›K°EåMø±T›}á)©¶8Ú›r#„÷Ù#jÛÀL¸Ò2^wÎïl>ÿË<DÅ
šèÈíÐC}rçüÉPºÏ”Ý‰dûBÓ;–m]ó;ÑŒ÷'xìâ|ŸéùoÜ¶í€ýTÃšÃcÔÿSØê"kyÐ™i|9àeŠüvÞX+]—¸àZÏ½œKàó ,6Q)¾%J‚ºßøÄ\_ˆxmŒæÀúKtÍžVÕKñˆƒñue@IUã ‰ K”Y&/‡í¤.úE°û"Î°]tqÔ=þKWÀ–ˆ².ºˆ·_Ÿcldól‹`+Ù»Óº/í|÷¯€=ÜÏ÷}HÿÒî
ß¥LìX·G@¹±¯ÝÞÇˆ×Ä´îÆóQ(ŽªIc<‹â´xö¼èŠãN®snú¸[7%Ž T ¡à3éê¿1‹)ñØ>üŽ®¯å°!#Û3øÄõ¶øÜÎr[º±78Å470á¾Ð¦ºoØÒK©	ú5ý}ÍÂû|†»p£:É¥µSãI3“Iã‘~]–ºDã
Êº¨šˆ±ó4‘Ì1â|™Ñ¶_(CÚH\¸éüEbôa´ãDˆ‹^:]÷’<­R†¼<E ŽÆqrÕ}ëÉ‰4S§PL]#¿Zõkå™Ô2`í/ÕÊ€DW¡–¢÷~Ž•¨Û³žÙý	%s„4{ççi’<±÷ã%r¬{ßç<*ùTçgC>G‰µw}N¦—Oõ}œ<Ž_ïùœO)ŸèúÄÒÙ/Sð3U}5ò8‰Ï!¿¿ˆ¬0F3TäY’Us¸L`Lbo.÷¼ Í ËÂ’EGWø @«Ë„g3uQ`P»º z¾¤¿Æ2–‘aª±IÛÄÙèu“v®M´ÖøéB+#±*€å?É]ñÕcO¥A©üŸåÉÍ€Ød
Ì6ãv˜ì¿í:•.ÜyF¶`+	"ž
&ç
ôÜæ Bkd]Ø…E]aåo˜$ù¶2Miƒa<[ñµÂçg¨·‹ðØ÷¢Â¢Õ¯¾þæî‡¿}°9ªæÛ•%1Í‡áxýýTC€ZhÕŸny|4ìÍõÆ³R…ñ¦(J{SÉ)_d³0  	e
˜ËîëœQéí­ž+R4"J R–ÈMzèŸ„I™Vÿ‚ùU¨IÒÎ-¡mÒA4½øËBÙ7ü°¶lîò¡ì¿P	/ú2 “(¢ŒdS![üË¾œ[Q•=P®ÄÇŠÅµ	ZÌTØºû8I*zÇÖµd1åš	PJ³(ãâ²4”%Š"Óë¶¬Ü+RåxbÜ¹4ð½CðÉAZ•.{ ÊZƒÁõLÝ²ÙU)çsL­tÀHÉ0â»ZQ´Ë±T!dºÎR4úÎ¸G.:&¼Ñ LéšRW/íHÏ‹¿8 žÚL17à™µJLì/Á-àyÊ.”¶+ÇÅ€	 Çfç®DiÓ#ÄV¸J
ƒk·ç~0ftÁ2MãžÖ•;AÔÖÒ€à·v``~®±â;ˆŒ­dÜ¢¬ûTõ0Ÿ—bŽ-!àì»MÝÃ_Â@í‹À7Kûž{:xýBÀgt/Ù?ý·`m‡¿Æ/»â£Ô*1x]éœW2!_Ô›UôzÜ‡ì#fu¶ÑÜÒRÉä,²u^PŽY›Jä†P#>òücR•†_ïN€ªGô¬˜ßO³©ïWBÎÿgÑÂ#T0¡RÓNJˆº/æ¸¤ýˆcþi§Já¦tŽEFý™:ç¿}SÌ 'K°µÄ]l%ÉjàTT+!»‘¯F£ÑýU”J„ÍvÍdé>"bCZ Œ?ŒöH6)ê{lšá½X÷Šu£èû{p'I…i¸ó…Á0:LÌè<žý(¼ar@þÄ·ûÝF=™¯VqÚHNÓÔèÃ"xL²ðåEÎuŠ´°Û*•(‡ì›ÖPÐ\ÿMªú#KèƒMq—Wà*æà
àfžhã} ‚Ž³BIèÜ}‘ÜÉ:Ÿ‚s ,ÈeRj# þ)Ü
ë†ž,wR§Xmü‡Ñïþ÷šÀ¿‘Æ—0N¾Ñ¾…o\o>_­´}Þ+@jB?}ã€
˜XmÓëÀ)L›5RMSŒ¤ÈQêâ@M”._®?g–ÿCöm\º	¢¹œÒ!™ C{Z8èˆ2µnmÖáà‘Ò]ŽaQ*~ñ‚gay;-Šv7Ë!È«56Ä:–P˜Âƒ#ÄóÊºº¢"k;Ž€ö-DàÕõ~ôõ¸ël{ïóØ½9L%.Ÿ­7NáEöÙ—ðôËB44pãÚSK0çxÀï˜Wƒk¼0º¼nEJ½é—û„™‹üžli/Ñc-‰?Í¡õ×¤æ¦‹ž·²àaÌ¯¥Šâ°A~cý}]]¾Ÿ]~gŸ{€ý-¨ï#(v—kãØ»»q^3Œ‚¬ôZÞHÛ*ùˆþ;€­ó{Œx”:ë1rŠÕæŽÍ2>g¯G¨®Wo>Fið’HZôqˆ¤v
hŒrX'‰: ,¨¿Ø}•¥Ï+ØÙôîíêÜ¢1 å€Hðlò~ÝÀ¿Ãð/¤©j~ÿþ»€¤ïûï<daáôƒƒ×š>Å°líæ j‚?¡÷í¹‘™zj7•_à,|ØVV°Bƒl»
Q Û`Ü¬xQH%L¿°âàå¬åªs©|¯A´¥¼ñŽ}~À2õ²:ŸâTŽ¢èa^ë»à:…c”ÛA§dÙ{%¿
¯ä÷¨çvMˆRûe·[Ç£w¦,Îº¤ámæCfâ­È2tqp£vÀÇéÛc>I{Œ‹´?ZürLsLßó¼=1d×¡(´o„ù”0Ý¥Ø©´ï~yÎî—ŽÂ;T Ú·¼|º–—N"ì\öÙ÷¹|š>—ƒRìÞåÒ7·<osKÑuñbúŽ–OØÑÒYˆÝœ˜¾åÛXºÉî Ó÷®|²Þ•.<Â…y©"ôÄ~5ÖT'GÅ8²ˆ5ÕôãDƒ@‰Õ(ÖZ3dÿæ¥eÆ?š·dŽçc¥´©Ê:¡ºÛúBÇbcE`œ¬Â
CŒùý ‚^ÖkÓF'yÅ~Zä“AçÞÉóå¥ÎcÚ“­/·O¶Æ²'iøÈ…Œ;ï¼¬•Š3qœ]4ó¹h,Á]K†2|öFJÄ§X—2½}Mñ‹ÛÁp8|3±G–Ñ›KÐ9”b­W]L…ëu¦G‡³×–zô{ÄðY‘T¾WÄÄài@±§‹Ê•s¿¼_Y8gaõ®ÌÞ‡ö9[i¿¶§Ä¹£‹[ër£né 8ã—°DYŒ¨)þ˜ˆ«8ôô-®Jž8¶m´y…ÇEÑa{®JM»š6õcQæ£4þÔ%Ÿ˜•ú?BµOmVç(ÙçšÏ`á`Xðj±éNá³m/®u 5 ¬vfn\°G””,ß8ŒÔ†Çoñ†Kî#Ü%RnGƒxòáÈðû;véž•¶¾ÒóÒ[õj¹ÃpM²i‰IT™Z;À³€?×>MÚfr?"íÐÒÚûnb÷©È8l ìÈÌFE<¹ 0¦+,Ý›°t}ï¼ÿ¶®|Á±DëO+™Ô‹*šVW·7:LÌfØé€ÝlxýÜ¢Â¿¶@²ÿÆ¢
›Ôb×dú\[’¿v?.â÷ž£6Ë2¦·\çüŠÃr©d^çŒØá¥µ.9TÛËÏwš×´Ña•!R'Ø¨iõçÍ¿•ŽØk¥£‚xÇ›PèV¢ûDO•šißLv®nÄ§:Ë¾ÕÌµ‘…ÎJ˜ÜÚÚö‹ïÁt€hÐ¿÷òÜêªØ¶šåœMk	0jßßý·ÓdÉ›‘P“²mOÊvöèØ²w’6w8jgiñ[›€*V©Í7=9û†=EØ¾|øÇ#ú°Xç®5Û(nMˆ¿VÉ)÷Û´êx/Ä¸ñ¹ÈÉÅ(`É¢ì·V®ÈßrK¶‰~BÆ¿baSOÑÍÐ3WDëº—=q„6yxTðºÀŽ2ô[.Ã*y,	Ah­’<sŸªÖ•V—T„ï±cA®+8¸‡5‹ºV®Cý&l‹›a¥¾G>dkÙÔ‡Cú”ü˜}{˜jsŠ±2?ç±^8´­¶¶ ¾xø§Tú]ê9w©Äã—°CÁ°gwJçµ÷ 9/htÑ	3!%<ü£+fv!ekç¸‰™ 
û C&ú7Z>Å9W±/GÛãæ=Ë)Ë\»®íSW|þVÁ]§³H5½"«@—Ê©¬J<ÿí½B>Â–júözBÔˆÙ®ÆôóÜ€8Î:-]©fõç[…*å2j–˜
< 	ÈEÍ¦»šå—Z¦Ø‰¾v‡ÏÐö|Ãf¸J_³Q ŸlüÂ$>µÆÕý$º‰¯ÓµÑtkÞÚP8ÍñquÉ°—›>	s[[i’@ÖÝ²q¦l_¾#x²wÎêœ~	å.`Õøªûr†=œ  0707010001f026000081a40000000000000000000000016a102a8a00000331000000e600010003ffffffffffffffff0000003f00000000root/usr/share/doc/opensvc/template.cluster.arbitrator.conf.gz    ‹     ÝUKoÓ@¾çWŒ”­ä	©
T*H\¸PN©{ì¬²Þu÷‘4ÿžoÖylš‚8Dð%±½û½fv<žóšLéŒ—À)?×Ñ«èüYàÎ«î¼ÙM~ÌN½¶?ýœˆè%oÖÎ77n«zÆ‹«3^N¨Ý æ†÷LŸ•	BDž“ö|ÐpïS~Ñp«’‰m_åd³_†kÝêšn©cË^×ÙB÷.‹í98³1d]ÃÙñŒîl¹FRVu^UøÂ'kµí(7…¤9YÖ(î½Äcì®M
‘}Þ‹;ô°¤Ðª€Z¹È´^°ÍX»=Ø£#DíAgŠ^sCÚR@hlk®
0hµU½ÈK^±¸Î}±b‹GXo
 L^‘2¦Ø?
à'D¡!:šÃ½=PµÞõYƒq!–hÜõlÅL$Ã
òPFáÅÚ^¼>Ã<E,Á’_é•„€-$f7»{ü`{
‚j\­ÀQ@ÕÐ	CÖ¥n!ÔBKt¾¢à€Ò<pÌ˜Jù*±@z¦Xîul•V(´–’ØLr€/J"$óB¸Û½/Ìèc*ƒÏ±I¸ÂîÍØ(n«´óŽsFß¥•Í¦Ú60¹Ô\ƒg©”
7pl(èÈUfÊ·^ÕKlhpk3Úr˜e­ÒHG“@q¦àÅ­9TÄ«|$ÆÒdÉ.™-Žû]‹ùš¨×ÝBÊBKëÖP¾AùMÔƒÙ«Ün[öë¨|ap6häŽÛiÌ€"1‚ôÝMÝ77¨FžPWWùéûâØÉt¹}@“wò—>`¸Éo†ïhæ?¿Û*á˜%ô£œ6[ûÍ_7œ1<£Â$@»ÛY¯u\<;úE¦wŸ¾½¹~{™SÝBÿ1×¨{véŸ»‹ï$×}â'Á^ÿuª˜ZÎ®Ø››„L´³'‘÷êI÷©ÏÞ%ñµÂÈP<þæoP¤¹=&çS_ÄÎÃø˜8%2¬õHXe ¼[Æ…œ hGídþïˆ
 |°üFHäˆäÊm«‚Ò]O&¿ }§W@.
     0707010001f107000081a40000000000000000000000016a102a8a000010d3000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.service.sync.netapp.conf.gz   ‹     í]ßsÛ6~Ï_I¦“dNVd·½7Î4wmç2×47—äî¡Ó1!’0&	† %«süí. 
ˆ²¥(néÇÉ°ß·`…]>y²ËŸGOØPœ^–é€™e%X)¯ªû‰Ûmïv«»G¿â`ŸÔ²ÌÄåo¾£AŸ¹a?z„Ý¿Ë…ª³ÓU/&25\9ÚáêI§ªâã\´M}¨jñ©‘µÈ®\¨j5—ZªR–SºøÏ5]ÉÄ„7¹YõúUÒYÎD-/S×N.ø„½b3Á3ß	Á`À¢¶×u%R9‘)Ü3%<›’Z Âf‚ýbYBzÎj•Ïql¦´a%/k´ÈØxÉÜ]ªLûAN&¢¥	(B4KyÉÆ‚iaXbUÍOgô H‚±’œ$¡Û¿Ç±‘$	$¬†_¡«ßÙnu¢7³ÏhÏ—Œè{{YhÒò\åM³O¦‚Å²ZÎ’W…¬k¸¢JR4jñv=ê§ç’kžû×4*Ç
èXëKUYŠÔÐEkC žez¦šJŒ7u£µ2P¼F!<M…Ö«çÙBBf&55F Q«g~7à6ÎUzš:¯À¢Ï[ÝÔ¶€}&ó°*
^fh':­eePÿâR¤ŒO(a@çØÔiˆÛizÄSÝ?MÚ‘$Ì~4d?¢iè0H¨›Ê’Á]Døn@æv3ŒÒ†×¦‡ò®P† ’*Ãièn0’˜­!„_e6^öHîÖ(CÝ&÷ÔÉØWUõxîÆ2UµÃTÕVøÁæâ<«{wm“N¯w¶G÷üÖXâ^C÷hîMÒì½ð$	[#
]3ª=¦{ÀÔéö^¨:wÁþéaÝ¬ðÏ}Q…¶µ©2nz[Ý¨Vµ÷ÕŠØÔ¦ìc	{5Pí]ADtµ}th[DÇb‚ÜžÃC!2[ Ù‡î‡äŽ£C‘mìcCû1Éû‡ÖðÙ
Ö>4´3»¼olÈá±|}dhOyÐP€Ë¶Pö¡½yçÈP„Í¶€öq¡=BzÀÐ>w€µíÕ;Æ…bt¶Å´
íÓ»‡…bt¶À´
íÓ{F…btnÇ;lO÷ð¸×]0Ü~ÿye]Áïž¢ŸLå iBÃõ¹„Í	^ó@ê[Ìd: •š•x0@•G)Ïs¦›ªRµa•Pž¼äs:&	7T¢Fû¸¸—ž›‰%ã5žÊ4Ð¿d-±éJ•ZâN ‘ÀÁ	˜ßx&5¢òàl¶½r-èÑfxl5“èìôéª›ì«RUÎE4=V >/×LÝ)rE¶ ñX09-ÁÎ3ÓS…ÂME”Ñ³ÆdjVÛ6ñÆh¼Ç4iÃó_jŒdAIR¾àI2dì½0  öÃ?½þøó‡Ñq1SyHß&ò}†ýõ×3ëm$ÔLAo‰œ$Ôíõ­¸ó=zˆ‡‹µwE:4 àb&AÓDVÓÔ †;±‘";MTÁ^âÔW¾{	hn×Â„’N/§‰ôÐß	ƒÒ+ûÆgÐ’¤[:ãåTdƒhxqË¢´·ú°\X“‰¹ÁÄ,cn°3Q¦¼úƒYÙ¶®uWÆ†¬'™$žNüÛÚ˜=%Û X†?º®Q²—¹€Ÿ¢Ç„N3iüí„TôQ:‹‡sÁ2Jð!æÎÊs1p&ª\-0N°74”Êe‰'ƒ‘@ÔÞ&ú€–QuO ]ykp¸^©W|¶©åt
œà6yA72Ž0i§û«éX–)ÔñfÎ¹GÑKÌ@‡2¦ÏÐ•‚+F
8‘›H *t©<p,@;¥ŒÃõ,p‹¡ãõ\gìi©ìÌñtÀÈc“]3h €6M1C¨Ð¸Ìè#ÎÝ^ûAŸq	–+ê÷¸1@*‚—‚Ê ü•à4x€O˜»€©*×ŠÈqEj0ï£0ÐJ-¦Ð½ÐÏû¬,5©ƒ=åsè¨}ô†}Y=çîª@°ftÙ¿}Û˜Ûá¯ÓÄcag|ƒñ?±xcTÁLi-êÝ*®zÜ ‡ì£“&G\+%—0É6…MIÒKmD¡mÑ%/`}L¦Òêël¬º)±hz>Î/¤:_9ýÁ¾HÝÂT`e¦W‚ÓZ £Î«)Ni¿bŸ»Ö¤pS:Å¤¡À¢þ†ûQöæ1žÌÁ×’v‡ì?<o ƒcaB”ìxDF£!û§¨K‘|iK×))FˆñíèdÓª9ÿÔ(Ã{Xo…u-[6¤)î$a[*î|¡3 |MûY°y•â*Û9&å0Mkó Ì›_Åû`×/X‡[k¤EÓX«Ã"s¤eS‰vM	dU¼æ…0¢²ŸVŽb€îúgY6—,¥'À9$ÉÑ1,X
àf>—…4ñ> IÇY¥´$v®õÇ>HËÉ¦Ãâ TPÈ´VZ ý3ÌÜ
ey…P
$èN*Øt%É·£¯¾‡kmGàÿ(c¨Kèg¼<µOœ¬ßoÊÞï %5±ŸÚØ`z^[÷6°×f”ï¬#ŒM×Ë\¨ÒÀÇÇ£“o˜Õÿ½)÷#Us4VÎ¶Ò4hk%ØH©(¼²Y‡6`EJWau)£m2µ|©8'ÚIÑîf`:„y³–¸¿µ*¡0…'GÈç…]J(èFíûÐ>…<>¹}=ï:ûÞ×¨ùvï‡±Ä¸2GˆÞ£"Ö@«¿Ão\—…lhoá¸ØÒ£Ò5†CØ)Ž'øÁèèd)‚‰Üf!Šsò¥=¢Ûz-÷?’™ë.v¾Â Põ’=“e‡€òskèïsônrôÖÞwû[0ß)FPì.×Æ'R®ÑSD™É@´ŠkÌÜ÷VÞ¢m|D?Ø¡TqŽZå=Gv1Û¼f“œOÙ³šëñs$„QjüÈ$- }œ "ôÃÒ
£v‘sÑÄÉÉ@úóÝWYùÜÀnÌ~}…{;,.P¨È½8"=Ûï'ü¼‡á_êH[[âÝ»· ÛIGÞwo=eaâôƒÇü\ê ²¥ƒ-4Sâ{¦›±ÝT~Gå\ØV˜¡[ÁŽC–¬®oà¸^ðª’%LOñ]P¼œ­´ê–T´×_ß„¨Oœ±o6x¦«ý-BœÉQ=ü^ëmð99…m%ˆÛÆEÉ¼_•|«’¿ =î{iB’V-»Ýz£)Ì>¥Q×Ô½õïÃõLâ½È<\âàFmÃ§¯¬rÊ*.Ò~ãá—mêªôåT>o9•NØuÈ—ék¨²†Jw;eÉô…S>gá”ŽàmÊé«¥¢ZJ7ð:eÄô%RU"¥;ˆò`úº(‡¬‹ÒÊNÙ/}1”ÃCÙ
Ê9/}”ÃU@éŽd—L—¾ìÉáÊžtB²s~K_ëä0µN6¢Ø½ÒI_àäó8é]—pM_Õä€UM:ƒØ-ZÓ—2ùŒ¥Lºa·1XÓ×/9@ý’NØu‹ÕôEKT´¤3†]B5}¥’V*éŒd·HM_žä`åI¶Ars ¦¯Ir°š$ì§é‘¬I ·Ó<T½°/1až’)³êBVñn YAƒD‰Í(Þ™è!û/¯­2þÕ>%‹*Ç·bA›¦nRJ¢²uLðJïŒOcº(àú°‚VK½:jÆwüY1‡”¼‰:çÏ‡·VÞXÌ$Ð¨â©8Òç07Ø¹œ 2i~Â_E–\7e«æìi;ž§­'x½CÇµ/+<¨Gg;£œ¶—µÌ^={IY×¯Ãáð93y3±¥:ìÁPUHƒ	zp{S´Á˜DS%	d5V:qÙ	‡fEj|á“§%Å-%q\nþƒ£ÍhŸYò×Óþàk]ø²	K$Sp¤õ./ËWÌÈ´î¦ðDyÌ¨1O/|ú¨/‡°âU+ÉÇ«C¢vg¸‡µ:pVjk· k“Qq:ÆZÚjTò!"Þ1©Õï¢\ÝÕÕñ)øN’½¯m³@ÃìeËMJõ5L\´€0uÍ¸vÅ"@†w*Ê|ˆÉ_“Ùðø)ÞjÉ5ÂÝ©ØW£A<ø°gx¹îÞ]XÛb”k!K›vÞ±bÎu:Y“‹$J¦Í¬ày È½!®´õLH´cÀJöÐ^w;ÏDÎ—‰ë™^+o@KN›QŽé²îI˜ºÞ»çªHÀŒc¾/&™693dÚld—„99LL&X¶+ »^:ðöyE€b‰dÿ2ö„2;!×çj¡øò^¾‘36Úà³¬bzÏz®¬±KÈuý@Ìa…,eÑŒÔñeå]<s(Q›i^\ëhžÑT´˜Áã!S¬ºeíçù[3„GìY©¢êÐç!èÑÛ ÷6ò‡Ýíø‰½:íƒaoÿ
Wa}\\.
ÅÎjèùïÜVkq-€Ë"ZÀ¼htý‚Ò_d*}—zž¾h½UÒ¦*ùÜ«#æÐ‹”S[Ö¤½ïŒýúx4:ŽFÇðûû¿ãtøxÀNèÓ¯Ý§¦u\S>þíF^`ÿ²¾bÔ¾JŽÝwåëI¹Ú@4sç5niçü+lÀ”“À‰þ½ÇóJéœ­3Ÿ²q#ÁP@Úû×¿à¢çB·©0mO¨¡­A”_[ˆÇæa%J[~’Ú]’r¬_R1uº¾Â·OX^Û‡7¶ê¿Þ¦4•­i—!ÿ˜
[Rý·ñ«S[”öÒ¢ ­Y{X5ÔŠEì¯¬h¢uøêeï!îîK›fŒËO˜4í×"öð{ÒmþA¨YVX6
×³Ga)ÌûÂ•2¸å§À¡	•¦(UyäHEü>u*(”x‹£{˜˜¬šÒ•ÁÂõ4Ö¾š`:#½½«ºæKìÙ«P‡lá"§ìØÛšõ!ÆÆ ý˜O]÷Â®]©]|·¾Á`Þï£ûŒ^ŽBä"&Ã-QÜ"ö¤Ù3iTÕ‰371%¬ðÛ•3×1åJDa3n£¹è?iŽtH­å´Œ×r6iŸ³š²ÊµóÚ­__Ðek¹‹Þ"÷k‘­¢„/¿J‹Û¬Óê)´
šBJl¢OÁ/?_Hp·/t8Î¾ž}Ž°ày!Ëfm‹õ±”¤·—h÷dCöÄ*‘ãc…5L©¸Ezð˜‰‹¬ÇÕ*Ök‡ÓÆ9¨KÅÿa{ïKGãôŒ…!Ù·A²– …ñªÊaÃä*
Ûï!rØúåŒOk!ð…®*†*Ù
¹á—¯mH^G­ú (E0-déƒíqeÊàÏ±dÅÑö3¬¢@ÞFRÚö^nÿ^Žý`ü\L‹MžÎØëi´9U?"­S£•ì?=“öÏ$î= "…ÄØÄ#{b±çÑþyd5ý`x´FŒ[xdøôOÌ›ëÊÓa¼*â“§ª
¯ÇÒÔO"Áÿý×„Évó/ã .DËëUX!Í, 4X¢«B¬äÊr’ÓÛ<,èµœG¥UÇ_çâ¢Òô×½Zc®d†ï­XºWUÑù5¢»×‘ÔªZ³ÇŒÜ±Ë$ñ¯Æ8³ßQÆï¸ò­Ú
}Aúª~vÓ‚ß¼°g-iŒ7³ò!±~¸çsÃÃÓWÎáXw;NÅ¾í@mpg?—íu.4ýæ²ëˆA^ãÿ_ßZì+Ÿ   0707010001f0e5000081a40000000000000000000000016a102a8900001329000000e600010003ffffffffffffffff0000003700000000root/usr/share/doc/opensvc/template.service.fs.conf.gz    ‹     í]ësÛ6¶ÿž¿3Ží¹’VÎm¦³n’Ùl›îíÜf½³N»:	"A	k’`	R²úøß÷<@,Knä$Þ2ÓÆÀ9¿ó pøÉ'ÇüóäqÄ?H.±G%wÜÞ—wO~Hì'¥Îcuóã“/ªu¡Äñä	vûZ­W¦Œ/ºÖ£…Š®'¥’1Üñ2ÉF¦³Tµí}-S«ðN©~ªu©âí;Ei–Új“ë|~Ü‰U"ë´ê:ßÞÑùB•º’yäšJ•LÄK±àaQ?”€«’ïÛBE:Ñ<3W9¼ác‘ÌcËJY×ÆÛ²Vâ×®ÈäKUz=˜MåÄ[èŸàÖ7‰°ª•Ói¦SQ-T.âd2‡‘[S—‘¶’UmY ø<žèTyà²k[©L pø1 ¥­¨­ŠÅ
Iâ[øNó$ÜÌLWp†‚·=jL¾ytüR>ÕX[U<oðOà°/-`T•Š*‡0‰G-…‚¤A,¾¬6ZÚèþ…8©t¦L]yÔ~AfLÜõßˆ5b˜ˆ_²¼úí„Æâ cEbÒÔ¬“‘péTWë‹`˜q©AJvÔpz”ØQsätz[G‰%¥[˜Ãl‡×ìõŠoš°•ˆN€''?>;x„Nl6ã[ :úùç‚ÎÝOéÞŒšëi]*ñ>H~ ÊðÉÓx41E…à¬¤Î-t¯4ÓéÙPùE7n°Œã† VË÷aP¥n5 Í{èÿßóGSOƒ³Ô€ZStä ¬ÆŸªerîn,Œ­¡ºyÏ‚) µ‰¯t’¨RåUCÕl¦È`Ô 	æžðuÍ@Ç@í]#qµü^Íe† ´kýÊn€ cv‰y^šºø ‚þ=–þ!%M¾‚É±S§1
Ä¬àaÔjäw†ò‚1D•)×#ñZÃÅúâ‹¨Î8‚Ä®³™Iá÷Ä”)!·ðíÌ.±d$ð^ûœLH]Da4ü&·T­µ>u‹ø‹ò¹ôÎDö°ßb1pzb6’#¹“•¹™¥RYQMäÌ”§%hˆËBåWß)V:M…c:ôÊjŒ!U¹”•­À	é™¢æÉÈdFNTèÿÁ˜Éö&Þ ÿQ†#qe2Å¤+YÎ•ï|tV¤*‡DÍA(d Ðº(àUPGˆ€Ri+ T¬Ä©²7g£n8aü‰,wF’H¨j .®WÓMPM¿(!ò„qBà#©ÊaIÏ¤ÕáMBû‚ “õVÁ³
Èm-IzËõ5þq!—j$^A˜îf42Ç È¾n"˜"øDIJøoÌùçŸ¾S0%\ë%ã$ó•Jt! ìè+%}k \Ã¸ƒ˜ßÎ<¡¼•×@§(µþ­…ËDÏ"ˆG‡©ZªíÅ,…[¦’zŽÀ®%MšgY¼ôÖ^Mw»_uxO¤ý…»2" ”p;|&N7§½ê&‚Ù'L¡À¯9`¬”(äéúŒäö¿»°ÿzÊµo¦%8'h•®ã,úÚâª„Økö-FÅëÍÓ($ ¾TÛ®ÐV±âYš/.”Õ°.ÈÿªÝ±’Ÿ÷H¥0´
&q•¢'ÿƒWØÛ\õ€†’Òdà‰°cÈ(4+^÷ÈÍ”æi£@;]^§³ó¨…no—³ëHìss6—ÅÄêŸUoQ;u!üù3z+fµNÏ‘e8,‘š¹ŽÀ‰-M
s<¤¶¾) ‚É\w/œþ;±±¼Á¿G£Ñ7’'t#xJóùÐ¤º‘‹óñ§Í´ÓëÊ1]Q;Èbƒà+ºIëYþÒas—òJEXšq'‡*çµ¬Ò®óhHëÃø[«‹v“Xk"ôFÍål†Šá×€j©÷VöüžÓÎošñ¸.ÉÊmOÍäÎê¬ðYG.]ò²(`1E(€´
“Ç#ñ/\’T7v°ªÍÅf\ÊTóRÆ ~øÈ¬d™`Ž­£…Èô|ÔýÃÛË/…N6º– -5.´Êpðäˆx¶@¼ìÍ'mdÈƒ.?Q/DiÀîÊÏ<¾^(P!WWÙc ’@ÔÓø÷AsÇ_«
\Ì‹$®ä¢®å j•£KAJ#aCJmÝiCÙ‚OÜbÍG*¨æ•-9ýðãCZQ»HÕM ¿Wq¬‘Qà´ÇÐÐÒÒO”Â†Ogö6&þté
ÝÎ
Ê”6S  ¼¿H¤ƒÝµyØxò£ßë%èLÒA†	¹%L^«tÌ¼“ãqÄ»“ÛG5j·ñ›x²aí(ŠsÞfC7PsšÎ»•… l‰Kß¥:¯o4£±ôeš@Ô€{Ö0	{ž¿üô—¯ÿùúõ¯ÿøþê×ïÿöÇEžÇÙ.Y.çhInŒÌ‘ÎªÛM¬`ÖàöÛ°"˜I»a¸).‰a9¿ÛÑlÆ=)Œ­&-‰>phO»æŽqXTê‚VÔŠpÒ&“J…+sž@.¢¡¤4‹‹i;’©àKó•¥èÂ˜=Öï‚¹›(¹’¹8i­¤åï¥/@båtc=çþb$2÷!ü•Ç³~íüÈJéóvú.u4î'WÓ§lI3MqÅ4Å}äWç½¯| µôXû{µÒ#q¨PKÕG?÷•èLÁìA=tøãKæÒìƒŸw“ä‘£ŸF"÷•`û<ŒJ¾kð³!Ÿ{‰µ}Ž¦—ïû8yÜC|}äópJùŽ¡O(=ûÐaõ~2”›«óòû?•T„|®sx‹¬Ì¤í²\“”ÂÅ+ngÝí´úé•Väø ÀäCÚÜmR+e
,/KJü‡
UâF'(0ýpvM{˜iRÛh­Ý6±Õ˜™ ÀâMçUa®Þå¿,¹÷½'e½ê
‡Z‹LÉX3%ô<Ç½sf¿T»¨«Ø¬r7ê›Êzù2]É5nL"X€ÐtšÿIbe“¸R0@|õúëWß}û–7*WÖ–¹6ƒÊ/¯¿ÍýØ(Jý½#<˜R·7ý³=œRbSdƒüâLÅ8M`­êøQG +ðËâ‹©ÉÄsÜ4}ÙtoJéh¸	çS¢¼*7èQó$ªË2ó
¹4z[´ù\Åƒ`xaËœ43íøÁ¶ÌûSîò¾Ä,•G²èSè¢lW›E—ÆéX˜iˆš
ÁEUX4§n(Þ¯üC‹c’EÕ<N’
ÞáB?!¦Z€f”bÀƒŸ¸f/#®0ÿ¦HÍšr?áÊ÷°pDííƒËÀét,k·KkÚ°ÙU©çs…yX0R:Œí’tZQtîXç×!Ôåõ8ã„è¸WJ¹¬tM©Ë¨r$÷€·ÅeúèP€zÊ[ÊÒ^Ch„Iˆ6Œ—àð<'¹aÏq2
è‰äØÚH›\Dˆ.„N¤Nk¬EÆ¤gÇ}¯Ï‚¥†ú=««¶`gNav	,ÀO5¨;–Tà–9ÕMlRõü>®”jŽY>àø]¬÷Avˆ¹„Žò‹À7ìK÷ž{Ú¯.ÆÔTˆÝ#ñÏ¦m'ðíðVëpÇÙã£ÔnÉ8d6Èº2™¬0¡ üucV1êq‰ï¬Jêqp+•T_ƒ“­³ÂÛØ¶„u#±4‰T¥å×‹# jWIÊ|2K¯µ™¬æIþwÍgï¡*àÀTÛ‹Íì´bŽ.íìó·ªNJç˜üéiÔ_)ÿ›KbJ™Ô‚¹;ßË´†ÎTµR*çc’áùx<‰ÿWe®Òíìé¾OE(¡´0#u|‡d£¢žüT›Jöb½S¬Ûåb•`"3Î|1•12\¯:o"*°`ÃƒÂ°Þåª€ßü4œ·µDN)hšY“â²Èá€”¬jc²€`PuRÊLUª‰¯;C1@sý-æ'‰ˆÞ ã0Ï!TÌ ÀÉ|ª3]…ó *’…±šÐ¹Ñ~‘ÂÉ:›qÊn¦£ÒXðmXÙ2DÉh¼Ó&]Óé³ñ§{mGžq ® /¡ŸaxÊo<Ý|¾Z~¾Q€Ô„~jc
ØXmÛëÀ1L©¶°ƒãŽoi2î›U¸|>~ú™`þÄ7aµ+ˆf835z[]ÕT4 :’ÛÚ-nMÖ1ƒ?ã©D—:˜&Sˆ Š>±)ÀzË+…5l¢ZãáŽ%´LÑ€ÃÇóŠC	C9é® ù-DàùÓ»Ñ×ãî`Ûû
9ßŠ½±ƒ ‡™Æue\!\´Õ™ÈÞ‘øþÞ(©ë‘lÙ
W¥K\b<8<ÅãáÓN¤(L¼°[˜™Ê&dK{‰Þ×’låç¾&5·‡èy'K ÖkŸE"TºuÆŠ~YWÃËdø†Ÿ»†ù-¨ïWPx–Ëë‘´*<ö©Bƒ©ôÀÊFË[i³’éÏt“MpÅ£4i‘cx›W"Iå\œŽQ]ÏÏ¸ ˆ×(-^r’Œ#>:€@úþÑETmÆA•»4ƒ§ã³Ï«˜¾¬*:M‚*§£Ñ#å€Hðl÷'¿¿ûË¿Ô‘¸Y/»¼|ãâN:ð^¾i Ž³é¼ÖøZí*‘Ýøè¤J6×3[ÏxRùŽ¢Y¶ÕÕÀvî£ÜC-¦«ïÁ¸]É¢ÀºýÞ±â¸À+EÇUR5Ç³_jÞ/Äg{,S/«‡BœÊq™¼§Žo¼ë\„Ï å¶7(YöQÉG•üêãC‡&\ÖÔ¶ìfëµ¥eö9šŠnóÍýC?ž™6Vdé‡88QÛãôå1¤<Æ­´ïL~¹OqL_ó~kb’ÝI¡}!Ì‡,„9\Š¥‚öÕ/ï³úå@áíK íK^>\ÉËA"<8í³¯sù0u.{¥xx•K_Üò~‹[Ý!QL_Ñò+ZâaAL_ÆòËX“ÝÞ¦¯]ù`µ+‡Hð!ÌcaCìcÌ±¦<9JÆ¹ÖE¨© N4”PB­µ#ñ/Y23þÑ¾…'ŽkÌÕW¶*ëˆòn¸ô…ÎØÂŒÀp³
3qÍï;ô²YÛnuRVÒá§C1yt&Í·g†Æ¬`TÈH-qGé¤¼JŸ ŒøÀ.Ú	ÈFF‘M.ë<wâ¤8iÇsÒZ‚WÚáóN
Ò ž—:~yúœu_ð°U¬Áóùð
ï%˜Oí1×«.¶B]ôåÿˆSw>¨äv¡Y>˜©…àiAqG•Kç~t°?dbõm;{o›òˆà¨jÇ>ÀHk]žç/é@TÛ¸°Diˆ¨™ÄÏi?)ßÃUK©!ŽeÝ¾B".v ê¯Ûrú²BPE;Å Mð§*ð¬`x")ÍÏ*ïžjë°š¬mG‰Ÿk›ÁÄÁ48¤±)ø`a.{q¥¨~¶³XðÙ×À1 ‡¡OA›å!øKR¾%[.¹F¤ÛHy9„ƒ÷{†Iîc-%ç·¯ÛcåAƒZ-&3±ÑBÅuª¦Aþewþ_G¨ù:DŒçØnnî¤:Ú#¾ï6‰U*a»öCáSÃi“1ÃÒ½	®ëÊEÿ]^9~³Eá¾ŽêTúIm«ËÛ›¡’„>¢ÑÝn4ú¹E„ÿ”ÄÿÆ¤
ÞÔOÉô¹²6$ÿ”¬WÓÈ1Þc³˜1½åÚ{^3šAd:§#›‰m^:ëÒ ‡¿Äbev«¡9í>ïç#uJG7“þœ°Õ0©t,Ns$ÄC?Î|¡³Dï=ejÆ}1ÙCU#¾k°Ü”š¹²!²Ðtíšm{ŒÉ÷`:ðƒhÐ¯ynUÕ¸¬f9§ïñãâêÕßÝ÷š]rÿˆHãÊ“Ò[kt8­€N·§Êtj·%lNÅ,µùf$ÇoðQ üò¾ôËb÷¨ZãBq6ý>þ˜%Gßíh¼N…¸yÆRe‚‡b÷`²(û-ÏÄ[Ý‡}¹»W6õÃ0Žþ÷ì‰#4É¢Õº #jÁRý,yL	ÁˆhG'x®)e­ç&:P¾/2SññÝw?gÑ}BÕ}cËâÌtÂ÷èÀ‡t=K°@}4¢ôQòâ+˜ÃT›C•Á6c¾pÝó»¶UÖæ­/îÑ°ïý,õ!g©ÄãÇ0CÁpÇì§=h4¦83»âþq(fnCÊÖÌq3î‚™è?hú¤kõ<c9š·ï1§˜¹ì×î\¦®äü¬‚·%œ"ÌÕl´YºTÎtUJ\(‚7Ñ!Á©šMy=u!(Äì¼1=³ëPs_Ãléê<I©>ŸŠ>.î¯7)< 	ÈÅ¦·Ë/Ž±}íŸ¡éù†ÍpàGÇºÖxÈ­ŠO§M±û÷·p‹ÅµÊk*S)¢&O—Wk0¬ù/…Ów«ûcÞ{¼Û'þÞÖÖ6‰'ëÃv;Â²»ö;¼'û°àAÃÓ!¢¼d5þDâ·Äô…     0707010001f0a1000081a40000000000000000000000016a102a8a000001c3000000e600010003ffffffffffffffff0000003a00000000root/usr/share/doc/opensvc/template.node.pool.shm.conf.gz ‹     å”ÁnÛ0†ï~
¹vÍ}ÅvÜq—õV"S³IôDÉMúô%Ç¸ØV£(0^›4E~?ÅÕjI«V° iºŽÈ_A>tÜ„7§[¶ºeÙUwÚì*µ±Æý}u34ýEÛ®*­}‡‡Jõçço4H|Ÿ4ÅÄ–:³õ8öÍxÖƒ áïÒ&¬/=5:S|~®oïø”A*Çtôq‡¶u­…¯ð#¦ÖýI¶qÛ ¸Ö#8c€Là(“!‹CI`ßZdxhss-ÿÞŒ „–ž9C+ì¶~·¡.T^w÷ŒÀRì1ýÍÇýÈÇ^ïï¯ÀÄH&Øz²»"Ø„&·%aCL§¯¬ÜË±ÐÑäC—°3	/”D˜øŠÒÊ¬
"Ö&‚ö<Ã÷•ñ¡q½Î4æ÷Bz›ÊßýNÿu*1OÈÈ¦N-
LÁ?îÈD¸9¢9pœM.¼aÛ`]ü‡]¤o!Ø_†)cÌúPX/Ú>9ñ••QÂùÍ§í/´™ÁxÙw—(LÜ¯á¢sZs#Ûa]“]O¹e¸5ð,ÛäâCÌf?¨õ²¢Qµ''®±ž   0707010001f063000081a40000000000000000000000016a102a8a0000010f000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.cluster.sysreport.conf.gz ‹     ­Q1nÃ0Üý
^ëØSíØd,
ˆ¥h[ˆ"¹”ŒÄKß^¹i”]ÅÃ‘8ÞëºdU5¬.¬AxöËÐ•UW6»ê-›}¯6±^O^t»hb½XNÃ¦`mÁò3~XÎ×^Ñ†í.FXÿŸhp±ñ&ð«ëú®kºÇÔ¯¤ÉËe)ÌLf0/0²c1ôã41Jû_0£à‘#^ N=5HÑx×«“ç5Ã€Ódhº{r"£ µpôk tsïìšÁÁâ8²¥p‰>•ÚÁžùŽi	Ò†	…[í©½~!kË@X]Äó.yzÊØóßTªo¤ÍÃ8r   0707010001f0cf000081a40000000000000000000000016a102a890000102e000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.disk.disk.conf.gz ‹     í]ÿo·ÿ= °Iªì%mêÆÁ¼6Å‚5õ°¤Ý€¢ðQw”Äùîx=ò,«Ø¿÷É;R¶%¹‘z¹`+’»ã#ùÞç}!ùõôé.ÿ<yÊvøÉeR_˜YV‚þú¡äv;ºÝòîÉÏ8Ã§µ,3qõË“¯iÒ'4í'Opðb¹PuvÜßËÞwøù¤SUñI.ÚÎÞ×Àµøµ‘µèFñÏ5½©ju)µT¥,gÇÑ›LLy“›nà?¨’^Èr.jix™ºŽrÁ§ì›žùQsµ}¯+‘Ê©Lá›™(¡mJœt
_¼Ÿ¶X”LM™™[ÄŒàƒ¯Ÿ€›_<ñU:MÇôç«/Ÿ½x6yô"{19:<|Á¿¸Ó¥:¯j!ŠÊœó‰ªÍ£cyûf'<g,åe&3n„>îFËþÛõ“ªòRÔÁ&JAWe$°7S¦…aF±$1@ Iì¬å»Ÿ¾a™çÌ1F¥%0B‹ú’˜0¼5sÆ•ö_¦ª(`l0Om`JˆÞ¾Äô”áˆ½S…°¤¯gÂädQå¢¥¡î4Ë` ÆÑT4xIÍr®+T&Ø¾ÐW£n:¡$™"K’xjI3`Ç‹ãdT	r•Mã©‘0O£/ãIŠz\PM
6áZjÂ÷*! º•Ã-h®jxÖ[ÂoÅT–BLÚÏÀ[â@\cS#/†Q ž÷üèTµT0¾%S  ô-R©æâRä81è/³R 8Â7•5õ™úñøoIÖ¶Õ	ŒCÝ.^U(#ÎëÅCÈ¸Ø§l§L½Ä·Žó$tPOpA¯c“PÃ†˜œ@ýÖ`{Œ­—Û&F›eÙKøa%ÌSbŒÏ†ÏÙ>	2®¸JóFËK1 ~ÍÀŠÔÌˆ*óåÉíÏ·YøØ‡zÚ{^z¥çÏh`Ož±ÉÒº Ö
±×W@¸’›V‹ë>ì‰±îl’«ôÂ›'‹ÄaS‘##»å°×ø}@*‡©Ypvà?ÇÎ ‰uÛ€ã;š¦Á¥áÀQ0ikñy@n¢`¢høô(Å­¾³Ó ï5j±ÿ¼Íkv$6©¯kþGõ—¾ÉƒÙÄgðìpÚ€›$Küµ°+S‹Bª!|›®[ÊÌjÕT=›WÙL\%ÞlâxËD\7ÚÞº á…èYÝ±ùqmÝH×‡ZàJz6ÞÌFäõR—*oà9zžƒËáÞ3Ø°¿[Ëe-û´ÁzCcy°ÿ”Aø^^“ðK2ŽñðÜ:?üö„g·2=W=²;d·£ èC}ÔdujòQDbïòŽŸLê .™«RÕ.*AÓ®ý~†G¤)ûâê˜½~ûûéíé¿HP¹9UlÈ²Û¤E‘9¯”6çí`ûE~«~c”B§µ¬(ÂW"m †åS#âu} ¶ãtˆ¥*“v&	³ ð®kUk0P¨$;þ%
ðÉ¬·xq#ŠÃ{Qþ^Q†$V&+k…»‹‘ÈÜY„ðŸ2›ô;o;VÊ·É‡ÔÑ¸›\UÕËs7š©ª](¦ªî"¿¦ì}å=©eÀÚß«•‰m…Z‹>ú¹«D'–
â¾ÃŸP2wfü|˜$wýx‰ÜU‚}ìs?*ù¡ÁÏŠ|î$Ö>ôÙ™^~hìãäqñõ‘Ïý)å†>±tÖË,JóøÎïC~y¥Y£ð¹)¡Y5+0•·;Ó~ëßyAê[Ìe:s€4+ñ3@€*‡)Ïó6ÿ§ªzs~I[wðA%jnô¹§‚^—t>Š§˜n€Ö»®T©%ð÷j	Nœ ó5'e(•ÿ³´?ð?õŒì `ú'‚ÉY»O7AFÏ“©E¦þ¼1¿1FØð|»»µ@° ¡$)?çI2bì0À öíëïNüþý€à¸˜«<‹ïù1ŽÂñú÷™t,o÷•×„	{Õß8Û£qÇZhoŠt”WˆL§	¬¦©M
ºm€\‘'ª`/1Ká•^B©xRR¢3{7é‘ÿ&Õe0è`~5IÚ¹¥s^ÎD6ˆ¦÷,JÛqËûàº€µ?%¥×ýE™òªÏóÜ‰²½‹SkðïVÇâ,ÔTJAÊF\QÒf ü-&šIã?'IEmÒYßf”2ÀC˜w²´^f
œ‰*WK‘yÂ^Ñ*—À êo|
–!­ì´k×3õšÍ6µœÍ œ FJ¦€&­»ïDÑ¹cY^h„ž«ó©sÆ=
Ñ1³ˆò¤èšR0ÅGrT…&•ç¨§öü˜ëô²Lu/Á+àyÆöJe=ÇÞ€	 Ç¦»FÐ*@@Úä"b„Ð s™70FôÝžûÁ˜1Ë{Ò˜6«WNq®X€_Pw®*×ŠÀqjà÷‘p¥3LqgÛbR:²ƒíñK¨m|Ã±tíÜ×Aó=Ì™ƒ˜Ñ5±ú¾À·Ãß0¥ÜÜz|”Z”w²7FÜÈ”bQoV1êq±µ˜69âàF*¹¼ 'Û›Ê\è¥6¢Ð„qÅ1žT¥å×ÉPu[Îì|’_Hu¾r6ÿ?Û-¼ƒª€e¦;Âi- QçÕ]ÚÏ8æ_nT)\”‚4ê¯”úæ¦˜sLm`–»#öÏàD˜…%;“Çãñˆý]Ô¥È¼´“¥÷”ðK€ñ|¼F²iÕœÿÚ(Ã{±®ëjÂŠ }ÂcLúÁ•/_ÓzÖ§ayÃ“Â°Þ¥‚ßü,^·•N)hšh•ã¶È%Â)iÑÆdÁ(£¹æ…0¢±ï:C1@sý½,›+–R0I2<„P±€P ó¹,¤‰×TÉÃ*¥%¡se<¶!…“M1à XPÈ´VZ ü³•œü–!‚§säT°èJ’çãÏþïÚÀ¿©¤‚ä%Œ3Om‹£ÕïÍBÙï½´ &ôST@ÏÁjë^vaÚ¬‘òƒu€‘´s”¹} v—Žž1Ëÿ{—dh†Õ ·•¦¡;Ð‘R7n£ðÚbú€ˆ”ÞBt)£e2õxªè}rÿ{»Sòj-q}kYBÛ!ž6”P0ŒÚ# m…<<Z¾w[ÛÞSä|+voA‰ûÊ¸9B¸hk»½#öüw¥\£û„c°¥îJ×¸ÂŽÙxp88ÂãáQ'R&>¸]˜…(ÎÉ–ö½«%¹–rûšÔ\o£ç,A ª^²}YFû°@>°Š~Ö˜áÙtøÖ~wë[Pßî ØU®ÝŸH±¼LMÃ5ŒŠk¬ôZÞJÛ*¹­{Þ€¥ŠsÜñ¨UÞcdÞæ”Ms>cûcT×Ã„ß£ÔøÈmHZôÑDÒ„Œ«'î‚$*•²t2 ~0°ë*KŸC%ÏFÑÚóï)D‚g{>áýþ=Üþ¥d~¿ìììm@ÈÒ÷ì­‡,8N?8hæ}­tUnn & Õø¤ÄL7»¨ügá·m¥0[<y¢<@-`ýpÆõ‚WVýöŽm'Ç^Î:®ºÊW D«PjA‹ölƒeêeuAˆS9[‚¨ãÛà¹-ð¼cP‚rÛ”\öQÉ"*ùêã}‡&D©ëÙ­ÖMÛì3šuMÃ[=?ã™Ä[‘Ë0ÄÁ…Ú†§/ù(å1n§ýÖä—»Çô51[³•ì¶H
ía>f!ÌöRÜ*´¯~yÈê—-…·)´/yùx%/[‰pë´Ï¾ÎåãÔ¹l”âöU.}qËÃ·l#ºm¢˜¾¢å#V´l-Äí‚˜¾ŒåËX¶“ÝÆ¦¯]ùhµ+ÛHð!Ìc¡'öGÌ±¦<9JÆ¹U¬© N4”Xb­Õ#ö/^[fü£m…×âJÌÕÚÔ]xãJ_èºAÌŒ«0Ã÷ü~PAÞ~ãw'¹áþÌ9ÄäMÐ9wò||G÷cs	0ªx*†Z`®¦“Ú]ú)Â(“VP€BXÀð4 ÉuS–ñIg{í|öZKpÚ‘¡>{ÝDiP/k™½ÚI‰º¯£Ñè ë_ðnG|bÏT7Bf˜ëÕTmÐ_7U’Ä·&5–º½ÊÊ"B·ö~IK-OŠ5UT.ûÑÁf|Ÿ‰Õ7ì½÷åÑ5¨Ž}€‘Öº¼,_1#àºs5`‰òQž^øŒCŸAßáª¥ä‰cÙFw®0eÇqRtXÞ^©-÷¡ë¿£z,:ù¨m‚?U	D Ã/¦µúM”ÝWm–ÏÚv”ìwm7˜8˜GÀ"6)kÒ—½¸ÒÔ€0Û™Íí½ªÀ1QRzE‡å1økR·â-—\'Ü¤¼âÉ‡#ÃOÈ$pëº§µÍo_¶—RƒFµZ–Ì¹Nç"kr‘Dù—™µ<ù+Ì³¨­îG¤:Ú#ûÞMì<9‡@æ®çŽ…O!L§MBÆK×\×;ýwyåsŽ)¢˜jdÚä<LªhX]ÞÞè01ÒMïØõ*ÑA{Ëæ*-þ‘’ý7&UØC-vD¦Ï•µ!ù#²^¾“6Þ`³,czËZ®¬±!È»³B–²h
Fl‹ðÒYûsÚ^ŽzÍÐì“+ZÌ¡yˆÔ5­þŒ°×8©tÌöK%ÄÃ8B¡[‰®=ejf}1Ù}U#~h°ìKÍ\ÙYèïy_ZÛžaò=˜¼Œú;/ÏkU5[Vs9c“Fæxq8{wúƒ»ûÛŸ’‡—Q*Wž”ßX£cÓ
°HÝV¦S¿Ý,‰ ÇÒ& ŠYj³ÕHÎ¶°w†ÚÆ›î¦ï·ÅîPµfÅ­éñ7À,¹Òý> y…ØzmX3‰‚BðXìá…–,ÊþšçŠâ-ç²uô(¾‰…M3Á0Œ£M"¿ÞÞqŠ¡E5Ë
+Ê0n†Yò˜‚Ñ²L÷ðò_ÊZ/U9t "|;Ðo(L[¸‡9‹ª)ƒûë±,nŠ™NØŽ.|È—8²)¨FláV°Çì[XÃ˜Õ)ÆÊ ýœÝðÂ¡]+kö7hØ÷~•zŸ«TâñcX¡Æ`X³:Å¥@š{ª¶ÂÌmH	/ÿØ37!åÚÊq3ÖA†Lô'š>Ek9+ãXŽ–Çm;Ë)Ë\ë×ÖnS>û„Uð¦„S„Y¤š^‘U KõDššãFüÛG„|„MÕôåõ4„•GòÞØþÄ[7* æ~²­¥+ËiNõùV¡jyKL^Ðä¢bÓ›Šå/•Ì°}é.Ÿ¡åùŠÍpàÚt½Ù] ·+ž$¾ØýÄý†P|Äâzµ{*	†©ÏÓµ»5Ö|n·ÂiŽ·«ûc>{¼Ç'áÙÖµc’@ÖÛvÄ'eëÎ;‚/û°à^Ã‚€Ó!¢¼	d5þN_yÙw    0707010001f0c0000081a40000000000000000000000016a102a890000125d000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.service.container.lxc.conf.gz ‹     í]_“Û¶÷§ÀL&c{*Éw×äåbßôÚ$ÓL›ºS;éC&#A$(aŽ$ ”N™~øî. ÐNºZwöÕô4[$—Àþö»à_óÏ³/Øÿ ¹LÕ–ËZè³›F°ò:ûrÇÝqy÷ì—n²_hYçâú×gßÐ¤ßà´Ÿ=Ã	\‰ÍZéü¼G÷Ð4ç–Os©á¶ñÿ ÛL¦>/E÷Þ÷ºxA‹ßZ©E? ïyièJ£ÕJ©jY/Î“+¹(x[Ú~ÿP5]õRhiyù•‚ì‚-ÏÃ(ƒéí®›Fd²Ü³À™“à&ƒ;~(˜]J˜Æà¯FX¤¥€¿è•Ì²¯‹VÃ8ÌÆ¨VgbÜh¹âVDÓ±™!›™±J‹	{¿t4Û‰ó²Tkø·…G‹¶ïˆ¨hQªŒ—òwn3í77ñ¼_½zeVÙé+üñUwÙ%‹VÀÌç= Äë\Ë„ñ¯hkyÍþÃÖ O M"ïA
€¨(ê3cEã éXIm[^²ŠgKà;åYè Ñ]@,•±5¯Ä „›A3j!rf›£âY&‹Û¸Üñu)—E!´¨m¤=…V=À›	›îÉ7¬Pj6OÓÞøø¦w=,ýÛn¤‡áÆ°ž}ãA¸mÐ»@QÓFQ5vÊçJÛ'Pwå¡lŽlX÷`ìJèhs¥àUõ¶ïCM›Í,˜ÍFìm#êw?ÿ¬aY2Ït•‘èøÀW‘S‚«vÉx¤váÎLUŒæ	¦’çLà2ÃE¼@ÿ!†öNiËõBÄZ,«¦½KÀ8Ú¦GÇ.¹±¬R¹`/„¹~9é§šÍ
dÉl<u$Ð¹Ÿ_­ÏgÛB5C®¢õá™%Ï£¯ÓI
=îŸÍ¹‘ÆÉó!m‡ÃiVà´§Z);m¸]N ŽÄD'€|vpFQ‚¨	àXI%ž]Á¥à„ãØ„"‹k0)LKÕ‚  öÎ³LØw05ç\x	jãa÷ª€¿C`VzlbŸ1 ^öCëÆEÆÎ‰…“ˆaŒÕTcá?QÃ…]bTÊŠ©^?†Htø~ÎöÍê^õœ'lA­<1¹IM‰†)Œ×09v°†`÷¸íS|Uùg}og–¥¸N`»¬£¸@‚&øk™‹Hýˆ
d°[	¹µ ²±`ã-®å«g}r
üg»BoäüÜ ƒ«ƒ<#@´€éŒñ×ì©Z¤â:+[#WbüZHc5ôU]n^nDÿ>Ù¢PÄÁ
üOÀ@7‰s ³o¥ß!l5WØSæl¾qÁEpóà,®¥³üIXÆkq3úqëò4°„¿êòd+Æm3	>.üÀÖÜàý©¦feÅ½9¢D¼q(CfƒÏ™2
&MºA¿Gäæ
&ZCd&	;£¢^B<QK#£]ñPObŸA$>LaºBµO/¨?ûêä!cÞj¶DþÍ%,® µ×áò@? ÓÊJ®CŒÒ[Pà*¸,[íÖ`	ç&œËNTód1:={ÊõŒˆp*»ò»Å§ê®˜¶ŠøVÀQLÔâ6@ÈÜ:GPÈ’l^=¨s®s†éÖ>ÕZ„Ôjµéðyåèìd?„† º›ÌTÔ+©U]¹ÄÔ§¨/;Qùå×Çõ0£´…À	™°¦;‡(î|†Aœcì\¿ÈZ‚0â-fÖcç´^B¼``}ˆK9\ý¡Ÿ*Áycb6{ï¹xóÚDé¦‹W¯KŠ.f³	»ŒˆˆJx/xvƒœƒ)–“˜Âoàï"Á’åª…¡³ßZðË¦K hÕÖy’…Øý^Š`Ð¯—=m4"rÛîx‡1BÛ€GÎ`“;QÞ)– â€ôÝÒ<Hñ ÅŸßSz€¿ØÁ?š_Þ[†D/Ã;ß»K†ÝGá[„òNYÆba†Ð®PCB‰|an7iX+XÁ«­])J÷îÞí³°l/Ý&þGäqàä'ÄâV—©G¬­1ù.psÇçÉ£4‡DéHOðÚìÛ¦Ôpc\²ä†}!}én<¼©j()óäå±-<î¸r½hÑðJ|€ÉÅòSïQÆ4O¤(}®TËÆ&x$î´j”ÞLeì´ãÖ°Òß}²[{&Sª’ü. X ó#3èSo&çÙØ¥ÎgÝLf>»0aßi­4ºv  [$»á"Â¸™»µ0}ˆòJ”ÿ+”1€ÄÊÙVöõþ0™{CHÉ‘ùf@ò¸Jóvö!€z÷ÃU5žÇÑLÕC1UsüÚzð•¤–kÿW­ŒH
ªCôs_Dý>Ê‡?12÷@s~>É#G?‘û"8Ä>£’ülás/X‡Ðçhzù¡±Çãð‘ÏÃ)å†>):wcŠ~¤}˜ãVÖ=~ecXkð¹­á)²j>½]v)ÓPB†×f„;,Y\Œí3xH€ªÇ/Ë®V¾ªzK¾¢²åû™„+áºÙ~oÝÐÖ…µ¦Z|u£j#q‡Ë7¥U~cà¹4ˆÊÿY·Æ£—P^Fö"àJ'ç‚ÉEzNõxIµaÛÈ˜eksµ®'À?X×~Ø×¸æ¬wDaB³YýŠã†–ëö©Ù·ß}ùÓßß»ŽHÚ‹sÀþÔûãÆ8‰Ç®çJP¡#[ª:Ù"¸ñÑ°·ý·=†6ËM0E&i©D.Ó$¬¶ÕÀ6]g|f[Uì5m×…áù?#lL‰ª ý¤'áN˜T_j¢ùa'iÌ-[òz!òQ2½ôÍ¢v/îøá~¸	°«¤ô?ï+£uÆ›¡'ê(Êö.-VÆ¿;Kë‚Ãþ8•ÖÒ>˜¸¦§´ØÈwªm¸Jž!‘ŽÈâ~h&ˆRòWònúšw@Ñ”jC{qÛ­Ê¾˜ˆÞ·O|*@Ë*=Ð±¬5ÜÀÔ6Ûj¹X`Å'‰)™a;ƒo«ì¡èÝ±¬¯ŠYª+Ô±º€u±B Ž¾Ç	~CSêk<É½Mi´sÇË''¨§Tmk¹¹‚ÐÈlêÌ¤ñ\žçìy­œçx>b{ÀŠcKÐ¶€ Úä"R	qÅ¾¾Ô× ïÜÆŒ!X©hÜóÖv¸r²Kqcà·Ô]€«*Û¸½A5òûH¸¢Å«sbsÏRwcÏù
ê¾áXúçüÝÑãÏ±bFÿÀ„ý+¼Ûƒ ¾þ†í—nàÎã#jI¯UÌÞZUq+3ŠEƒYÅ¨ÇOpÂ~2tšB‘èNO¥”WàdÛª¡`×ÇoHjÄ5Ç^S×¶øõæRµ«Ëo1—WRM×B.–ÿgÙÂ{¨
80Qçæ|»p¯Y KûÇüë­*…‹ÒUøôõgj·ùá-žr²[KÜÅ²¬²…Î…]Q³ÓÂðôäädÂþ&t-ÊH^ºÉÒuêíKÑÁøúäd³¦b`½Ö­š¬•£¦-,ÃÇ•/€×´žÅS2j‡òÕ(ZaXÏZá‰*û2]ûqaU }¾|nT‰i×ð”Œèb²„àmÄö}o(Fh®ÿ.ëöšeô‡Ùl|
¡b¡ .æKYI›®¨ë5ÊH’Î­ñ¸)œl«9À‚JfZâŸoõ¡vØà¼“
]³Ù×'_þ	®uSû=ÈK'ß*…Ä'Î¶ï·kåî
Ð	5I?½c
˜%Xm3èÀ1L›3Ra°^`$eŽrŸê²4ðóéÉÙWÌñÂ~H/ hÆs¬‡e †-µ¼€ŽÔ¦õ‰Â‹uxD¤t¢K™,“é(/@}bh—|ï2…xd‹Wklºñ,¡4EŽXž×.”P0Æ‘ {
%ðôìnéäî`Û{‰œï`và‘˜WÆäÉE°¨ˆ5ˆÕ_àÿ·`û[¸ñ•ÞÌy½ìœŒNGgøÃÉø¬‡ÁÄvƒY‰jJ¶t@ô¾–ÄÈßÓ5Çw¤ææ=ï± °¸ú…¬“<,_:EÛÚñÛbü£»ï
Ö· ¾Ì ¸U®ËO`<XŠx‹O•AË;´’ŸÐŸ=Ò¡T…íMV«r‘cx›KV”|Á^œ ºž¾D9J¬íyJ'G€>:€ýø$.…Y$Qó¹£“õ—#·®rô¹µt<U´¶k+')/ˆ$žÝþDðø÷8ýKÉC¾ìíÛ#Bn^xßþDg<|­ôçø9€š ª[åí~d¦»Eå78‹¶•vÄÜ!§±”GR‹m½{dÜ¬yÓ`KëàØŽ"â˜àå¬çª©h­¿½ˆQ‹žxÃ¾Úc™¬.ñ*çµˆÔñÇèwwdÆ=ƒÄmoP²¢’O"*ùêãC‡&D©³_­ãqbsg­ixÛû‡q<3Vd‡8¸PÛãí1¥=ÆgÚw¿Ü§9fè‰yÜž˜ƒ°; (th„ù˜0‡£xP)èÐýò˜Ý/‚·¯ thyùx-/AxpÙçÐçòqú\ö¢xx—ËÐÜò¸Í-‡@wH3t´|ÄŽ–ƒA<,ˆÚX±å0ìöÆ0CïÊGë]9Á{„0OÂ@ìS¬±¦:9*Æ¹’Mª© H
JªF©Öš	û7×ŽÿìžÂOHH¬ÕÆê6£ºÿ)<¡+ÓÍªŠ>ò3a?ERA«é³“Üòpê{'9Ää}¢3õx>½­‡1ý!qã›‡ÄaÙ“t@|TÂ	Ï2š¬ÛºNwâ8{ÞÍçyg	.{2´ÃçŽ£í€ôD8-ó‹¯©P÷b4™L^bÿž–¿¸½UáÛ9ÖzµÍÈXô×m3›±ä›­£îNîö»ˆðZwb·£–
O'wtQùrî''6zðòm;{ï»OÄË{öE_nc¯ë†ç*›àjÀ•©DÍyv*C}/W¥@Û6ú}…‚§EÑq{z¥®Ý‡>•“ôcÑÎG3ê
ü©K 2¼£ÐêwQ÷wu}X¡jÛSr÷u¯¡Ë%Gê£lúoÞ¹¶ß:€W;³¥;©8&jªBoh³<~MjÃÓ§xÇ%ÿî7R.NFéäã‘á-d¸sÝ…võí´=/k·SŸôj92S“-EÞ–b–Ô_æÎð2"ÎˆÌs ¶½¹ŸöÐÓž¸ë~bÓ\” ¹?{:ŸB˜NW„Œ–þIp]ï|ôß×•/9–ˆbý©•Y[ò¸¨¢+`õu{s ÃDQÐW‘za7ÛDGýg2¶høgNÜ¿±¨Âmj±32}¾­ÉŸ‘õ
/yÃNöØ,Ç˜Árí=2ÞZÎ*YËª­±-‘—ÞºÉqŸÖ2îsu7ÍrEë%<Kê5þ¼œà[Ó¢Òö¢VIA<ŒãeºCô.è©R3šÉªñCƒåÐjæÛ†ÈB—øm£³í9ßƒéÀÏ{ Að¼ÑU3rm5«›·²ÄO±°w—ÿð_S	»äÝH¨IÙµ'•·öè¸²lRwéôÞ~–DckPÅ*µÅv$çžpŸNpïÿüÈ;¸kÍ5Š;ÓËß«äjÿM,ò:!
qýÚ°f…à)ìñŽ,}cuÛs%ñVÿ©¶wÿˆ›vŽaGWD~½ûº/r„y@Ônì(Ã¸eWÉcIFD›:{ŽÚ¡ªuú`„*’ïsÏú8UÑ‰{\³¨Ú:ú"¶ÅXéD§ëâåGV`ƒúdÂÖ~{Î¾…5ŒÝžbª&ÌùÜ/Ú¶¶(¿¸ÿc<Ã*õ!W©Äã§°BM…áŽÕ)}¤fš‡Õ$3»$%>üãP™¹MRn¬·e&…»D†LôgZ>Š1rQ§±-»ç§s_»û@t¾øŒUð¶‚S³D5ƒ"«@—ô\ZÍ1QÿÑEëÏ¦w¥š¡½ž†°õAÐàÝçûQ5ÿyãŽ®¬‹’úóBi¹Jš%æhrI³émÍò+%sìDßøÃghy¾e3üáÜîm.ä³â³Yhvã¿Ê˜n±ø·ºœÊCŠ,Ôéºl†5¯\*œæ¸[ÝŸòØÓÝ>‰÷¶nl“DX¶Û‘î”ÝµßÝ9„Dœ~
åm‚AVã¿E;A_ˆ     0707010001f0bd000081a40000000000000000000000016a102a890000106c000000e600010003ffffffffffffffff0000004300000000root/usr/share/doc/opensvc/template.service.container.jail.conf.gz    ‹     í]mÛ6þž_A (’ÅÙ®w/í‡Í.×4¸¦9\ÒöCQX´DÛ¼•DU¤ìuq?þf†¤DÚëµ·±“n£E$z‘ó<3’3Ü‡ùóà!;âŠKUi¸,E=`f]	ö_.ó÷wÜÖWw~i;û°–e&®}ð”:ýœºýàöàJ¬WªÎ.»†Ì¡ÒpoxÄT–NUÅ§¹h?ö®nÞ¨Åo¬E×Šoy®éNU«¥ÔR•²œ_Fw21ãMnº†ÿ Jº!Ë…¨¥áeê>”>c/ØBðÌ·B0è³¨í}]‰TÎd
ÏÌhK¦øXÊËLfÜí>Ñ”òšý­@•j¥IyÐÂíwX‰šh&ÓkmDí`®/emž³‚§Àb/>u* Pè. J›’âÓF"R³Ô¬"cF±©`Z&g7i¹Õƒ729›‰Z”&0ØY­
zÑ¿€6í›ÏÙL©]ØÈêËÞBT¹u ?—ÚlÙ…¬–_2žeµÐZh¦,^è€HÝ¨È}ªî5}¨¦ŸÜªé=ŠÆ'k¥Ì‡Ðv«º-u{þþæ­0štˆZa3ô$µHª×^¹í‹
öú»]Ë÷Ò£¿ûê}åIîz÷µ]yçÀ·šõà©sß75z(jRÕB•™ð©ªÍ½¨½sªè[ÑOû-—4U
>UFx¾šÑ£u’$ö¦åÛŸ¾‚8*Ï™S:´JKP„õb(UÂ]³`<°ý“©*
hô‚,ž¡!òö&Þ ÿÃ{«€$Úðz.Âñ_U.
 }ˆ¥€ÐŽ¦ªàU -çÚ°Be‚=úúlÔu'”$3TI’€N­aìòju™l’*A­bÜÂS#¡ŸÇ@O£NŠzZPM
6åZjËçAÀm‹Ã6+½L'è‰&7‹>|ìØˆÃÏÑ ÎH%žç ÇR—xz·|øN!X¼—bÀµ±·1éˆ}]³a)ÏÁlìÎðú¬És‡Míãìo»¦µí"ggia#|ø\Uþ%ÜØE€ PFLêÕÇá?!ÿf T€»Nó„-˜µ€÷—°Œ]I]® sý`>ÁîpÛgøèë¬íþ°ó”T c®°¿`	È ]qæ–K1 }Í!~¯9Ø¸*óõáöwôþàñš~8ÂºŽ\=|•®CP¤¯4¨'ÏØtm‡.?ˆ€+º–Ö¯"5ãµØ[í¼šüX®Ò+†ÏK-‡M5òÔ_`+®ñù@T]3²àŽì ŽƒWìp<N÷hSCEA§™‚ÎÐõ@ÜTAGK‚õ(‚bç˜ÛY€miñ¸»k´íDì57ÔÃº+TsÿBÆ‹'ãSNQ³¦&²Eæô3—ºjÏüí \@—˜æ¼ö#`·#h×¦WMm#üHó öe'Hªº·_ÜgŒ:ÅDØ•‘»©“
b I«ž>puˆ¼lg=€ŠNkY‘×"mÀÑò¡´Ná¼Yàó.Ó¡Eç2i{’8À`t¨kUcêÅ.„¿‰î@æöŽø%2ÕÊ?
e ©2ÙÐî#‰¹3„ðG™M×=’Ç5ÊP·Éû êdÜWUõxÇ2UuÃTÕ]ðkÊ~¬<‘Yªý£Vˆ8ÔZôÑÏ]u¡é‰ÃŸ™; Ù?ï‡ä‘£È]ìcŸÓ˜äû?øÜ	Ö>ô9š]¾oìãð¸|}äs:£|ÏÐ'FçvL±Á6/ëžmVœ¿ïD^iÖhznJx‹¼šË?É…O9ñ«òxÏ©lµi¸ˆ™rø0@•Ã”çy»¹]	U¼_ÒJžr©‹Â®Š;ù(øêšñq©½ÑÈZã§+Uj	:ÆÅB"ƒ0ßx&5¢òK¯øà»R/½";
ØÝ¨©`r^‚ÓG´ÓTD½hL¦Vå( ø•ÑøŒi4Ò†ç+¾Æ-$$J’òsž$#—žS²¯¿ùöåß¿W•‡dñß¤dÛÆQØ^?S‚öŽØBA{o	jöæxã|a–ö®HG…È$hšÈjšôÑ¤`+ðˆ+²ËDìn¥½ðÍKh?·“BI´±ä:=òOB§ºm6ôÏ %IÛ·tÁË¹ÈQ÷â/‹Ò~¸Õ‡½°°Ýœr—÷íL‰2åUŸÄtc{ïÿâß­Å[­>a„v+ioD\SFR ~ŠÍ¤ñRÑ;Dé@,¦ ‚e•2àC¸9ºî’E€¢ÊÕZd^°7´v—Æˆ¾·> eÈ*{Å[ƒÃõJÝòÙ¦–ó9n¢uÀIÉ8âó ;(ºáX–W)¤ªÁMÁœsBtÜþvIIp])¸b¤€¹7‹¬B—Êó{Ç´SÚÀ4\_Ah¤×eªãx	nÎ3ö¨Tväx4`“¶fÇfÐ&A m"b†ØýS·{ªqìöÚÚŒ!X®¨ÝÓÆ´)‹8sà·f`à~kÀÜU¹¶yg[Rƒq…Vj1Ç‰pö]JGqì_BCí‹ 7lK÷ž{:xý&v@Ìè^±ÿøo;`l‡¿a¾¤m¸ñµ(9*ToŒ*¸‘)Å¢Þ­bÔã:8b?j1kräÁRryƒlSTl&saKv4±F\sLµyv^_ÏÀª]iyóÉ4¿’j²r¾ø‹­ÞÁT` e¦;Ái-€Q“jŽCÚ/Øæ_o4)œ”Î)¿³¨RÓ«7ÐÅx²_KÚ±Ÿ0%ó¼¢Y	Q²ó1ax>Gì_¢.Eð¥í,Ý§d¼- Æã[M«fò[£ïa½ÖR˜¦”‡ù'8ó…Æ ð5ÍgÁæUJfÖ1A§0¬góZÁ8ãægñ<Øµâpk4MµÊqYÄfè‚$-Ú˜,xSÆïˆ}Û9ŠºëïeÙ\³”Þ ç$Ãsp2ŸËBšx@iê¬RZ;7Úc_¤p²)¦€

™ÖJ ¶‘8Ú*Dðtº“
&]IòÅø³À½¶!ðoÊ—‡ u	íŒÃSûÆÅæóf¥ìóÞ ZRûé{L@/ÀkëÞŽáÚ¬“òu„‘´r”¹u v•.Ÿ/ž0«ÿ{× 4Ã©jp´•¦¡\/°‘R7n¡pk²ß€ˆ”îBt)£i2}ùRqLô¨ïìJ!Vg:³–8¿µ*¡e
OŽÏ+J(hFíÛÐ¾…<¿¸}=ïö½/Qó-ìÞbµ­Äue\!^xŠX­¾‚?7rŠ»G¸¦rE\•®q9„]²ñà|pÆÃ‹R/ì³Å„|iè]=‰–¿ÇsŽoÈÌõ!vÞa	 `õãcYFë0A>³†þ¦1Ã7³ákûÜÌoÁ|ç¸‚bg¹v}"Å5çè0*®5¨Ò[y‹¶5ò1ýìa‡RÅW<j•÷9Æhó’Ír>gÇh®çgH¿F©ñ’[´<ôq ˆÐ‹î®rØ ‰òù­œ¤Ÿì¼ÊÊçÆP=ŸQ4·k
ËÆ@”#"Ñ³ÝŸðãþ=\þ¥†d~½ìÍ›× ÛHGÞ7¯=eaàôƒ×üX+])†ë˜	 ï”ø–éfj'•O±~ÙVš³>ç!ËÖb¦ôŽë¯*Y‚Áô?Åq—³N«.¤¢¹þæl D-xã9{²Ç3õX.q&gë„s|\·UHwJ·½AÉ²JþQÉßÐOš¤îËn¶Žõ¿S{]Só6÷Ãx&ñ^d†88QÛãôå1¥<Æ­´ïL~¹KqL_óakbÂî€¤Ð¾æcÂŽâA© }õË‡¬~9¼}	 }ÉËÇ+y9ÂƒÓ>û:—Sç²ÅÃ«\úâ–[Ürt‡D1}EËG¬h9ÄÃ‚˜¾Œå–±†ÝÞ¦¯]ùhµ+‡ x‡æ¾Bè…ýs¬)OŽ’q®d[* ÒAƒD‰Í(¶Z=b?óÚ*ãßí[xæ£Ä\}¡MÝ¤”wãNíÆ3±0#0Þ¬*è<ïû1`½¬Öº[ä†ûcÚZæ’÷Qgâð¼[§q«…U<C-0×
ÓIí*ýi”Ið£–0<MXrÝ”e¼ÇÙ£¶?ZOð²C;|×•ŒÛQÔ³Zf/?£DÝƒÑht†õ/x ^±{	ªÀcË2Ìõjª68^7U’°èÈÆJ·‡¡¹]Dø¬=ÍJ‹ÉÓ’â–**—Î}ïhsÒ³¬nÚÙ{×ž¥žÕçÔi½Ë³òÃ£ª´jÀå1£¦<½ò‡>ƒ¾ãU+ÉÇ²n_aÆ.ã¤è°¼G¥¶Ü‡Î¶ê±hç£´	þT%‘Ÿ˜ÕêwQvOµuX>kÛI²ÏµŸÁÄÁ<:¥¹IY“¾ìÅ• „ÙÎlaÿ‰’²Ð+Ú,É_“Ùðø-ÞjÉ}„»”ãAÜù°eø¹n‡îYmóÛi{^–v§>ªÕ²b&:]ˆ¬ÉEå_fÖð<äÏçÍ2¶¹¹‰vèdì}×±I&r Û2½‘O!t§MBÆK÷&]o]ôßå•/8¦ˆbþ©‘i“ó0©¢M`uy{SÃÄlFÇwd×›BÞ>·dø–Höß˜Ta7µØ¹>WÖ†â/È{ù<gã=>Ë*¦÷\{Oáûš˜Ã
YÊ¢)©-âKç]<sìYØÚž/¿åhÓP´ZÀë!S,Ô´ös6Â¯ÆI¥cö¸TQB<´ã,Ý"zô”©™õÅd§ªF|ß`Ù—š¹²!òÐ9F¼¶¾=Ãä{pxb*:ô·Ï­ªš-«YÎÙ´‘9žnËÞ¾üÁPëwÉÛ–P‘²-OÊo¬Ñ±iX¤n+Óé»]/I ÇÒ&ŠYjóÍHÎ¾aO£´/ï?Ñµ_;¸jÍŠ[×òo€Yr¥;ÄšF…Øzm˜3‰‚Bðöð@+–~ÒæÈÅ[ÝÙê!îîK›fŠa8G›DãºÇž4B“<jÖV”aÜ2³ä1%#¢u™>Â³‹)k½TåÐ‘Šø}éT@}ÏZº‡9‹ª)ƒC–±,n†™Nøø¯±e3,PØÊÍ`/Ù×0‡1›]ŒAû>_ºæ…MÛ*kÖ÷ŸoÜÏRO9K%ß‡jL†[f§tîoOšÓ’FUqfSÂÃ?åÌMLÙš9nr& Âm”!ý‰¦O†DÑZÎË8–£éqûžÕ”U®×n]¦6|þ	›àM	§Âl˜¦·BTØR=•¦æ¸PÿöÑ1€°©š¾¼žš°ñ<ühlQ×*æ~Q+W–³œêó­AÕrKLÐâ¢bÓ›Šå—JfX‰¾v‡ÏÐô|Ãg¸ü/Í²_³«@nU<I|±ûs÷‹.â-÷U»¦’`H‘ú<]»ZƒaÍçv)œú¸ÛÜïóØýÝ>	÷¶¶¶I¬ÛíˆwÊnÛïžìÃ‚“†¦ïCDy1Èkü‘7Ez  0707010001f04b000081a40000000000000000000000016a102a8a000000cd000000e600010003ffffffffffffffff0000004200000000root/usr/share/doc/opensvc/template.cluster.network.weave.conf.gz ‹     ­Í‚0„ï}Š&\µÞ4zô	¼¥´ ¶Eäímù‘vOMf:»ßDQÈ!8>®‚íj]¬¨íÐü‰âÂ^¶;r™`#­*‰×•ìèÃˆMˆ(Ð;‹Ü-wLŸœ¶8¾+#ê†g%>ËN¼4ðŠÆ£UòW‘Èy[Úå¾$fÛ”Å,Þ$é
ê G“i T®=Ò*h%P—d„sœï ¢l…¦*9#3gÜOo×Ò×&òô~Ï÷Ù     0707010001f100000081a40000000000000000000000016a102a8900001237000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.sync.dds.conf.gz  ‹     í]QsÛ6~Ï¯À4ÓI2'+²ÛÞƒ›dš^Û¹Ì5uç’ô:"!	c’`R¶:ýñ·» I@²%Ê–¢¨¥'“8"¹ð}» V»‹Çwùóè1ÛáŠ3‹<°rQ–$æÁâvÛºÝŽÝ£_±³µÌqóÛ£¯©Ó/±ÛaÛ¯ÄâZéä¼mBbJøüd‡?8D&V§¢yÑ{]	¼ Åï•Ô"Y¹Ph5—Fª\æSºøO]IÄ„WiÙ¶ù'•Ó™Ï„–%Ïc÷žTð	{Åf‚'u#ƒî
m¯›BÄr"c¸g*rx6¦A7˜îxÏõT”l"S|ŠS_Áµ¹ŒÅ]%4Ž§Cöma¥bFÇCö³’9þw&<vd"‘oÑ¢Hù¯²±Ì¹^œ$"-9|9	›h•1£*–«DÀCCö~&ÍŠ´¬2%›ñ¹`œù‡`¦* ÐVøËSj‰B×¾pý‰Rw¡or^\¢¨ÁÑ4W>8xq$OGŸ35gåTæCö&go¿0­*Ð®o(f#cvqSŠ¼ô ãM\KaØxÁÒy·«ÔÙ;Ä„"pôÍLwb-x	2áÞ‹Bäï~ùÜï‰ùšÇ¥c#bÎ0óØÉ0Œ—,S ü™'ÿÜ“ãÆàS]ž¤Ä?èÇ”Cs4‹g<ŸÂ'	~¢ðãyB¤*Ÿ6÷{Â–žŒgU~eX*áý¶u¿¨´Ê›Â¨ß[¥‰åp¨/9¨ƒ1Ø9SpÐž^ótfÎeŠ,">7tÝÀj–ÏŸQk“ãñ*ùœ‘"—Â¢Fc­ãõ£\’¹ü{4c1hˆLPñÜ+Ð„ö'KtA¿ÒM*Ÿí5$€éu‹-Àœ§*±,ŠjQ4€ÿ¹_inRålÀ®gÞ‹êrâi’×hMü™É¡åZw÷´d@Û‡ž°o»æ:GµŸa1 ¦„1¶çFxæ#O>»–%èy‰³T©Š m°:¹¬
¯’ÅâEœ
èŠLS6veEÃ}3pþÁqÚÀ¤Ÿ‡S"¾IV˜÷ÒtÈ”€Ii-Z=rnñ‡fÌÙž\ð˜H<iý‹òZˆÜv‰ñªTÙi×u0Á`ž`K*ãÆà¥}æ.½¡uŒáeVú²aîAWaŸÒüÔ%ËhâÑð°–MÆâFÄ€Ã'%L8H@œ–:Œçñ	L“Ðüó¨éIÄìGCö½ÖJh0HÐŠ)î"âw2ëÍ`ø‘º‡ò¾Pú ÒPF¸÷ƒÑš™m!„¿òd¼è‘Ü­Rúc=P'c;\UÑã¹ÍTÅ.S[á‡k˜×{w¬“n\ï­îù­±¬—Ç=š»FÓî‚§]õn‹(®Á•=¦{ÀÔíƒPu2îƒ+üÓÃºXáŸ‡¢
ÿlªÝ†÷ îT;´ÕŠØÔ*ï}	{ÕÚû‚ê‰è
ª½wh[DÇb‚ÜžÝC>2[ Ù;‡†äŽ½C5"Û"Øû†ö£’u-á³¬½khgzùPßÃcøzÏÐž4ò®!—m¡ìC{óÞž¡ ›míýB{„ôŽ¡%|îkïÚª÷ô…èl‹iïÚ¦÷w…èliïÚ¦ô
…è¬ÇlL,Jwøý[¤Æ}	gÅÕ´á³€©´®uáix­ÒPÐ\<ó •†åx0@å'1OS«.”.Y!Tòf|N!opC!0²ÎF¼9ù~ÙL,(rcÌ¼¸´BåFÂÂ6PÌÁ	˜ß–/¢rt:Û\¹ô‡ÄUb3ÙŸíVb*Ç
Àçù’ª»l)àB“ÓôÃ›ªû *\¸`U&ê:ˆ†|S¼§¬ÒÆ…(kdAQ”?çQ4dì(a ØwßÿðúÃïDÇë™JE¦iß	‚ê6ýöÖ×ëm$ÔLA{×xN"jöòVÜÙƒ)ÂÔ¦È¤”e”‚ÈZVÆ£Š1{ˆ/É’óHeìEÁËÙ«ºyŒÜnDéK:¿º>\§‡õÐ)ÓêŸ×¿5IÚ¾¹øñAÐ½ðÍ"·/nÆÃ~°
°±©öcP1Ë˜;ôLä1/þbZ¶ƒ æ{)²Œd•ðtáïVÇ(t·U@ÐT»“É¥¸)‘»ø1ZLh4“e};!<C”öÄb 3h&P)IƒàÞ±sæÐcPàD©Z aœàZÑP*—0&–@ô¾MôÉ ­’´²'ÐN¬5ÜzPWlv©åt
œà6ÊÜT2–n§ûŠv:–˜Œr¡ßöm{à½„Æ×Qá¸æhÌ‰Nä&(—vt,@=Å5	+¹¹‚¥n1L¸^‚KSLìy’+;s<0òØd×Z& MSÄRš¶¢î+h#ÎÝõè{mÆ%Xª¨ÝãªR¼äT“Žl—Àü^º˜ªRJ4«R½y…Á¨h1Å\€0‰ Ÿ•¹¡á`O({É>ã†miŸsw{?ÁDX3º†ì¿õ»0·ÃoçQ…ñ¦þ	‡Á¥,`’Ì×µYÅUO“ÏøÁˆI•"n•’Ê+˜d«¬ ÄH³0¥È±FÜð¬p™YÍx½Ü«îÐ³bz9N¯¤º¼r:û‹}‘º…ªÀ&òÄ´‚mæße1Å)íWlóo·ªnJÁ€õ-e¹¾¹€.¦À“9ØZÝ!û3‘Î›<˜Óax:†ì?Bç"õøÒt–®##–Ðb|5Zƒl\T—¿Wªä=¬kaõó•ÁÖÀ†4Æ¤Æ<)JVÍq%£i?:¯(¡É&è.ëm2&Î›Ÿ‡û`×.X‡[m¤EÓØ¨Ý"”˜FùÅ¢Y“=Y˜€–‰Rè!û¡54×?Ê¼ºa±ÍÅ¬¶“SX*f°ÀÍ|*3Y†û $g…2’Ø¹Ôû -'«l‹‚LÆZôOL˜­ÚˆàñŒQî4lº¢è«ÑçßÀµ¦!_ÙÄ_ôàXšÁÒòÔ>q¶|y­ìýµ4¤&öÓ;6¨€™Õ6½ìÂ´Y#U7Ö†2‘áäj¼4ðñéèìKfÇŸr½¹¿)ªJéf@ÃÊå$ŠÜTÎQ¸²Y‡wÀŠ”®ÂêRÛdzò¤âœèöIŸa:Ä4~§ÖX‰À	¹)jrø|¾¶K	EyØu^¢O@û2ðôl=ûzÞu¶½¯qäØk;8Œ%eÌ§vËÙXTÄhõ/ø×e>š[¸Kœ‹©ÑÂÎÙhp:8ÃF'g-¤&~p7˜™È.É–öˆnkIl5êïIÍM=o± ”^°§2ü°A~fý¢*O.&'oí}W°¿õÅìa·Ëµþ‰˜´þFÁ¡¬µ¼AÛ*ùˆ~6°C©ì=Z¥=Gv1Û¼f“”OÙÓªëé3›/o}”?rIË#@'€ }dÜ=q·H‚¹hâä$ ýÙÀî«¬|^ÂnÌ~}…{;¬´‘)Ö¡¡W=›ï'êy÷Ý¿Ô¤ö—]\¼õÙF:ò^¼­)gÝ¸ñ¢™kaøÙÂõÔP¿)©[fª±ÝT~MyûÎm+K˜¡[ÁN}–{¬®Ÿnà¸¹æE!sP˜žâ» 8:x9kGÕ-©h¯¿¼ðQóžxÉ¾Ü`™z¬ö·q*G^tÿ{­·Þçd¶]” n%ó~UòI¬Jþú¸ï¥	IjßìvëXÀlŒÕ °×šš·üý¡¿ž‰j+2÷—8¸QÛ°Æé+«¤²Šó´ßü²M]•¾œÊÇ-§Ò	»ù2}•CÖPéŽb§,™¾pÊÇ,œÒ¼M¹1}µ”CTKé^§Œ˜¾DÊ¡J¤t±CL_åuQºCÙ)û¥/†r¸b([A¹1ç¥¯€r¸
(Ý‘ì’éÒ—=9\Ù“NHvÎoék¦ÖÉF»W:éœ|Ü'] ëâ®é«š°ªIg»ykúR&±”I7ì6:kúú%¨_Ò	»n¾š¾hÉŠ–tÆ°‹«¦¯TrÀJ%‘ìæ©éË“¬<É6HnvÔô5IV“¤3ü4}!’ƒ"éänšc…°ö)&ÌSÒ#eV]É"Ü  -4H”PÂ‰²ÿqmãçæ)™)C˜àùXULIT¶Ž	>Béaä1¦‹b ×ô°Z˜6ÔŒ—Üñ§eò&ê\:<o­¼àâÖœÜæB.'H£DZ €™°„átXÓUž‡aÕœ=iúó¤±¯[1®}S` Åv9m/´L^=}AY×¯Ãáð3y3±¥:l`¨Êd‰	zp{UL‰>‰ªˆ"œÍVYéÄå:$^+èt6+-$OCŠ5%q\nþÑÑf´Ï,ùÛÂ´ß×µ.ê²	{¼õòÖº¼È_±RfxF©jÀ¥!£Æ<¾ªÓGër-¯Iµp¬ÁÑ‰NØy˜áî×êÀY©©Ý‚¦MÅu(Œµ4Õr{ÜŸb¨¢3Fÿy{WST§NÁw’ì}Ík04ŽWEnR
l]ÃÄÕ@ðS×ÙŒW,ä¡{§ Ì‡üšÔ†‡Oñf”ÜK¸‹Š}5„÷[†·Iàvêžh[¬€r-dnÓ.‚Â;VÌ¥‰g"©RÉ´‰µ<õaý‹±Àk m9S#íÐÊÚë®c—‰Hù"r-[>™– Ð&£ÓeÝ“x0®óp¶EÜIÇ9W)÷3dšld—„99LL&X¶Ë#»Y:¨õsE€f‰dÿ26B™‘é«Ï¸ñgd½ê—¼d£6ËLo¹|Ë•Tv	²|ø20‡e2—Y•1¶€/­u©™C‰ÚÌðìVCó”¦¢ë<î35Âª[Vž5'z·D±§¹
ª@;žù [D×A_ëÈ_v·SOìM×i?èu{ëÅ­‡gËå¢–Î‚mL†ÁæyeôsJw~ž¨ø¹²c?o¬UÔ¤*Õ¡ð’ßÌ™ ½rw€zsßKöëg£Ñùht2:…¿¿ùç)N‡ŸØg£3úô÷iYÁ:®Ê?ûíN^`û’¾bÔ¾JŽ=tU×“rµhæN5tnaçü+lÀ”“À‰þ]çJéœ­3Ÿ²q%AQ@Ú»×?á¢çÊ4©0MK¨¡­A”ÞZˆÇæa%J[~’ÞÛö’r¬_R1uº¼Â·OX^Û‡7¶ê¿Þ¦4•­i—>ÿ˜
›Sý·©W§¶(#ì¥EF[³v¿jh{ðöÊŠ&X‡»¥œþú¦~ÄÒ¦ãò&Mûµ­÷jìiDhóBËEe£p=ëáMy_Ò¬ý84¡Ò¹ÊO©ˆßçn2…gÊ7tÎï®rW×ÓXûj‚éŒøUuMØ²	V¡érDþœ}{Ûr¹‹¡2˜ºÏç®y~ÓVjWyß­oÐ˜÷{ïÅ>½4ÆÇà¹É°Æk[Äž4{&*:qæ.¦ø~»ræ6¦¬x–9ãQaeÈDÿMs¤}¢#§y¸–#·Ióœ);¸v^[ûõmPÆ°–»ê5r¿ÙôQØòUZ¬ÓNÔShÿ‚a>ù”ØDŸŒß|<—ànt8M¾˜}·àe&óji‹õ!—ä¤·—h÷d]öÄ*ë‘ãc…5L©¸yz0ÌÄyÖÃjËµÃiãìUÈ¥âÿ°½¯KGãôŒ….ÙÓ <YÂxQ¤°ar…í÷)lýRÆ§Z<ðÁUÅpN%[!×ÿòµqÉ›à­µ”<˜–@2¯íaeJï–šcQËÑæ3¬¢@®#)í{+·+G}4v.¤Å&Kç"özíŸFn¨†HËÔè@%ûOÏ¤ý3	{GD$Ÿ›xd#{íŸGv¤†GKÄXÃ£’OÿÆ¼¹­<ú«>ÕÔÁ¡äõX–šc$ü¿þš‚Ð"ÙÂnõaÔ„`yÝºõ¯e9ó@ –è*­\™OR:ÍÃ‚®å<(­:xœˆJÓßv´Æ\ÉÏ­X¸£ª(þc‰èî8­2ïm6ÌÈ…]FQ}4ÆKûexÆUýV»C¡/¨ãºªŸÝ´à÷#Ïm¬%õñnVsˆõñÆçúÁÓ+q¸ÖÝÂiÃPìuµÞý\¶×¹Ìéc˜Ën#Yÿû²{¨£   0707010001f0e6000081a40000000000000000000000016a102a8900000df9000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.fs.directory.conf.gz  ‹     í\mo7þž_A (œàdU6ZPãàr×\Ó.éÝ‡¢ðR»”Dx—Ü,¹VTÜ¿™!¹KÊ/’b9®ÛõÃÖ.‡ä<Ï‡äŒž>=äÏ“§ì€?(nnFÌ®kÁ
ÙˆÜêf}'q‡Ýau÷ä—¹yÚHUˆ¿>ù–&}ÖOûÉÿ…X¯tSLûa,ÝÖðäø€?¨(“ëšÏJÑuõ¾i>hÄ‡Õá^zR7úR©•T‹iò¤sÞ–¶öOZÑ©–¢‘–«ÜwT
>g/ÙRð"ŒB0˜±hÜsS‹\Îeï,„‚¶9éz09¼ñ~)œF˜]rËÌR·eÁf b/3=‡Ï#.Ù÷>h`Lµ•×R7Ì¬«™.¡»¹nª1tõ­“~OÌMÔÜ.‘ðàwÈ¼-KÔÉõ3«YÞn©–Þ9Uë5+šjàz¤ZTˆ48³¡ZOý%¿cöŠžeªa„²ø4ˆÙž[^Æ&ð?`ròÍ7ßÜJkD3€‚
ùdÿ}E \õ?$üŒ5ZÛ› ™•:¿ œ×ÚØóN)F£W,×UÅUA>odmÑ‰"o­`|n	?³0ºmòØJ¦ù1Ï-šu3É˜ûÐlÝ0HhZRüCïdnwzi#cyc(?Ê@Reûi0’˜½!„_ª˜­$k”±n³» êeì‡«"ñY¦®a˜ºÞ¿Vkå=™e¤ÚOµÊHÄ® 6bˆ~öEt& Ô÷þÄÈìæüÜÉG?‘}bŸû1É»?øìëúÌ.ïûx<ö€oˆ|îÏ(ïú¤èÜŽ)X¨Ï²@vxÃûÀï¢¬ÅÁo­È«9Àt)ÂY<¿”`ø, iFlµ”ù2T¦ð5`€VÇ9/KfÚºÖ°øÕB× oÉ/éP^¨EÃ" ò#QÐëšqàŽÒÆ×‚¬5v]ke$èÏúˆNÀü&Ài•Gg³Ý“kAßm`<À&P¶™öÃdÿë;ÈµºMÔõLø\m˜ºWdO¶’ ñL0¹P`ç‚¨Bkd[eÌ²µ…^¡Õv]¼¶ß±­AÚðrÅ×OÞ, (ËÔ—<ËÆŒ½~¿ûþ‡W?ÿø~Dt\-u“%ô	‚ÂÇñxÃóBC„Zj•œ _‰øhØ›ë÷=fŒØ&¸" p± i"«mÐG›ƒ­@WÓLWìÞá¼ÃË@kðº6–4½XM3?éqx&ezû‹ægÑ’¤›[¾äj!ŠQ2½´g¡\Ç>ÜWv×þc01Ç˜ìL¨œ×0+Û×µÊØõà$³ÌBë,Ã¿ÿeÏ´4¥¡J¶â£EîFàçè1¼>±áuB*iC”ŽÄâEX&P© >DÒ à¥Uf\ˆºÔk Œ¥r	:q¢þ¶Ñ§´¬nÊ[ƒÃJ½â³m#à'ê€“’9p„I·Ü÷PôË±T)ÔßÖyçž„è0xr(3ú])¸b¤€¹ºF—ÊËGÇ´SŒI˜åæB#³V¹Iã%x:/Ø‘Ònå81òØüÐÚ$ MKDÊt.ËÆˆkwÐ~4fÁJMãžµHEðÒÎ)½wSð¡sÇëôÒh"Ç©ÑºÂ@+XÀðb?c][©©ƒñK¨kzÃ±ôíüÛQó#€˜Ñ7³‡¾=°¶Ã_Ó,`áV|D-’’ª·VWÜÊœbÑàV1êñ³Ÿ˜·%òàZ)¥¼€E¶­j6—¥0kcEeˆ5â#¯ >&Séôuv VÝ”#²8Ÿ•RŸ¯„\,ÿ`§…{˜
,`B¦ì2mÎë.i¿à˜½Ö¤pSºÀ”ŽÈ¢þ†ûQöú-L±ž\‚¯%íŽÙxÙÂ gÂ®„PìdBžL&“1û§h”(#¾t“¥ç”_’¢Äøzr²yÝžhµå¬·Âz5M(Ç$lK¥Æ/€oh?6¯sŒ²½c‚IaXïóè`Ýü"ÝûqAî¬‘‚¦™Ñ%‹\"P’]L–ŒdÕ¼á•°¢³zG1Bwý£TíG–SpYv|¡b¡ næKYI›îtœÕÚHbçÆx\C
'Ûjæq*™7Ú t,+(Dð|‰º“6]Yöõä‹¿Â³n ð?
Â³ Ô%Œ3O]‹ÓÍ÷íJ»÷ƒt¤&öS[LÀ,Ák›ÁáÚœ“
ƒõ„‘trTøs î”>>™œ~ÅœþÇìµb<ÞÔíñL·¸ÚJÛ¢­)°eZPxe³}@DJO!º”É6™ú@¾€T\Ý¢èv3°¶.YÍZâþÖ©„Ž)9b>¯\(¡)ÛÌ#! k…<9½}ïvö½¯PóìÁ3‰çÊx8B¼±Zý~c\³¡{…c°e,žJ7xÂ¦l2:â“ãÓR?¸ÌJTçäKD÷õ$Fþ–î9¾'37»Øy% €¹ºÏ¤JÎ!`ƒüÜúÛÖ¿¿qï]ÀþÌw'(n—ëÎ'rnÐSÄ{t57T¬¼CÛù„~¶°CëêO<]9ÄjóŠÍK¾`Ï&h®'Ï]š°;£4ø‘?t<ôqHÐ@ÆÝ÷A¬Es/§ éÏGn_åäs»1w}…{»¶rlŒDy"=»û‰°nàßññ/¤çeoß¾‰¹Azò¾}(g4k-¨O€ÊÖ~`&€jzSFfÚ™ÛT~‹³Ç¶ÒÂ
Ø
v³<b-æ®oá¸Yñº–
f ø!(Ž¼œõZõ!íõ7w1jQ‹3öÕÏ4`uAˆ79:EïµÞDŸ“SØ7(AÜ¶%—CTò»ˆJþ‚öxß¡	Iê{ö»õÖÐ1û‚fM¥4jóþ0Žg²àE.ã7j[bœ¡<æAÊcüIûÉ/ûÇ51Ÿ·&f'ìvH

a²fwwJª_>gõËŽàmK J^®äe'wNûê\¦Îe+Š»W¹Å-Ÿ·¸eèv‰b†Š–¬hÙÄÝ‚˜¡Œå3–±ì†ÝÖf¨]y°Ú•]Ü#„y¬a¿ÇkÊ“£dœY§–
€ôÐ QR3J­ÖŒÙyã”ñ¯®•¬êRb®¾0¶isÊ»q¥/ôuD˜˜^Va†!žùý±‚ëµéO'¹åž?=sHÉÛ¨sîñ||W÷ãVK	4ªy.ŽÀ\+L'u§ôs¤Q!PÀJ8Âð<`ÉM«TzÇÙQ7Ÿ£Î¼êÅÐßGür)C×IÔ‹F/Ÿ½ DÝ—£ñxüë_^Ï]u‡»KÐ•´˜Ó¯·õÈX\¯Û:Ë@p$«uÒÝ·(ù[DèVä6ÔŠ˜”<)n©¢òéÜŽ6“ûL¬¾îfï}(™öôn^}À‘Î»¼P/™•hÝ/5à‰Ê”Q3ž_„ŒÃAßóª“„cÙF¯0gÓ4):.ïÀU©+÷A×&“z,ºù¨G]‚?U	$$Ã7æþM¨þ­®+dm{Iî½®LŒ^7)k2”½øÒ´€8Û™-¹ñõ CŸš.ËSò7d6<mÅ;-ùN¸¿Hy9¥“G†¯Kànéž7.¿®ç¥r7õI­–snò¥(ÚRdIþeáü /#AX21ø¤m^î'¢=zÙc÷ÜOì¼%‡€™ÙÈˆ§¦Ó%!c†¥o	K×;ý÷yåø¥x˜-
ÏeÞ–<NªèX}ÞÞä01Ÿc¥gDv³)tìóŠ, ÿÔÉýIîR‹’ëóem(þ”¼WèäŒM¶ø,§˜ÁsÅž«h]’¸®ïˆ9¬’JVmÅHm	_zï˜C¹½ÌðêZGóŒ–¢ÕšÇLÍ°PÓÙÏó1öš&•NØ3¥“„xÇót‡èmÐS¦f1“ÝW5â]ƒåPjæË†ÈC—Lní|{É÷à:À	tèïžWªjF®¬ærÁf­´w¯~ÂÅíÂt·äÝH¨HÙ•'•×Öè¸´,Rw•éÔo?KÈ±´	¤b–Úb3’s-ˆ­¾ñ–š·áXlŸª5W(î\Ì¿fÉ)Jíö«NˆB\½6ì™DE!x
{ü…N,beåJâ-¿dC ãî›8Ú´33À9ºD Z×ö¤ÚäP»®±¢ã–ã8KSB0"Z«ü84§¬u¥Õ±'ñ{êUPi+ðO÷8gQ·ÊWÈaÜ„eqsÌtÂvô…åG6Çõñ˜­üvÊ¾ƒ=ŒÝœbj&Ìyê‡íJY[t¾¸ÅÀ¿»ÔûÜ¥’ŽÃ5%Ã-»SÜ
¤¹gÒèz'ÎÜÄ”øË?våÌuL¹²sÜäLD…Û(C.úOš>Å¹Pi,GÛã®Ó”S®[×n=¦¶|ñ'6ÁëN‘f‰i+DU-53iŽEðˆ.ˆù „KÕåõ4„¤³_WÒ.cÜ@Îº½\©æ%Õç;ƒjäeR,1øM .)6½®XþRË+Ñ×þËgh{¾á3ü4ºŠzs§@þT<ËB±û™ÛZ¤ßZzug*†yÈÓu§5Ö|éŽÂiŽ7›ûc¾{¼×'ñÝÖ•k’ëÝn;Ò›²Ûî;¢7‡°à^Ã‚HÓ!¢¼Žä5þâ†K`äi     0707010001f0e3000081a40000000000000000000000016a102a890000042a000000e600010003ffffffffffffffff0000004100000000root/usr/share/doc/opensvc/template.service.expose.envoy.conf.gz  ‹     íXMoã6½ûWÈ5	zÎ6{)Pt`±ÀæVM,6‡%)'úãûHJŠœØNÚ:Ýk›3Ã÷æ‘Cžó³8Gü$sôà8Ð¹ˆ½#AvÃý¿2wÜèŽ‹Ýâ×²Ø3¯mE¿->äE_—e/)þ;êïÙWWa(Ó…H~©ÈG]k%#¼8â'á;¹24y¾õ¥OtÚÓcH?Kró¼ÑA³Õv}µÕSQ-;Wñ™mîÐ¶!¯£´jpdHÖâ£hHVc$  ùÒ©´jŒY“Å\•†)¶Àñhßè3~pÚo·7_Å5ÑªDdLoÛÎæFq¯c3b"*ÇÚÆp	cv!¶jæ—¸ªd”'Žžpô{`ûŒ£¢xDY±IÌ8£¥"AˆPò,@î['þVÌ1D/ÝLðÆ~h„ëZ¯EK!È5…KñEú×¥ƒÀÔNÅÎc×1úŽfF&þÏ…ô$d‹†âÒSmHÅìFb²4SÒ„ˆÄØJ˜û«Åy½Áì%F,kmÈÂå÷8³ü¸a˜,¡ZmÉ‰ÀWTÈ,@L0)2ãôoŠ}#‡¹Ä¨¼Kä¤;IÿÛHÿ@
``ãŸož;lóÛú_õ¢eüžV;$AÉœçüÎ—5Òë^ö'™Ïr!«l4"ñ mÄ	mà?4Ü™J4ÒV†
‡¥˜ÊŒCânVKÇF«÷‡<rÒVø»Òö˜*–ºJEÍÕ37âÏd4ÄeŠŸBÄo­ëe#C“¾c*·øÂ^¯µ•fYa¬Y¡¥•kC›ƒáÛ°a	…IƒåÀƒ(eV'ÂÀë‚}ü[ÀRV•w'Z	ä1ç¡Ú‰´èàò­4çºÀyJûk•¨=·…ƒ9¨àá@˜/ÑrºÀç³ê³ŠF\·izý]ešâØÇw)—!WLøxù–ì J¤5ù}JÛµ+lgO4”‡e/(e7Òt$~:Ì
ŽdÛ;OÔÊ ¤·\T>Bv‘/¤1¨„#pÙæ:»½ÞÅÊ¿9ÓcÇÿHx{©q:[åÂYÀ? °±²}wÚŠÊ½e™ó¨ºÊ=Ç|¼ì|ì¼Îî=X}:[^s¶|ýüIò™k³|øã¢¥KW,dà€Gk áå#Ý›0C«O&ÃVÇêŽâh¹E‰'Bç²Xpše"AÑaálñT"¼ŠÆÔ˜ä’1Ã:†×]‘Mu ùÏ‘§¦ñoTÜâþûŸ¨G!â-
æ_È¸\PÜDð¶«,ÏfºÚú©o$#œ‹ûF«f~æaÓ0¨‹í…Â?©Ä;Økä&Ý†Ò G>½]A–r‡þàµÏ–Ó+I[}ríØŒýå«Ð	Î‹¿ ¾F„ü    0707010001f06d000081a40000000000000000000000016a102a8a00000248000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.node.array.hds.conf.gz    ‹     í˜ÁnÛ0†ïy
¹f1ºíÔ.½tR`Û¥Ý.Ã°(2«u$M”³äíGÉq¬Èi.
Ô%±Dý&¿Ÿ:Xãñc4†G’S!¨ÝâÎ#Ô%ý¯Ü°ÙËnô#;Æ–¸ý9ºÊEÏRÙ£QJþw\(/9,åù7ŽÄˆ´ójÙàáEŸTC˜VþnMÀòéJ‰•j›xÌí«³Ø‹!pÚºEò¨Me4\Ã
-£sq¬@š#îk„ùÇ;X+«V ·¨Û˜Òè %œrøÎd
çcÁx
~,ææÆ­yWyóùö¯‡€¿¼Šõk„–ê‚ÚQ4v‘'ÔF±UÓ¨hœÝL?ULq'§âaÆÜóêY'Ésl­ZãkäšêWe¤ù€Ná¶ë"øà6¦Är#^*ý˜Ð¦8Bï™ôƒÚª2ÛL6«Î`þíîû—éÅÛwïÏaõŠ(M½,ÚûÐ>GÇv¥ôeãVÜ²îOKP
>¤&¼æ´*huU§`QŒÓ¬Úý‡uK–ÈjÙ‘Å‚vq½XdSÈ+VáNïâjµ9õ©ÛÐ§Ç[Ø–ìÚ!ãtr¿­È]q!Šb (Šb (Šb 8mh^¡w\Uö³{Çû‡ÆLÀ;c—ýuû kVé“üÔÝÇyâ›TgPÇèé²HDûÝÅY¸„áåï/ž‡ð¾´'§£#Õ¯Î 8…ð>ÂGøá#|„ð>Âg8>âxp"  0707010001f023000081a40000000000000000000000016a100daf00000a96000000e600010003ffffffffffffffff0000002400000000root/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>]
          ~
            run randomly in <begin>-<begin+interval> instead of <begin>
          <begin> <end> := <hour>:<minute>[:<second>]
          <end>
            default: run only once at <begin>.
          <interval>
            type: duration expression (default unit: minutes)
            example: 20m
            default: run only once in the timerange.
       <timerange> := *
          same as 00:00:00-23:59:59

     <days> := <day>[-<day>][,<day>[-<day>]]
        <day> := <day_of_week>[:<day_of_month>]
          <day_of_week>
             * iso week day format
               type: integer between 1 and 7
             * literal format
               type: string in ("mon", "tue", "wed", "thu", "fri", "sat",
                     "sun", "monday", "tuesday", "wednesday", "thursday",
                     "friday", "saturday", "sunday")
             * sun-tue is interpreted as 1,2,7 (wrapping)
          <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")
             * dec-feb is interpreted as 1,2,12 (wrapping)
          <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".

  0707010001f078000081a40000000000000000000000016a102a8a000000e1000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.brocade.conf.gz  ‹     ­R1nÃ0ÛõŠ<'Þ´cÆ.‹QNt-Ô•œ;	i~_¹qœ ]Åñxà‘”š¦&LC1Ë$²u¨%W×]ÝîÌûöÃÌV¿p½DqÝã¢ò —GrSs-Êq²§ëµ½u>D‚sö÷ŸqèmÓÃà[¸‹ŠwÈÔ	ì{ÏôJŸÏ	‹‚rÙ8,¹h²b¿‘ ÔG¡4€:ÞXN>†î8e–‚Žbù7fKàéi³J«ƒ´.r{ïlU\zÉþl‹“Ý:{)kÑ˜_ÞSâ`     0707010001f0e7000081a40000000000000000000000016a102a8900000e13000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.service.fs.docker.conf.gz ‹     í]ßsÛ6~Ï_™LÇÉœ¬ÊžöE3—»¶s™kš›Kz÷Ðé˜	I“ K€–Õ¹?þv 	È?$År7ôCj“ÄØïÛÅØeŸ>=äÏ“§ì€?(nnFÌ®kÁ
_ˆæ¾âÛ»ÃêîÉ¯só´‘ªW¿=ùŽ}æ‡ýä	vþB¬Wº)¦}ŠF^ÂÝ§ìø€?¨&“ëšÏJÑ½ë}Ó
¼Ñˆß[Ùˆ¾?òÒÐºÑ—ÒH­¤ZL“;…˜ó¶´}¿KóïHµ´\åþM¥àsö’-/B7ƒ1‹ÆÝ7µÈå\æðÌB(h›“fà&‡'Þ/;¢\ê²­„×³šµF°¹†_á™FÝ6¹Cëïü#g¾[·¨Z×Æf®ÖJLÕŒåZ²"ùfYŠ«- äàVÞF'u
ÏN³ãã’ÏD™1®Šîü'CØ‚´3F×˜>3òqv2™T£VøËÄßB[:³U=7þB!.eî/Ýx­ë¶„n¦ˆ‡&ðR{o¼ÈôœUºhKa„5®Çt—Í%\BÔ¥2–—%ü—€wì Ê4á…s¹¨x=EHý½(õìvù^ÀëÎkmìy§¯/Û`#˜^&«
-	ìÎäDÃÐL\‰¼Åó¹©=Ææ˜óÍmšu#«¤KcöCÓèÆ@‡ABÓ¢Xâo"¸· XÏµÞNàMc(?Ê@Reûa0’˜½!„T1[HÖ(cÝf÷ÔËØW]xÆ2u}ÃÔõ>øµj˜+È,#Õ~¨UF"vµCô³/¢3«ñÐáOŒÌhÁÏý<pôÙÁ!öy“¼oð³Ï^°¡ÏÁìò¾±Çcø†ÈçáŒòž¡OŠÎÝ˜b‡…ú(d‡ÇA0|üþ!ÊÚàö*è¹UÐŠ¼šL—÷èðw¸; ´±€4#¶ZÊ|*Sø0@«ã÷îL[×&¿Zèä-ù%nñáµhh—W1äG¢à­kÆ;J[è_²ÖøêZ+#AÇ¸L$ðpæ·žzHƒ¨<:›íîÜú>{² ›,@ÙfÚw“ý¯ÁµMÛ™ð¹Ú0u¯Èžl%â™`r¡ÀÎ3P…æÈ¶&Ê˜ek½Rñ–ýkkðÛ¤/W|m@0’e™úšgÙ˜±wÂâð÷?üøê—ŸÞˆŽ«¥.c²„w‚ ÐÇqÜßp¿ÐÂ¡–ú{Gxà6æï{Ì÷°MpE&6 àb!AÓDVÛ6 6[6 ®*¦™®Ø‹šÛåËÐ½´ã&xÜ£‹Õ4óƒ‡'aP¦·¿h|-Iº±åK®¢%ÃKß,”{q§wá:ÀÆx¹Ë`bŽ1·Ø™P9¯ÿdVö 16d=8É,³Ð:Ëè4„lø/{„£¥¡J¶âÊ"w#ðsô˜Ði&mxœJÚ¥#±žË*À‡H¼4ÃˆÁ€Q—z„ñ‚ƒ¡¡T.A'Ž@ô¾mô© -«›@‡òÖàpƒR¯ùlÛÈÅ8Á‰:à¤daÒM÷=ýt,Õ…A
™¥nË¾wîIˆ'‡2£kèJÁ#¼Èm$p§´tÚÿ¸X€vêŽ¨¹¹€ÐÈ¬UnÒx	nÎv¤´›9ŽFL”x²}hmÐ¦)"eõ^.ËúˆswÐ~ÔgÁJMýžµHEðÒÊ	€¿¶ð{æ.`ª*&r\“Íû(´Òˆt/ö3Öµ¥#`ÇŽø%tÔ5½a_úvþé¨ù‘@ÌèŒÙ¿Ã»=0·ÃoÓ,`áf|D-’’ª·VWÜÊœbÑàV1êñ³_Œ˜·%òàF)¥¼€I¶­jwÆ½6VT†X#®xññ¸ÏT }€U·e+,Îgå…Ôç+!Ë?Ùná¦˜P…é»Ü“ózSÚ¯Øçßn4)\”.0—*²¨¿áz”½~C,'—àkI»cö^¶ÐÁ™°+!;™†˜r2fÿeÄ—n°tŸÒR´€ßNî@6¯Ûóß[mù ë°n$¥À‚4Ç•$,K¥Æ•/t€oh=6¯sŒ²½c‚AaXÏ†yæÍ¯Òu°ïÄáÎ)hš]â¶È%Ò%ÑÅd‰ÀHVÍ^	+š1û±w#t×?IÕ^±œZ€sÈ²ã+p1_ÊJÚt€¤ã¬ÖF;7úãR8ÙV30AGæ6è_`JN,+(Dð|‰º“]Yöíä«¿Â½®#ð7
Â½ Ô%ô3O]‹ÓÍçíJ»çƒt¤&öÓ;¶˜€Y‚×6ƒÂµ9':ë	#iç¨ðû@Ý.\>™œ~ÃœþÇìµb<^ÔíñL·8ÛJÛ¢­)°eZ¿Qxm±ŽÉ•[ú@t)“e2½ùRqNô©dÏ0B‡‚YK\ß:•Ð6E GÌç•%4t£	ýHèZ!ONïfßÀ»}ï+Ô|{ðƒ€ÃLâ¾2nŽ/‚GE¬V‡1.‹ÙÐ=ÂÏ1lÀ«…`S6ŒNñÂäø´‡ÁÄ·ƒY‰êœ|é€è¾ž3l¨ 37»Øy%  ›5{&U²äçÎÐß¶öøíüø{îÖ·`¾ÜAq«\·?‘sƒž"^c Ã¨¹1 Ê`åÚÎÈ'ô³…ZWç¸ãÑèràÈ!f›Wl^ò{6As=yŽ„{”/ùIÇ#@'€ýd\=q$Á\4÷r
þ|äÖUN>·°sÇW¸¶ÃDtdc$Ê‘èÙO„y·©#EØ/{ûöM$ÈuÒ“÷í›@Y˜8Cç Y˜kA}T¶öc 3TÓ“’Ð3ÓÎÜ¢ò;EØ¶•fhÀV°“˜åkë'[8nV¼®¥ƒ(~Šã/g½V}HŠ’Õ@ŒZÔâŒ}³Å3X=\âMŽvÑãs­7Ñur
û%ˆÛÖ ärˆJ>‹¨ä/hš¤þÍ~µÞÚf_Ð¨êÞæùaÏdÁ‹\Æ!.Ô¶Ä8CyÌ')ñ;í·&¿ìS3ÔÄ|Üš˜°Û!)t(„ù”…0»£¸S*èPýò1«_vo[èPòòéJ^v‚pç´Ï¡ÎåÓÔ¹lEq÷*—¡¸åã·ìÝ.QÌPÑò	+Zvq· f(cùˆe,»a·5†jW>YíÊ.îÂ<Vƒ°Ï1Çšòä(çBÖ©¥ =4H”ÔŒR«5cö_Þ8eü«k%«º”˜«/ŒmÚœòn\é}3ÓÃ*Ì0Ä=¿_"VPc½6ýî$·Üó§g)yuÎ=žïhàa\Àj)F5ÏÅ±˜k…é¤áóD˜ö$»PUÂ†ç¹ KnZ¥Ò“8ÎŽºñužàU/†Nø®jÜÛ¥ã€$êE#‹—Ï^P¢îËÑx<~Žõ/¯ç®ºÃ%èJZÌé‚ÇÛzd,Î×me 8’Õ:éÄåpŠ¯¹µ"&%OGŠ;ª¨|:÷££Íä!«o:Ù{Ê#B¦ýIãÕé¼Ëõ’YYÖýTž¨L5ãùEÈ8ô=¯:IA8–môç
s6M“¢ãòœ•ºrtm2©Ç¢“zÔ%øS•@B2|bÞè?„êŸêê°BÖ¶—äžë^Ó}Ï/7)k2”½øÒ´€8Û™-¹ñõ CŸšËSò7d6<mÅ;-ù—pòr2J÷!—ÀÝÔ=o\~;ÏKåNê“Z-'æÜäKß6Ë’üËÂù^F‚°db&ðHÛ<ÜOD{ô²Çî¾Øy!J ×3³‘O!§KBÆKß¦®w>úïóÊ—SD1ÿÔÊ¼-yœTÑ%°ú¼½Èab>ÇJÏˆìfSè(Øç5Y þ©#’û“*Ü¡;%×çËÚPü)y¯ð’36Ùâ³œbÏ{®¢u!Hâº¾'æ°J*Yµ#µ%|é½K`åö2Ã«Í3šŠVKh35ÃBMg?ÏÇøÖ4©tÂž)$ÄC?žÇ ;Dï‚ž25‹¡˜ì¡ªï,‡R3_6Dºl`pkçÛL¾×H Cð¼VU3re5—6k%Ð¤½{õ3Nn¦;%ïzBEÊ®<©¼±FÇ¥`‘º«L§÷ö£$K›@*f©-6#9×‚Øêo©y¶Åö©Zs…âÎõÇüa–œ¢Ôn?ë„(ÄÕkÃšIT‚§°Çpbûk3Woù)‰wßÄÑ¦a˜ÎÑ%Ñ¼°'Ð"„Úue·ÇYò˜‚ÑZåGÀ¡9e­+­Ž=©ˆßS¯‚J[xºÇ9‹ºU¾Bã&,‹›c¦¶£>”kúæ*¨ÇlåW°Sö=¬aìæSc0aÌSß½¸k×ÊÚ¢ýÅ-Ö þ}X¥>ä*•tüV¨)îXâR` Í“F×;qæ6¦ÄÿØ•371åÚÊq“3î¢¹è/4}2&Š1r¡ÒXŽ–Ç];§)§\7¯Ý¹Mmùâ6Á›N…Ý0Í`…¨*°¥f&mÃq£þÑ1€p©š¡¼žºbö³ñJÚeŒHƒÀYW¢—+Õ¼¤ú|gPáÿÑÿ?Ðâ’bÓ›Šå/µ,°}í?>CËóŸá?0Ðè*z›Ûò»âYŠÝÏÜÒ"ýjMx«ÛSÉ0¤ÈCž®Û­Á°æk·Nc¼ÝÜó	Øã=>‰Ï¶®“DXïvÚ‘ž”ÝuÞ=9„Dš~åMÄ ¯ñª§¯ëÚh   0707010001f079000081a40000000000000000000000016a102a8a000000e4000000e600010003ffffffffffffffff0000003900000000root/usr/share/doc/opensvc/template.node.centera.conf.gz  ‹     ­Q1nÃ0Üõ
žïÚ1c—ŒE°Ô¹êJ%!Éï+7Ž «näÇ»cÓÔ„i¨"f9OP®%W×]ÝîÌû¶Qç-.fvüë9¨í‡£°yD!71·%Lü9b½¶ç1Î‡HqÊNaÿ3=ç1=¾»¨x‡ÞÈ8A\ï„^éêä/aQˆR6K.šXù¥	êƒR@lX’¾;N9KOGòÁ‚nÌ–ÀÓ‡sÔ6¬hmöÞÙª¸âÕ'¾l‹“Ý:{)kÁ˜_¤ì(5  0707010001f0e4000081a40000000000000000000000016a102a8900001333000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.fs.btrfs.conf.gz  ‹     í]msÛ6¶þž_™NÇö\I+ç6ÓY7Él¶M÷vn³ÞY§ÝŽ‘ „5I°)Y}ùï{^@,Knä$Þ2ÓÆIç<çÀ9à'ŸóÏ“OÄÿ ¹ÄDµ.”˜Uebß™Üq{w\Þ=ù!±Ÿ”:ÕÍO¾ A¿àa?y‚}¿Vë•)ã‹®ÑBE×“RÉnø9e#SÈYªÚ÷}-S«ðN©~ªu©âí;Ei–Új“ë|~Ü‰U"ë´ê:ßÞÑùB•º’yä^•*™ˆ—bÁÃ¢~(W%ß·…Št¢#xf®rhác‘ÌcËJY÷Ž·e­Ä¯Ý{"“/Uéõ`f¼*'ÞBÿl·¾I„U•¨Œ˜N+ 0Šj¡r× ˜9ŒÜšºŒ”°•¬jËÀ'àñD§ÊC˜]ÛJe…Ã)mEmU,VH[a›æI¸™™:¯à>o{Ô˜|óè
ø¥|ª±¶(ªx$Þ.àŸÀ`_ZÀ¨*U aZš#HƒXl¬6Þ´ÑýqRéL™ºò¨ý‚Ì˜¸ë¿kÄ0¿dyõÛ	ÅÆŠÄ¤©Y!'#	àÒ©®ÖÁ0ãRƒ”ì¨áô(±£æ8Èéô¶ŽK<J·0‡ÙÍì•‹oš°•ˆN€''?>;x„Nl6ã[ :úùç‚ÎÝOéZFÍõ´†.•øŸN¤Š?Peøä€i<š˜¢BpVRçºWšéôl¨ü¢7ÆñC«åû° ¨R·€æÆ=ôÿïÀù£©¿§ÁYj@-):rVãÏFÕ29w7ÆVˆÇPÝ¼gÁZŽÄW:IT©òª¡Œj6Sd0êHsOøºf c öî%qµü^Íe† ´kýÊn€ cv‰y^šºø ‚þ=–þ!%M¾‚É±S§1
Ä¬àaÔjäw†ò‚1D•)×#ñZÃÅúâ‹¨Î8‚Ä®³™Iá÷Ä”)!¿áÚ™]bÉHà½ö9™ºˆÂhøLn©Zj}ëñås'è‰ìa¿Åbà
ôÄ6l$Gr'+s3)J¥²¢šÈ™)OKÐ8—…Ê¯¾ÿR¬tš
Çtè•ÕCªr))*[5Ò3EÍ“‘É2Œœ $¨Ðÿƒ1“íM¼Aÿ£GâÊdŠIW²œ+ßùè¬HU‰^¡@?ê¢€¦ Ž¥ÒV ¨X‰SeoÎFÝpÂøYî
Œ$‘PÕ@\\¯.¦› š6~QBä	ã„ÀFR•Ã6’žI«Â›„ö=@&ë­‚g!ÚZ’ô–ëküãB.ÕH¼‚0ÝÍh
dŽA'"ln"˜"øDIJøoÌùçŸ¾S0%\ë%ã$ó•Jt! ìè+%}k \Ã¸ƒ˜ßÎ<¡¼•×@§(µþ­…ËDÏ"ˆG‡©Zªï‹Y
·L%1ô]Kšþ4Ï²x©Õ^Mw»_uxO¤ý…»2" ”p;|&N7§½ê&‚Ù'L¡À¯9`¬”(äéúŒäö¿»°ÿzÊµo¦%8'x+]ÇYôµÅU±×ì9ZŒŠ×7š§QI@|©¶] ­b/Ä³4^\6(«a]ÿ!T»b%->ï‘JahLâ*EOÿ%¾š°·5¸ê/JJ“'ÂŽ!£`Ð¬4xÝ#730Pš§Qìty4ÎÎ£º½]Î®#±ÏÍÙ\«V½EíÔ…ðçÏè­˜Õ:u<G–á°Djæ:'¶4)ÌñPÚú¦€
&sÝ58ý7.8/oðïÑhtÆ/Éº<¥Æù|hRÝÈÅùøÓfÚéõe‡€‚˜®¨dñ…à+ºIëYþÒas—òJEXšq'‡*çµ¬Ò®óhH‹Äø[«‹v“Xk"ôFÍål†Šá×€j©÷VöüžÓÎošñ¸.ÉÊmOÍäÎê¬ðYG.]ò²(`1E(€´
“Ç#ñ/\’T7v°ªÍÅf\ÊTóRÆ ~øÈ¬d™`Ž­£…Èô|/¨-ú‡·—_
lt-AZj\x+ÃÁ“3 âÙ.< ñ²7Cž´‘!ºüD/x!Jp‡P~æñõBi„‚¸ºÊ–•¢Ð˜Æ¿š;þZUà`^$q%u-U«]
ŠP	Rz×6”-øÄ-Ö|¤‚jšlÉé‡ÒŠÚEªnù½ŠcŒ§í8††®–~¢6|:³·Y0ñ§KWèÞpþCPÀøP¦´™ àý•@2 Eì®EÈëÄ~Ä«ø^/Agj2LÈ-aòZ¥cæÿˆ#ÞÜ>ªQ»ßÄ“kGQœó6ºšÓtÞ­,aóH\úŽ(Õy}3 ŒÝ /ƒÐ¢Ü³†IØóüå§¿|ýÏ×¯ýÇ÷W¿~ÿ·ß8và(ò|<ÎvÉr9ÿCKrC`dŽœÀpVÝnb³·ÇØ†ÁLÚÃMqIËùÝnˆfÛ0îIal5iIôCãxÚ5wŒÃ¢R´
 nT„“6™T*\™ór%¥Y\LÛ‘L_‚˜¯,DFÀì±.xÌÝDÉíÌ}ÄIk%½(¯(}+§ë9÷#‘¹·á¯<žõkçGVJŸ·Ów¨£q?¹š>eãHšiŠc(¦)î#¿:ï}å©¥ÇÚß«•‰C…Zª>ú¹¯Dg
fê¡Ã_2÷fü¼›$ý4¹¯ûØçaTò]ƒŸùÜK¬}ès4½|×ØÇÉãâë#Ÿ‡SÊw}BéìÙ€«÷“¡|Ü\‡ßÿ©´ "àsC+²j,0“¶ËrMR
¯¸u·Óê§WZ‘ãc€ “is·I­,”)°¼@.)ñ(T‰œ ÀôÃýÙ5íQ`¦Imk µvÛÄVcf ‹7-Xœ óÅ \ïò_–ÜûÞ“²^u…C­E¦d¬™zžãÞ9
3È_ª‚Œ]ÔUlV¹¿õMe½|™®ä7&,@h:Íÿ$±²I\©
 ¾zýõ«ï¾}Ë•«…	kËÜ;ƒÊ/¯¿ÍýØ(Jý½#<˜R·7ý³=œRbSdƒüâLÅ8M`­êøQG +ÐÈeñÅÔdâ9nš¾lº7¥t4Ü„ó)Q^•ô¨yÕe™y…\ÈG=Ž-ZÈ|®âA0¼ðÍœ43íøÁ¶ÌûSîò¾Ä,•G²èSè¢lW›E—ÆéX˜iˆš
ÁEUX4§n(Þ¯üC‹c’EÕ<N’
Úp	¡ŸS-@3J1àÁÏ\³†W˜S¤fM9Ÿpå{X¸ˆ ¢÷íƒËÀét,k·KkÚ°ÙU©çs…yX0R:Œí’tZQtîXç×!Ôåõ8ã„è¸WJ¹¬tM©Ë¨r$÷€·ÅeúèP€zÊ[ÊÒ^Ch„Iˆ6Œ—àð<'¹aÏq2
è‰äØÚH›\Dˆ.„N¤Nk¬EÆ¤gÇ}¯Ï‚¥†ú=««¶`gNav	,ÀO5¨;–Tà–9ÕMlRõü>®”jŽY>à¸-Öû ;Ä‰\BG¹!ðûÒµsOûÕÅ˜š
1£k0ÿlÞí„ ¾~Ãjî8{|”Ú-‡ÌYW&“&€¿nÌ*F=n€#ñUI"n¥’êkp²uVxÛ–P£n$–&‘ª´üzqTí*I™Oféµ6“•Â<Éÿ®ùì=T˜Êc{±™VÌÑ¥ý€}þñV•ÂIé“?=ú+%ðs	CL)“Z0wGâ{™ÖÐÁ™ªVJåâ|L2<Ç#ñÿªÌUº=}Á÷©%”f¤ŽïlTÔ“ŸjSÉ^¬wŠu»\,¢2LdÆ™/¦2F†ëuAçMDl˜`PÖ»\ð›Ÿ†óà¶–Èi#M3kR\Y"’UmLªNJ™©J•#ñug(h®¿Åü$Q0ÓéðBÅBœÌ§:ÓU8 "IQ«	ýá†NÖÙŒSv3•Æ*€lÃ
È–!JFä60éšNŸ?ýÜk;òŒk p- y	ýÃSnñtóùjeøùFZPúé{TÀ.ÀjÛ^ŽaÚØHµ…w|K“qß¬ÒÀåóñÓÏó$¾	«]A4Ã™©ÑÛêª¦¢Ð‘ÜÖn¡pk²ŽüO} ºÔÁ4™Þxªè›¬·¼RXsÁ&ª5náXBË8|<¯8”0”“îú [!ÏŸÞ¾wÛÞWÈùVì9Ì4®+ãâá¢­ÎDöŽÄ—ð÷FI]÷ˆÄ`ËV¸*]ârˆ¸ãÁùà)^Ÿv"Eaâ…ÝÂÌT6![ÚKô¾–d+?÷5©¹=DÏ;Y‚ °^û4(¡Ò­3VôËº^&Ã7üÜ5ÌoA}ç¸‚Â³\^Ÿˆ¤Uá±OL¥V6ZÞJ›•|Lö Ã˜l‚+¥I{ŒÃÛ¼I*çâtŒêz~ÆE¼Fiñ’[dôÑÒ÷.¢j3’¨Ü¥©<Ÿx^ÅôeUÑiT™8Í)D‚g»?ÑøüÝ_þ¥ŽÄÍzÙååwÒ÷òMYpœMç Yãkµ«Dvcà£“6*Ù\Ïl=ãIå8ŠfÙVWwBÚ¹rµ˜®¾ãv%‹ëö{Çvˆã¯W]HÕwÌ|©y-^ˆÏöX¦^V„8•ã2yOßx×¹ÿžA	ÊmoP²ì£’"*ùÔÇ‡M¸¬©}³›­×––Ùç4j*ºÍ7÷ýxfÚX‘¥âàDmOŒÓ—Ç|ò·Ò¾3ùå>Å1}MÌû­‰9Hv$…ö…0²æp)”
ÚW¿¼Ïê—…·/´/yùp%/‰ðà´Ï¾ÎåÃÔ¹ì•âáU.}qËû-n9Dt‡D1}EË¬h9Xˆ‡1}Ë{,c9Lv{c˜¾våƒÕ®"Á{„0U„±1Çšòä(çZ¡¦‚@:Ñ PB5
µÖŽÄ¿dÉÌøGÛ
O×˜«¯lUÖåÝpé±…áffâšßw*¨±YÛnuRVÒá§C1yt&Í·g†Æ¬`TÈH-qGé¤¼JŸ ŒøÀ.Ú	ÈFF‘M.ë<wâ¤8iÇsÒZ‚WÚáóN
Ò ž—:~yúœu_ð°U¬Áóùð
ï%˜Oí1×«.¶B]ôåÿˆSw>¨äváµ|0SÁÓ‚âŽ**—Îýè`3~ÈÄêÛvöÞ6åÁQÕŽ}€‘Öº<Ï_Ò¨¶q5`‰ÒQ3‰ŸÓ2~R¾‡«–RCË6º}…D\ì8@Õ%^·å>ôe… ‹v>ŠA›àOUáYÁðDRšŸUÞ=ÕÖa5YÛŽ?×¾ÓànÄ¦àƒ…¹ìÅ• øÙÎbÁg_Ç€†>m–‡à/ImdØJ¶\r/‘n#ååxÞï>B&Aºµ”œß¾n•jµ˜ÌÄF×©šù—Ýù¡æë1žc»¹¹vèhø¾Ø$V©„	@ìØ…O!§MBÆK×\×•‹þ»¼rü0f‹Â}Õ©ô“*ÚV—·7:B%	}D£»Ý$:hôs‹ÿ)‰ÿI¼©%ž’ésemHþ)Y¯æ%/ÄxÍbÆô–kïyÍüi‘éœŽl&¶xé¬Kƒþ‹•Ù­†æ´û¼ŸÔ)ÝLús6Â·†I¥cqš› !úqæ%z—è)S3î‹Éªñ]ƒå¦ÔÌ•‘…¦Óh×lÛcL¾Ó@ƒ~ÕÈs«ªfÀe5Ë9xwW¯þî¾ÏÐì’ûGDWž”ÞZ£Ãitº=U¦Ó{»QÁæ4PÌR›oFrÜ‚åÆû>hÐ/‹Ý£jÅÙôûø`–}·£ñ:Mâ>äK•QŠÝ?P€É¢ì·<Wou:ôåîš0lê†`ýï:ÙGh’D«uAGÔ‚¥úYò˜‚Ñ:Nð\SÊZÏM>t "|_8d¦âã»î~Î¢û„ªûÆ–Å%˜é„íèÀ‡t=K°@}4¢ôQòâ+˜ÃT›C•Á6c¾pÝó»¶UÖæ­/îÑ°ïý,õ!g©ÄãÇ0CÁpÇì§=h4¦83»âþq(fnCÊÖÌq3î‚™è?hú¤kõ<c9š·í˜SÌ\ökw.SWrþVÁÛNfj6Zˆ¬]*gº*%.Á¿›è‚‚àTÍ¦¼žºbvÞ˜¿žÙõ
¨¹¯a¶tuž¤TŸÏ
E÷×›Ðä‚bÓÛŠå—FÇX‰¾v‡ÏÐô|Ãf¸ð£cÝÛxÈ­ŠO§M±û÷·p‹Å½•×T¦RDMž.¯Ö`Xó'^
§1îV÷Ç¼öx·Oü½­­mOÖ‡ív„;ewíwxOöaÁƒ†§CDy0Èjü:Öçåù…   0707010001f04e000081a40000000000000000000000016a102a8a000000ea000000e600010003ffffffffffffffff0000003c00000000root/usr/share/doc/opensvc/template.cluster.patches.conf.gz   ‹     ­Q»nÃ0Üý<;ÖÔÁE;ö2¢PçZ¨k9¤„6K¾½rN‹®âÀGïŽu]²ªš
ÖB7ÛÈ´]Yue³«^¯fßªEêŽ_A\w¿¨tiD›‚µÄ¢f»±^{±£.‡HpH^àþ#½Mc¼<ÓÓ˜‡Üo¤ ìrYÒì{ÏôLï˜ žÏN3“rÞØ^ýå§‹ýD„P„â ê¸±}˜ºÝœt8µ£)8Ðe¾¡-ðëÁI¥ÕÁ
Z¸½%·ò­=NÑ~o²ŽÇuöô×Iõ¹´¾\$    0707010001f0a8000081a40000000000000000000000016a102a8a0000014a000000e600010003ffffffffffffffff0000003700000000root/usr/share/doc/opensvc/template.node.stats.conf.gz    ‹     ÍR±NÃ0Ýó'u)m31•	1²tD¨uí±šÚæÎnéÂ·c'i„2pƒ#½sÞ»wÏ³Ù”•Í`ÂJtì…çéè¦nÚÝe/­Ù×,ºÇóÉ’*¯zJ³Ø5{ó	+í„¥u‰z{'! |šPýì(¬Dhüu¾gkðB†GGêšìPêJKx€74HZ¦kÒš#ÒèÿF³oGj–ìü‚\Ce	DÿÞÈ7<lBÒÅÃ‘•9ðI¸z…Ç|v‡l(‡]cå¾ÿ¤kPñ&Ü.¢ò}¿`Xº­HöK",kTáÿFòYeQÌ‹»xþ9šQëÞ8Aâ€©ÍÁ×¥œéµ5å6åÓ³cB‡/`8Ê 0-¹„Keåò²¹o øl¼øhó°Õw'Ù¬j>!´    0707010001f0ed000081a40000000000000000000000016a102a8900000fe8000000e600010003ffffffffffffffff0000003d00000000root/usr/share/doc/opensvc/template.service.fs.tmpfs.conf.gz  ‹     í][sÛ¶~Ï¯ÀL¦ãdŽ¬Êžöá¨I¦9Mrš9MÝiÒö¡Ó± ”0&– -«—ÿ~v 	Ê¶.‰×-óÇ¼,ý¾],€]äáÃ}þyðíñŠËÌ€Ùe)˜-ÊÌ|°¸ý¶n¿º{ðsfVR¥âê—_P§Ÿºn?x€m¿Ë…®ÒqÛ„d.’‹óJðnïñjÊ$ºäÓ\4ß{Ås#ðN%~­e%ÒëwÊJ_J#µ’j6îÜIEÆëÜ¶oîH5•´\%þS¹à{Ææ®[ÔÁ ã¢r÷M)™Éž™	ï&øXÂU*Sn…ñßxWÕ‚ýÑ~'ÑêRTQ¦ZÃ§éÚg¸õ:cFXf5›L,˜L˜ÅÒ€™AÏ®«D0c¹­ Ÿ€Ç3™‹ˆafi¬(‚ãQÒ°Úˆ”-P$¾…ï„'áf¡keá>toGÒœøðèô%b©©4U:dïæð+èÔ——Ð++x0EÒXô ï4ÀàËbåK+Í³#+¡kIû•qî¯ÿIªaÇû½PöÏ#ê‹'Œa™Îs½@M&È%si—ãN7ÓJJf4=ÌÌ°¥9vr2¹©¡¤’HÒÊqj‡×Ì9—»©p+c?NŽ|v4ˆ™bênéèç¿K÷særÿf®ç54©Â_b9‰(çî²é£_€Ótt®K‹ä´\*Í«ôdòx¬ü¢í78Çã[A*.?†@“ºÑ„;Øÿ· ù½™dÁHi®Á,A)2ñUãÏ`jŸùsm,ò±knÑ³à
È,‡ì…Ì2Q	eƒd4³© ‡Q—À$`š"¶5³÷AŠ‹Ë/ñªâ’Ð,ú+Än Ð™Ö·Á\äf3áÂJ-áo°íJ4`5ÎÕHÖÊÛ¨]´Å;ÕðûŒ¤‡W1hZb‚Éc­Ue<"Ü;}~¾Ï¨d5è€@‚£2¯kš_É¢†ŒK /¨ô[1î†Ó„ç9’FÅR+æÂ¡L\•ØÙÁŠóZRp³Š§0Â¿AÌ‚Wj &#“9+älŽ€À¾öîì+&ãP©H!Š’VÃW‰1ÎÀˆÏoãÃoŽ¢½]´Q! ç©´FgècÐA¸{9i:8—„UªÖË-¹³Jk›9‹¥o­7WùÛ_žðÊaÑ¹ÁPNV`Ã‹.¢ªs=“`—ìRçuACOÓx2]‹‘x4±3—KU_(„€¾kŒ#ÀœÁÒp~À{¢ž}òû«ï_¾üã»ßþñãÿtö†ßÊNF£â6,)öí—Ùœ7Mê­ÏÃø .
œ• F&©dIÓ=q%’&U<ƒÐ½ãõ"ÐÆÉ1§éÔxÒôdÂÜ¥!{YULlQTU]º ÄßDðnAf½iv_[õÊ{CHªœDà¾Œ$fgá/•N—=’û5ÊX·“ÔËØW]öxîÇ2u¹ÃÔå.øÕª+d–‘jß×*#Û‚Z‰>úÙÑ©€ÈU:ü‰‘ÙÍ>øù0$÷ýDvE°}c’ü¬à³¬}è³7»üÐØÇã±|}äs8£üÀÐ§‹ÎzL±Áâãlí7àø}-ò’6ÛAÏµ‚·È«9ÀtÞ¬áÁ}ÚÉs›ÔHãWÆ#@¥a
huL‹ñ¦.Kƒ_)t‰Ûˆü’6øàRT(pñ ¿»ž¾d¸£´…öÕ ké—õœæ´êï6œ€ù­›¾n_ûÞÙìúÌžòñ¼Mh<òBÄSÁäLá^‚¨Bcd]eÌ¼¶©^ Õ6ŸxmM´ñÂó_âê>’M&êSŽì­°  öâå«ç?|óÎ­ö/æº›Câ¿ÙÉðˆÚî§Z"Ô\C{×„jöêxã}K 1ÁÅ[¸˜JÐ4‘ÕÖè£NÀVàW¤ã‰.ØÜyxš7­ÁãFØXÒøb1žøNÃ“Ð)ÓÚ_Ô?‹–$]ß’9W3‘:Ýë~Y(÷áFîÂu€ÝÆˆ¿¼)¹B¨„—3+»«üª·«ÉUÚÛ˜Û.iw]~”KžÀäqe‘»q†zLh4“6<NHuÞq©Bñ¦ƒe•RàC$^…¡Ç÷KË\/ic­3Nø4LPBÑ÷6ÑÇï˜öÚ—·‡ÛnC¯øl[ÉÙLà¾9Ò œ”L€#Lºá¾…¢Ž¥º0H!3×už¢Ó÷Î½¢ã>:”)]CWêwÀ½ÈM$p<¿w,@;uÛ™Ü\@hd–*1Ýx	nÎSv¤´9ŽL€<–í›A«´iˆè2Ä%<f\æ5æbò£×~ÔfÁrMížÖHEðÒÌ©›Máºà×Ì]ÀP…ÛµHŽkR£q…V*1ƒæÅ~ÆïÓKeHìˆ_BCÝ‹ 7lKûž:Î"Ô ˆýCö}ø¶Ævø×x°p#>¢vC†ˆS¯­.¸ÅÍl¯ƒ[Å¨ÇwpÈ~0"«säÁRryƒl]”Ñ~·!Öˆ+^@|L¦ÒèëéXu‹•³ói~!õùB`^Ëßk>»ƒ©À &TjZÁI%€Qçå‡´Ÿ±Í¿ÜhR8)Ú±¨ÿP:åë3èb<¹_KÚ²y^C§Â.„PìdDžŒF£!ûŸ¨”È#¾4¥ûÈˆ´0ƒh´Ù¤¬Ï­µå=¬ka]IZ	i‚3IJ<Ã™/æ%Ú¥K‚Íë£lï˜ SÖ³Y¥a„qó“î<Ø·³ÎÈ)hšã²È%Ò%ÑÄd‘¬’W¼VTCöªut×ß`nKèp“Éñ	„Š„8™Ïe!mw€¤ã¬ÔF;WÚã^¤p².¦€

™TÚ ŠIW±¬ Á“9êNj˜tM&Ÿ>ùî5ßQ® .¡ÝðÔ½qºú¼]h÷|0€†ÔÄ~úÆ0sðÚ¦·}¸6ç¤Bc=a|™FÈ«4pùdtúsú²×Šñx>RÖÇS]ãh+mMIž`#ÊÔ~¡ðÚd3.7õèRv¦ÉôäHÅ1ÑC=Ãp
fIì^%´LÈóyáB	Í¨B;:to!ON×³¯çÝÖ¾÷9j¾=øAÀa*q]GˆÁ£"Ö@«¯àoŒËb64p¶ŒÅUé
—CØ˜'ƒS¼0:>m!E0ñÂí`¢8'_Ú#º«'¹–ú’ÌÜlcç-– €®–ì‘Tu˜ ?v†~VÛã³ìø{îæ·`¾3\Aq³\·>‘p#ºå]FÉÖœ+oÐvF>¢?Ø¡uqŽ+•Î{Žìc´yÎ²œÏØ£šëÉc$DX£4xÉ/H:ú8 tÐK”¨:ÀI”3îä¤ ýñÀÍ«œ|na6æ¶¯pn‡¹ÊÈÆH”'"Ñ³ÙŸãþ;^þ¥†¤a½ìììM$È5Ò“÷ìM ,œ¡qðZkA}T¶ô}p%R+•¾e¦žºIåØ‹°l+íÀWBžÄ,X‹©Ò8n¼,¥ƒé)¾Šã/g­V}HåâWf1jÑOÙg<SÕá‚or´Šïk½‰®“SØ5(AÜ6%—}Tò—ˆJþ…öxèÐÄ•Ô4_ö³õÚÐ2ûŒz]QóV÷ãxf¼ÈeâàDmCŒÓ—ÇÜIyŒ_i¿5ùe—â˜¾&æãÖÄl…ÝI¡}!Ì]ÂlâV© }õËÇ¬~Ù¼M	 }ÉËÝ•¼láÖiŸ}ËÝÔ¹lDqû*—¾¸åã·lÝ6QL_Ñr‡-[ƒ¸]Ó—±|Ä2–í°ÛÃôµ+wV»²‚;„0÷Â ì¯˜cMyr”Œs!Ë®¥ -4H”®u­ÖÙO¼rÊø®yKe.1W_[Õ	åÝ¸Ò|…2»›U˜aˆk~?D¬ —õÒ´«“ÜrÏŸ–9¤äMÔ9gLÞ;Æ,æhTòD<'ŠÒIÝ*}†4J¥
OÌŽ0<IXrU+ÕÝ‰ãì¨éÏQã	ž·bh‡/:½¨“õ¤’é³GO(Q÷Ù`8>Æú<ä
¯¸½]H‹9]ðx]ŒÅñº.é„ÎHVíÏFB.‡]Dø¬Hl¨1]ò4¤XSEåÓ¹ïmF‡L¬¾igï](™öK"EÊ¼õ.OÔ3:ÀÎ„¡<QÞeÔ”ã±¹:NÊxÕH
Â±l£ÝWÈØø–ï|âuSîƒ®Mvê±hç£4	þT%Ð!>‘Uú7¡Ú§š:¬µí%¹çšÏ`â`œðê¸IY“¡ìÅ— ÄÙÎlÎ¯/ yú”´YÞ%EfÃ»oñFKþ#Üo¤<º[†Kàþ¬ÌÊå·Óö¼Tn§¾S«åÄœ›d.Ò:“Nþeêü Ï#AþtT¸ÒV7÷;¢=ZÙCwßwì<9‡	€k™YÉˆ§ºÓ$!c†¥†®·>úoóÊçSD1ÿÔÊ¤ÎyœTÑ$°ú¼½)Èa"Ë°Ò3"»Y:öyM€êˆä~Ç¤
·©ÅNÉõù²6JÞ+|ä)mðYN1½çÚx¾æb+¤¢#6Im¾´Þ%0‡r{™áÅŽæQ{ŒwÌÔ	µIöóxˆ_í&•ŽØ#¥;	ñÐŽÇ1èÑuÐS¦fÚ“ªñCƒåPjæË†ÈCçx„÷Òùö“ïÁu€èÐß<¯UÕ\YÍåŒMk	4ioŸ‹ƒÛ…ivÉ›–P‘²+OÊo¬ÑqiX¤î*Óé»m/I`8‰³Ôf«‘œ{ÃCé^ÞPóÖ/‹íRµæ
Åëù7À,9E©Ý~Ô	Qˆ?°?å¢ ¼{| €‹Ø_¹:ñV{ yŒ»ÅÑ¦žb˜Î1>×ÜcO¡IµË’ŽGOygÉcJFDK•‡2ÊZWZ{R¿Ç^…¶î\G÷8gÑÿW	Ö-D`Y\†™Nøø/±e¨‡tâ1"?f/`cW»Ø5ú<öÍ‹›v­¬-Z_Ü`àßûYê!g©¤ãû0Cí’aÍì§=iL]nÅ™Û˜þ±-gnbÊµ™ã*g"*¬£¹èhúdLcäLuc9š7ï9M9åºqmí2µå³°	Þ”pŠ4ë˜f°BTØR5•¶â¸P¿‡è‚˜@¸TÍP^OMèb¶£ñBÚyŒHƒÀY¢•+U–S}¾3(úO„âõ&4¸N±éMÅò—Z¦X‰¾ô‡ÏÐô|Ågø*]D_s«@~U|2	ÅîOÝÔ¢{jMøª[S™`H‘„<]·ZƒaÍ§n)œúx»¹ßç°û»}ïm]Û&‰°Þn·£»S¶n¿#z²Dš¾åMÄ ¯ñŒcD-áq  0707010001f06e000081a40000000000000000000000016a102a8a0000018e000000e600010003ffffffffffffffff0000003e00000000root/usr/share/doc/opensvc/template.node.array.hp3par.conf.gz ‹     í”=OÃ0†÷üŠ“²:°Q¥##b0ö%±Hmc;´‘øñÜ9I‹(‘*R…ê!‰î|ï“³ó|Î•å0ãâtÂ{Ñ- v¡v7Nø_¥›·»yÙeIlîµQ¸{ÊVIt1ÈÎ2îÿ»­õêöÐ†l4Ù¯f\Œ)HëÄsƒûBw¢	È¯­ö¨Ž=
KÑ6ñÐ·Ý·—ò!Pçè{p(u©%¬¡Bƒ^Ë¤’I;j'bÑB¤ïôãÕC©dOÒ«±hsM	VTŽˆ…'‘å\‘Ý[ƒsðr^¿‰ˆ,õ{Pì(`É1Ëh—Œd‚ÖFQ¡?Wb[–kÄ†h•Iz:DëŸBLÑ£f¨mˆ)yb6Z‹É¢S1ÖV+Áê“iÑ}#ŒÒŠf+œ·»Þù"¢gJö…©´Æ ŒÚšÄ0’=ÔÞR¤È	~n[þ÷{êG(>Ÿ[&3b=è$7j;b´ ]ò¼•ºj©„l¿¹ om¼ðºðú[^À³¶T    0707010001f0b3000081a40000000000000000000000016a102a8a000005dd000000e600010003ffffffffffffffff0000003b00000000root/usr/share/doc/opensvc/template.secret.DEFAULT.conf.gz    ‹     åXÛn7}÷WðCZÀVsk€ª‰QÁvÚ´ŽíÊr^ŠÂ¢¸#-á¹áE²‚~|ÏpµÒÚÉ¶i,B¬Z^r‡ÃsÎ\¨ÝÝM~vvÅ?lîèøuïòd°)s›õn³Øíü±<ì®Ó&£›?wØãkZÌ­Ëºëe®Œœ’Çìþ?W¶”£‚VÛ\$žpô>jGk?^ËÂ§™ŒÆ2aíà©5TÛ"ßÉU“¾$¥ÇZ‰1!CN+^¦¬™‘k¼_hÒÑaÚ+<?Ä4¿'‰=1ÚLDþ¢WrF=#qd§RqÊØt`áÇRâ•˜ÏçøcüLu”ŠÆ÷ G:l+ÆÏÿðâ>{ýZ1>wzÆ£…8!3	yB“á iëÄÔ× ÉÏTÞ¡&¸E‚F—±“FQ+2ò+ƒf“àØv,<)GU€’ª]°"zÒãyÒF>îÅ[§Ã¢ÃÚ×§@H ªöó¼_
èR*ª—€à¶Án”"§S[å¿
Ã²´¶¬'þ„V@üˆ¾ž_¨(=‹ÍaDu>H“%ñ8[$±&!‘›iEbÎ‘·Ñ)ò{bžk•7T	!^ýZ³¯dQËÒº J²%ìårÆð‚’“dm¿a
».„t¯ð/ÂÖ‚·.­ñH'W‘V‘æÚhË´gl·TØ«™Ï-Mà@g@Îw×{Š¿Öv>ª]#&¥¹Å{¯FeÍ§˜kðÅÉcb,|gfjÞ¡
b™ø÷y™›Nƒ­7¨tX¢gÈb.†™yÍwr8ìq…Ü´ìÛö’¶æ¹-šÌ×{ÂPíc§éo=ŸYô)¬ŽÜÂßÊEv°i*Xø¬‚†h8Q'Õê_¡!›LÇ¤«Nd•¢«¦YwˆNgmëe)C~P»1:xÑÞ½žw‡Ë#uêy¸ì×¡Ò°³>G`ùëê*—fBÙÞjçz?2Õvõi–ª©‰¬z¹åcÄE¥Œ¶àp¥±ÙÖöÆ÷í‹¯
;'w[úË˜ÄHªkˆ:Ÿ“!Ý4³²±È8"X3Üáe,^Ù à¨ŽÖ0DÉšºJ––˜>ªÉ•3©F¢Ö`Ú‘³[Ã °qd¸0*wÖè•Y{Â:CÆƒ<öAswÈk†w_Rù©Ðð¨Âóy8éä@y3üôw2³m­ó/S['}’;zpŸòqxÜ |¿ãzF)9ëc|sÊÏß^^`<?;ä±ßKãQåñ¼_üè÷ËÞ	þôù½þ1¿q1øãà-[\ðóËÞàN!2hØÀ*ê¤IQƒ¸9'9êYµ	¼Œ½'F1Tÿ¢p°Ñºf¥7ëMdßpßŽÝoSh6Ót…º˜É"¦,Í‘]^)ÊÌ §OÑ¢(m­ pËPrbÔÙÁ—„`Oô_ŠçOž>SÕÒ”6G‹„ŸÄ“ ¤W×Gc$]s«ó9«¼Æ¾â‚´¬ØÆ’ÙÝž”ï7{É³ñ°an©ì‚uš\œX<ã1!«s/Ê½î­D
×_Ý¥…Ó‡”KO¬J¡’ âT:±q¦)Þ´‚cÕõUÐSÞÛ*ýïÓadÑ%Hî¶ÖËÇ‚nJ(Ëãëº‘kB'üdúì±ç^˜˜Ê=S1—:F*Õøp+UýjßÓÁª—«NqŸ_æY´¤eZjá¤ÓÜ„\7[ÏR:Ä ºŸŠÂ=`H´ð¸ÍýáËÜúPÿ¬Â‘¬¢s|d§îÃm:õ•§iÂºjVÆ¹¦škÐ¼4¾¨ó
· ì™*"r—[AYØ{^»ºó¾×y¨îæþå-´Ù”›ÎÜDÖýr‚Î¶e—×ŠO|  ÉB\¢¦V8E …»IH>< .øFž`ñœ­n¿6£-Ô™‹-EçÙ‹ï³M¶«`¯2¹hEðÝ†Õ	½`˜Ùù—“5     0707010001f050000081a40000000000000000000000016a102a8a000002d9000000e600010003ffffffffffffffff0000004000000000root/usr/share/doc/opensvc/template.cluster.pool.dorado.conf.gz   ‹     í˜ÍnÛ0€ïy
¹¦?çv-0lvØzYw*ŠT‘èD‹,z¢ì&{úQ²ãzMm–« ND†?Ò"­áp—×`(vxEs¢‰°,@hôRãËÌívu»e7¸‰Î½q·ƒóäôEãö`—?‡å=z}ö°
é½\²äh‡WäD
9±ÐNuíKˆ?KãA¯	4d²´áamWè`e
/|-¤”ÉŒ—b
¼QÉ9¶@Š5®g œÌù/™ü=¹8s‡÷NHgê(Ý¥Ûð]T¾¼Æ%u‡:‘F/”-) ßÑefZzºcžó<`Æö€{À=àpø +ÌDü—}`þ$-=Í¹•¬n%E:ºä*ð®ý ÝQx¯‚©d€. ÁåGµ°¥£Ä°+¿hÖÒ³üç,5è²x›µ²ößªô,?É2)ô÷BÑÐ|êñµIî§%oErWÒZT‘g…¶ÌDæ1¯Ñ­¨l/0=¼ÞëÀ›ñK¾Ï!xÔ˜Ks°5ù¹?¤ÍL|ù~ÛÆÏÑÛ¯Ñ[ž»°F¥®ÆÓH”dÜ”I5‹Äñ1 Æù`åcŸžžnâ›Ñ8ž£*ÖEFÏMÍÌX %7ÕyLÄ}.CÊÓxŠÄŠ•Qœ÷&ÌÇã‹sn •Ï'v>Æ"*¯›Û—ÔbšYX¬üUÅ#F[„tZ #ôbÂõ¼XäXœ¹5Ì“NRä^R÷E‡¸w,¤‡µH¤ ´|9ìÊÆ(p°ÞX¢ÏønIïjk;Sö…tã[úîw×DKZdM­Š0›6ÿp5“íŠ‚%IÍ¸µð¿Õ§H°’¶LYFâ-Âzäö
ÌŠo]î»G“ µN@ËýX|àdö'4ãÝáD£:imsrGÅŽµVDKä"EëñŠš¨ýhØ*Á     0707010001f122000041ed0000000000000000000000056a102a9200000000000000e600010003ffffffffffffffff0000001700000000root/usr/share/opensvc    0707010001f123000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000001b00000000root/usr/share/opensvc/bin    0707010001f138000081ed0000000000000000000000016a100daf000002cd000000e600010003ffffffffffffffff0000002800000000root/usr/share/opensvc/bin/preuninstall   #!/bin/sh

ARGS=$1

OSFLAVOR=`uname`
if [ $OSFLAVOR = 'SunOS' -o $OSFLAVOR = 'FreeBSD' ]
then
  if [ "$PKG_INSTALL_ROOT" != "/" -a "$PKG_INSTALL_ROOT" != "" ]; then
    echo "skipped pre-install when PKG_INSTALL_ROOT=$PKG_INSTALL_ROOT"
    exit 0
  fi
	ARGS=0
fi

if [ $OSFLAVOR = 'Linux' -a $ARGS = 'remove' ]
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" = '0' ]
then
	# uninstall package
	if [ $RUNNING -eq 0 ]
	then
                echo "stopping opensvc daemon"
		nodemgr daemon stop
	fi
	exit 0
fi

   0707010001f137000081ed0000000000000000000000016a100daf00000583000000e600010003ffffffffffffffff0000002600000000root/usr/share/opensvc/bin/preinstall #!/bin/sh

if [ "$PKG_INSTALL_ROOT" != "/" -a "$PKG_INSTALL_ROOT" != "" ]; then
  echo "skipped pre-install when PKG_INSTALL_ROOT=$PKG_INSTALL_ROOT"
  exit 0
fi

# 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 
type nodemgr >/dev/null 2>&1 || {
        echo "adding opensvc daemon restart flag"
	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
 0707010001f134000081ed0000000000000000000000016a100daf00000992000000e600010003ffffffffffffffff0000001e00000000root/usr/share/opensvc/bin/om #!/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/conf.d/opensvc" ]
then
	# Alpine, Gentoo
	. "/etc/conf.d/opensvc"
elif [ -r "/etc/rc.config.d/opensvc" ]
then
	# AIX
	. "/etc/rc.config.d/opensvc"
fi

INTER="$OSVC_PYTHON $OSVC_PYTHON_ARGS"
BASENAME=`basename $0`

case $BASENAME in
om)
	;;
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" "$@"
	;;
nodemgr)
	case $1 in
	daemon)
		shift
		KIND=daemon
		;;
	dns)
		KIND=daemon
		;;
	net|network)
		shift
		KIND=network
		;;
	pool)
		shift
		KIND=pool
		;;
	*)
		KIND=node
		;;
	esac
	;;
svcmgr)
	KIND=svc
	;;
volmgr)
	KIND=vol
	;;
usrmgr)
	KIND=usr
	;;
secmgr)
	KIND=sec
	;;
cfgmgr)
	KIND=cfg
	;;
ccfgmgr)
	KIND=ccfg
	;;
nscfgmgr)
	KIND=nscfg
	;;
svcmon)
	KIND=mon
	;;
esac

main() {
	if test -z "$OSVC_CONTEXT"
	then
		case "`id`" in
		uid=0\(*)
			;;
		*)
			SUDO=`which sudo 2>/dev/null`
			;;
		esac
	fi
	if test -x "$SUDO" 
	then
		SUDO="$SUDO OSVC_NAMESPACE=$OSVC_NAMESPACE OSVC_KIND=$OSVC_KIND"
		exec $SUDO $0 "$@"
		exit
	fi

	if [ ! -z "$OSVC_COMMAND_LOG" ]
	then
		log "$@" >/dev/null 2>&1
	fi
	OSVC_CWD=$PWD
	export OSVC_CWD
	cd $OSVC_ROOT_PATH
	if test -n "$KIND"
	then
		PYTHONPATH="$OSVC_ROOT_PATH:$PYTHONPATH" $INTER $OSVC_PYTHON_ARGS -m opensvc $KIND "$@"
		return $?
	fi
	case $1 in
	ns)
		echo "The 'om' alias must be sourced to handle ns actions" >&2
		exit 1
		;;
	*)
		PYTHONPATH="$OSVC_ROOT_PATH:$PYTHONPATH" $INTER $OSVC_PYTHON_ARGS -m opensvc "$@"
		;;
	esac
}

log() {
	OSVC_COMMAND_STRING="$0 $@"
	if [ ! -f "$OSVC_COMMAND_LOG" ]
	then
		touch $OSVC_COMMAND_LOG
	fi
	if test -w $OSVC_COMMAND_LOG
	then
		if [ "`uname`" = "SunOS" ] ; then
			GREP=`which ggrep 2>/dev/null`
		else
			GREP=`which grep 2>/dev/null`
		fi
		if test -x "$GREP"
		then
			echo "$OSVC_COMMAND_STRING" | $GREP -qE 'sec/|secret' && return
			$GREP -qxF "$OSVC_COMMAND_STRING" $OSVC_COMMAND_LOG || echo "$OSVC_COMMAND_STRING" >> $OSVC_COMMAND_LOG
		fi
	fi
}

main "$@"
  0707010001f136000081a40000000000000000000000016a100daf000003e1000000e600010003ffffffffffffffff0000002600000000root/usr/share/opensvc/bin/postremove #!/bin/sh

set -e
clean_systemd() {
        systemctl --version >/dev/null 2>&1 || return
        systemctl disable opensvc-agent || true
        rm -f /lib/systemd/system/opensvc-services.service
        rm -f /lib/systemd/system/opensvc-agent.service
        rm -f /usr/lib/systemd/system/opensvc-services.service
        rm -f /usr/lib/systemd/system/opensvc-agent.service
        systemctl daemon-reload
        systemctl kill opensvc-services || true
        systemctl stop opensvc-services || true
        systemctl reset-failed opensvc-services.service || true
}

case "$1" in
purge)
        rm -rf /var/lib/opensvc /var/log/opensvc /etc/opensvc /usr/share/opensvc
        rm -f /etc/default/opensvc 
        ;;

remove)
        clean_systemd
        rm -f /etc/profile.d/opensvc.sh
        ;;

upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
        ;;

*)
        echo "postrm called with unknown argument \`$1'" >&2
        exit 1
        ;;
esac

#DEBHELPER#

exit 0

   0707010001f135000081ed0000000000000000000000016a100daf0000c45c000000e600010003ffffffffffffffff0000002700000000root/usr/share/opensvc/bin/postinstall    #!/usr/bin/env python

import os
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)
utf8patterns = ['UTF-8', 'UTF8', 'utf-8', 'utf8']

# Cleanup possible env var tempfile candidate tempdir list
# some installer may define values, and cleanup those directories once installation is done
for tmp_var in ['TMPDIR', 'TEMP', 'TMP']:
    if tmp_var in os.environ:
        del os.environ[tmp_var]

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'
    if sysname != "SunOS":
        pathtmp = "/var/tmp/opensvc"
    else:
        pathtmp = "/var/lib/opensvc/tmp"
    pathlog = "/var/log/opensvc"
    pathbin = "/usr/share/opensvc/bin"
    pathlib = "/usr/share/opensvc/opensvc"
    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, 'opensvc')
    pathini = os.path.join(pathsvc, 'bin', 'init')
    pathusr = os.path.join(pathsvc, 'usr')

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

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

def osrun(cmd, stdout=False, stderr=False):
    ret = None
    if sysname == 'Windows':
        logit("cmd [%s]"%cmd, stdout, stderr)
        os.system(cmd)
    else:
        ret = os.WEXITSTATUS(os.system(cmd))
        logit("cmd [%s]  ret[%s]"%(cmd, ret), stdout, stderr)
    if ret is not None:
        return ret

make_sure_path_exists(pathlog)

if sysname == 'SunOS':
    osvcfmri = 'svc:/site/application/opensvc:boot'
    cmd = ['svcs', '-vl', osvcfmri, '2>/dev/null']
    ret = osrun(' '.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 = osrun(' '.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 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 = osrun(' '.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 = osrun(' '.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):

    if not os.path.islink('/lib') and os.path.isdir('/lib/systemd/system'):
        systemd_unit_path = '/lib/systemd/system'
    else:
        systemd_unit_path = '/usr/lib/systemd/system'

    systemd_unit_paths = [
        systemd_unit_path,
        '/etc/systemd/system/',
    ]

    installed = False
    for path in systemd_unit_paths:
        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)
            try:
                shutil.copyfile(src, dst)
                os.chmod(dst, p0644)
                installed = True
                break
            except IOError as exc:
                if exc.errno == 30:
                    continue
                logit("issue met while trying to install systemd unit file: %s"%str(exc), stderr=True)
            except OSError as exc:
                if exc.errno == 30:
                    continue
                logit("issue met while trying to install systemd unit file: %s"%str(exc), stderr=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)
    else:
        logit("installing %s systemd unit file in %s"%(systemdsvc, path), stdout=True)

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

    # reset path in PIDFile
    osrun("sed -i 's@/var/lib/opensvc@"+pathvar+"@' "+dst)

    # reload systemd configuration
    logit("reloading systemd configuration", stdout=True)
    cmd = ['systemctl', '-q', 'daemon-reload']
    ret = osrun(' '.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', 'disable', systemdsvc]
    ret = osrun(' '.join(cmd))
    cmd = ['systemctl', '-q', 'enable', systemdsvc]
    ret = osrun(' '.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 = osrun(' '.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 = osrun(' '.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 = osrun(' '.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_openrc(launcher):
    logit("begin")
    cmd = ['rc-update', '--help', '>>/dev/null 2>&1']
    ret = osrun(' '.join(cmd))
    if ret == 127:
        logit("rc-update command not found, skipping opensvc init setup", stdout=True)
        return False
    cmd = ['rc-update', 'delete', '-a', 'opensvc']
    ret = osrun(' '.join(cmd))
    cmd = ['rc-update', 'add', 'opensvc', 'default']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to install opensvc rc launchers", stderr=True)
        return False
    return True

def activate_FreeBSD(launcher):
    logit("begin")
    cmd = ['sysrc', '-f', '/etc/rc.conf.d/opensvc', 'opensvc_enable=YES']
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("issue met while trying to install opensvc rc launchers", stderr=True)
        return False
    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 get_keyval_in(key, path2f):
    logit("begin")
    val = None
    try:
        f = open(path2f, 'r')
        data = f.readlines()
        f.close()
    except:
        f.close()
        import traceback
        traceback.print_exc()

    for line in data:
        if line.startswith(key):
            val=line.split('=')[-1].strip()
            logit("found value <%s> for key <%s> in file <%s>"%(val, key, path2f))
            break

    return val

def is_lang_utf8(file):
    logit("begin")
    lang = get_keyval_in('LANG', file)
    if lang is not None:
        if any(pattern in lang for pattern in utf8patterns):
            return True
    return False

def is_system_locale_utf8():
    """
    Check if system locale is utf8
    """
    logit("begin")
    localetmp = pathtmp + '/locale.postinstall'
    cmd = ['locale', '>', localetmp]
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("error while trying to query locale with command <%s>"%(cmd), stderr=True)
        return False
    try:
        return is_lang_utf8(localetmp)
    finally:
        try:
            os.unlink(localetmp)
        except Exception:
            pass

def is_osvc_locale_utf8(file):
    """
    Check if osvc locale is utf8
    """
    logit("begin")
    return is_lang_utf8(file)

def appends2f(str, fname):
    try:
        f = open(fname, "a")
        f.write(str + "\n")
        f.close()
        logit("added string <%s> to file <%s>"%(str, fname))
    except:
        logit("issue met while trying to append string <%s> to file <%s>"%(str, fname), stderr=True)
        f.close()
        import traceback
        traceback.print_exc()

def find_locale():
    logit("begin")
    """ try to identify best utf8 locale
    """
    localetmp = pathtmp + '/locale.all'
    cmd = ['locale', '-a', '>', localetmp]
    ret = osrun(' '.join(cmd))
    if ret > 0:
        logit("error while trying to build locale list with command <%s>"%(cmd), stderr=True)
        return None

    data = []
    try:
        f = open(localetmp, 'r')
        while True:
            try:
                line = f.readline()
            except Exception:
                break
            if not line:
                break
            data.append(line)
        f.close()
    except:
        f.close()
        import traceback
        traceback.print_exc()

    candidates = []
    for l in data:
        if any(pattern in l for pattern in utf8patterns):
            candidates.append(l.strip())

    logit("utf8 locale candidates <%s>"%(candidates))
    for l in candidates:
        if l.startswith('C'):
            return l

    for l in candidates:
        if l.startswith('en_US'):
            return l

    for l in candidates:
        if l.startswith('en_'):
            return l

    logit("no suitable utf8 locale found")
    return None

def install_locale(path2file):
    logit("begin")
    """ install utf8 locale
    """
    lang = get_keyval_in('LANG', path2file)
    if lang is not None:
        logit("LANG <%s> already set with non utf8 locale"%(lang), stderr=True)
    else:
        locale = find_locale()
        if locale is not None:
            appends2f("LANG=" + locale, path2file)
            appends2f("export LANG", path2file)

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

    src = os.path.join(pathini, 'opensvc.defaults.parameters')
    with open(src, "r") as f:
        buff = f.read()
    if sys.executable != "/usr/bin/python":
        buff += "OSVC_PYTHON=%s\n" % sys.executable
    if os.path.join("share", "opensvc", "bin") not in __file__:
        root_path = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
        buff += "OSVC_ROOT_PATH=%s\n" % root_path
    try:
        logit("writing new <%s>"%path2file)
        with open(path2file, "w") as f:
            f.write(buff)
        os.chmod(path2file, p0644)
    except:
        import traceback
        traceback.print_exc()

def os_release():
    os_release_f = os.path.join(os.sep, "etc", "os-release")
    data = {"ID": "==magic1234"}
    if not os.path.exists(os_release_f):
        return data
    filep = open(os_release_f, "r")
    try:
        for line in filep.readlines():
            line = line.strip("\n")
            try:
                var, val = line.split("=", 1)
            except:
                continue
            data[var] = val.strip('"')
    finally:
        filep.close()
    return data

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

    if reldata["ID"] in ("gentoo"):
        rc = '/etc/init.d/opensvc'
        params = '/etc/conf.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.openrc')
        if systemd_mgmt():
            logit("gentoo with systemd")
            copyrc = False
            activate = activate_systemd
        else:
            logit("gentoo with openrc")
            activate = activate_openrc
    elif reldata["ID"] in ("ubuntu", "debian") or 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 reldata["ID"] == "arch" or 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 reldata["ID"] in ("sles", "caasp", "opensuse-leap", "opensuse-tumbleweed") or 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 reldata["ID"] in ("alpine") or os.path.exists('/etc/alpine-release'):
        rc = '/etc/init.d/opensvc'
        params = '/etc/conf.d/opensvc'
        src = os.path.join(pathini, 'opensvc.init.openrc')
        activate = activate_openrc
    elif reldata["ID"] in ("centos", "rhel", "fedora") or 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 = '/usr/local/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)

    if params is not None:
        logit("checking locale setup")
        if not is_system_locale_utf8() and not is_osvc_locale_utf8(params):
            logit("locale is not utf8")
            install_locale(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', '--help', '>>/dev/null 2>&1']
    ret = osrun(' '.join(cmd))
    if ret == 127:
        logit("ssh-keygen command not found, skipping key creation", stdout=True)
        return
    cmd = ['ssh-keygen', '-t', 'rsa', '-P', '""', '-f', priv]
    try:
        ret = osrun(' '.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 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, linkto))
                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, linkto))
                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 -qw %(pathbin)s; then
	PATH=${PATH}:%(pathbin)s
fi

om() {
        case "$1 $2" in
        "ctx get")
		[ -z "$OSVC_CONTEXT" ] && echo "(local)" || echo $OSVC_CONTEXT
		;;
        "ctx set")
                test -z "$3" && {
                        echo "om ctx set <context>" >&2
                        return 1
                }
                echo "context switch: $OSVC_CONTEXT => $3"
                export OSVC_CONTEXT=$3
                ;;
        "ctx unset")
                unset OSVC_CONTEXT
                ;;
        "ns get"|"getns "*)
		[ -z "$OSVC_NAMESPACE" ] && echo "(none)" || echo $OSVC_NAMESPACE
		;;
        "ns set")
                test -z "$3" && {
                        echo "om ns set <ns>" >&2
                        return 1
                }
                test "$3"=="root" -o -d %(pathetc)s/namespaces/$3 || {
                        echo "namespace $2 does not exist" >&2
                        return 1
                }
                echo "namespace switch: $OSVC_NAMESPACE => $3"
                export OSVC_NAMESPACE=$3
                ;;
        "ns unset")
                unset OSVC_NAMESPACE
                ;;
        "setns "*)
                test -z "$2" && {
                        echo "om setns <ns>" >&2
                        return 1
                }
                test "$2"=="root" -o -d %(pathetc)s/namespaces/$2 || {
                        echo "namespace $2 does not exist" >&2
                        return 1
                }
                echo "namespace switch: $OSVC_NAMESPACE => $2"
                export OSVC_NAMESPACE=$2
                ;;
        "unsetns "*)
                unset OSVC_NAMESPACE
                ;;
        *)
                %(pathbin)s/om "$@"
                ;;
        esac
}
""" % dict(pathetc=pathetc, pathbin=pathbin)

    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 sysname == 'SunOS':
        if os.path.exists("/etc/bash") and not os.path.exists("/etc/bash/bash_completion.d"):
            os.makedirs("/etc/bash/bash_completion.d", p0755)
    root = pathsvc if pathsvc else ""
    src = os.path.join(root, 'usr', 'share', 'bash_completion.d', 'opensvc.sh')
    dst = None
    ds = [os.path.join(os.sep, 'etc', 'bash_completion.d'),
          os.path.join(os.sep, 'etc', 'bash', 'bash_completion.d')]
    for d in ds:
        if os.path.exists(d):
            dst = os.path.join(d, 'opensvc.sh')
            break
    if dst is None:
        logit("no bash completion directory found")
        return
    deprecated_dst = os.path.join(d, 'opensvc')
    if os.path.exists(deprecated_dst):
        logit("remove deprecated bash completion file %s"%deprecated_dst)
        os.unlink(deprecated_dst)
    if not root:
        # the completion script is installed by the package
        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 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 = osrun(' '.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
    kwargs = {}
    try:
        ConfigParser.RawConfigParser(strict=False)
        kwargs["strict"] = False
    except Exception:
        # older version of python have no strict kwarg,
        # but behave like newer with strict=False
        pass
    try:
        config = ConfigParser.RawConfigParser(**kwargs)
    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 purge_collector_rpc_method_cache():
    logit("begin")
    for name in ("proxy", "comp_proxy"):
        fname = os.path.join(pathvar, "node", "rpc_list_methods.%s" % name)
        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 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_ns():
    import re
    if sysname == 'Windows':
        logit("skip : unsupported on Windows")
        return
    for p in glob.glob(pathvar+"/services"):
        d = re.sub("/services$", "/svc", p)
        if not os.path.exists(d):
            logit("migrate %s => %s" % (p, d))
            shutil.move(p, d)
    for p in glob.glob(pathvar+"/namespaces/*/services"):
        d = re.sub("/services$", "/svc", p)
        if not os.path.exists(d):
            logit("migrate %s => %s" % (p, d))
            shutil.move(p, d)
    for p in glob.glob(pathetc+"/namespaces/system/cluster.conf"):
        d = pathetc+"/cluster.conf"
        make_sure_path_exists(os.path.dirname(d))
        if not os.path.exists(d):
            logit("migrate %s => %s" % (p, d))
            shutil.move(p, d)
    for p in glob.glob(pathetc+"/namespaces/*/*.conf"):
        l = p.split("/")
        l.insert(-1, "svc")
        d = "/".join(l)
        make_sure_path_exists(os.path.dirname(d))
        if not os.path.exists(d):
            logit("migrate %s => %s" % (p, d))
            shutil.move(p, d)
    for p in glob.glob(pathlog+"/*/*.log*"):
        l = p.split("/")
        l.insert(-2, "namespace")
        l.insert(-1, "svc")
        d = "/".join(l)
        make_sure_path_exists(os.path.dirname(d))
        if not os.path.exists(d):
            logit("migrate %s => %s" % (p, d))
            shutil.move(p, d)

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
    try:
        # never risk triggering "boot" actions on services, for
        # example due to a reference or format change for boot id
        os.unlink(os.path.join(pathvar, "node", "last_boot_id"))
    except:
        pass
    os.unlink(flag)
    os.environ['OPENSVC_AGENT_UPGRADE'] = "1"
    cmd = "%s/nodemgr daemon start" % pathbin
    osrun(cmd, stdout=True)
    cmd = "%s/nodemgr daemon running" % pathbin
    osrun(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
    osrun(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 = osrun(' '.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 = osrun(' '.join(cmd))
            if ret == 0:
                logit("fmri refresh successfully executed", stdout=False)

def remove_svclinks():
    logit("remove svclinks (deprecated)", stdout=False)
    svcmgr = os.path.realpath(os.path.join(pathsbin, 'svcmgr'))
    for path in glob.glob(os.path.join(pathetc, "*", "*", "*", "*")) + \
                glob.glob(os.path.join(pathetc, "*", "*", "*")) + \
                glob.glob(os.path.join(pathetc, "*", "*")) + \
                glob.glob(os.path.join(pathetc, "*")):
        if not os.path.islink(path):
            continue
        if os.path.realpath(path) != svcmgr:
            continue
        logit(" rm %s" % path, stdout=False)
        os.unlink(path)

try:
    reldata = os_release()
    move_var_files_in_subdirs()
    move_usr_to_opt()
    missing_dirs()
    install_cron()
    install_rc()
    gen_keys()
    install_profile()
    install_etc_path()
    install_bash_completion()
    move_host_mode()
    nodeconf_params()
    purge_collector_api_cache()
    purge_collector_rpc_method_cache()
    chmod_directories()
    convert_to_lsb()
    move_env_to_conf()
    move_var_files_19()
    dump_install_content()
    convert_to_ns()
    restart_daemon()
    init_secret()
    solaris_fmri_setprop()
    remove_svclinks()
    logit("\nOpenSVC postinstall terminated\n", stdout=True)
except:
    save_exc()
    sys.exit(1)
0707010001f124000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002000000000root/usr/share/opensvc/bin/init   0707010001f132000081a40000000000000000000000016a100daf0000033c000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/bin/init/systemd.opensvc-agent.service [Unit]
Description=OpenSVC agent
Documentation=https://docs.opensvc.com/ file:/usr/share/doc/opensvc/ man:nodemgr(1) man:svcmgr(1) man:svcmon(1)
Before=opensvc-services.service
After=network-online.target
After=time-sync.target
After=multi-user.target
After=network.target
After=docker.service
After=libvirtd.service
After=virt-guest-shutdown.target
After=libvirt-guests.service
After=blk-availability.service
Wants=libvirtd.service
Wants=libvirt-guests.service
Wants=blk-availability.service
Requires=opensvc-services.service

[Service]
Type=forking
TimeoutStopSec=5m
ExecStart=/usr/share/opensvc/bin/om daemon start
ExecStop=/usr/share/opensvc/bin/om daemon stop
KillMode=process
Restart=on-failure
OOMScoreAdjust=-1000
PIDFile=/var/lib/opensvc/osvcd.pid
Delegate=yes
Slice=opensvc.slice

[Install]
WantedBy=multi-user.target
0707010001f125000081a40000000000000000000000016a100daf00000255000000e600010003ffffffffffffffff0000004000000000root/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>
   0707010001f128000081ed0000000000000000000000016a100daf000001b7000000e600010003ffffffffffffffff0000003100000000root/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/om daemon start
	${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
	${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

 0707010001f130000081ed0000000000000000000000016a100daf00000335000000e600010003ffffffffffffffff0000003400000000root/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/om daemon start
	[ -d /var/lock/subsys ] && touch /var/lock/subsys/opensvc
        ${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
	rm -f /var/lock/subsys/opensvc
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

   0707010001f129000081ed0000000000000000000000016a100daf0000032a000000e600010003ffffffffffffffff0000003400000000root/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/om daemon start
        echo
        echo "OpenSVC : Pushing node information"
	${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
	echo "OpenSVC : Stopping services and daemon"
	${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

  0707010001f12a000081ed0000000000000000000000016a100daf000003a1000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/bin/init/opensvc.init.FreeBSD  #!/bin/sh
#

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

. /etc/rc.subr

name="opensvc"
desc="OpenSVC agent"
rcvar=${name}_enable

start_cmd="opensvc_start"
restart_cmd="opensvc_restart"
stop_cmd="opensvc_stop"
status_cmd="opensvc_status"
extra_commands="status"

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

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

opensvc_start()
{
        echo "OpenSVC daemon start"
        ${OSVC_ROOT_PATH}/bin/om daemon start
        ${OSVC_ROOT_PATH}/bin/om node pushasset
}

opensvc_restart()
{
        echo "OpenSVC daemon restart"
        ${OSVC_ROOT_PATH}/bin/om daemon restart
}

opensvc_stop()
{
        echo "OpenSVC daemon shutdown"
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
}

opensvc_status()
{
        ${OSVC_ROOT_PATH}/bin/om daemon running
}

load_rc_config $name
run_rc_command "$1"
   0707010001f12f000081ed0000000000000000000000016a100daf00000323000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/bin/init/opensvc.init.openrc   #!/sbin/openrc-run
# Copyright 1999-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# default values, unless overridden by conf.d
: ${OSVC_ROOT_PATH:=/usr/share/opensvc}


# https://github.com/OpenRC/openrc/blob/master/service-script-guide.md

depend() {
	# openrc deps. Report yours with "rc-status -s"
	need hostname localmount
	use net dns logger lvm mdadm mdraid zfs-import device-mapper \
		libvirtd docker
	before opensvc-services
	provide opensvc-agent
	# Agent is not meant to be inside a container (lxc, docker, xen, jail...)
	keyword -containers
}

start() {
	${OSVC_ROOT_PATH}/bin/om daemon start
}

stop() {
	${OSVC_ROOT_PATH}/bin/om daemon shutdown
}

status() {
	${OSVC_ROOT_PATH}/bin/om daemon status
	${OSVC_ROOT_PATH}/bin/om daemon running
}

 0707010001f126000081a40000000000000000000000016a100daf00000831000000e600010003ffffffffffffffff0000003200000000root/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>
   0707010001f131000081ed0000000000000000000000016a100daf000002c3000000e600010003ffffffffffffffff0000003200000000root/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/om daemon start
        ${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

 0707010001f133000081a40000000000000000000000016a100daf00000194000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/bin/init/systemd.opensvc-services.service  [Unit]
Description=OpenSVC agent and services oneshot stopper
Documentation=https://docs.opensvc.com/ file:/usr/share/doc/opensvc/ man:nodemgr(1) man:svcmgr(1) man:svcmon(1)
After=opensvc-agent.service
After=drbd-graceful-shutdown.service

[Service]
Type=oneshot
TimeoutStopSec=1h
RemainAfterExit=true
ExecStart=/usr/share/opensvc/bin/om node pushasset
ExecStop=/usr/share/opensvc/bin/om daemon shutdown
0707010001f12e000081ed0000000000000000000000016a100daf000003ae000000e600010003ffffffffffffffff0000003200000000root/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/om daemon start
	${OSVC_ROOT_PATH}/bin/om node collect stats
        ${OSVC_ROOT_PATH}/bin/om node 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/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac
  0707010001f12d000081ed0000000000000000000000016a100daf000002bf000000e600010003ffffffffffffffff0000003400000000root/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/om daemon start
        ${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac
 0707010001f12c000081ed0000000000000000000000016a100daf00000285000000e600010003ffffffffffffffff0000003300000000root/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/om daemon start
        ${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

   0707010001f12b000081ed0000000000000000000000016a100daf000001d0000000e600010003ffffffffffffffff0000003200000000root/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/om daemon start
        ${OSVC_ROOT_PATH}/bin/om node pushasset
	;;
stop)
        ${OSVC_ROOT_PATH}/bin/om daemon shutdown
	;;
status)
        ${OSVC_ROOT_PATH}/bin/om daemon running
	;;
restart)
        ${OSVC_ROOT_PATH}/bin/om daemon restart
	;;
esac

0707010001f127000081a40000000000000000000000016a100daf00000183000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/bin/init/opensvc.defaults.parameters   # OpenSVC startup and wrapper configuration file
#
# You may need to adapt parameters to fit your environment
# This file is not modified during software upgrades
# If empty, default settings are used in the init script


#

#
# Wrapper configuration
#
#OSVC_ROOT_PATH=/opt/opensvc
#OSVC_PYTHON=python
#LD_LIBRARY_PATH=
#LD_PRELOAD=
#OSVC_COMMAND_LOG=/var/log/opensvc/node.commands.log

 0707010001f139000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000001c00000000root/usr/share/opensvc/html   0707010001f13a000081a400000000000000000000000168ac987800000185000000e600010003ffffffffffffffff0000002700000000root/usr/share/opensvc/html/index.html    <!doctype html><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta name="viewport" content="minimum-scale=1,initial-scale=1,width=device-width,shrink-to-fit=no"/><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/><title>OpenSVC Cluster</title><script defer="defer" src="/index.js"></script></head><body><div id="app" class="container"></div></body></html>   0707010001f13b000081a400000000000000000000000168ac98780016aa63000000e600010003ffffffffffffffff0000002500000000root/usr/share/opensvc/html/index.js  /*! For license information please see index.js.LICENSE.txt */
(()=>{var e,t,n={22:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"}),"LockOpen");t.A=a},24:e=>{"use strict";var t={foo:{}},n=Object;e.exports=function(){return{__proto__:t}.foo===t.foo&&!({__proto__:null}instanceof n)}},41:(e,t,n)=>{"use strict";var r=n(592)(),o=n(453),i=r&&o("%Object.defineProperty%",!0);if(i)try{i({},"a",{value:1})}catch(e){i=!1}var a=o("%SyntaxError%"),s=o("%TypeError%"),l=n(5795);e.exports=function(e,t,n){if(!e||"object"!=typeof e&&"function"!=typeof e)throw new s("`obj` must be an object or a function`");if("string"!=typeof t&&"symbol"!=typeof t)throw new s("`property` must be a string or a symbol`");if(arguments.length>3&&"boolean"!=typeof arguments[3]&&null!==arguments[3])throw new s("`nonEnumerable`, if provided, must be a boolean or null");if(arguments.length>4&&"boolean"!=typeof arguments[4]&&null!==arguments[4])throw new s("`nonWritable`, if provided, must be a boolean or null");if(arguments.length>5&&"boolean"!=typeof arguments[5]&&null!==arguments[5])throw new s("`nonConfigurable`, if provided, must be a boolean or null");if(arguments.length>6&&"boolean"!=typeof arguments[6])throw new s("`loose`, if provided, must be a boolean");var r=arguments.length>3?arguments[3]:null,o=arguments.length>4?arguments[4]:null,u=arguments.length>5?arguments[5]:null,c=arguments.length>6&&arguments[6],d=!!l&&l(e,t);if(i)i(e,t,{configurable:null===u&&d?d.configurable:!u,enumerable:null===r&&d?d.enumerable:!r,value:n,writable:null===o&&d?d.writable:!o});else{if(!c&&(r||o||u))throw new a("This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.");e[t]=n}}},195:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(5862);t.withRouter=r.withRouter,t.OidcRoutes=r.OidcRoutes,t.useHistory=r.useHistory;var o=n(347);t.Authenticating=o.Authenticating,t.Callback=o.Callback;var i=n(583);t.configurationPropTypes=i.configurationPropTypes,t.configurationDefaultProps=i.configurationDefaultProps;var a=n(1243);t.compose=a.compose;var s=n(8656);t.getUserManager=s.getUserManager,t.authenticationService=s.authenticationService,t.authenticateUser=s.authenticateUser,t.signinSilent=s.signinSilent,t.logoutUser=s.logoutUser,t.isRequireAuthentication=s.isRequireAuthentication,t.setLogger=s.setLogger,t.oidcLog=s.oidcLog,t.InMemoryWebStorage=s.InMemoryWebStorage,t.setUserManager=s.setUserManager},251:(e,t)=>{t.read=function(e,t,n,r,o){var i,a,s=8*o-r-1,l=(1<<s)-1,u=l>>1,c=-7,d=n?o-1:0,f=n?-1:1,p=e[t+d];for(d+=f,i=p&(1<<-c)-1,p>>=-c,c+=s;c>0;i=256*i+e[t+d],d+=f,c-=8);for(a=i&(1<<-c)-1,i>>=-c,c+=r;c>0;a=256*a+e[t+d],d+=f,c-=8);if(0===i)i=1-u;else{if(i===l)return a?NaN:1/0*(p?-1:1);a+=Math.pow(2,r),i-=u}return(p?-1:1)*a*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var a,s,l,u=8*i-o-1,c=(1<<u)-1,d=c>>1,f=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:i-1,h=r?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=c):(a=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-a))<1&&(a--,l*=2),(t+=a+d>=1?f/l:f*Math.pow(2,1-d))*l>=2&&(a++,l/=2),a+d>=c?(s=0,a=c):a+d>=1?(s=(t*l-1)*Math.pow(2,o),a+=d):(s=t*Math.pow(2,d-1)*Math.pow(2,o),a=0));o>=8;e[n+p]=255&s,p+=h,s/=256,o-=8);for(a=a<<o|s,u+=o;u>0;e[n+p]=255&a,p+=h,a/=256,u-=8);e[n+p-h]|=128*m}},345:(e,t,n)=>{e.exports=n(7007).EventEmitter},347:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1740);t.Authenticating=r.default;var o=n(9030);t.NotAuthenticated=o.default;var i=n(1954);t.NotAuthorized=i.default;var a=n(7669);t.Callback=a.default;var s=n(4720);t.SessionLost=s.default},412:function(e,t,n){var r;r=function(e){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=11)}([function(e,t,n){(function(t){if("production"!==t.env.NODE_ENV){var r="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;e.exports=n(14)((function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r}),!0)}else e.exports=n(16)()}).call(t,n(2))},function(t,n){t.exports=e},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var l,u=[],c=!1,d=-1;function f(){c&&l&&(c=!1,l.length?u=l.concat(u):d=-1,u.length&&p())}function p(){if(!c){var e=s(f);c=!0;for(var t=u.length;t;){for(l=u,u=[];++d<t;)l&&l[d].run();d=-1,t=u.length}l=null,c=!1,function(e){if(r===clearTimeout)return clearTimeout(e);if((r===a||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(e);try{return r(e)}catch(t){try{return r.call(null,e)}catch(t){return r.call(this,e)}}}(e)}}function h(e,t){this.fun=e,this.array=t}function m(){}o.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];u.push(new h(e,t)),1!==u.length||c||s(p)},h.prototype.run=function(){this.fun.apply(null,this.array)},o.title="browser",o.browser=!0,o.env={},o.argv=[],o.version="",o.versions={},o.on=m,o.addListener=m,o.once=m,o.off=m,o.removeListener=m,o.removeAllListeners=m,o.emit=m,o.prependListener=m,o.prependOnceListener=m,o.listeners=function(e){return[]},o.binding=function(e){throw new Error("process.binding is not supported")},o.cwd=function(){return"/"},o.chdir=function(e){throw new Error("process.chdir is not supported")},o.umask=function(){return 0}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e.reduce((function(e,t){return e+t}))/e.length}},function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,n){"use strict";(function(t){var n=function(e){};"production"!==t.env.NODE_ENV&&(n=function(e){if(void 0===e)throw new Error("invariant requires an error message argument")}),e.exports=function(e,t,r,o,i,a,s,l){if(n(t),!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[r,o,i,a,s,l],d=0;(u=new Error(t.replace(/%s/g,(function(){return c[d++]})))).name="Invariant Violation"}throw u.framesToPop=1,u}}}).call(t,n(2))},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return Math.min.apply(Math,e)}},function(e,t,n){"use strict";(function(t){var r=n(4);if("production"!==t.env.NODE_ENV){var o=function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];var o=0,i="Warning: "+e.replace(/%s/g,(function(){return n[o++]}));"undefined"!=typeof console&&console.error(i);try{throw new Error(i)}catch(e){}};r=function(e,t){if(void 0===t)throw new Error("`warning(condition, format, ...args)` requires a warning message argument");if(0!==t.indexOf("Failed Composite propType: ")&&!e){for(var n=arguments.length,r=Array(n>2?n-2:0),i=2;i<n;i++)r[i-2]=arguments[i];o.apply(void 0,[t].concat(r))}}}e.exports=r}).call(t,n(2))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return Math.max.apply(Math,e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=(r=n(3))&&r.__esModule?r:{default:r};t.default=function(e){var t=(0,o.default)(e),n=e.map((function(e){return Math.pow(e-t,2)})),r=(0,o.default)(n);return Math.sqrt(r)}},function(e,t,n){e.exports=n(12)},function(e,t,n){"use strict";e.exports=n(13)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SparklinesText=t.SparklinesNormalBand=t.SparklinesReferenceLine=t.SparklinesSpots=t.SparklinesBars=t.SparklinesCurve=t.SparklinesLine=t.Sparklines=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=m(n(0)),i=n(1),a=m(i),s=m(n(17)),l=m(n(18)),u=m(n(19)),c=m(n(20)),d=m(n(21)),f=m(n(22)),p=m(n(27)),h=m(n(28));function m(e){return e&&e.__esModule?e:{default:e}}var g=function(e){function t(e){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.data,n=e.limit,r=e.width,o=e.height,i=e.svgWidth,s=e.svgHeight,l=e.preserveAspectRatio,u=e.margin,c=e.style,d=e.max,f=e.min;if(0===t.length)return null;var p=(0,h.default)({data:t,limit:n,width:r,height:o,margin:u,max:d,min:f}),m={style:c,viewBox:"0 0 "+r+" "+o,preserveAspectRatio:l};return i>0&&(m.width=i),s>0&&(m.height=s),a.default.createElement("svg",m,a.default.Children.map(this.props.children,(function(e){return a.default.cloneElement(e,{data:t,points:p,width:r,height:o,margin:u})})))}}]),t}(i.PureComponent);g.propTypes={data:o.default.array,limit:o.default.number,width:o.default.number,height:o.default.number,svgWidth:o.default.number,svgHeight:o.default.number,preserveAspectRatio:o.default.string,margin:o.default.number,style:o.default.object,min:o.default.number,max:o.default.number,onMouseMove:o.default.func},g.defaultProps={data:[],width:240,height:60,preserveAspectRatio:"none",margin:2},t.Sparklines=g,t.SparklinesLine=l.default,t.SparklinesCurve=u.default,t.SparklinesBars=c.default,t.SparklinesSpots=d.default,t.SparklinesReferenceLine=f.default,t.SparklinesNormalBand=p.default,t.SparklinesText=s.default},function(e,t,n){"use strict";(function(t){var r=n(4),o=n(5),i=n(8),a=n(6),s=n(15);e.exports=function(e,n){var l="function"==typeof Symbol&&Symbol.iterator,u="<<anonymous>>",c={array:p("array"),bool:p("boolean"),func:p("function"),number:p("number"),object:p("object"),string:p("string"),symbol:p("symbol"),any:f(r.thatReturnsNull),arrayOf:function(e){return f((function(t,n,r,o,i){if("function"!=typeof e)return new d("Property `"+i+"` of component `"+r+"` has invalid PropType notation inside arrayOf.");var s=t[n];if(!Array.isArray(s))return new d("Invalid "+o+" `"+i+"` of type `"+m(s)+"` supplied to `"+r+"`, expected an array.");for(var l=0;l<s.length;l++){var u=e(s,l,r,o,i+"["+l+"]",a);if(u instanceof Error)return u}return null}))},element:f((function(t,n,r,o,i){var a=t[n];return e(a)?null:new d("Invalid "+o+" `"+i+"` of type `"+m(a)+"` supplied to `"+r+"`, expected a single ReactElement.")})),instanceOf:function(e){return f((function(t,n,r,o,i){if(!(t[n]instanceof e)){var a=e.name||u;return new d("Invalid "+o+" `"+i+"` of type `"+((s=t[n]).constructor&&s.constructor.name?s.constructor.name:u)+"` supplied to `"+r+"`, expected instance of `"+a+"`.")}var s;return null}))},node:f((function(e,t,n,r,o){return h(e[t])?null:new d("Invalid "+r+" `"+o+"` supplied to `"+n+"`, expected a ReactNode.")})),objectOf:function(e){return f((function(t,n,r,o,i){if("function"!=typeof e)return new d("Property `"+i+"` of component `"+r+"` has invalid PropType notation inside objectOf.");var s=t[n],l=m(s);if("object"!==l)return new d("Invalid "+o+" `"+i+"` of type `"+l+"` supplied to `"+r+"`, expected an object.");for(var u in s)if(s.hasOwnProperty(u)){var c=e(s,u,r,o,i+"."+u,a);if(c instanceof Error)return c}return null}))},oneOf:function(e){return Array.isArray(e)?f((function(t,n,r,o,i){for(var a=t[n],s=0;s<e.length;s++)if(l=a,u=e[s],l===u?0!==l||1/l==1/u:l!=l&&u!=u)return null;var l,u;return new d("Invalid "+o+" `"+i+"` of value `"+a+"` supplied to `"+r+"`, expected one of "+JSON.stringify(e)+".")})):("production"!==t.env.NODE_ENV&&i(!1,"Invalid argument supplied to oneOf, expected an instance of array."),r.thatReturnsNull)},oneOfType:function(e){if(!Array.isArray(e))return"production"!==t.env.NODE_ENV&&i(!1,"Invalid argument supplied to oneOfType, expected an instance of array."),r.thatReturnsNull;for(var n=0;n<e.length;n++){var o=e[n];if("function"!=typeof o)return i(!1,"Invalid argument supplid to oneOfType. Expected an array of check functions, but received %s at index %s.",v(o),n),r.thatReturnsNull}return f((function(t,n,r,o,i){for(var s=0;s<e.length;s++)if(null==(0,e[s])(t,n,r,o,i,a))return null;return new d("Invalid "+o+" `"+i+"` supplied to `"+r+"`.")}))},shape:function(e){return f((function(t,n,r,o,i){var s=t[n],l=m(s);if("object"!==l)return new d("Invalid "+o+" `"+i+"` of type `"+l+"` supplied to `"+r+"`, expected `object`.");for(var u in e){var c=e[u];if(c){var f=c(s,u,r,o,i+"."+u,a);if(f)return f}}return null}))}};function d(e){this.message=e,this.stack=""}function f(e){if("production"!==t.env.NODE_ENV)var r={},s=0;function l(l,c,f,p,h,m,g){if(p=p||u,m=m||f,g!==a)if(n)o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use `PropTypes.checkPropTypes()` to call them. Read more at http://fb.me/use-check-prop-types");else if("production"!==t.env.NODE_ENV&&"undefined"!=typeof console){var v=p+":"+f;!r[v]&&s<3&&(i(!1,"You are manually calling a React.PropTypes validation function for the `%s` prop on `%s`. This is deprecated and will throw in the standalone `prop-types` package. You may be seeing this warning due to a third-party PropTypes library. See https://fb.me/react-warning-dont-call-proptypes for details.",m,p),r[v]=!0,s++)}return null==c[f]?l?null===c[f]?new d("The "+h+" `"+m+"` is marked as required in `"+p+"`, but its value is `null`."):new d("The "+h+" `"+m+"` is marked as required in `"+p+"`, but its value is `undefined`."):null:e(c,f,p,h,m)}var c=l.bind(null,!1);return c.isRequired=l.bind(null,!0),c}function p(e){return f((function(t,n,r,o,i,a){var s=t[n];return m(s)!==e?new d("Invalid "+o+" `"+i+"` of type `"+g(s)+"` supplied to `"+r+"`, expected `"+e+"`."):null}))}function h(t){switch(typeof t){case"number":case"string":case"undefined":return!0;case"boolean":return!t;case"object":if(Array.isArray(t))return t.every(h);if(null===t||e(t))return!0;var n=function(e){var t=e&&(l&&e[l]||e["@@iterator"]);if("function"==typeof t)return t}(t);if(!n)return!1;var r,o=n.call(t);if(n!==t.entries){for(;!(r=o.next()).done;)if(!h(r.value))return!1}else for(;!(r=o.next()).done;){var i=r.value;if(i&&!h(i[1]))return!1}return!0;default:return!1}}function m(e){var t=typeof e;return Array.isArray(e)?"array":e instanceof RegExp?"object":function(e,t){return"symbol"===e||"Symbol"===t["@@toStringTag"]||"function"==typeof Symbol&&t instanceof Symbol}(t,e)?"symbol":t}function g(e){if(null==e)return""+e;var t=m(e);if("object"===t){if(e instanceof Date)return"date";if(e instanceof RegExp)return"regexp"}return t}function v(e){var t=g(e);switch(t){case"array":case"object":return"an "+t;case"boolean":case"date":case"regexp":return"a "+t;default:return t}}return d.prototype=Error.prototype,c.checkPropTypes=s,c.PropTypes=c,c}}).call(t,n(2))},function(e,t,n){"use strict";(function(t){if("production"!==t.env.NODE_ENV)var r=n(5),o=n(8),i=n(6),a={};e.exports=function(e,n,s,l,u){if("production"!==t.env.NODE_ENV)for(var c in e)if(e.hasOwnProperty(c)){var d;try{r("function"==typeof e[c],"%s: %s type `%s` is invalid; it must be a function, usually from React.PropTypes.",l||"React class",s,c),d=e[c](n,c,l,s,null,i)}catch(e){d=e}if(o(!d||d instanceof Error,"%s: type specification of %s `%s` is invalid; the type checker function must return `null` or an `Error` but returned a %s. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).",l||"React class",s,c,typeof d),d instanceof Error&&!(d.message in a)){a[d.message]=!0;var f=u?u():"";o(!1,"Failed %s type: %s%s",s,d.message,null!=f?f:"")}}}}).call(t,n(2))},function(e,t,n){"use strict";var r=n(4),o=n(5),i=n(6);e.exports=function(){function e(e,t,n,r,a,s){s!==i&&o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t};return n.checkPropTypes=r,n.PropTypes=n,n}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=a(n(0)),i=a(n(1));function a(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.point,n=e.text,r=e.fontSize,o=e.fontFamily,a=t.x,s=t.y;return i.default.createElement("g",null,i.default.createElement("text",{x:a,y:s,fontFamily:o||"Verdana",fontSize:r||10},n))}}]),t}(i.default.Component);s.propTypes={text:o.default.string,point:o.default.object,fontSize:o.default.number,fontFamily:o.default.string},s.defaultProps={text:"",point:{x:0,y:0}},t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=a(n(0)),i=a(n(1));function a(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.data,n=e.points,r=(e.width,e.height),o=e.margin,a=e.color,s=e.style,l=e.onMouseMove,u=n.map((function(e){return[e.x,e.y]})).reduce((function(e,t){return e.concat(t)})),c=[n[n.length-1].x,r-o,o,r-o,o,n[0].y],d=u.concat(c),f={stroke:a||s.stroke||"slategray",strokeWidth:s.strokeWidth||"1",strokeLinejoin:s.strokeLinejoin||"round",strokeLinecap:s.strokeLinecap||"round",fill:"none"},p={stroke:s.stroke||"none",strokeWidth:"0",fillOpacity:s.fillOpacity||".1",fill:s.fill||a||"slategray",pointerEvents:"auto"},h=n.map((function(e,n){return i.default.createElement("circle",{key:n,cx:e.x,cy:e.y,r:2,style:p,onMouseEnter:function(r){return l("enter",t[n],e)},onClick:function(r){return l("click",t[n],e)}})}));return i.default.createElement("g",null,h,i.default.createElement("polyline",{points:d.join(" "),style:p}),i.default.createElement("polyline",{points:u.join(" "),style:f}))}}]),t}(i.default.Component);s.propTypes={color:o.default.string,style:o.default.object},s.defaultProps={style:{},onMouseMove:function(){}},t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=a(n(0)),i=a(n(1));function a(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.points,n=(e.width,e.height),r=e.margin,o=e.color,a=e.style,s=e.divisor,l=void 0===s?.25:s,u=void 0,c=t.map((function(e){return function(e){var t=void 0;if(u){var n=(e.x-u.x)*l;t=["C",u.x+n,u.y,e.x-n,e.y,e.x,e.y]}else t=[e.x,e.y];return u=e,t}(e)})).reduce((function(e,t){return e.concat(t)})),d=["L"+t[t.length-1].x,n-r,r,n-r,r,t[0].y],f=c.concat(d),p={stroke:o||a.stroke||"slategray",strokeWidth:a.strokeWidth||"1",strokeLinejoin:a.strokeLinejoin||"round",strokeLinecap:a.strokeLinecap||"round",fill:"none"},h={stroke:a.stroke||"none",strokeWidth:"0",fillOpacity:a.fillOpacity||".1",fill:a.fill||o||"slategray"};return i.default.createElement("g",null,i.default.createElement("path",{d:"M"+f.join(" "),style:h}),i.default.createElement("path",{d:"M"+c.join(" "),style:p}))}}]),t}(i.default.Component);s.propTypes={color:o.default.string,style:o.default.object},s.defaultProps={style:{}},t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=a(n(0)),i=a(n(1));function a(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this,t=this.props,n=t.points,r=t.height,o=t.style,a=t.barWidth,s=t.margin,l=t.onMouseMove,u=1*(o&&o.strokeWidth||0),c=s?2*s:0,d=a||(n&&n.length>=2?Math.max(0,n[1].x-n[0].x-u-c):0);return i.default.createElement("g",{transform:"scale(1,-1)"},n.map((function(t,n){return i.default.createElement("rect",{key:n,x:t.x-(d+u)/2,y:-r,width:d,height:Math.max(0,r-t.y),style:o,onMouseMove:l&&l.bind(e,t)})})))}}]),t}(i.default.Component);s.propTypes={points:o.default.arrayOf(o.default.object),height:o.default.number,style:o.default.object,barWidth:o.default.number,margin:o.default.number,onMouseMove:o.default.func},s.defaultProps={style:{fill:"slategray"}},t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=a(n(0)),i=a(n(1));function a(e){return e&&e.__esModule?e:{default:e}}var s=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"lastDirection",value:function(e){return Math.sign=Math.sign||function(e){return e>0?1:-1},e.length<2?0:Math.sign(e[e.length-2].y-e[e.length-1].y)}},{key:"render",value:function(){var e=this.props,t=e.points,n=(e.width,e.height,e.size),r=e.style,o=e.spotColors,a=i.default.createElement("circle",{cx:t[0].x,cy:t[0].y,r:n,style:r}),s=i.default.createElement("circle",{cx:t[t.length-1].x,cy:t[t.length-1].y,r:n,style:r||{fill:o[this.lastDirection(t)]}});return i.default.createElement("g",null,r&&a,s)}}]),t}(i.default.Component);s.propTypes={size:o.default.number,style:o.default.object,spotColors:o.default.object},s.defaultProps={size:2,spotColors:{"-1":"red",0:"black",1:"green"}},t.default=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=s(n(0)),i=s(n(1)),a=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(n(23));function s(e){return e&&e.__esModule?e:{default:e}}var l=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.points,n=e.margin,r=e.type,o=e.style,s=e.value,l=t.map((function(e){return e.y})),u="custom"==r?s:a[r](l);return i.default.createElement("line",{x1:t[0].x,y1:u+n,x2:t[t.length-1].x,y2:u+n,style:o})}}]),t}(i.default.Component);l.propTypes={type:o.default.oneOf(["max","min","mean","avg","median","custom"]),value:o.default.number,style:o.default.object},l.defaultProps={type:"mean",style:{stroke:"red",strokeOpacity:.75,strokeDasharray:"2, 2"}},t.default=l},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.variance=t.stdev=t.median=t.midRange=t.avg=t.mean=t.max=t.min=void 0;var r=u(n(7)),o=u(n(3)),i=u(n(24)),a=u(n(25)),s=u(n(10)),l=u(n(26));function u(e){return e&&e.__esModule?e:{default:e}}t.min=r.default,t.max=r.default,t.mean=o.default,t.avg=o.default,t.midRange=i.default,t.median=a.default,t.stdev=s.default,t.variance=l.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=i(n(7)),o=i(n(9));function i(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){return(0,o.default)(e)-(0,r.default)(e)/2}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e.sort((function(e,t){return e-t}))[Math.floor(e.length/2)]}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=(r=n(3))&&r.__esModule?r:{default:r};t.default=function(e){var t=(0,o.default)(e),n=e.map((function(e){return Math.pow(e-t,2)}));return(0,o.default)(n)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=l(n(0)),i=l(n(1)),a=l(n(3)),s=l(n(10));function l(e){return e&&e.__esModule?e:{default:e}}var u=function(e){function t(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,(t.__proto__||Object.getPrototypeOf(t)).apply(this,arguments))}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"render",value:function(){var e=this.props,t=e.points,n=e.margin,r=e.style,o=t.map((function(e){return e.y})),l=(0,a.default)(o),u=(0,s.default)(o);return i.default.createElement("rect",{x:t[0].x,y:l-u+n,width:t[t.length-1].x-t[0].x,height:2*s.default,style:r})}}]),t}(i.default.Component);u.propTypes={style:o.default.object},u.defaultProps={style:{fill:"red",fillOpacity:.1}},t.default=u},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=i(n(7)),o=i(n(9));function i(e){return e&&e.__esModule?e:{default:e}}t.default=function(e){var t=e.data,n=e.limit,i=e.width,a=void 0===i?1:i,s=e.height,l=void 0===s?1:s,u=e.margin,c=void 0===u?0:u,d=e.max,f=void 0===d?(0,o.default)(t):d,p=e.min,h=void 0===p?(0,r.default)(t):p,m=t.length;n&&n<m&&(t=t.slice(m-n));var g=(l-2*c)/(f-h||2),v=(a-2*c)/((n||m)-(m>1?1:0));return t.map((function(e,t){return{x:t*v+c,y:(f===h?1:f-e)*g+c}}))}}])},e.exports=r(n(6540))},453:(e,t,n)=>{"use strict";var r,o=SyntaxError,i=Function,a=TypeError,s=function(e){try{return i('"use strict"; return ('+e+").constructor;")()}catch(e){}},l=Object.getOwnPropertyDescriptor;if(l)try{l({},"")}catch(e){l=null}var u=function(){throw new a},c=l?function(){try{return u}catch(e){try{return l(arguments,"callee").get}catch(e){return u}}}():u,d=n(4039)(),f=n(24)(),p=Object.getPrototypeOf||(f?function(e){return e.__proto__}:null),h={},m="undefined"!=typeof Uint8Array&&p?p(Uint8Array):r,g={"%AggregateError%":"undefined"==typeof AggregateError?r:AggregateError,"%Array%":Array,"%ArrayBuffer%":"undefined"==typeof ArrayBuffer?r:ArrayBuffer,"%ArrayIteratorPrototype%":d&&p?p([][Symbol.iterator]()):r,"%AsyncFromSyncIteratorPrototype%":r,"%AsyncFunction%":h,"%AsyncGenerator%":h,"%AsyncGeneratorFunction%":h,"%AsyncIteratorPrototype%":h,"%Atomics%":"undefined"==typeof Atomics?r:Atomics,"%BigInt%":"undefined"==typeof BigInt?r:BigInt,"%BigInt64Array%":"undefined"==typeof BigInt64Array?r:BigInt64Array,"%BigUint64Array%":"undefined"==typeof BigUint64Array?r:BigUint64Array,"%Boolean%":Boolean,"%DataView%":"undefined"==typeof DataView?r:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":Error,"%eval%":eval,"%EvalError%":EvalError,"%Float32Array%":"undefined"==typeof Float32Array?r:Float32Array,"%Float64Array%":"undefined"==typeof Float64Array?r:Float64Array,"%FinalizationRegistry%":"undefined"==typeof FinalizationRegistry?r:FinalizationRegistry,"%Function%":i,"%GeneratorFunction%":h,"%Int8Array%":"undefined"==typeof Int8Array?r:Int8Array,"%Int16Array%":"undefined"==typeof Int16Array?r:Int16Array,"%Int32Array%":"undefined"==typeof Int32Array?r:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":d&&p?p(p([][Symbol.iterator]())):r,"%JSON%":"object"==typeof JSON?JSON:r,"%Map%":"undefined"==typeof Map?r:Map,"%MapIteratorPrototype%":"undefined"!=typeof Map&&d&&p?p((new Map)[Symbol.iterator]()):r,"%Math%":Math,"%Number%":Number,"%Object%":Object,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":"undefined"==typeof Promise?r:Promise,"%Proxy%":"undefined"==typeof Proxy?r:Proxy,"%RangeError%":RangeError,"%ReferenceError%":ReferenceError,"%Reflect%":"undefined"==typeof Reflect?r:Reflect,"%RegExp%":RegExp,"%Set%":"undefined"==typeof Set?r:Set,"%SetIteratorPrototype%":"undefined"!=typeof Set&&d&&p?p((new Set)[Symbol.iterator]()):r,"%SharedArrayBuffer%":"undefined"==typeof SharedArrayBuffer?r:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":d&&p?p(""[Symbol.iterator]()):r,"%Symbol%":d?Symbol:r,"%SyntaxError%":o,"%ThrowTypeError%":c,"%TypedArray%":m,"%TypeError%":a,"%Uint8Array%":"undefined"==typeof Uint8Array?r:Uint8Array,"%Uint8ClampedArray%":"undefined"==typeof Uint8ClampedArray?r:Uint8ClampedArray,"%Uint16Array%":"undefined"==typeof Uint16Array?r:Uint16Array,"%Uint32Array%":"undefined"==typeof Uint32Array?r:Uint32Array,"%URIError%":URIError,"%WeakMap%":"undefined"==typeof WeakMap?r:WeakMap,"%WeakRef%":"undefined"==typeof WeakRef?r:WeakRef,"%WeakSet%":"undefined"==typeof WeakSet?r:WeakSet};if(p)try{null.error}catch(e){var v=p(p(e));g["%Error.prototype%"]=v}var y=function e(t){var n;if("%AsyncFunction%"===t)n=s("async function () {}");else if("%GeneratorFunction%"===t)n=s("function* () {}");else if("%AsyncGeneratorFunction%"===t)n=s("async function* () {}");else if("%AsyncGenerator%"===t){var r=e("%AsyncGeneratorFunction%");r&&(n=r.prototype)}else if("%AsyncIteratorPrototype%"===t){var o=e("%AsyncGenerator%");o&&p&&(n=p(o.prototype))}return g[t]=n,n},b={"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},w=n(6743),E=n(9957),S=w.call(Function.call,Array.prototype.concat),x=w.call(Function.apply,Array.prototype.splice),A=w.call(Function.call,String.prototype.replace),k=w.call(Function.call,String.prototype.slice),_=w.call(Function.call,RegExp.prototype.exec),C=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,O=/\\(\\)?/g,P=function(e,t){var n,r=e;if(E(b,r)&&(r="%"+(n=b[r])[0]+"%"),E(g,r)){var i=g[r];if(i===h&&(i=y(r)),void 0===i&&!t)throw new a("intrinsic "+e+" exists, but is not available. Please file an issue!");return{alias:n,name:r,value:i}}throw new o("intrinsic "+e+" does not exist!")};e.exports=function(e,t){if("string"!=typeof e||0===e.length)throw new a("intrinsic name must be a non-empty string");if(arguments.length>1&&"boolean"!=typeof t)throw new a('"allowMissing" argument must be a boolean');if(null===_(/^%?[^%]*%?$/,e))throw new o("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var n=function(e){var t=k(e,0,1),n=k(e,-1);if("%"===t&&"%"!==n)throw new o("invalid intrinsic syntax, expected closing `%`");if("%"===n&&"%"!==t)throw new o("invalid intrinsic syntax, expected opening `%`");var r=[];return A(e,C,(function(e,t,n,o){r[r.length]=n?A(o,O,"$1"):t||e})),r}(e),r=n.length>0?n[0]:"",i=P("%"+r+"%",t),s=i.name,u=i.value,c=!1,d=i.alias;d&&(r=d[0],x(n,S([0,1],d)));for(var f=1,p=!0;f<n.length;f+=1){var h=n[f],m=k(h,0,1),v=k(h,-1);if(('"'===m||"'"===m||"`"===m||'"'===v||"'"===v||"`"===v)&&m!==v)throw new o("property names with quotes must have matching quotes");if("constructor"!==h&&p||(c=!0),E(g,s="%"+(r+="."+h)+"%"))u=g[s];else if(null!=u){if(!(h in u)){if(!t)throw new a("base intrinsic for "+e+" exists, but the property is not available.");return}if(l&&f+1>=n.length){var y=l(u,h);u=(p=!!y)&&"get"in y&&!("originalValue"in y.get)?y.get:u[h]}else p=E(u,h),u=u[h];p&&!c&&(g[s]=u)}}return u}},483:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M13 3h-2v10h2V3zm4.83 2.17-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"}),"PowerSettingsNew");t.A=a},487:(e,t,n)=>{"use strict";var r=n(6743),o=n(453),i=n(6897),a=o("%TypeError%"),s=o("%Function.prototype.apply%"),l=o("%Function.prototype.call%"),u=o("%Reflect.apply%",!0)||r.call(l,s),c=o("%Object.defineProperty%",!0),d=o("%Math.max%");if(c)try{c({},"a",{value:1})}catch(e){c=null}e.exports=function(e){if("function"!=typeof e)throw new a("a function is required");var t=u(r,l,arguments);return i(t,1+d(0,e.length-(arguments.length-1)),!0)};var f=function(){return u(r,s,arguments)};c?c(e.exports,"apply",{value:f}):e.exports.apply=f},537:(e,t,n)=>{var r=n(5606),o=Object.getOwnPropertyDescriptors||function(e){for(var t=Object.keys(e),n={},r=0;r<t.length;r++)n[t[r]]=Object.getOwnPropertyDescriptor(e,t[r]);return n},i=/%[sdj%]/g;t.format=function(e){if(!b(e)){for(var t=[],n=0;n<arguments.length;n++)t.push(u(arguments[n]));return t.join(" ")}n=1;for(var r=arguments,o=r.length,a=String(e).replace(i,(function(e){if("%%"===e)return"%";if(n>=o)return e;switch(e){case"%s":return String(r[n++]);case"%d":return Number(r[n++]);case"%j":try{return JSON.stringify(r[n++])}catch(e){return"[Circular]"}default:return e}})),s=r[n];n<o;s=r[++n])v(s)||!S(s)?a+=" "+s:a+=" "+u(s);return a},t.deprecate=function(e,n){if(void 0!==r&&!0===r.noDeprecation)return e;if(void 0===r)return function(){return t.deprecate(e,n).apply(this,arguments)};var o=!1;return function(){if(!o){if(r.throwDeprecation)throw new Error(n);r.traceDeprecation?console.trace(n):console.error(n),o=!0}return e.apply(this,arguments)}};var a={},s=/^$/;if(r.env.NODE_DEBUG){var l=r.env.NODE_DEBUG;l=l.replace(/[|\\{}()[\]^$+?.]/g,"\\$&").replace(/\*/g,".*").replace(/,/g,"$|^").toUpperCase(),s=new RegExp("^"+l+"$","i")}function u(e,n){var r={seen:[],stylize:d};return arguments.length>=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),g(n)?r.showHidden=n:n&&t._extend(r,n),w(r.showHidden)&&(r.showHidden=!1),w(r.depth)&&(r.depth=2),w(r.colors)&&(r.colors=!1),w(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=c),f(r,e,r.depth)}function c(e,t){var n=u.styles[t];return n?"["+u.colors[n][0]+"m"+e+"["+u.colors[n][1]+"m":e}function d(e,t){return e}function f(e,n,r){if(e.customInspect&&n&&k(n.inspect)&&n.inspect!==t.inspect&&(!n.constructor||n.constructor.prototype!==n)){var o=n.inspect(r,e);return b(o)||(o=f(e,o,r)),o}var i=function(e,t){if(w(t))return e.stylize("undefined","undefined");if(b(t)){var n="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(n,"string")}return y(t)?e.stylize(""+t,"number"):g(t)?e.stylize(""+t,"boolean"):v(t)?e.stylize("null","null"):void 0}(e,n);if(i)return i;var a=Object.keys(n),s=function(e){var t={};return e.forEach((function(e,n){t[e]=!0})),t}(a);if(e.showHidden&&(a=Object.getOwnPropertyNames(n)),A(n)&&(a.indexOf("message")>=0||a.indexOf("description")>=0))return p(n);if(0===a.length){if(k(n)){var l=n.name?": "+n.name:"";return e.stylize("[Function"+l+"]","special")}if(E(n))return e.stylize(RegExp.prototype.toString.call(n),"regexp");if(x(n))return e.stylize(Date.prototype.toString.call(n),"date");if(A(n))return p(n)}var u,c="",d=!1,S=["{","}"];return m(n)&&(d=!0,S=["[","]"]),k(n)&&(c=" [Function"+(n.name?": "+n.name:"")+"]"),E(n)&&(c=" "+RegExp.prototype.toString.call(n)),x(n)&&(c=" "+Date.prototype.toUTCString.call(n)),A(n)&&(c=" "+p(n)),0!==a.length||d&&0!=n.length?r<0?E(n)?e.stylize(RegExp.prototype.toString.call(n),"regexp"):e.stylize("[Object]","special"):(e.seen.push(n),u=d?function(e,t,n,r,o){for(var i=[],a=0,s=t.length;a<s;++a)P(t,String(a))?i.push(h(e,t,n,r,String(a),!0)):i.push("");return o.forEach((function(o){o.match(/^\d+$/)||i.push(h(e,t,n,r,o,!0))})),i}(e,n,r,s,a):a.map((function(t){return h(e,n,r,s,t,d)})),e.seen.pop(),function(e,t,n){return e.reduce((function(e,t){return t.indexOf("\n"),e+t.replace(/\u001b\[\d\d?m/g,"").length+1}),0)>60?n[0]+(""===t?"":t+"\n ")+" "+e.join(",\n  ")+" "+n[1]:n[0]+t+" "+e.join(", ")+" "+n[1]}(u,c,S)):S[0]+c+S[1]}function p(e){return"["+Error.prototype.toString.call(e)+"]"}function h(e,t,n,r,o,i){var a,s,l;if((l=Object.getOwnPropertyDescriptor(t,o)||{value:t[o]}).get?s=l.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):l.set&&(s=e.stylize("[Setter]","special")),P(r,o)||(a="["+o+"]"),s||(e.seen.indexOf(l.value)<0?(s=v(n)?f(e,l.value,null):f(e,l.value,n-1)).indexOf("\n")>-1&&(s=i?s.split("\n").map((function(e){return"  "+e})).join("\n").slice(2):"\n"+s.split("\n").map((function(e){return"   "+e})).join("\n")):s=e.stylize("[Circular]","special")),w(a)){if(i&&o.match(/^\d+$/))return s;(a=JSON.stringify(""+o)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.slice(1,-1),a=e.stylize(a,"name")):(a=a.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),a=e.stylize(a,"string"))}return a+": "+s}function m(e){return Array.isArray(e)}function g(e){return"boolean"==typeof e}function v(e){return null===e}function y(e){return"number"==typeof e}function b(e){return"string"==typeof e}function w(e){return void 0===e}function E(e){return S(e)&&"[object RegExp]"===_(e)}function S(e){return"object"==typeof e&&null!==e}function x(e){return S(e)&&"[object Date]"===_(e)}function A(e){return S(e)&&("[object Error]"===_(e)||e instanceof Error)}function k(e){return"function"==typeof e}function _(e){return Object.prototype.toString.call(e)}function C(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(e){if(e=e.toUpperCase(),!a[e])if(s.test(e)){var n=r.pid;a[e]=function(){var r=t.format.apply(t,arguments);console.error("%s %d: %s",e,n,r)}}else a[e]=function(){};return a[e]},t.inspect=u,u.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},u.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.types=n(9032),t.isArray=m,t.isBoolean=g,t.isNull=v,t.isNullOrUndefined=function(e){return null==e},t.isNumber=y,t.isString=b,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=w,t.isRegExp=E,t.types.isRegExp=E,t.isObject=S,t.isDate=x,t.types.isDate=x,t.isError=A,t.types.isNativeError=A,t.isFunction=k,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=n(1135);var O=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function P(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){var e,n;console.log("%s - %s",(n=[C((e=new Date).getHours()),C(e.getMinutes()),C(e.getSeconds())].join(":"),[e.getDate(),O[e.getMonth()],n].join(" ")),t.format.apply(t,arguments))},t.inherits=n(6698),t._extend=function(e,t){if(!t||!S(t))return e;for(var n=Object.keys(t),r=n.length;r--;)e[n[r]]=t[n[r]];return e};var R="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function T(e,t){if(!e){var n=new Error("Promise was rejected with a falsy value");n.reason=e,e=n}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(R&&e[R]){var t;if("function"!=typeof(t=e[R]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,R,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,n,r=new Promise((function(e,r){t=e,n=r})),o=[],i=0;i<arguments.length;i++)o.push(arguments[i]);o.push((function(e,r){e?n(e):t(r)}));try{e.apply(this,o)}catch(e){n(e)}return r}return Object.setPrototypeOf(t,Object.getPrototypeOf(e)),R&&Object.defineProperty(t,R,{value:t,enumerable:!1,writable:!1,configurable:!0}),Object.defineProperties(t,o(e))},t.promisify.custom=R,t.callbackify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');function t(){for(var t=[],n=0;n<arguments.length;n++)t.push(arguments[n]);var o=t.pop();if("function"!=typeof o)throw new TypeError("The last argument must be of type Function");var i=this,a=function(){return o.apply(i,arguments)};e.apply(this,t).then((function(e){r.nextTick(a.bind(null,null,e))}),(function(e){r.nextTick(T.bind(null,e,a))}))}return Object.setPrototypeOf(t,Object.getPrototypeOf(e)),Object.defineProperties(t,o(e)),t}},561:(e,t,n)=>{"use strict";n.d(t,{A:()=>s});var r=n(8168),o=n(6540),i=n(8777),a=n(4848);function s(e,t){function n(n,o){return(0,a.jsx)(i.A,(0,r.A)({"data-testid":`${t}Icon`,ref:o},n,{children:e}))}return n.muiName=i.A.muiName,o.memo(o.forwardRef(n))}},579:(e,t,n)=>{var r=n(3738).default;e.exports=function(e){if(null!=e){var t=e["function"==typeof Symbol&&Symbol.iterator||"@@iterator"],n=0;if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length))return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}}throw new TypeError(r(e)+" is not iterable")},e.exports.__esModule=!0,e.exports.default=e.exports},583:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(5556));t.configurationPropTypes=o.shape({client_id:o.string.isRequired,redirect_uri:o.string.isRequired,response_type:o.string.isRequired,scope:o.string.isRequired,authority:o.string.isRequired,silent_redirect_uri:o.string.isRequired,automaticSilentRenew:o.bool,loadUserInfo:o.bool,triggerAuthFlow:o.bool,storeJwtInMemory:o.bool,metadata:o.shape({issuer:o.string,jwks_uri:o.string,authorization_endpoint:o.string,token_endpoint:o.string,userinfo_endpoint:o.string,end_session_endpoint:o.string,revocation_endpoint:o.string,introspection_endpoint:o.string})}).isRequired,t.configurationDefaultProps={automaticSilentRenew:!0,loadUserInfo:!0,triggerAuthFlow:!0,storeJwtInMemory:!1}},592:(e,t,n)=>{"use strict";var r=n(453)("%Object.defineProperty%",!0),o=function(){if(r)try{return r({},"a",{value:1}),!0}catch(e){return!1}return!1};o.hasArrayLengthDefineBug=function(){if(!o())return null;try{return 1!==r([],"length",{value:1}).length}catch(e){return!0}},e.exports=o},634:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6.99 11 3 15l3.99 4v-3H14v-2H6.99v-3zM21 9l-3.99-4v3H10v2h7.01v3L21 9z"}),"SwapHoriz");t.A=a},684:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"m10 17 5-5-5-5v10z"}),"ArrowRight");t.A=a},790:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 4c1.93 0 3.5 1.57 3.5 3.5S13.93 13 12 13s-3.5-1.57-3.5-3.5S10.07 6 12 6zm0 14c-2.03 0-4.43-.82-6.14-2.88C7.55 15.8 9.68 15 12 15s4.45.8 6.14 2.12C16.43 19.18 14.03 20 12 20z"}),"AccountCircle");t.A=a},884:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M3 17v-2c0-1.1.9-2 2-2h14c1.1 0 2 .9 2 2v2c0 1.1-.9 2-2 2H5c-1.1 0-2-.9-2-2zM3 7v2c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2z"}),"ViewStream");t.A=a},887:(e,t,n)=>{var r=n(6993),o=n(1791);e.exports=function(e,t,n,i,a){return new o(r().w(e,t,n,i),a||Promise)},e.exports.__esModule=!0,e.exports.default=e.exports},908:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M10.59 9.17 5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"}),"Shuffle");t.A=a},920:(e,t,n)=>{"use strict";var r=n(453),o=n(8075),i=n(8859),a=r("%TypeError%"),s=r("%WeakMap%",!0),l=r("%Map%",!0),u=o("WeakMap.prototype.get",!0),c=o("WeakMap.prototype.set",!0),d=o("WeakMap.prototype.has",!0),f=o("Map.prototype.get",!0),p=o("Map.prototype.set",!0),h=o("Map.prototype.has",!0),m=function(e,t){for(var n,r=e;null!==(n=r.next);r=n)if(n.key===t)return r.next=n.next,n.next=e.next,e.next=n,n};e.exports=function(){var e,t,n,r={assert:function(e){if(!r.has(e))throw new a("Side channel does not contain "+i(e))},get:function(r){if(s&&r&&("object"==typeof r||"function"==typeof r)){if(e)return u(e,r)}else if(l){if(t)return f(t,r)}else if(n)return function(e,t){var n=m(e,t);return n&&n.value}(n,r)},has:function(r){if(s&&r&&("object"==typeof r||"function"==typeof r)){if(e)return d(e,r)}else if(l){if(t)return h(t,r)}else if(n)return function(e,t){return!!m(e,t)}(n,r);return!1},set:function(r,o){s&&r&&("object"==typeof r||"function"==typeof r)?(e||(e=new s),c(e,r,o)):l?(t||(t=new l),p(t,r,o)):(n||(n={key:{},next:null}),function(e,t,n){var r=m(e,t);r?r.value=n:e.next={key:t,next:e.next,value:n}}(n,r,o))}};return r}},926:(e,t,n)=>{"use strict";n.d(t,{Ay:()=>y,q_:()=>b});var r=n(8168),o=n(6540),i=n(6289),a=/^((children|dangerouslySetInnerHTML|key|ref|autoFocus|defaultValue|defaultChecked|innerHTML|suppressContentEditableWarning|suppressHydrationWarning|valueLink|abbr|accept|acceptCharset|accessKey|action|allow|allowUserMedia|allowPaymentRequest|allowFullScreen|allowTransparency|alt|async|autoComplete|autoPlay|capture|cellPadding|cellSpacing|challenge|charSet|checked|cite|classID|className|cols|colSpan|content|contentEditable|contextMenu|controls|controlsList|coords|crossOrigin|data|dateTime|decoding|default|defer|dir|disabled|disablePictureInPicture|download|draggable|encType|enterKeyHint|form|formAction|formEncType|formMethod|formNoValidate|formTarget|frameBorder|headers|height|hidden|high|href|hrefLang|htmlFor|httpEquiv|id|inputMode|integrity|is|keyParams|keyType|kind|label|lang|list|loading|loop|low|marginHeight|marginWidth|max|maxLength|media|mediaGroup|method|min|minLength|multiple|muted|name|nonce|noValidate|open|optimum|pattern|placeholder|playsInline|poster|preload|profile|radioGroup|readOnly|referrerPolicy|rel|required|reversed|role|rows|rowSpan|sandbox|scope|scoped|scrolling|seamless|selected|shape|size|sizes|slot|span|spellCheck|src|srcDoc|srcLang|srcSet|start|step|style|summary|tabIndex|target|title|translate|type|useMap|value|width|wmode|wrap|about|datatype|inlist|prefix|property|resource|typeof|vocab|autoCapitalize|autoCorrect|autoSave|color|incremental|fallback|inert|itemProp|itemScope|itemType|itemID|itemRef|on|option|results|security|unselectable|accentHeight|accumulate|additive|alignmentBaseline|allowReorder|alphabetic|amplitude|arabicForm|ascent|attributeName|attributeType|autoReverse|azimuth|baseFrequency|baselineShift|baseProfile|bbox|begin|bias|by|calcMode|capHeight|clip|clipPathUnits|clipPath|clipRule|colorInterpolation|colorInterpolationFilters|colorProfile|colorRendering|contentScriptType|contentStyleType|cursor|cx|cy|d|decelerate|descent|diffuseConstant|direction|display|divisor|dominantBaseline|dur|dx|dy|edgeMode|elevation|enableBackground|end|exponent|externalResourcesRequired|fill|fillOpacity|fillRule|filter|filterRes|filterUnits|floodColor|floodOpacity|focusable|fontFamily|fontSize|fontSizeAdjust|fontStretch|fontStyle|fontVariant|fontWeight|format|from|fr|fx|fy|g1|g2|glyphName|glyphOrientationHorizontal|glyphOrientationVertical|glyphRef|gradientTransform|gradientUnits|hanging|horizAdvX|horizOriginX|ideographic|imageRendering|in|in2|intercept|k|k1|k2|k3|k4|kernelMatrix|kernelUnitLength|kerning|keyPoints|keySplines|keyTimes|lengthAdjust|letterSpacing|lightingColor|limitingConeAngle|local|markerEnd|markerMid|markerStart|markerHeight|markerUnits|markerWidth|mask|maskContentUnits|maskUnits|mathematical|mode|numOctaves|offset|opacity|operator|order|orient|orientation|origin|overflow|overlinePosition|overlineThickness|panose1|paintOrder|pathLength|patternContentUnits|patternTransform|patternUnits|pointerEvents|points|pointsAtX|pointsAtY|pointsAtZ|preserveAlpha|preserveAspectRatio|primitiveUnits|r|radius|refX|refY|renderingIntent|repeatCount|repeatDur|requiredExtensions|requiredFeatures|restart|result|rotate|rx|ry|scale|seed|shapeRendering|slope|spacing|specularConstant|specularExponent|speed|spreadMethod|startOffset|stdDeviation|stemh|stemv|stitchTiles|stopColor|stopOpacity|strikethroughPosition|strikethroughThickness|string|stroke|strokeDasharray|strokeDashoffset|strokeLinecap|strokeLinejoin|strokeMiterlimit|strokeOpacity|strokeWidth|surfaceScale|systemLanguage|tableValues|targetX|targetY|textAnchor|textDecoration|textRendering|textLength|to|transform|u1|u2|underlinePosition|underlineThickness|unicode|unicodeBidi|unicodeRange|unitsPerEm|vAlphabetic|vHanging|vIdeographic|vMathematical|values|vectorEffect|version|vertAdvY|vertOriginX|vertOriginY|viewBox|viewTarget|visibility|widths|wordSpacing|writingMode|x|xHeight|x1|x2|xChannelSelector|xlinkActuate|xlinkArcrole|xlinkHref|xlinkRole|xlinkShow|xlinkTitle|xlinkType|xmlBase|xmlns|xmlnsXlink|xmlLang|xmlSpace|y|y1|y2|yChannelSelector|z|zoomAndPan|for|class|autofocus)|(([Dd][Aa][Tt][Aa]|[Aa][Rr][Ii][Aa]|x)-.*))$/,s=(0,i.A)((function(e){return a.test(e)||111===e.charCodeAt(0)&&110===e.charCodeAt(1)&&e.charCodeAt(2)<91})),l=n(5684),u=n(2422),c=n(7308),d=n(1287),f=s,p=function(e){return"theme"!==e},h=function(e){return"string"==typeof e&&e.charCodeAt(0)>96?f:p},m=function(e,t,n){var r;if(t){var o=t.shouldForwardProp;r=e.__emotion_forwardProp&&o?function(t){return e.__emotion_forwardProp(t)&&o(t)}:o}return"function"!=typeof r&&n&&(r=e.__emotion_forwardProp),r},g=function(e){var t=e.cache,n=e.serialized,r=e.isStringTag;return(0,u.SF)(t,n,r),(0,d.s)((function(){return(0,u.sk)(t,n,r)})),null},v=function e(t,n){var i,a,s=t.__emotion_real===t,d=s&&t.__emotion_base||t;void 0!==n&&(i=n.label,a=n.target);var f=m(t,n,s),p=f||h(d),v=!p("as");return function(){var y=arguments,b=s&&void 0!==t.__emotion_styles?t.__emotion_styles.slice(0):[];if(void 0!==i&&b.push("label:"+i+";"),null==y[0]||void 0===y[0].raw)b.push.apply(b,y);else{b.push(y[0][0]);for(var w=y.length,E=1;E<w;E++)b.push(y[E],y[0][E])}var S=(0,l.w)((function(e,t,n){var r=v&&e.as||d,i="",s=[],m=e;if(null==e.theme){for(var y in m={},e)m[y]=e[y];m.theme=o.useContext(l.T)}"string"==typeof e.className?i=(0,u.Rk)(t.registered,s,e.className):null!=e.className&&(i=e.className+" ");var w=(0,c.J)(b.concat(s),t.registered,m);i+=t.key+"-"+w.name,void 0!==a&&(i+=" "+a);var E=v&&void 0===f?h(r):p,S={};for(var x in e)v&&"as"===x||E(x)&&(S[x]=e[x]);return S.className=i,S.ref=n,o.createElement(o.Fragment,null,o.createElement(g,{cache:t,serialized:w,isStringTag:"string"==typeof r}),o.createElement(r,S))}));return S.displayName=void 0!==i?i:"Styled("+("string"==typeof d?d:d.displayName||d.name||"Component")+")",S.defaultProps=t.defaultProps,S.__emotion_real=S,S.__emotion_base=d,S.__emotion_styles=b,S.__emotion_forwardProp=f,Object.defineProperty(S,"toString",{value:function(){return"."+a}}),S.withComponent=function(t,o){return e(t,(0,r.A)({},n,o,{shouldForwardProp:m(S,o,!0)})).apply(void 0,b)},S}}.bind();function y(e,t){return v(e,t)}["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","main","map","mark","marquee","menu","menuitem","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","param","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr","circle","clipPath","defs","ellipse","foreignObject","g","image","line","linearGradient","mask","path","pattern","polygon","polyline","radialGradient","rect","stop","svg","text","tspan"].forEach((function(e){v[e]=v(e)}));const b=(e,t)=>{Array.isArray(e.__emotion_styles)&&(e.__emotion_styles=t(e.__emotion_styles))}},961:(e,t,n)=>{"use strict";!function e(){if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE)try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(e){console.error(e)}}(),e.exports=n(2551)},989:(e,t,n)=>{"use strict";function r(e,t){"function"==typeof e?e(t):e&&(e.current=t)}n.d(t,{A:()=>r})},1020:(e,t,n)=>{"use strict";var r=n(6540),o=Symbol.for("react.element"),i=Symbol.for("react.fragment"),a=Object.prototype.hasOwnProperty,s=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function u(e,t,n){var r,i={},u=null,c=null;for(r in void 0!==n&&(u=""+n),void 0!==t.key&&(u=""+t.key),void 0!==t.ref&&(c=t.ref),t)a.call(t,r)&&!l.hasOwnProperty(r)&&(i[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps)void 0===i[r]&&(i[r]=t[r]);return{$$typeof:o,type:e,key:u,ref:c,props:i,_owner:s.current}}t.Fragment=i,t.jsx=u,t.jsxs=u},1083:(e,t,n)=>{var r=n(1568),o=n(8835),i=e.exports;for(var a in r)r.hasOwnProperty(a)&&(i[a]=r[a]);function s(e){if("string"==typeof e&&(e=o.parse(e)),e.protocol||(e.protocol="https:"),"https:"!==e.protocol)throw new Error('Protocol "'+e.protocol+'" not supported. Expected "https:"');return e}i.request=function(e,t){return e=s(e),r.request.call(this,e,t)},i.get=function(e,t){return e=s(e),r.get.call(this,e,t)}},1135:e=>{e.exports=function(e){return e&&"object"==typeof e&&"function"==typeof e.copy&&"function"==typeof e.fill&&"function"==typeof e.readUInt8}},1159:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(2501).A},1183:function(e){var t,n;e.exports=(t={772:(e,t,n)=>{const r=n(826).remove,o=/[.*+?^${}()|[\]\\]/g,i=/[a-z0-9_]/i,a=/\s+/;e.exports=function(e,t,n){var s,l;l={insideWords:!1,findAllOccurrences:!1,requireMatchAll:!1},s=(s=n)||{},Object.keys(s).forEach((e=>{l[e]=!!s[e]})),n=l;const u=Array.from(e).map((e=>r(e)));let c=u.join("");return(t=r(t)).trim().split(a).filter((e=>e.length>0)).reduce(((e,t)=>{const r=t.length,a=!n.insideWords&&i.test(t[0])?"\\b":"",s=new RegExp(a+t.replace(o,"\\$&"),"i");let l,d;if(l=s.exec(c),n.requireMatchAll&&null===l)return c="",[];for(;l;){d=l.index;const t=r-u.slice(d,d+r).join("").length,o=d-u.slice(0,d).join("").length,i=[d+o,d+r+o+t];if(i[0]!==i[1]&&e.push(i),c=c.slice(0,d)+new Array(r+1).join(" ")+c.slice(d+r),!n.findAllOccurrences)break;l=s.exec(c)}return e}),[]).sort(((e,t)=>e[0]-t[0]))}},826:e=>{var t={Ã€:"A",Ã:"A",Ã‚:"A",Ãƒ:"A",Ã„:"A",Ã…:"A",áº¤:"A",áº®:"A",áº²:"A",áº´:"A",áº¶:"A",Ã†:"AE",áº¦:"A",áº°:"A",È‚:"A",Ã‡:"C",á¸ˆ:"C",Ãˆ:"E",Ã‰:"E",ÃŠ:"E",Ã‹:"E",áº¾:"E",á¸–:"E",á»€:"E",á¸”:"E",á¸œ:"E",È†:"E",ÃŒ:"I",Ã:"I",ÃŽ:"I",Ã:"I",á¸®:"I",ÈŠ:"I",Ã:"D",Ã‘:"N",Ã’:"O",Ã“:"O",Ã”:"O",Ã•:"O",Ã–:"O",Ã˜:"O",á»:"O",á¹Œ:"O",á¹’:"O",ÈŽ:"O",Ã™:"U",Ãš:"U",Ã›:"U",Ãœ:"U",Ã:"Y",Ã :"a",Ã¡:"a",Ã¢:"a",Ã£:"a",Ã¤:"a",Ã¥:"a",áº¥:"a",áº¯:"a",áº³:"a",áºµ:"a",áº·:"a",Ã¦:"ae",áº§:"a",áº±:"a",Èƒ:"a",Ã§:"c",á¸‰:"c",Ã¨:"e",Ã©:"e",Ãª:"e",Ã«:"e",áº¿:"e",á¸—:"e",á»:"e",á¸•:"e",á¸:"e",È‡:"e",Ã¬:"i",Ã­:"i",Ã®:"i",Ã¯:"i",á¸¯:"i",È‹:"i",Ã°:"d",Ã±:"n",Ã²:"o",Ã³:"o",Ã´:"o",Ãµ:"o",Ã¶:"o",Ã¸:"o",á»‘:"o",á¹:"o",á¹“:"o",È:"o",Ã¹:"u",Ãº:"u",Ã»:"u",Ã¼:"u",Ã½:"y",Ã¿:"y",Ä€:"A",Ä:"a",Ä‚:"A",Äƒ:"a",Ä„:"A",Ä…:"a",Ä†:"C",Ä‡:"c",Äˆ:"C",Ä‰:"c",ÄŠ:"C",Ä‹:"c",ÄŒ:"C",Ä:"c",CÌ†:"C",cÌ†:"c",ÄŽ:"D",Ä:"d",Ä:"D",Ä‘:"d",Ä’:"E",Ä“:"e",Ä”:"E",Ä•:"e",Ä–:"E",Ä—:"e",Ä˜:"E",Ä™:"e",Äš:"E",Ä›:"e",Äœ:"G",Ç´:"G",Ä:"g",Çµ:"g",Äž:"G",ÄŸ:"g",Ä :"G",Ä¡:"g",Ä¢:"G",Ä£:"g",Ä¤:"H",Ä¥:"h",Ä¦:"H",Ä§:"h",á¸ª:"H",á¸«:"h",Ä¨:"I",Ä©:"i",Äª:"I",Ä«:"i",Ä¬:"I",Ä­:"i",Ä®:"I",Ä¯:"i",Ä°:"I",Ä±:"i",Ä²:"IJ",Ä³:"ij",Ä´:"J",Äµ:"j",Ä¶:"K",Ä·:"k",á¸°:"K",á¸±:"k",KÌ†:"K",kÌ†:"k",Ä¹:"L",Äº:"l",Ä»:"L",Ä¼:"l",Ä½:"L",Ä¾:"l",Ä¿:"L",Å€:"l",Å:"l",Å‚:"l",á¸¾:"M",á¸¿:"m",MÌ†:"M",mÌ†:"m",Åƒ:"N",Å„:"n",Å…:"N",Å†:"n",Å‡:"N",Åˆ:"n",Å‰:"n",NÌ†:"N",nÌ†:"n",ÅŒ:"O",Å:"o",ÅŽ:"O",Å:"o",Å:"O",Å‘:"o",Å’:"OE",Å“:"oe",PÌ†:"P",pÌ†:"p",Å”:"R",Å•:"r",Å–:"R",Å—:"r",Å˜:"R",Å™:"r",RÌ†:"R",rÌ†:"r",È’:"R",È“:"r",Åš:"S",Å›:"s",Åœ:"S",Å:"s",Åž:"S",È˜:"S",È™:"s",ÅŸ:"s",Å :"S",Å¡:"s",Å¢:"T",Å£:"t",È›:"t",Èš:"T",Å¤:"T",Å¥:"t",Å¦:"T",Å§:"t",TÌ†:"T",tÌ†:"t",Å¨:"U",Å©:"u",Åª:"U",Å«:"u",Å¬:"U",Å­:"u",Å®:"U",Å¯:"u",Å°:"U",Å±:"u",Å²:"U",Å³:"u",È–:"U",È—:"u",VÌ†:"V",vÌ†:"v",Å´:"W",Åµ:"w",áº‚:"W",áºƒ:"w",XÌ†:"X",xÌ†:"x",Å¶:"Y",Å·:"y",Å¸:"Y",YÌ†:"Y",yÌ†:"y",Å¹:"Z",Åº:"z",Å»:"Z",Å¼:"z",Å½:"Z",Å¾:"z",Å¿:"s",Æ’:"f",Æ :"O",Æ¡:"o",Æ¯:"U",Æ°:"u",Ç:"A",ÇŽ:"a",Ç:"I",Ç:"i",Ç‘:"O",Ç’:"o",Ç“:"U",Ç”:"u",Ç•:"U",Ç–:"u",Ç—:"U",Ç˜:"u",Ç™:"U",Çš:"u",Ç›:"U",Çœ:"u",á»¨:"U",á»©:"u",á¹¸:"U",á¹¹:"u",Çº:"A",Ç»:"a",Ç¼:"AE",Ç½:"ae",Ç¾:"O",Ç¿:"o",Ãž:"TH",Ã¾:"th",á¹”:"P",á¹•:"p",á¹¤:"S",á¹¥:"s",XÌ:"X",xÌ:"x",Ðƒ:"Ð“",Ñ“:"Ð³",ÐŒ:"Ðš",Ñœ:"Ðº",AÌ‹:"A",aÌ‹:"a",EÌ‹:"E",eÌ‹:"e",IÌ‹:"I",iÌ‹:"i",Ç¸:"N",Ç¹:"n",á»’:"O",á»“:"o",á¹:"O",á¹‘:"o",á»ª:"U",á»«:"u",áº€:"W",áº:"w",á»²:"Y",á»³:"y",È€:"A",È:"a",È„:"E",È…:"e",Èˆ:"I",È‰:"i",ÈŒ:"O",È:"o",È:"R",È‘:"r",È”:"U",È•:"u",BÌŒ:"B",bÌŒ:"b",ÄŒÌ£:"C",ÄÌ£:"c",ÃŠÌŒ:"E",ÃªÌŒ:"e",FÌŒ:"F",fÌŒ:"f",Ç¦:"G",Ç§:"g",Èž:"H",ÈŸ:"h",JÌŒ:"J",Ç°:"j",Ç¨:"K",Ç©:"k",MÌŒ:"M",mÌŒ:"m",PÌŒ:"P",pÌŒ:"p",QÌŒ:"Q",qÌŒ:"q",Å˜Ì©:"R",Å™Ì©:"r",á¹¦:"S",á¹§:"s",VÌŒ:"V",vÌŒ:"v",WÌŒ:"W",wÌŒ:"w",XÌŒ:"X",xÌŒ:"x",YÌŒ:"Y",yÌŒ:"y",AÌ§:"A",aÌ§:"a",BÌ§:"B",bÌ§:"b",á¸:"D",á¸‘:"d",È¨:"E",È©:"e",ÆÌ§:"E",É›Ì§:"e",á¸¨:"H",á¸©:"h",IÌ§:"I",iÌ§:"i",Æ—Ì§:"I",É¨Ì§:"i",MÌ§:"M",mÌ§:"m",OÌ§:"O",oÌ§:"o",QÌ§:"Q",qÌ§:"q",UÌ§:"U",uÌ§:"u",XÌ§:"X",xÌ§:"x",ZÌ§:"Z",zÌ§:"z"},n=Object.keys(t).join("|"),r=new RegExp(n,"g"),o=new RegExp(n,""),i=function(e){return e.replace(r,(function(e){return t[e]}))};e.exports=i,e.exports.has=function(e){return!!e.match(o)},e.exports.remove=i}},n={},function e(r){var o=n[r];if(void 0!==o)return o.exports;var i=n[r]={exports:{}};return t[r](i,i.exports,e),i.exports}(772))},1210:(e,t,n)=>{"use strict";function r(...e){return e.reduce(((e,t)=>null==t?e:function(...n){e.apply(this,n),t.apply(this,n)}),(()=>{}))}n.d(t,{A:()=>r})},1243:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.compose=function(e){for(var t=[],n=1;n<arguments.length;n++)t[n-1]=arguments[n];return t.reduce((function(e,t){return function(n){return e(t(n))}}),e)}},1245:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=o(n(6540)),a=function(){return Math.random().toString(36).substr(2,6)};t.CreateEvent=function(e,t){return function(n,r){if("function"==typeof e.CustomEvent)return new e.CustomEvent(n,r);var o=r||{bubbles:!1,cancelable:!1,detail:void 0},i=t.createEvent("CustomEvent");return i.initCustomEvent(n,o.bubbles,o.cancelable,o.detail),i.prototype=e.Event.prototype,i}};var s=function(e,t,n){return{push:function(r,o){var i=n(),a=o||e.history.state;e.history.pushState({key:i,state:a},null,r),e.dispatchEvent(t("popstate"))}}};t.useHistory=function(){return s(window,t.CreateEvent(window,document),a)},t.withRouter=function(e,t,n){return function(o){return function(a){var l=s(e,t,n),u=r({history:l,location:e.location},a);return i.default.createElement(o,r({},u))}}},t.default=t.withRouter(window,t.CreateEvent(window,document),a)},1260:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M4 15h16v-2H4v2zm0 4h16v-2H4v2zm0-8h16V9H4v2zm0-6v2h16V5H4z"}),"ViewHeadline");t.A=a},1270:function(e,t,n){var r;e=n.nmd(e),function(){t&&t.nodeType,e&&e.nodeType;var o="object"==typeof n.g&&n.g;o.global!==o&&o.window!==o&&o.self;var i,a=2147483647,s=36,l=/^xn--/,u=/[^\x20-\x7E]/,c=/[\x2E\u3002\uFF0E\uFF61]/g,d={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},f=Math.floor,p=String.fromCharCode;function h(e){throw new RangeError(d[e])}function m(e,t){for(var n=e.length,r=[];n--;)r[n]=t(e[n]);return r}function g(e,t){var n=e.split("@"),r="";return n.length>1&&(r=n[0]+"@",e=n[1]),r+m((e=e.replace(c,".")).split("."),t).join(".")}function v(e){for(var t,n,r=[],o=0,i=e.length;o<i;)(t=e.charCodeAt(o++))>=55296&&t<=56319&&o<i?56320==(64512&(n=e.charCodeAt(o++)))?r.push(((1023&t)<<10)+(1023&n)+65536):(r.push(t),o--):r.push(t);return r}function y(e){return m(e,(function(e){var t="";return e>65535&&(t+=p((e-=65536)>>>10&1023|55296),e=56320|1023&e),t+p(e)})).join("")}function b(e,t){return e+22+75*(e<26)-((0!=t)<<5)}function w(e,t,n){var r=0;for(e=n?f(e/700):e>>1,e+=f(e/t);e>455;r+=s)e=f(e/35);return f(r+36*e/(e+38))}function E(e){var t,n,r,o,i,l,u,c,d,p,m,g=[],v=e.length,b=0,E=128,S=72;for((n=e.lastIndexOf("-"))<0&&(n=0),r=0;r<n;++r)e.charCodeAt(r)>=128&&h("not-basic"),g.push(e.charCodeAt(r));for(o=n>0?n+1:0;o<v;){for(i=b,l=1,u=s;o>=v&&h("invalid-input"),((c=(m=e.charCodeAt(o++))-48<10?m-22:m-65<26?m-65:m-97<26?m-97:s)>=s||c>f((a-b)/l))&&h("overflow"),b+=c*l,!(c<(d=u<=S?1:u>=S+26?26:u-S));u+=s)l>f(a/(p=s-d))&&h("overflow"),l*=p;S=w(b-i,t=g.length+1,0==i),f(b/t)>a-E&&h("overflow"),E+=f(b/t),b%=t,g.splice(b++,0,E)}return y(g)}function S(e){var t,n,r,o,i,l,u,c,d,m,g,y,E,S,x,A=[];for(y=(e=v(e)).length,t=128,n=0,i=72,l=0;l<y;++l)(g=e[l])<128&&A.push(p(g));for(r=o=A.length,o&&A.push("-");r<y;){for(u=a,l=0;l<y;++l)(g=e[l])>=t&&g<u&&(u=g);for(u-t>f((a-n)/(E=r+1))&&h("overflow"),n+=(u-t)*E,t=u,l=0;l<y;++l)if((g=e[l])<t&&++n>a&&h("overflow"),g==t){for(c=n,d=s;!(c<(m=d<=i?1:d>=i+26?26:d-i));d+=s)x=c-m,S=s-m,A.push(p(b(m+x%S,0))),c=f(x/S);A.push(p(b(c,0))),i=w(n,E,r==o),n=0,++r}++n,++t}return A.join("")}i={version:"1.4.1",ucs2:{decode:v,encode:y},decode:E,encode:S,toASCII:function(e){return g(e,(function(e){return u.test(e)?"xn--"+S(e):e}))},toUnicode:function(e){return g(e,(function(e){return l.test(e)?E(e.slice(4).toLowerCase()):e}))}},void 0===(r=function(){return i}.call(t,n,t,e))||(e.exports=r)}()},1287:(e,t,n)=>{"use strict";var r;n.d(t,{i:()=>s,s:()=>a});var o=n(6540),i=!!(r||(r=n.t(o,2))).useInsertionEffect&&(r||(r=n.t(o,2))).useInsertionEffect,a=i||function(e){return e()},s=i||o.useLayoutEffect},1333:e=>{"use strict";e.exports=function(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var e={},t=Symbol("test"),n=Object(t);if("string"==typeof t)return!1;if("[object Symbol]"!==Object.prototype.toString.call(t))return!1;if("[object Symbol]"!==Object.prototype.toString.call(n))return!1;for(t in e[t]=42,e)return!1;if("function"==typeof Object.keys&&0!==Object.keys(e).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(e).length)return!1;var r=Object.getOwnPropertySymbols(e);if(1!==r.length||r[0]!==t)return!1;if(!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){var o=Object.getOwnPropertyDescriptor(e,t);if(42!==o.value||!0!==o.enumerable)return!1}return!0}},1338:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"}},1438:(e,t)=>{"use strict";function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];if(e===t)return!1;var o=Object.keys(e),i=Object.keys(t);if(o.length!==i.length)return!0;var a,s,l={};for(a=0,s=r.length;a<s;a++)l[r[a]]=!0;for(a=0,s=o.length;a<s;a++){var u=o[a],c=e[u],d=t[u];if(c!==d){if(!l[u]||null===c||null===d||"object"!==n(c)||"object"!==n(d))return!0;var f=Object.keys(c),p=Object.keys(d);if(f.length!==p.length)return!0;for(var h=0,m=f.length;h<m;h++){var g=f[h];if(c[g]!==d[g])return!0}}}return!1}},1447:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"}),"Error");t.A=a},1523:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(6540),o=n(989);function i(...e){return r.useMemo((()=>e.every((e=>null==e))?null:t=>{e.forEach((e=>{(0,o.A)(e,t)}))}),e)}},1529:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(6540);const o="undefined"!=typeof window?r.useLayoutEffect:r.useEffect},1547:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(6540),o=n(1529);const i=function(e){const t=r.useRef(e);return(0,o.A)((()=>{t.current=e})),r.useRef(((...e)=>(0,t.current)(...e))).current}},1568:(e,t,n)=>{var r=n(5537),o=n(6917),i=n(7510),a=n(6866),s=n(8835),l=t;l.request=function(e,t){e="string"==typeof e?s.parse(e):i(e);var o=-1===n.g.location.protocol.search(/^https?:$/)?"http:":"",a=e.protocol||o,l=e.hostname||e.host,u=e.port,c=e.path||"/";l&&-1!==l.indexOf(":")&&(l="["+l+"]"),e.url=(l?a+"//"+l:"")+(u?":"+u:"")+c,e.method=(e.method||"GET").toUpperCase(),e.headers=e.headers||{};var d=new r(e);return t&&d.on("response",t),d},l.get=function(e,t){var n=l.request(e,t);return n.end(),n},l.ClientRequest=r,l.IncomingMessage=o.IncomingMessage,l.Agent=function(){},l.Agent.defaultMaxSockets=4,l.globalAgent=new l.Agent,l.STATUS_CODES=a,l.METHODS=["CHECKOUT","CONNECT","COPY","DELETE","GET","HEAD","LOCK","M-SEARCH","MERGE","MKACTIVITY","MKCOL","MOVE","NOTIFY","OPTIONS","PATCH","POST","PROPFIND","PROPPATCH","PURGE","PUT","REPORT","SEARCH","SUBSCRIBE","TRACE","UNLOCK","UNSUBSCRIBE"]},1572:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==l(e)&&"function"!=typeof e)return{default:e};var t=s();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}return n.default=e,t&&t.set(e,n),n}(n(6540)),o=a(n(5556)),i=a(n(1438));function a(e){return e&&e.__esModule?e:{default:e}}function s(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return s=function(){return e},e}function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function u(){return u=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},u.apply(this,arguments)}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function d(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function f(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function p(e){return p=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},p(e)}function h(e,t){return h=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},h(e,t)}function m(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var g=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&h(e,t)}(s,e);var t,n,o,a=(o=s,function(){var e,t=p(o);if(function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}()){var n=p(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return function(e,t){return!t||"object"!==l(t)&&"function"!=typeof t?f(e):t}(this,e)});function s(){var e;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,s);for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return m(f(e=a.call.apply(a,[this].concat(n))),"storeItemReference",(function(t){null!==t&&(e.item=t)})),m(f(e),"onMouseEnter",(function(t){var n=e.props,r=n.sectionIndex,o=n.itemIndex;e.props.onMouseEnter(t,{sectionIndex:r,itemIndex:o})})),m(f(e),"onMouseLeave",(function(t){var n=e.props,r=n.sectionIndex,o=n.itemIndex;e.props.onMouseLeave(t,{sectionIndex:r,itemIndex:o})})),m(f(e),"onMouseDown",(function(t){var n=e.props,r=n.sectionIndex,o=n.itemIndex;e.props.onMouseDown(t,{sectionIndex:r,itemIndex:o})})),m(f(e),"onClick",(function(t){var n=e.props,r=n.sectionIndex,o=n.itemIndex;e.props.onClick(t,{sectionIndex:r,itemIndex:o})})),e}return t=s,n=[{key:"shouldComponentUpdate",value:function(e){return(0,i.default)(e,this.props,["renderItemData"])}},{key:"render",value:function(){var e=this.props,t=e.isHighlighted,n=e.item,o=e.renderItem,i=e.renderItemData,a=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,["isHighlighted","item","renderItem","renderItemData"]);return delete a.sectionIndex,delete a.itemIndex,"function"==typeof a.onMouseEnter&&(a.onMouseEnter=this.onMouseEnter),"function"==typeof a.onMouseLeave&&(a.onMouseLeave=this.onMouseLeave),"function"==typeof a.onMouseDown&&(a.onMouseDown=this.onMouseDown),"function"==typeof a.onClick&&(a.onClick=this.onClick),r.default.createElement("li",u({role:"option"},a,{ref:this.storeItemReference}),o(n,function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?c(Object(n),!0).forEach((function(t){m(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):c(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({isHighlighted:t},i)))}}],n&&d(t.prototype,n),s}(r.Component);t.default=g,m(g,"propTypes",{sectionIndex:o.default.number,isHighlighted:o.default.bool.isRequired,itemIndex:o.default.number.isRequired,item:o.default.any.isRequired,renderItem:o.default.func.isRequired,renderItemData:o.default.object.isRequired,onMouseEnter:o.default.func,onMouseLeave:o.default.func,onMouseDown:o.default.func,onClick:o.default.func})},1574:(e,t,n)=>{"use strict";n.d(t,{A:()=>V});var r=n(8168),o=n(8587),i=n(9453),a=n(3272),s=n(7008),l=n(8552),u=n(3571),c=n(4279);const d={black:"#000",white:"#fff"};var f=n(5878);const p="#f3e5f5",h="#ce93d8",m="#ba68c8",g="#ab47bc",v="#9c27b0",y="#7b1fa2";var b=n(1338);const w="#ffb74d",E="#ffa726",S="#ff9800",x="#f57c00",A="#e65100",k="#e3f2fd",_="#90caf9",C="#42a5f5",O="#1976d2",P="#1565c0";var R=n(9577),T=n(3542);const F=["mode","contrastThreshold","tonalOffset"],j={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.6)",disabled:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:d.white,default:d.white},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.04)",hoverOpacity:.04,selected:"rgba(0, 0, 0, 0.08)",selectedOpacity:.08,disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)",disabledOpacity:.38,focus:"rgba(0, 0, 0, 0.12)",focusOpacity:.12,activatedOpacity:.12}},N={text:{primary:d.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:"#121212",default:"#121212"},action:{active:d.white,hover:"rgba(255, 255, 255, 0.08)",hoverOpacity:.08,selected:"rgba(255, 255, 255, 0.16)",selectedOpacity:.16,disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)",disabledOpacity:.38,focus:"rgba(255, 255, 255, 0.12)",focusOpacity:.12,activatedOpacity:.24}};function I(e,t,n,r){const o=r.light||r,i=r.dark||1.5*r;e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=(0,c.a)(e.main,o):"dark"===t&&(e.dark=(0,c.e$)(e.main,i)))}const L=["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","fontWeightBold","htmlFontSize","allVariants","pxToRem"],M={textTransform:"uppercase"},D='"Roboto", "Helvetica", "Arial", sans-serif';function B(e,t){const n="function"==typeof t?t(e):t,{fontFamily:i=D,fontSize:s=14,fontWeightLight:l=300,fontWeightRegular:u=400,fontWeightMedium:c=500,fontWeightBold:d=700,htmlFontSize:f=16,allVariants:p,pxToRem:h}=n,m=(0,o.A)(n,L),g=s/14,v=h||(e=>e/f*g+"rem"),y=(e,t,n,o,a)=>{return(0,r.A)({fontFamily:i,fontWeight:e,fontSize:v(t),lineHeight:n},i===D?{letterSpacing:(s=o/t,Math.round(1e5*s)/1e5+"em")}:{},a,p);var s},b={h1:y(l,96,1.167,-1.5),h2:y(l,60,1.2,-.5),h3:y(u,48,1.167,0),h4:y(u,34,1.235,.25),h5:y(u,24,1.334,0),h6:y(c,20,1.6,.15),subtitle1:y(u,16,1.75,.15),subtitle2:y(c,14,1.57,.1),body1:y(u,16,1.5,.15),body2:y(u,14,1.43,.15),button:y(c,14,1.75,.4,M),caption:y(u,12,1.66,.4),overline:y(u,12,2.66,1,M),inherit:{fontFamily:"inherit",fontWeight:"inherit",fontSize:"inherit",lineHeight:"inherit",letterSpacing:"inherit"}};return(0,a.A)((0,r.A)({htmlFontSize:f,pxToRem:v,fontFamily:i,fontSize:s,fontWeightLight:l,fontWeightRegular:u,fontWeightMedium:c,fontWeightBold:d},b),m,{clone:!1})}function U(...e){return[`${e[0]}px ${e[1]}px ${e[2]}px ${e[3]}px rgba(0,0,0,0.2)`,`${e[4]}px ${e[5]}px ${e[6]}px ${e[7]}px rgba(0,0,0,0.14)`,`${e[8]}px ${e[9]}px ${e[10]}px ${e[11]}px rgba(0,0,0,0.12)`].join(",")}const z=["none",U(0,2,1,-1,0,1,1,0,0,1,3,0),U(0,3,1,-2,0,2,2,0,0,1,5,0),U(0,3,3,-2,0,3,4,0,0,1,8,0),U(0,2,4,-1,0,4,5,0,0,1,10,0),U(0,3,5,-1,0,5,8,0,0,1,14,0),U(0,3,5,-1,0,6,10,0,0,1,18,0),U(0,4,5,-2,0,7,10,1,0,2,16,1),U(0,5,5,-3,0,8,10,1,0,3,14,2),U(0,5,6,-3,0,9,12,1,0,3,16,2),U(0,6,6,-3,0,10,14,1,0,4,18,3),U(0,6,7,-4,0,11,15,1,0,4,20,3),U(0,7,8,-4,0,12,17,2,0,5,22,4),U(0,7,8,-4,0,13,19,2,0,5,24,4),U(0,7,9,-4,0,14,21,2,0,5,26,4),U(0,8,9,-5,0,15,22,2,0,6,28,5),U(0,8,10,-5,0,16,24,2,0,6,30,5),U(0,8,11,-5,0,17,26,2,0,6,32,5),U(0,9,11,-5,0,18,28,2,0,7,34,6),U(0,9,12,-6,0,19,29,2,0,7,36,6),U(0,10,13,-6,0,20,31,3,0,8,38,7),U(0,10,13,-6,0,21,33,3,0,8,40,7),U(0,10,14,-6,0,22,35,3,0,8,42,7),U(0,11,14,-7,0,23,36,3,0,9,44,8),U(0,11,15,-7,0,24,38,3,0,9,46,8)];var $=n(7091);const H={mobileStepper:1e3,fab:1050,speedDial:1050,appBar:1100,drawer:1200,modal:1300,snackbar:1400,tooltip:1500},W=["breakpoints","mixins","spacing","palette","transitions","typography","shape"];const V=function(e={},...t){const{mixins:n={},palette:L={},transitions:M={},typography:D={}}=e,U=(0,o.A)(e,W);if(e.vars)throw new Error((0,i.A)(18));const V=function(e){const{mode:t="light",contrastThreshold:n=3,tonalOffset:s=.2}=e,l=(0,o.A)(e,F),u=e.primary||function(e="light"){return"dark"===e?{main:_,light:k,dark:C}:{main:O,light:C,dark:P}}(t),L=e.secondary||function(e="light"){return"dark"===e?{main:h,light:p,dark:g}:{main:v,light:m,dark:y}}(t),M=e.error||function(e="light"){return"dark"===e?{main:b.A[500],light:b.A[300],dark:b.A[700]}:{main:b.A[700],light:b.A[400],dark:b.A[800]}}(t),D=e.info||function(e="light"){return"dark"===e?{main:R.A[400],light:R.A[300],dark:R.A[700]}:{main:R.A[700],light:R.A[500],dark:R.A[900]}}(t),B=e.success||function(e="light"){return"dark"===e?{main:T.A[400],light:T.A[300],dark:T.A[700]}:{main:T.A[800],light:T.A[500],dark:T.A[900]}}(t),U=e.warning||function(e="light"){return"dark"===e?{main:E,light:w,dark:x}:{main:"#ed6c02",light:S,dark:A}}(t);function z(e){return(0,c.eM)(e,N.text.primary)>=n?N.text.primary:j.text.primary}const $=({color:e,name:t,mainShade:n=500,lightShade:o=300,darkShade:a=700})=>{if(!(e=(0,r.A)({},e)).main&&e[n]&&(e.main=e[n]),!e.hasOwnProperty("main"))throw new Error((0,i.A)(11,t?` (${t})`:"",n));if("string"!=typeof e.main)throw new Error((0,i.A)(12,t?` (${t})`:"",JSON.stringify(e.main)));return I(e,"light",o,s),I(e,"dark",a,s),e.contrastText||(e.contrastText=z(e.main)),e},H={dark:N,light:j};return(0,a.A)((0,r.A)({common:(0,r.A)({},d),mode:t,primary:$({color:u,name:"primary"}),secondary:$({color:L,name:"secondary",mainShade:"A400",lightShade:"A200",darkShade:"A700"}),error:$({color:M,name:"error"}),warning:$({color:U,name:"warning"}),info:$({color:D,name:"info"}),success:$({color:B,name:"success"}),grey:f.A,contrastThreshold:n,getContrastText:z,augmentColor:$,tonalOffset:s},H[t]),l)}(L),q=(0,s.A)(e);let K=(0,a.A)(q,{mixins:(G=q.breakpoints,J=n,(0,r.A)({toolbar:{minHeight:56,[G.up("xs")]:{"@media (orientation: landscape)":{minHeight:48}},[G.up("sm")]:{minHeight:64}}},J)),palette:V,shadows:z.slice(),typography:B(V,D),transitions:(0,$.Ay)(M),zIndex:(0,r.A)({},H)});var G,J;return K=(0,a.A)(K,U),K=t.reduce(((e,t)=>(0,a.A)(e,t)),K),K.unstable_sxConfig=(0,r.A)({},l.A,null==U?void 0:U.unstable_sxConfig),K.unstable_sx=function(e){return(0,u.A)({sx:e,theme:this})},K}},1578:e=>{"use strict";var t=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&s.return&&s.return()}finally{if(o)throw i}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")};e.exports=function(e){var n=e.data,r=e.multiSection;function o(e){var o=t(e,2),i=o[0],a=o[1];return r?null===a||a===n[i]-1?null===(i=function(e){for(null===e?e=0:e++;e<n.length&&0===n[e];)e++;return e===n.length?null:e}(i))?[null,null]:[i,0]:[i,a+1]:0===n||a===n-1?[null,null]:null===a?[null,0]:[null,a+1]}return{next:o,prev:function(e){var o=t(e,2),i=o[0],a=o[1];return r?null===a||0===a?null===(i=function(e){for(null===e?e=n.length-1:e--;e>=0&&0===n[e];)e--;return-1===e?null:e}(i))?[null,null]:[i,n[i]-1]:[i,a-1]:0===n||0===a?[null,null]:null===a?[null,n-1]:[null,a-1]},isLast:function(e){return null===o(e)[1]}}}},1601:e=>{"use strict";e.exports=function(e){return e[1]}},1609:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(9071);const o={active:"active",checked:"checked",completed:"completed",disabled:"disabled",error:"error",expanded:"expanded",focused:"focused",focusVisible:"focusVisible",open:"open",readOnly:"readOnly",required:"required",selected:"selected"};function i(e,t,n="Mui"){const i=o[t];return i?`${n}-${i}`:`${r.A.generate(e)}-${t}`}},1668:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(4661).A},1706:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M14.94 4.66h-4.72l2.36-2.36zm-4.69 14.71h4.66l-2.33 2.33zM6.1 6.27 1.6 17.73h1.84l.92-2.45h5.11l.92 2.45h1.84L7.74 6.27H6.1zm-1.13 7.37 1.94-5.18 1.94 5.18H4.97zm10.76 2.5h6.12v1.59h-8.53v-1.29l5.92-8.56h-5.88v-1.6h8.3v1.26l-5.93 8.6z"}),"SortByAlpha");t.A=a},1730:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM8 9h8v10H8V9zm7.5-5-1-1h-5l-1 1H5v2h14V4z"}),"DeleteOutline");t.A=a},1731:(e,t,n)=>{var r=n(5606),o=n(8287).Buffer,i=n(8835).parse,a=n(7007),s=n(1083),l=n(1568),u=n(537),c=["pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","secureProtocol","servername","checkServerIdentity"],d=[239,187,191],f=262144,p=/^(cookie|authorization)$/i;function h(e,t){var n=h.CONNECTING,a=t&&t.headers,u=!1;Object.defineProperty(this,"readyState",{get:function(){return n}}),Object.defineProperty(this,"url",{get:function(){return e}});var v,y=this;function b(t){n!==h.CLOSED&&(n=h.CONNECTING,_("error",new m("error",{message:t})),A&&(e=A,A=null,u=!1),setTimeout((function(){n!==h.CONNECTING||y.connectionInProgress||(y.connectionInProgress=!0,k())}),y.reconnectInterval))}y.reconnectInterval=1e3,y.connectionInProgress=!1;var w="";a&&a["Last-Event-ID"]&&(w=a["Last-Event-ID"],delete a["Last-Event-ID"]);var E=!1,S="",x="",A=null;function k(){var g=i(e),S="https:"===g.protocol;if(g.headers={"Cache-Control":"no-cache",Accept:"text/event-stream"},w&&(g.headers["Last-Event-ID"]=w),a){var x=u?function(e){var t={};for(var n in e)p.test(n)||(t[n]=e[n]);return t}(a):a;for(var O in x){var P=x[O];P&&(g.headers[O]=P)}}if(g.rejectUnauthorized=!(t&&!t.rejectUnauthorized),t&&void 0!==t.createConnection&&(g.createConnection=t.createConnection),t&&t.proxy){var R=i(t.proxy);S="https:"===R.protocol,g.protocol=S?"https:":"http:",g.path=e,g.headers.Host=g.host,g.hostname=R.hostname,g.host=R.host,g.port=R.port}if(t&&t.https)for(var T in t.https)if(-1!==c.indexOf(T)){var F=t.https[T];void 0!==F&&(g[T]=F)}t&&void 0!==t.withCredentials&&(g.withCredentials=t.withCredentials),v=(S?s:l).request(g,(function(t){if(y.connectionInProgress=!1,500===t.statusCode||502===t.statusCode||503===t.statusCode||504===t.statusCode)return _("error",new m("error",{status:t.statusCode,message:t.statusMessage})),void b();if(301===t.statusCode||302===t.statusCode||307===t.statusCode){var i=t.headers.location;if(!i)return void _("error",new m("error",{status:t.statusCode,message:t.statusMessage}));var a=new URL(e).origin,s=new URL(i).origin;return u=a!==s,307===t.statusCode&&(A=e),e=i,void r.nextTick(k)}if(200!==t.statusCode)return _("error",new m("error",{status:t.statusCode,message:t.statusMessage})),y.close();var l,c;n=h.OPEN,t.on("close",(function(){t.removeAllListeners("close"),t.removeAllListeners("end"),b()})),t.on("end",(function(){t.removeAllListeners("close"),t.removeAllListeners("end"),b()})),_("open",new m("open"));var p=0,g=-1,v=0,w=0;t.on("data",(function(e){l?(e.length>l.length-w&&((v=2*l.length+e.length)>f&&(v=l.length+e.length+f),c=o.alloc(v),l.copy(c,0,0,w),l=c),e.copy(l,w),w+=e.length):(function(e){return d.every((function(t,n){return e[n]===t}))}(l=e)&&(l=l.slice(d.length)),w=l.length);for(var t=0,n=w;t<n;){E&&(10===l[t]&&++t,E=!1);for(var r,i=-1,a=g,s=p;i<0&&s<n;++s)58===(r=l[s])?a<0&&(a=s-t):13===r?(E=!0,i=s-t):10===r&&(i=s-t);if(i<0){p=n-t,g=a;break}p=0,g=-1,C(l,t,a,i),t+=i+1}t===n?(l=void 0,w=0):t>0&&(l=l.slice(t,w),w=l.length)}))})),v.on("error",(function(e){y.connectionInProgress=!1,b(e.message)})),v.setNoDelay&&v.setNoDelay(!0),v.end()}function _(){y.listeners(arguments[0]).length>0&&y.emit.apply(y,arguments)}function C(t,n,r,o){if(0===o){if(S.length>0){var i=x||"message";_(i,new g(i,{data:S.slice(0,-1),lastEventId:w,origin:new URL(e).origin})),S=""}x=void 0}else if(r>0){var a,s=r<0,l=t.slice(n,n+(s?o:r)).toString();n+=a=s?o:32!==t[n+r+1]?r+1:r+2;var u=o-a,c=t.slice(n,n+u).toString();if("data"===l)S+=c+"\n";else if("event"===l)x=c;else if("id"===l)w=c;else if("retry"===l){var d=parseInt(c,10);Number.isNaN(d)||(y.reconnectInterval=d)}}}k(),this._close=function(){n!==h.CLOSED&&(n=h.CLOSED,v.abort&&v.abort(),v.xhr&&v.xhr.abort&&v.xhr.abort())}}function m(e,t){if(Object.defineProperty(this,"type",{writable:!1,value:e,enumerable:!0}),t)for(var n in t)t.hasOwnProperty(n)&&Object.defineProperty(this,n,{writable:!1,value:t[n],enumerable:!0})}function g(e,t){for(var n in Object.defineProperty(this,"type",{writable:!1,value:e,enumerable:!0}),t)t.hasOwnProperty(n)&&Object.defineProperty(this,n,{writable:!1,value:t[n],enumerable:!0})}e.exports=h,u.inherits(h,a.EventEmitter),h.prototype.constructor=h,["open","error","message"].forEach((function(e){Object.defineProperty(h.prototype,"on"+e,{get:function(){var t=this.listeners(e)[0];return t?t._listener?t._listener:t:void 0},set:function(t){this.removeAllListeners(e),this.addEventListener(e,t)}})})),Object.defineProperty(h,"CONNECTING",{enumerable:!0,value:0}),Object.defineProperty(h,"OPEN",{enumerable:!0,value:1}),Object.defineProperty(h,"CLOSED",{enumerable:!0,value:2}),h.prototype.CONNECTING=0,h.prototype.OPEN=1,h.prototype.CLOSED=2,h.prototype.close=function(){this._close()},h.prototype.addEventListener=function(e,t){"function"==typeof t&&(t._listener=t,this.on(e,t))},h.prototype.dispatchEvent=function(e){if(!e.type)throw new Error("UNSPECIFIED_EVENT_TYPE_ERR");this.emit(e.type,e.detail)},h.prototype.removeEventListener=function(e,t){"function"==typeof t&&(t._listener=void 0,this.removeListener(e,t))}},1740:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540));t.default=function(){return o.createElement("div",{className:"oidc-authenticating"},o.createElement("div",{className:"oidc-authenticating__container"},o.createElement("h1",{className:"oidc-authenticating__title"},"Authentification en cours"),o.createElement("p",{className:"oidc-authenticating__content"},"Vous allez Ãªtre redirigÃ© sur la page de login")))}},1791:(e,t,n)=>{var r=n(5172),o=n(5546);e.exports=function e(t,n){function i(e,o,a,s){try{var l=t[e](o),u=l.value;return u instanceof r?n.resolve(u.v).then((function(e){i("next",e,a,s)}),(function(e){i("throw",e,a,s)})):n.resolve(u).then((function(e){l.value=e,a(l)}),(function(e){return i("throw",e,a,s)}))}catch(e){s(e)}}var a;this.next||(o(e.prototype),o(e.prototype,"function"==typeof Symbol&&Symbol.asyncIterator||"@asyncIterator",(function(){return this}))),o(this,"_invoke",(function(e,t,r){function o(){return new n((function(t,n){i(e,r,t,n)}))}return a=a?a.then(o,o):o()}),!0)},e.exports.__esModule=!0,e.exports.default=e.exports},1823:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M22.99 9C19.15 5.16 13.8 3.76 8.84 4.78l2.52 2.52c3.47-.17 6.99 1.05 9.63 3.7l2-2zm-4 4c-1.29-1.29-2.84-2.13-4.49-2.56l3.53 3.53.96-.97zM2 3.05 5.07 6.1C3.6 6.82 2.22 7.78 1 9l1.99 2c1.24-1.24 2.67-2.16 4.2-2.77l2.24 2.24C7.81 10.89 6.27 11.73 5 13v.01L6.99 15c1.36-1.36 3.14-2.04 4.92-2.06L18.98 20l1.27-1.26L3.29 1.79 2 3.05zM9 17l3 3 3-3c-1.65-1.66-4.34-1.66-6 0z"}),"WifiOff");t.A=a},1848:(e,t,n)=>{"use strict";n.d(t,{Ay:()=>l,_n:()=>s,ep:()=>a});var r=n(9834),o=n(2765),i=n(8312);const a=e=>(0,r.MC)(e)&&"classes"!==e,s=r.MC,l=(0,r.Ay)({themeId:i.A,defaultTheme:o.A,rootShouldForwardProp:a})},1935:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(4705).A},1954:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540));t.default=function(){return o.createElement("div",{className:"oidc-not-authorized"},o.createElement("div",{className:"oidc-not-authorized__container"},o.createElement("h1",{className:"oidc-not-authorized__title"},"Autorisation"),o.createElement("p",{className:"oidc-not-authorized__content"},"Vous n'Ãªtes pas autorisÃ© Ã  accÃ©der Ã  cette ressource.")))}},2032:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return r.createSvgIcon}});var r=n(8228)},2048:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z"}),"ExpandMore");t.A=a},2092:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"}),"Clear");t.A=a},2162:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"}),"Help");t.A=a},2183:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"}),"FilterList");t.A=a},2243:(e,t,n)=>{"use strict";e.exports=n(7424).default},2274:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"}),"Menu");t.A=a},2299:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"}),"Cancel");t.A=a},2325:(e,t,n)=>{"use strict";function r(e){return e&&e.ownerDocument||document}n.d(t,{A:()=>r})},2422:(e,t,n)=>{"use strict";function r(e,t,n){var r="";return n.split(" ").forEach((function(n){void 0!==e[n]?t.push(e[n]+";"):r+=n+" "})),r}n.d(t,{Rk:()=>r,SF:()=>o,sk:()=>i});var o=function(e,t,n){var r=e.key+"-"+t.name;!1===n&&void 0===e.registered[r]&&(e.registered[r]=t.styles)},i=function(e,t,n){o(e,t,n);var r=e.key+"-"+t.name;if(void 0===e.inserted[t.name]){var i=t;do{e.insert(t===i?"."+r:"",i,e.sheet,!0),i=i.next}while(void 0!==i)}}},2444:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==l(e)&&"function"!=typeof e)return{default:e};var t=s();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}return n.default=e,t&&t.set(e,n),n}(n(6540)),o=a(n(5556)),i=a(n(1438));function a(e){return e&&e.__esModule?e:{default:e}}function s(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return s=function(){return e},e}function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function u(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function c(e){return c=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},c(e)}function d(e,t){return d=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},d(e,t)}var f,p,h,m=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&d(e,t)}(s,e);var t,n,o,a=(o=s,function(){var e,t=c(o);if(function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}()){var n=c(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return function(e,t){return!t||"object"!==l(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}(this,e)});function s(){return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,s),a.apply(this,arguments)}return t=s,(n=[{key:"shouldComponentUpdate",value:function(e){return(0,i.default)(e,this.props)}},{key:"render",value:function(){var e=this.props,t=e.section,n=e.renderSectionTitle,o=e.theme,i=e.sectionKeyPrefix,a=n(t);return a?r.default.createElement("div",o("".concat(i,"title"),"sectionTitle"),a):null}}])&&u(t.prototype,n),s}(r.Component);t.default=m,f=m,p="propTypes",h={section:o.default.any.isRequired,renderSectionTitle:o.default.func.isRequired,theme:o.default.func.isRequired,sectionKeyPrefix:o.default.string.isRequired},p in f?Object.defineProperty(f,p,{value:h,enumerable:!0,configurable:!0,writable:!0}):f[p]=h},2501:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(6540);function o({controlled:e,default:t,name:n,state:o="value"}){const{current:i}=r.useRef(void 0!==e),[a,s]=r.useState(t);return[i?e:a,r.useCallback((e=>{i||s(e)}),[])]}},2543:function(e,t,n){var r;e=n.nmd(e),function(){var o,i="Expected a function",a="__lodash_hash_undefined__",s="__lodash_placeholder__",l=32,u=128,c=1/0,d=9007199254740991,f=NaN,p=4294967295,h=[["ary",u],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",l],["partialRight",64],["rearg",256]],m="[object Arguments]",g="[object Array]",v="[object Boolean]",y="[object Date]",b="[object Error]",w="[object Function]",E="[object GeneratorFunction]",S="[object Map]",x="[object Number]",A="[object Object]",k="[object Promise]",_="[object RegExp]",C="[object Set]",O="[object String]",P="[object Symbol]",R="[object WeakMap]",T="[object ArrayBuffer]",F="[object DataView]",j="[object Float32Array]",N="[object Float64Array]",I="[object Int8Array]",L="[object Int16Array]",M="[object Int32Array]",D="[object Uint8Array]",B="[object Uint8ClampedArray]",U="[object Uint16Array]",z="[object Uint32Array]",$=/\b__p \+= '';/g,H=/\b(__p \+=) '' \+/g,W=/(__e\(.*?\)|\b__t\)) \+\n'';/g,V=/&(?:amp|lt|gt|quot|#39);/g,q=/[&<>"']/g,K=RegExp(V.source),G=RegExp(q.source),J=/<%-([\s\S]+?)%>/g,Y=/<%([\s\S]+?)%>/g,X=/<%=([\s\S]+?)%>/g,Q=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Z=/^\w*$/,ee=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,te=/[\\^$.*+?()[\]{}|]/g,ne=RegExp(te.source),re=/^\s+/,oe=/\s/,ie=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,ae=/\{\n\/\* \[wrapped with (.+)\] \*/,se=/,? & /,le=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,ue=/[()=,{}\[\]\/\s]/,ce=/\\(\\)?/g,de=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,fe=/\w*$/,pe=/^[-+]0x[0-9a-f]+$/i,he=/^0b[01]+$/i,me=/^\[object .+?Constructor\]$/,ge=/^0o[0-7]+$/i,ve=/^(?:0|[1-9]\d*)$/,ye=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,be=/($^)/,we=/['\n\r\u2028\u2029\\]/g,Ee="\\ud800-\\udfff",Se="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",xe="\\u2700-\\u27bf",Ae="a-z\\xdf-\\xf6\\xf8-\\xff",ke="A-Z\\xc0-\\xd6\\xd8-\\xde",_e="\\ufe0e\\ufe0f",Ce="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Oe="["+Ee+"]",Pe="["+Ce+"]",Re="["+Se+"]",Te="\\d+",Fe="["+xe+"]",je="["+Ae+"]",Ne="[^"+Ee+Ce+Te+xe+Ae+ke+"]",Ie="\\ud83c[\\udffb-\\udfff]",Le="[^"+Ee+"]",Me="(?:\\ud83c[\\udde6-\\uddff]){2}",De="[\\ud800-\\udbff][\\udc00-\\udfff]",Be="["+ke+"]",Ue="\\u200d",ze="(?:"+je+"|"+Ne+")",$e="(?:"+Be+"|"+Ne+")",He="(?:['â€™](?:d|ll|m|re|s|t|ve))?",We="(?:['â€™](?:D|LL|M|RE|S|T|VE))?",Ve="(?:"+Re+"|"+Ie+")?",qe="["+_e+"]?",Ke=qe+Ve+"(?:"+Ue+"(?:"+[Le,Me,De].join("|")+")"+qe+Ve+")*",Ge="(?:"+[Fe,Me,De].join("|")+")"+Ke,Je="(?:"+[Le+Re+"?",Re,Me,De,Oe].join("|")+")",Ye=RegExp("['â€™]","g"),Xe=RegExp(Re,"g"),Qe=RegExp(Ie+"(?="+Ie+")|"+Je+Ke,"g"),Ze=RegExp([Be+"?"+je+"+"+He+"(?="+[Pe,Be,"$"].join("|")+")",$e+"+"+We+"(?="+[Pe,Be+ze,"$"].join("|")+")",Be+"?"+ze+"+"+He,Be+"+"+We,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",Te,Ge].join("|"),"g"),et=RegExp("["+Ue+Ee+Se+_e+"]"),tt=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,nt=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],rt=-1,ot={};ot[j]=ot[N]=ot[I]=ot[L]=ot[M]=ot[D]=ot[B]=ot[U]=ot[z]=!0,ot[m]=ot[g]=ot[T]=ot[v]=ot[F]=ot[y]=ot[b]=ot[w]=ot[S]=ot[x]=ot[A]=ot[_]=ot[C]=ot[O]=ot[R]=!1;var it={};it[m]=it[g]=it[T]=it[F]=it[v]=it[y]=it[j]=it[N]=it[I]=it[L]=it[M]=it[S]=it[x]=it[A]=it[_]=it[C]=it[O]=it[P]=it[D]=it[B]=it[U]=it[z]=!0,it[b]=it[w]=it[R]=!1;var at={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},st=parseFloat,lt=parseInt,ut="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,ct="object"==typeof self&&self&&self.Object===Object&&self,dt=ut||ct||Function("return this")(),ft=t&&!t.nodeType&&t,pt=ft&&e&&!e.nodeType&&e,ht=pt&&pt.exports===ft,mt=ht&&ut.process,gt=function(){try{return pt&&pt.require&&pt.require("util").types||mt&&mt.binding&&mt.binding("util")}catch(e){}}(),vt=gt&&gt.isArrayBuffer,yt=gt&&gt.isDate,bt=gt&&gt.isMap,wt=gt&&gt.isRegExp,Et=gt&&gt.isSet,St=gt&&gt.isTypedArray;function xt(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function At(e,t,n,r){for(var o=-1,i=null==e?0:e.length;++o<i;){var a=e[o];t(r,a,n(a),e)}return r}function kt(e,t){for(var n=-1,r=null==e?0:e.length;++n<r&&!1!==t(e[n],n,e););return e}function _t(e,t){for(var n=null==e?0:e.length;n--&&!1!==t(e[n],n,e););return e}function Ct(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(!t(e[n],n,e))return!1;return!0}function Ot(e,t){for(var n=-1,r=null==e?0:e.length,o=0,i=[];++n<r;){var a=e[n];t(a,n,e)&&(i[o++]=a)}return i}function Pt(e,t){return!(null==e||!e.length)&&Bt(e,t,0)>-1}function Rt(e,t,n){for(var r=-1,o=null==e?0:e.length;++r<o;)if(n(t,e[r]))return!0;return!1}function Tt(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n<r;)o[n]=t(e[n],n,e);return o}function Ft(e,t){for(var n=-1,r=t.length,o=e.length;++n<r;)e[o+n]=t[n];return e}function jt(e,t,n,r){var o=-1,i=null==e?0:e.length;for(r&&i&&(n=e[++o]);++o<i;)n=t(n,e[o],o,e);return n}function Nt(e,t,n,r){var o=null==e?0:e.length;for(r&&o&&(n=e[--o]);o--;)n=t(n,e[o],o,e);return n}function It(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(t(e[n],n,e))return!0;return!1}var Lt=Ht("length");function Mt(e,t,n){var r;return n(e,(function(e,n,o){if(t(e,n,o))return r=n,!1})),r}function Dt(e,t,n,r){for(var o=e.length,i=n+(r?1:-1);r?i--:++i<o;)if(t(e[i],i,e))return i;return-1}function Bt(e,t,n){return t==t?function(e,t,n){for(var r=n-1,o=e.length;++r<o;)if(e[r]===t)return r;return-1}(e,t,n):Dt(e,zt,n)}function Ut(e,t,n,r){for(var o=n-1,i=e.length;++o<i;)if(r(e[o],t))return o;return-1}function zt(e){return e!=e}function $t(e,t){var n=null==e?0:e.length;return n?qt(e,t)/n:f}function Ht(e){return function(t){return null==t?o:t[e]}}function Wt(e){return function(t){return null==e?o:e[t]}}function Vt(e,t,n,r,o){return o(e,(function(e,o,i){n=r?(r=!1,e):t(n,e,o,i)})),n}function qt(e,t){for(var n,r=-1,i=e.length;++r<i;){var a=t(e[r]);a!==o&&(n=n===o?a:n+a)}return n}function Kt(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}function Gt(e){return e?e.slice(0,fn(e)+1).replace(re,""):e}function Jt(e){return function(t){return e(t)}}function Yt(e,t){return Tt(t,(function(t){return e[t]}))}function Xt(e,t){return e.has(t)}function Qt(e,t){for(var n=-1,r=e.length;++n<r&&Bt(t,e[n],0)>-1;);return n}function Zt(e,t){for(var n=e.length;n--&&Bt(t,e[n],0)>-1;);return n}var en=Wt({Ã€:"A",Ã:"A",Ã‚:"A",Ãƒ:"A",Ã„:"A",Ã…:"A",Ã :"a",Ã¡:"a",Ã¢:"a",Ã£:"a",Ã¤:"a",Ã¥:"a",Ã‡:"C",Ã§:"c",Ã:"D",Ã°:"d",Ãˆ:"E",Ã‰:"E",ÃŠ:"E",Ã‹:"E",Ã¨:"e",Ã©:"e",Ãª:"e",Ã«:"e",ÃŒ:"I",Ã:"I",ÃŽ:"I",Ã:"I",Ã¬:"i",Ã­:"i",Ã®:"i",Ã¯:"i",Ã‘:"N",Ã±:"n",Ã’:"O",Ã“:"O",Ã”:"O",Ã•:"O",Ã–:"O",Ã˜:"O",Ã²:"o",Ã³:"o",Ã´:"o",Ãµ:"o",Ã¶:"o",Ã¸:"o",Ã™:"U",Ãš:"U",Ã›:"U",Ãœ:"U",Ã¹:"u",Ãº:"u",Ã»:"u",Ã¼:"u",Ã:"Y",Ã½:"y",Ã¿:"y",Ã†:"Ae",Ã¦:"ae",Ãž:"Th",Ã¾:"th",ÃŸ:"ss",Ä€:"A",Ä‚:"A",Ä„:"A",Ä:"a",Äƒ:"a",Ä…:"a",Ä†:"C",Äˆ:"C",ÄŠ:"C",ÄŒ:"C",Ä‡:"c",Ä‰:"c",Ä‹:"c",Ä:"c",ÄŽ:"D",Ä:"D",Ä:"d",Ä‘:"d",Ä’:"E",Ä”:"E",Ä–:"E",Ä˜:"E",Äš:"E",Ä“:"e",Ä•:"e",Ä—:"e",Ä™:"e",Ä›:"e",Äœ:"G",Äž:"G",Ä :"G",Ä¢:"G",Ä:"g",ÄŸ:"g",Ä¡:"g",Ä£:"g",Ä¤:"H",Ä¦:"H",Ä¥:"h",Ä§:"h",Ä¨:"I",Äª:"I",Ä¬:"I",Ä®:"I",Ä°:"I",Ä©:"i",Ä«:"i",Ä­:"i",Ä¯:"i",Ä±:"i",Ä´:"J",Äµ:"j",Ä¶:"K",Ä·:"k",Ä¸:"k",Ä¹:"L",Ä»:"L",Ä½:"L",Ä¿:"L",Å:"L",Äº:"l",Ä¼:"l",Ä¾:"l",Å€:"l",Å‚:"l",Åƒ:"N",Å…:"N",Å‡:"N",ÅŠ:"N",Å„:"n",Å†:"n",Åˆ:"n",Å‹:"n",ÅŒ:"O",ÅŽ:"O",Å:"O",Å:"o",Å:"o",Å‘:"o",Å”:"R",Å–:"R",Å˜:"R",Å•:"r",Å—:"r",Å™:"r",Åš:"S",Åœ:"S",Åž:"S",Å :"S",Å›:"s",Å:"s",ÅŸ:"s",Å¡:"s",Å¢:"T",Å¤:"T",Å¦:"T",Å£:"t",Å¥:"t",Å§:"t",Å¨:"U",Åª:"U",Å¬:"U",Å®:"U",Å°:"U",Å²:"U",Å©:"u",Å«:"u",Å­:"u",Å¯:"u",Å±:"u",Å³:"u",Å´:"W",Åµ:"w",Å¶:"Y",Å·:"y",Å¸:"Y",Å¹:"Z",Å»:"Z",Å½:"Z",Åº:"z",Å¼:"z",Å¾:"z",Ä²:"IJ",Ä³:"ij",Å’:"Oe",Å“:"oe",Å‰:"'n",Å¿:"s"}),tn=Wt({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"});function nn(e){return"\\"+at[e]}function rn(e){return et.test(e)}function on(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function an(e,t){return function(n){return e(t(n))}}function sn(e,t){for(var n=-1,r=e.length,o=0,i=[];++n<r;){var a=e[n];a!==t&&a!==s||(e[n]=s,i[o++]=n)}return i}function ln(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}function un(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=[e,e]})),n}function cn(e){return rn(e)?function(e){for(var t=Qe.lastIndex=0;Qe.test(e);)++t;return t}(e):Lt(e)}function dn(e){return rn(e)?function(e){return e.match(Qe)||[]}(e):function(e){return e.split("")}(e)}function fn(e){for(var t=e.length;t--&&oe.test(e.charAt(t)););return t}var pn=Wt({"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'"}),hn=function e(t){var n,r=(t=null==t?dt:hn.defaults(dt.Object(),t,hn.pick(dt,nt))).Array,oe=t.Date,Ee=t.Error,Se=t.Function,xe=t.Math,Ae=t.Object,ke=t.RegExp,_e=t.String,Ce=t.TypeError,Oe=r.prototype,Pe=Se.prototype,Re=Ae.prototype,Te=t["__core-js_shared__"],Fe=Pe.toString,je=Re.hasOwnProperty,Ne=0,Ie=(n=/[^.]+$/.exec(Te&&Te.keys&&Te.keys.IE_PROTO||""))?"Symbol(src)_1."+n:"",Le=Re.toString,Me=Fe.call(Ae),De=dt._,Be=ke("^"+Fe.call(je).replace(te,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Ue=ht?t.Buffer:o,ze=t.Symbol,$e=t.Uint8Array,He=Ue?Ue.allocUnsafe:o,We=an(Ae.getPrototypeOf,Ae),Ve=Ae.create,qe=Re.propertyIsEnumerable,Ke=Oe.splice,Ge=ze?ze.isConcatSpreadable:o,Je=ze?ze.iterator:o,Qe=ze?ze.toStringTag:o,et=function(){try{var e=ui(Ae,"defineProperty");return e({},"",{}),e}catch(e){}}(),at=t.clearTimeout!==dt.clearTimeout&&t.clearTimeout,ut=oe&&oe.now!==dt.Date.now&&oe.now,ct=t.setTimeout!==dt.setTimeout&&t.setTimeout,ft=xe.ceil,pt=xe.floor,mt=Ae.getOwnPropertySymbols,gt=Ue?Ue.isBuffer:o,Lt=t.isFinite,Wt=Oe.join,mn=an(Ae.keys,Ae),gn=xe.max,vn=xe.min,yn=oe.now,bn=t.parseInt,wn=xe.random,En=Oe.reverse,Sn=ui(t,"DataView"),xn=ui(t,"Map"),An=ui(t,"Promise"),kn=ui(t,"Set"),_n=ui(t,"WeakMap"),Cn=ui(Ae,"create"),On=_n&&new _n,Pn={},Rn=Mi(Sn),Tn=Mi(xn),Fn=Mi(An),jn=Mi(kn),Nn=Mi(_n),In=ze?ze.prototype:o,Ln=In?In.valueOf:o,Mn=In?In.toString:o;function Dn(e){if(es(e)&&!Ha(e)&&!(e instanceof $n)){if(e instanceof zn)return e;if(je.call(e,"__wrapped__"))return Di(e)}return new zn(e)}var Bn=function(){function e(){}return function(t){if(!Za(t))return{};if(Ve)return Ve(t);e.prototype=t;var n=new e;return e.prototype=o,n}}();function Un(){}function zn(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=o}function $n(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=p,this.__views__=[]}function Hn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function Wn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function Vn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function qn(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new Vn;++t<n;)this.add(e[t])}function Kn(e){var t=this.__data__=new Wn(e);this.size=t.size}function Gn(e,t){var n=Ha(e),r=!n&&$a(e),o=!n&&!r&&Ka(e),i=!n&&!r&&!o&&ls(e),a=n||r||o||i,s=a?Kt(e.length,_e):[],l=s.length;for(var u in e)!t&&!je.call(e,u)||a&&("length"==u||o&&("offset"==u||"parent"==u)||i&&("buffer"==u||"byteLength"==u||"byteOffset"==u)||gi(u,l))||s.push(u);return s}function Jn(e){var t=e.length;return t?e[Vr(0,t-1)]:o}function Yn(e,t){return Fi(Co(e),ir(t,0,e.length))}function Xn(e){return Fi(Co(e))}function Qn(e,t,n){(n!==o&&!Ba(e[t],n)||n===o&&!(t in e))&&rr(e,t,n)}function Zn(e,t,n){var r=e[t];je.call(e,t)&&Ba(r,n)&&(n!==o||t in e)||rr(e,t,n)}function er(e,t){for(var n=e.length;n--;)if(Ba(e[n][0],t))return n;return-1}function tr(e,t,n,r){return cr(e,(function(e,o,i){t(r,e,n(e),i)})),r}function nr(e,t){return e&&Oo(t,Rs(t),e)}function rr(e,t,n){"__proto__"==t&&et?et(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0}):e[t]=n}function or(e,t){for(var n=-1,i=t.length,a=r(i),s=null==e;++n<i;)a[n]=s?o:ks(e,t[n]);return a}function ir(e,t,n){return e==e&&(n!==o&&(e=e<=n?e:n),t!==o&&(e=e>=t?e:t)),e}function ar(e,t,n,r,i,a){var s,l=1&t,u=2&t,c=4&t;if(n&&(s=i?n(e,r,i,a):n(e)),s!==o)return s;if(!Za(e))return e;var d=Ha(e);if(d){if(s=function(e){var t=e.length,n=new e.constructor(t);return t&&"string"==typeof e[0]&&je.call(e,"index")&&(n.index=e.index,n.input=e.input),n}(e),!l)return Co(e,s)}else{var f=fi(e),p=f==w||f==E;if(Ka(e))return Eo(e,l);if(f==A||f==m||p&&!i){if(s=u||p?{}:hi(e),!l)return u?function(e,t){return Oo(e,di(e),t)}(e,function(e,t){return e&&Oo(t,Ts(t),e)}(s,e)):function(e,t){return Oo(e,ci(e),t)}(e,nr(s,e))}else{if(!it[f])return i?e:{};s=function(e,t,n){var r,o=e.constructor;switch(t){case T:return So(e);case v:case y:return new o(+e);case F:return function(e,t){var n=t?So(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case j:case N:case I:case L:case M:case D:case B:case U:case z:return xo(e,n);case S:return new o;case x:case O:return new o(e);case _:return function(e){var t=new e.constructor(e.source,fe.exec(e));return t.lastIndex=e.lastIndex,t}(e);case C:return new o;case P:return r=e,Ln?Ae(Ln.call(r)):{}}}(e,f,l)}}a||(a=new Kn);var h=a.get(e);if(h)return h;a.set(e,s),is(e)?e.forEach((function(r){s.add(ar(r,t,n,r,e,a))})):ts(e)&&e.forEach((function(r,o){s.set(o,ar(r,t,n,o,e,a))}));var g=d?o:(c?u?ni:ti:u?Ts:Rs)(e);return kt(g||e,(function(r,o){g&&(r=e[o=r]),Zn(s,o,ar(r,t,n,o,e,a))})),s}function sr(e,t,n){var r=n.length;if(null==e)return!r;for(e=Ae(e);r--;){var i=n[r],a=t[i],s=e[i];if(s===o&&!(i in e)||!a(s))return!1}return!0}function lr(e,t,n){if("function"!=typeof e)throw new Ce(i);return Oi((function(){e.apply(o,n)}),t)}function ur(e,t,n,r){var o=-1,i=Pt,a=!0,s=e.length,l=[],u=t.length;if(!s)return l;n&&(t=Tt(t,Jt(n))),r?(i=Rt,a=!1):t.length>=200&&(i=Xt,a=!1,t=new qn(t));e:for(;++o<s;){var c=e[o],d=null==n?c:n(c);if(c=r||0!==c?c:0,a&&d==d){for(var f=u;f--;)if(t[f]===d)continue e;l.push(c)}else i(t,d,r)||l.push(c)}return l}Dn.templateSettings={escape:J,evaluate:Y,interpolate:X,variable:"",imports:{_:Dn}},Dn.prototype=Un.prototype,Dn.prototype.constructor=Dn,zn.prototype=Bn(Un.prototype),zn.prototype.constructor=zn,$n.prototype=Bn(Un.prototype),$n.prototype.constructor=$n,Hn.prototype.clear=function(){this.__data__=Cn?Cn(null):{},this.size=0},Hn.prototype.delete=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t},Hn.prototype.get=function(e){var t=this.__data__;if(Cn){var n=t[e];return n===a?o:n}return je.call(t,e)?t[e]:o},Hn.prototype.has=function(e){var t=this.__data__;return Cn?t[e]!==o:je.call(t,e)},Hn.prototype.set=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=Cn&&t===o?a:t,this},Wn.prototype.clear=function(){this.__data__=[],this.size=0},Wn.prototype.delete=function(e){var t=this.__data__,n=er(t,e);return!(n<0||(n==t.length-1?t.pop():Ke.call(t,n,1),--this.size,0))},Wn.prototype.get=function(e){var t=this.__data__,n=er(t,e);return n<0?o:t[n][1]},Wn.prototype.has=function(e){return er(this.__data__,e)>-1},Wn.prototype.set=function(e,t){var n=this.__data__,r=er(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Vn.prototype.clear=function(){this.size=0,this.__data__={hash:new Hn,map:new(xn||Wn),string:new Hn}},Vn.prototype.delete=function(e){var t=si(this,e).delete(e);return this.size-=t?1:0,t},Vn.prototype.get=function(e){return si(this,e).get(e)},Vn.prototype.has=function(e){return si(this,e).has(e)},Vn.prototype.set=function(e,t){var n=si(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},qn.prototype.add=qn.prototype.push=function(e){return this.__data__.set(e,a),this},qn.prototype.has=function(e){return this.__data__.has(e)},Kn.prototype.clear=function(){this.__data__=new Wn,this.size=0},Kn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Kn.prototype.get=function(e){return this.__data__.get(e)},Kn.prototype.has=function(e){return this.__data__.has(e)},Kn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Wn){var r=n.__data__;if(!xn||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Vn(r)}return n.set(e,t),this.size=n.size,this};var cr=To(yr),dr=To(br,!0);function fr(e,t){var n=!0;return cr(e,(function(e,r,o){return n=!!t(e,r,o)})),n}function pr(e,t,n){for(var r=-1,i=e.length;++r<i;){var a=e[r],s=t(a);if(null!=s&&(l===o?s==s&&!ss(s):n(s,l)))var l=s,u=a}return u}function hr(e,t){var n=[];return cr(e,(function(e,r,o){t(e,r,o)&&n.push(e)})),n}function mr(e,t,n,r,o){var i=-1,a=e.length;for(n||(n=mi),o||(o=[]);++i<a;){var s=e[i];t>0&&n(s)?t>1?mr(s,t-1,n,r,o):Ft(o,s):r||(o[o.length]=s)}return o}var gr=Fo(),vr=Fo(!0);function yr(e,t){return e&&gr(e,t,Rs)}function br(e,t){return e&&vr(e,t,Rs)}function wr(e,t){return Ot(t,(function(t){return Ya(e[t])}))}function Er(e,t){for(var n=0,r=(t=vo(t,e)).length;null!=e&&n<r;)e=e[Li(t[n++])];return n&&n==r?e:o}function Sr(e,t,n){var r=t(e);return Ha(e)?r:Ft(r,n(e))}function xr(e){return null==e?e===o?"[object Undefined]":"[object Null]":Qe&&Qe in Ae(e)?function(e){var t=je.call(e,Qe),n=e[Qe];try{e[Qe]=o;var r=!0}catch(e){}var i=Le.call(e);return r&&(t?e[Qe]=n:delete e[Qe]),i}(e):function(e){return Le.call(e)}(e)}function Ar(e,t){return e>t}function kr(e,t){return null!=e&&je.call(e,t)}function _r(e,t){return null!=e&&t in Ae(e)}function Cr(e,t,n){for(var i=n?Rt:Pt,a=e[0].length,s=e.length,l=s,u=r(s),c=1/0,d=[];l--;){var f=e[l];l&&t&&(f=Tt(f,Jt(t))),c=vn(f.length,c),u[l]=!n&&(t||a>=120&&f.length>=120)?new qn(l&&f):o}f=e[0];var p=-1,h=u[0];e:for(;++p<a&&d.length<c;){var m=f[p],g=t?t(m):m;if(m=n||0!==m?m:0,!(h?Xt(h,g):i(d,g,n))){for(l=s;--l;){var v=u[l];if(!(v?Xt(v,g):i(e[l],g,n)))continue e}h&&h.push(g),d.push(m)}}return d}function Or(e,t,n){var r=null==(e=ki(e,t=vo(t,e)))?e:e[Li(Ji(t))];return null==r?o:xt(r,e,n)}function Pr(e){return es(e)&&xr(e)==m}function Rr(e,t,n,r,i){return e===t||(null==e||null==t||!es(e)&&!es(t)?e!=e&&t!=t:function(e,t,n,r,i,a){var s=Ha(e),l=Ha(t),u=s?g:fi(e),c=l?g:fi(t),d=(u=u==m?A:u)==A,f=(c=c==m?A:c)==A,p=u==c;if(p&&Ka(e)){if(!Ka(t))return!1;s=!0,d=!1}if(p&&!d)return a||(a=new Kn),s||ls(e)?Zo(e,t,n,r,i,a):function(e,t,n,r,o,i,a){switch(n){case F:if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case T:return!(e.byteLength!=t.byteLength||!i(new $e(e),new $e(t)));case v:case y:case x:return Ba(+e,+t);case b:return e.name==t.name&&e.message==t.message;case _:case O:return e==t+"";case S:var s=on;case C:var l=1&r;if(s||(s=ln),e.size!=t.size&&!l)return!1;var u=a.get(e);if(u)return u==t;r|=2,a.set(e,t);var c=Zo(s(e),s(t),r,o,i,a);return a.delete(e),c;case P:if(Ln)return Ln.call(e)==Ln.call(t)}return!1}(e,t,u,n,r,i,a);if(!(1&n)){var h=d&&je.call(e,"__wrapped__"),w=f&&je.call(t,"__wrapped__");if(h||w){var E=h?e.value():e,k=w?t.value():t;return a||(a=new Kn),i(E,k,n,r,a)}}return!!p&&(a||(a=new Kn),function(e,t,n,r,i,a){var s=1&n,l=ti(e),u=l.length;if(u!=ti(t).length&&!s)return!1;for(var c=u;c--;){var d=l[c];if(!(s?d in t:je.call(t,d)))return!1}var f=a.get(e),p=a.get(t);if(f&&p)return f==t&&p==e;var h=!0;a.set(e,t),a.set(t,e);for(var m=s;++c<u;){var g=e[d=l[c]],v=t[d];if(r)var y=s?r(v,g,d,t,e,a):r(g,v,d,e,t,a);if(!(y===o?g===v||i(g,v,n,r,a):y)){h=!1;break}m||(m="constructor"==d)}if(h&&!m){var b=e.constructor,w=t.constructor;b==w||!("constructor"in e)||!("constructor"in t)||"function"==typeof b&&b instanceof b&&"function"==typeof w&&w instanceof w||(h=!1)}return a.delete(e),a.delete(t),h}(e,t,n,r,i,a))}(e,t,n,r,Rr,i))}function Tr(e,t,n,r){var i=n.length,a=i,s=!r;if(null==e)return!a;for(e=Ae(e);i--;){var l=n[i];if(s&&l[2]?l[1]!==e[l[0]]:!(l[0]in e))return!1}for(;++i<a;){var u=(l=n[i])[0],c=e[u],d=l[1];if(s&&l[2]){if(c===o&&!(u in e))return!1}else{var f=new Kn;if(r)var p=r(c,d,u,e,t,f);if(!(p===o?Rr(d,c,3,r,f):p))return!1}}return!0}function Fr(e){return!(!Za(e)||(t=e,Ie&&Ie in t))&&(Ya(e)?Be:me).test(Mi(e));var t}function jr(e){return"function"==typeof e?e:null==e?nl:"object"==typeof e?Ha(e)?Dr(e[0],e[1]):Mr(e):dl(e)}function Nr(e){if(!Ei(e))return mn(e);var t=[];for(var n in Ae(e))je.call(e,n)&&"constructor"!=n&&t.push(n);return t}function Ir(e,t){return e<t}function Lr(e,t){var n=-1,o=Va(e)?r(e.length):[];return cr(e,(function(e,r,i){o[++n]=t(e,r,i)})),o}function Mr(e){var t=li(e);return 1==t.length&&t[0][2]?xi(t[0][0],t[0][1]):function(n){return n===e||Tr(n,e,t)}}function Dr(e,t){return yi(e)&&Si(t)?xi(Li(e),t):function(n){var r=ks(n,e);return r===o&&r===t?_s(n,e):Rr(t,r,3)}}function Br(e,t,n,r,i){e!==t&&gr(t,(function(a,s){if(i||(i=new Kn),Za(a))!function(e,t,n,r,i,a,s){var l=_i(e,n),u=_i(t,n),c=s.get(u);if(c)Qn(e,n,c);else{var d=a?a(l,u,n+"",e,t,s):o,f=d===o;if(f){var p=Ha(u),h=!p&&Ka(u),m=!p&&!h&&ls(u);d=u,p||h||m?Ha(l)?d=l:qa(l)?d=Co(l):h?(f=!1,d=Eo(u,!0)):m?(f=!1,d=xo(u,!0)):d=[]:rs(u)||$a(u)?(d=l,$a(l)?d=gs(l):Za(l)&&!Ya(l)||(d=hi(u))):f=!1}f&&(s.set(u,d),i(d,u,r,a,s),s.delete(u)),Qn(e,n,d)}}(e,t,s,n,Br,r,i);else{var l=r?r(_i(e,s),a,s+"",e,t,i):o;l===o&&(l=a),Qn(e,s,l)}}),Ts)}function Ur(e,t){var n=e.length;if(n)return gi(t+=t<0?n:0,n)?e[t]:o}function zr(e,t,n){t=t.length?Tt(t,(function(e){return Ha(e)?function(t){return Er(t,1===e.length?e[0]:e)}:e})):[nl];var r=-1;t=Tt(t,Jt(ai()));var o=Lr(e,(function(e,n,o){var i=Tt(t,(function(t){return t(e)}));return{criteria:i,index:++r,value:e}}));return function(e){var t=e.length;for(e.sort((function(e,t){return function(e,t,n){for(var r=-1,o=e.criteria,i=t.criteria,a=o.length,s=n.length;++r<a;){var l=Ao(o[r],i[r]);if(l)return r>=s?l:l*("desc"==n[r]?-1:1)}return e.index-t.index}(e,t,n)}));t--;)e[t]=e[t].value;return e}(o)}function $r(e,t,n){for(var r=-1,o=t.length,i={};++r<o;){var a=t[r],s=Er(e,a);n(s,a)&&Yr(i,vo(a,e),s)}return i}function Hr(e,t,n,r){var o=r?Ut:Bt,i=-1,a=t.length,s=e;for(e===t&&(t=Co(t)),n&&(s=Tt(e,Jt(n)));++i<a;)for(var l=0,u=t[i],c=n?n(u):u;(l=o(s,c,l,r))>-1;)s!==e&&Ke.call(s,l,1),Ke.call(e,l,1);return e}function Wr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var o=t[n];if(n==r||o!==i){var i=o;gi(o)?Ke.call(e,o,1):lo(e,o)}}return e}function Vr(e,t){return e+pt(wn()*(t-e+1))}function qr(e,t){var n="";if(!e||t<1||t>d)return n;do{t%2&&(n+=e),(t=pt(t/2))&&(e+=e)}while(t);return n}function Kr(e,t){return Pi(Ai(e,t,nl),e+"")}function Gr(e){return Jn(Bs(e))}function Jr(e,t){var n=Bs(e);return Fi(n,ir(t,0,n.length))}function Yr(e,t,n,r){if(!Za(e))return e;for(var i=-1,a=(t=vo(t,e)).length,s=a-1,l=e;null!=l&&++i<a;){var u=Li(t[i]),c=n;if("__proto__"===u||"constructor"===u||"prototype"===u)return e;if(i!=s){var d=l[u];(c=r?r(d,u,l):o)===o&&(c=Za(d)?d:gi(t[i+1])?[]:{})}Zn(l,u,c),l=l[u]}return e}var Xr=On?function(e,t){return On.set(e,t),e}:nl,Qr=et?function(e,t){return et(e,"toString",{configurable:!0,enumerable:!1,value:Zs(t),writable:!0})}:nl;function Zr(e){return Fi(Bs(e))}function eo(e,t,n){var o=-1,i=e.length;t<0&&(t=-t>i?0:i+t),(n=n>i?i:n)<0&&(n+=i),i=t>n?0:n-t>>>0,t>>>=0;for(var a=r(i);++o<i;)a[o]=e[o+t];return a}function to(e,t){var n;return cr(e,(function(e,r,o){return!(n=t(e,r,o))})),!!n}function no(e,t,n){var r=0,o=null==e?r:e.length;if("number"==typeof t&&t==t&&o<=2147483647){for(;r<o;){var i=r+o>>>1,a=e[i];null!==a&&!ss(a)&&(n?a<=t:a<t)?r=i+1:o=i}return o}return ro(e,t,nl,n)}function ro(e,t,n,r){var i=0,a=null==e?0:e.length;if(0===a)return 0;for(var s=(t=n(t))!=t,l=null===t,u=ss(t),c=t===o;i<a;){var d=pt((i+a)/2),f=n(e[d]),p=f!==o,h=null===f,m=f==f,g=ss(f);if(s)var v=r||m;else v=c?m&&(r||p):l?m&&p&&(r||!h):u?m&&p&&!h&&(r||!g):!h&&!g&&(r?f<=t:f<t);v?i=d+1:a=d}return vn(a,4294967294)}function oo(e,t){for(var n=-1,r=e.length,o=0,i=[];++n<r;){var a=e[n],s=t?t(a):a;if(!n||!Ba(s,l)){var l=s;i[o++]=0===a?0:a}}return i}function io(e){return"number"==typeof e?e:ss(e)?f:+e}function ao(e){if("string"==typeof e)return e;if(Ha(e))return Tt(e,ao)+"";if(ss(e))return Mn?Mn.call(e):"";var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}function so(e,t,n){var r=-1,o=Pt,i=e.length,a=!0,s=[],l=s;if(n)a=!1,o=Rt;else if(i>=200){var u=t?null:Ko(e);if(u)return ln(u);a=!1,o=Xt,l=new qn}else l=t?[]:s;e:for(;++r<i;){var c=e[r],d=t?t(c):c;if(c=n||0!==c?c:0,a&&d==d){for(var f=l.length;f--;)if(l[f]===d)continue e;t&&l.push(d),s.push(c)}else o(l,d,n)||(l!==s&&l.push(d),s.push(c))}return s}function lo(e,t){return null==(e=ki(e,t=vo(t,e)))||delete e[Li(Ji(t))]}function uo(e,t,n,r){return Yr(e,t,n(Er(e,t)),r)}function co(e,t,n,r){for(var o=e.length,i=r?o:-1;(r?i--:++i<o)&&t(e[i],i,e););return n?eo(e,r?0:i,r?i+1:o):eo(e,r?i+1:0,r?o:i)}function fo(e,t){var n=e;return n instanceof $n&&(n=n.value()),jt(t,(function(e,t){return t.func.apply(t.thisArg,Ft([e],t.args))}),n)}function po(e,t,n){var o=e.length;if(o<2)return o?so(e[0]):[];for(var i=-1,a=r(o);++i<o;)for(var s=e[i],l=-1;++l<o;)l!=i&&(a[i]=ur(a[i]||s,e[l],t,n));return so(mr(a,1),t,n)}function ho(e,t,n){for(var r=-1,i=e.length,a=t.length,s={};++r<i;){var l=r<a?t[r]:o;n(s,e[r],l)}return s}function mo(e){return qa(e)?e:[]}function go(e){return"function"==typeof e?e:nl}function vo(e,t){return Ha(e)?e:yi(e,t)?[e]:Ii(vs(e))}var yo=Kr;function bo(e,t,n){var r=e.length;return n=n===o?r:n,!t&&n>=r?e:eo(e,t,n)}var wo=at||function(e){return dt.clearTimeout(e)};function Eo(e,t){if(t)return e.slice();var n=e.length,r=He?He(n):new e.constructor(n);return e.copy(r),r}function So(e){var t=new e.constructor(e.byteLength);return new $e(t).set(new $e(e)),t}function xo(e,t){var n=t?So(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function Ao(e,t){if(e!==t){var n=e!==o,r=null===e,i=e==e,a=ss(e),s=t!==o,l=null===t,u=t==t,c=ss(t);if(!l&&!c&&!a&&e>t||a&&s&&u&&!l&&!c||r&&s&&u||!n&&u||!i)return 1;if(!r&&!a&&!c&&e<t||c&&n&&i&&!r&&!a||l&&n&&i||!s&&i||!u)return-1}return 0}function ko(e,t,n,o){for(var i=-1,a=e.length,s=n.length,l=-1,u=t.length,c=gn(a-s,0),d=r(u+c),f=!o;++l<u;)d[l]=t[l];for(;++i<s;)(f||i<a)&&(d[n[i]]=e[i]);for(;c--;)d[l++]=e[i++];return d}function _o(e,t,n,o){for(var i=-1,a=e.length,s=-1,l=n.length,u=-1,c=t.length,d=gn(a-l,0),f=r(d+c),p=!o;++i<d;)f[i]=e[i];for(var h=i;++u<c;)f[h+u]=t[u];for(;++s<l;)(p||i<a)&&(f[h+n[s]]=e[i++]);return f}function Co(e,t){var n=-1,o=e.length;for(t||(t=r(o));++n<o;)t[n]=e[n];return t}function Oo(e,t,n,r){var i=!n;n||(n={});for(var a=-1,s=t.length;++a<s;){var l=t[a],u=r?r(n[l],e[l],l,n,e):o;u===o&&(u=e[l]),i?rr(n,l,u):Zn(n,l,u)}return n}function Po(e,t){return function(n,r){var o=Ha(n)?At:tr,i=t?t():{};return o(n,e,ai(r,2),i)}}function Ro(e){return Kr((function(t,n){var r=-1,i=n.length,a=i>1?n[i-1]:o,s=i>2?n[2]:o;for(a=e.length>3&&"function"==typeof a?(i--,a):o,s&&vi(n[0],n[1],s)&&(a=i<3?o:a,i=1),t=Ae(t);++r<i;){var l=n[r];l&&e(t,l,r,a)}return t}))}function To(e,t){return function(n,r){if(null==n)return n;if(!Va(n))return e(n,r);for(var o=n.length,i=t?o:-1,a=Ae(n);(t?i--:++i<o)&&!1!==r(a[i],i,a););return n}}function Fo(e){return function(t,n,r){for(var o=-1,i=Ae(t),a=r(t),s=a.length;s--;){var l=a[e?s:++o];if(!1===n(i[l],l,i))break}return t}}function jo(e){return function(t){var n=rn(t=vs(t))?dn(t):o,r=n?n[0]:t.charAt(0),i=n?bo(n,1).join(""):t.slice(1);return r[e]()+i}}function No(e){return function(t){return jt(Ys($s(t).replace(Ye,"")),e,"")}}function Io(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=Bn(e.prototype),r=e.apply(n,t);return Za(r)?r:n}}function Lo(e){return function(t,n,r){var i=Ae(t);if(!Va(t)){var a=ai(n,3);t=Rs(t),n=function(e){return a(i[e],e,i)}}var s=e(t,n,r);return s>-1?i[a?t[s]:s]:o}}function Mo(e){return ei((function(t){var n=t.length,r=n,a=zn.prototype.thru;for(e&&t.reverse();r--;){var s=t[r];if("function"!=typeof s)throw new Ce(i);if(a&&!l&&"wrapper"==oi(s))var l=new zn([],!0)}for(r=l?r:n;++r<n;){var u=oi(s=t[r]),c="wrapper"==u?ri(s):o;l=c&&bi(c[0])&&424==c[1]&&!c[4].length&&1==c[9]?l[oi(c[0])].apply(l,c[3]):1==s.length&&bi(s)?l[u]():l.thru(s)}return function(){var e=arguments,r=e[0];if(l&&1==e.length&&Ha(r))return l.plant(r).value();for(var o=0,i=n?t[o].apply(this,e):r;++o<n;)i=t[o].call(this,i);return i}}))}function Do(e,t,n,i,a,s,l,c,d,f){var p=t&u,h=1&t,m=2&t,g=24&t,v=512&t,y=m?o:Io(e);return function u(){for(var b=arguments.length,w=r(b),E=b;E--;)w[E]=arguments[E];if(g)var S=ii(u),x=function(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}(w,S);if(i&&(w=ko(w,i,a,g)),s&&(w=_o(w,s,l,g)),b-=x,g&&b<f){var A=sn(w,S);return Vo(e,t,Do,u.placeholder,n,w,A,c,d,f-b)}var k=h?n:this,_=m?k[e]:e;return b=w.length,c?w=function(e,t){for(var n=e.length,r=vn(t.length,n),i=Co(e);r--;){var a=t[r];e[r]=gi(a,n)?i[a]:o}return e}(w,c):v&&b>1&&w.reverse(),p&&d<b&&(w.length=d),this&&this!==dt&&this instanceof u&&(_=y||Io(_)),_.apply(k,w)}}function Bo(e,t){return function(n,r){return function(e,t,n,r){return yr(e,(function(e,o,i){t(r,n(e),o,i)})),r}(n,e,t(r),{})}}function Uo(e,t){return function(n,r){var i;if(n===o&&r===o)return t;if(n!==o&&(i=n),r!==o){if(i===o)return r;"string"==typeof n||"string"==typeof r?(n=ao(n),r=ao(r)):(n=io(n),r=io(r)),i=e(n,r)}return i}}function zo(e){return ei((function(t){return t=Tt(t,Jt(ai())),Kr((function(n){var r=this;return e(t,(function(e){return xt(e,r,n)}))}))}))}function $o(e,t){var n=(t=t===o?" ":ao(t)).length;if(n<2)return n?qr(t,e):t;var r=qr(t,ft(e/cn(t)));return rn(t)?bo(dn(r),0,e).join(""):r.slice(0,e)}function Ho(e){return function(t,n,i){return i&&"number"!=typeof i&&vi(t,n,i)&&(n=i=o),t=fs(t),n===o?(n=t,t=0):n=fs(n),function(e,t,n,o){for(var i=-1,a=gn(ft((t-e)/(n||1)),0),s=r(a);a--;)s[o?a:++i]=e,e+=n;return s}(t,n,i=i===o?t<n?1:-1:fs(i),e)}}function Wo(e){return function(t,n){return"string"==typeof t&&"string"==typeof n||(t=ms(t),n=ms(n)),e(t,n)}}function Vo(e,t,n,r,i,a,s,u,c,d){var f=8&t;t|=f?l:64,4&(t&=~(f?64:l))||(t&=-4);var p=[e,t,i,f?a:o,f?s:o,f?o:a,f?o:s,u,c,d],h=n.apply(o,p);return bi(e)&&Ci(h,p),h.placeholder=r,Ri(h,e,t)}function qo(e){var t=xe[e];return function(e,n){if(e=ms(e),(n=null==n?0:vn(ps(n),292))&&Lt(e)){var r=(vs(e)+"e").split("e");return+((r=(vs(t(r[0]+"e"+(+r[1]+n)))+"e").split("e"))[0]+"e"+(+r[1]-n))}return t(e)}}var Ko=kn&&1/ln(new kn([,-0]))[1]==c?function(e){return new kn(e)}:sl;function Go(e){return function(t){var n=fi(t);return n==S?on(t):n==C?un(t):function(e,t){return Tt(t,(function(t){return[t,e[t]]}))}(t,e(t))}}function Jo(e,t,n,a,c,d,f,p){var h=2&t;if(!h&&"function"!=typeof e)throw new Ce(i);var m=a?a.length:0;if(m||(t&=-97,a=c=o),f=f===o?f:gn(ps(f),0),p=p===o?p:ps(p),m-=c?c.length:0,64&t){var g=a,v=c;a=c=o}var y=h?o:ri(e),b=[e,t,n,a,c,g,v,d,f,p];if(y&&function(e,t){var n=e[1],r=t[1],o=n|r,i=o<131,a=r==u&&8==n||r==u&&256==n&&e[7].length<=t[8]||384==r&&t[7].length<=t[8]&&8==n;if(!i&&!a)return e;1&r&&(e[2]=t[2],o|=1&n?0:4);var l=t[3];if(l){var c=e[3];e[3]=c?ko(c,l,t[4]):l,e[4]=c?sn(e[3],s):t[4]}(l=t[5])&&(c=e[5],e[5]=c?_o(c,l,t[6]):l,e[6]=c?sn(e[5],s):t[6]),(l=t[7])&&(e[7]=l),r&u&&(e[8]=null==e[8]?t[8]:vn(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=o}(b,y),e=b[0],t=b[1],n=b[2],a=b[3],c=b[4],!(p=b[9]=b[9]===o?h?0:e.length:gn(b[9]-m,0))&&24&t&&(t&=-25),t&&1!=t)w=8==t||16==t?function(e,t,n){var i=Io(e);return function a(){for(var s=arguments.length,l=r(s),u=s,c=ii(a);u--;)l[u]=arguments[u];var d=s<3&&l[0]!==c&&l[s-1]!==c?[]:sn(l,c);return(s-=d.length)<n?Vo(e,t,Do,a.placeholder,o,l,d,o,o,n-s):xt(this&&this!==dt&&this instanceof a?i:e,this,l)}}(e,t,p):t!=l&&33!=t||c.length?Do.apply(o,b):function(e,t,n,o){var i=1&t,a=Io(e);return function t(){for(var s=-1,l=arguments.length,u=-1,c=o.length,d=r(c+l),f=this&&this!==dt&&this instanceof t?a:e;++u<c;)d[u]=o[u];for(;l--;)d[u++]=arguments[++s];return xt(f,i?n:this,d)}}(e,t,n,a);else var w=function(e,t,n){var r=1&t,o=Io(e);return function t(){return(this&&this!==dt&&this instanceof t?o:e).apply(r?n:this,arguments)}}(e,t,n);return Ri((y?Xr:Ci)(w,b),e,t)}function Yo(e,t,n,r){return e===o||Ba(e,Re[n])&&!je.call(r,n)?t:e}function Xo(e,t,n,r,i,a){return Za(e)&&Za(t)&&(a.set(t,e),Br(e,t,o,Xo,a),a.delete(t)),e}function Qo(e){return rs(e)?o:e}function Zo(e,t,n,r,i,a){var s=1&n,l=e.length,u=t.length;if(l!=u&&!(s&&u>l))return!1;var c=a.get(e),d=a.get(t);if(c&&d)return c==t&&d==e;var f=-1,p=!0,h=2&n?new qn:o;for(a.set(e,t),a.set(t,e);++f<l;){var m=e[f],g=t[f];if(r)var v=s?r(g,m,f,t,e,a):r(m,g,f,e,t,a);if(v!==o){if(v)continue;p=!1;break}if(h){if(!It(t,(function(e,t){if(!Xt(h,t)&&(m===e||i(m,e,n,r,a)))return h.push(t)}))){p=!1;break}}else if(m!==g&&!i(m,g,n,r,a)){p=!1;break}}return a.delete(e),a.delete(t),p}function ei(e){return Pi(Ai(e,o,Wi),e+"")}function ti(e){return Sr(e,Rs,ci)}function ni(e){return Sr(e,Ts,di)}var ri=On?function(e){return On.get(e)}:sl;function oi(e){for(var t=e.name+"",n=Pn[t],r=je.call(Pn,t)?n.length:0;r--;){var o=n[r],i=o.func;if(null==i||i==e)return o.name}return t}function ii(e){return(je.call(Dn,"placeholder")?Dn:e).placeholder}function ai(){var e=Dn.iteratee||rl;return e=e===rl?jr:e,arguments.length?e(arguments[0],arguments[1]):e}function si(e,t){var n,r,o=e.__data__;return("string"==(r=typeof(n=t))||"number"==r||"symbol"==r||"boolean"==r?"__proto__"!==n:null===n)?o["string"==typeof t?"string":"hash"]:o.map}function li(e){for(var t=Rs(e),n=t.length;n--;){var r=t[n],o=e[r];t[n]=[r,o,Si(o)]}return t}function ui(e,t){var n=function(e,t){return null==e?o:e[t]}(e,t);return Fr(n)?n:o}var ci=mt?function(e){return null==e?[]:(e=Ae(e),Ot(mt(e),(function(t){return qe.call(e,t)})))}:hl,di=mt?function(e){for(var t=[];e;)Ft(t,ci(e)),e=We(e);return t}:hl,fi=xr;function pi(e,t,n){for(var r=-1,o=(t=vo(t,e)).length,i=!1;++r<o;){var a=Li(t[r]);if(!(i=null!=e&&n(e,a)))break;e=e[a]}return i||++r!=o?i:!!(o=null==e?0:e.length)&&Qa(o)&&gi(a,o)&&(Ha(e)||$a(e))}function hi(e){return"function"!=typeof e.constructor||Ei(e)?{}:Bn(We(e))}function mi(e){return Ha(e)||$a(e)||!!(Ge&&e&&e[Ge])}function gi(e,t){var n=typeof e;return!!(t=null==t?d:t)&&("number"==n||"symbol"!=n&&ve.test(e))&&e>-1&&e%1==0&&e<t}function vi(e,t,n){if(!Za(n))return!1;var r=typeof t;return!!("number"==r?Va(n)&&gi(t,n.length):"string"==r&&t in n)&&Ba(n[t],e)}function yi(e,t){if(Ha(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!ss(e))||Z.test(e)||!Q.test(e)||null!=t&&e in Ae(t)}function bi(e){var t=oi(e),n=Dn[t];if("function"!=typeof n||!(t in $n.prototype))return!1;if(e===n)return!0;var r=ri(n);return!!r&&e===r[0]}(Sn&&fi(new Sn(new ArrayBuffer(1)))!=F||xn&&fi(new xn)!=S||An&&fi(An.resolve())!=k||kn&&fi(new kn)!=C||_n&&fi(new _n)!=R)&&(fi=function(e){var t=xr(e),n=t==A?e.constructor:o,r=n?Mi(n):"";if(r)switch(r){case Rn:return F;case Tn:return S;case Fn:return k;case jn:return C;case Nn:return R}return t});var wi=Te?Ya:ml;function Ei(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||Re)}function Si(e){return e==e&&!Za(e)}function xi(e,t){return function(n){return null!=n&&n[e]===t&&(t!==o||e in Ae(n))}}function Ai(e,t,n){return t=gn(t===o?e.length-1:t,0),function(){for(var o=arguments,i=-1,a=gn(o.length-t,0),s=r(a);++i<a;)s[i]=o[t+i];i=-1;for(var l=r(t+1);++i<t;)l[i]=o[i];return l[t]=n(s),xt(e,this,l)}}function ki(e,t){return t.length<2?e:Er(e,eo(t,0,-1))}function _i(e,t){if(("constructor"!==t||"function"!=typeof e[t])&&"__proto__"!=t)return e[t]}var Ci=Ti(Xr),Oi=ct||function(e,t){return dt.setTimeout(e,t)},Pi=Ti(Qr);function Ri(e,t,n){var r=t+"";return Pi(e,function(e,t){var n=t.length;if(!n)return e;var r=n-1;return t[r]=(n>1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(ie,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return kt(h,(function(n){var r="_."+n[0];t&n[1]&&!Pt(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(ae);return t?t[1].split(se):[]}(r),n)))}function Ti(e){var t=0,n=0;return function(){var r=yn(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(o,arguments)}}function Fi(e,t){var n=-1,r=e.length,i=r-1;for(t=t===o?r:t;++n<t;){var a=Vr(n,i),s=e[a];e[a]=e[n],e[n]=s}return e.length=t,e}var ji,Ni,Ii=(ji=ja((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(ee,(function(e,n,r,o){t.push(r?o.replace(ce,"$1"):n||e)})),t}),(function(e){return 500===Ni.size&&Ni.clear(),e})),Ni=ji.cache,ji);function Li(e){if("string"==typeof e||ss(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t}function Mi(e){if(null!=e){try{return Fe.call(e)}catch(e){}try{return e+""}catch(e){}}return""}function Di(e){if(e instanceof $n)return e.clone();var t=new zn(e.__wrapped__,e.__chain__);return t.__actions__=Co(e.__actions__),t.__index__=e.__index__,t.__values__=e.__values__,t}var Bi=Kr((function(e,t){return qa(e)?ur(e,mr(t,1,qa,!0)):[]})),Ui=Kr((function(e,t){var n=Ji(t);return qa(n)&&(n=o),qa(e)?ur(e,mr(t,1,qa,!0),ai(n,2)):[]})),zi=Kr((function(e,t){var n=Ji(t);return qa(n)&&(n=o),qa(e)?ur(e,mr(t,1,qa,!0),o,n):[]}));function $i(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:ps(n);return o<0&&(o=gn(r+o,0)),Dt(e,ai(t,3),o)}function Hi(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r-1;return n!==o&&(i=ps(n),i=n<0?gn(r+i,0):vn(i,r-1)),Dt(e,ai(t,3),i,!0)}function Wi(e){return null!=e&&e.length?mr(e,1):[]}function Vi(e){return e&&e.length?e[0]:o}var qi=Kr((function(e){var t=Tt(e,mo);return t.length&&t[0]===e[0]?Cr(t):[]})),Ki=Kr((function(e){var t=Ji(e),n=Tt(e,mo);return t===Ji(n)?t=o:n.pop(),n.length&&n[0]===e[0]?Cr(n,ai(t,2)):[]})),Gi=Kr((function(e){var t=Ji(e),n=Tt(e,mo);return(t="function"==typeof t?t:o)&&n.pop(),n.length&&n[0]===e[0]?Cr(n,o,t):[]}));function Ji(e){var t=null==e?0:e.length;return t?e[t-1]:o}var Yi=Kr(Xi);function Xi(e,t){return e&&e.length&&t&&t.length?Hr(e,t):e}var Qi=ei((function(e,t){var n=null==e?0:e.length,r=or(e,t);return Wr(e,Tt(t,(function(e){return gi(e,n)?+e:e})).sort(Ao)),r}));function Zi(e){return null==e?e:En.call(e)}var ea=Kr((function(e){return so(mr(e,1,qa,!0))})),ta=Kr((function(e){var t=Ji(e);return qa(t)&&(t=o),so(mr(e,1,qa,!0),ai(t,2))})),na=Kr((function(e){var t=Ji(e);return t="function"==typeof t?t:o,so(mr(e,1,qa,!0),o,t)}));function ra(e){if(!e||!e.length)return[];var t=0;return e=Ot(e,(function(e){if(qa(e))return t=gn(e.length,t),!0})),Kt(t,(function(t){return Tt(e,Ht(t))}))}function oa(e,t){if(!e||!e.length)return[];var n=ra(e);return null==t?n:Tt(n,(function(e){return xt(t,o,e)}))}var ia=Kr((function(e,t){return qa(e)?ur(e,t):[]})),aa=Kr((function(e){return po(Ot(e,qa))})),sa=Kr((function(e){var t=Ji(e);return qa(t)&&(t=o),po(Ot(e,qa),ai(t,2))})),la=Kr((function(e){var t=Ji(e);return t="function"==typeof t?t:o,po(Ot(e,qa),o,t)})),ua=Kr(ra),ca=Kr((function(e){var t=e.length,n=t>1?e[t-1]:o;return n="function"==typeof n?(e.pop(),n):o,oa(e,n)}));function da(e){var t=Dn(e);return t.__chain__=!0,t}function fa(e,t){return t(e)}var pa=ei((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return or(t,e)};return!(t>1||this.__actions__.length)&&r instanceof $n&&gi(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:fa,args:[i],thisArg:o}),new zn(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(o),e}))):this.thru(i)})),ha=Po((function(e,t,n){je.call(e,n)?++e[n]:rr(e,n,1)})),ma=Lo($i),ga=Lo(Hi);function va(e,t){return(Ha(e)?kt:cr)(e,ai(t,3))}function ya(e,t){return(Ha(e)?_t:dr)(e,ai(t,3))}var ba=Po((function(e,t,n){je.call(e,n)?e[n].push(t):rr(e,n,[t])})),wa=Kr((function(e,t,n){var o=-1,i="function"==typeof t,a=Va(e)?r(e.length):[];return cr(e,(function(e){a[++o]=i?xt(t,e,n):Or(e,t,n)})),a})),Ea=Po((function(e,t,n){rr(e,n,t)}));function Sa(e,t){return(Ha(e)?Tt:Lr)(e,ai(t,3))}var xa=Po((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]})),Aa=Kr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&vi(e,t[0],t[1])?t=[]:n>2&&vi(t[0],t[1],t[2])&&(t=[t[0]]),zr(e,mr(t,1),[])})),ka=ut||function(){return dt.Date.now()};function _a(e,t,n){return t=n?o:t,t=e&&null==t?e.length:t,Jo(e,u,o,o,o,o,t)}function Ca(e,t){var n;if("function"!=typeof t)throw new Ce(i);return e=ps(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=o),n}}var Oa=Kr((function(e,t,n){var r=1;if(n.length){var o=sn(n,ii(Oa));r|=l}return Jo(e,r,t,n,o)})),Pa=Kr((function(e,t,n){var r=3;if(n.length){var o=sn(n,ii(Pa));r|=l}return Jo(t,r,e,n,o)}));function Ra(e,t,n){var r,a,s,l,u,c,d=0,f=!1,p=!1,h=!0;if("function"!=typeof e)throw new Ce(i);function m(t){var n=r,i=a;return r=a=o,d=t,l=e.apply(i,n)}function g(e){var n=e-c;return c===o||n>=t||n<0||p&&e-d>=s}function v(){var e=ka();if(g(e))return y(e);u=Oi(v,function(e){var n=t-(e-c);return p?vn(n,s-(e-d)):n}(e))}function y(e){return u=o,h&&r?m(e):(r=a=o,l)}function b(){var e=ka(),n=g(e);if(r=arguments,a=this,c=e,n){if(u===o)return function(e){return d=e,u=Oi(v,t),f?m(e):l}(c);if(p)return wo(u),u=Oi(v,t),m(c)}return u===o&&(u=Oi(v,t)),l}return t=ms(t)||0,Za(n)&&(f=!!n.leading,s=(p="maxWait"in n)?gn(ms(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h),b.cancel=function(){u!==o&&wo(u),d=0,r=c=a=u=o},b.flush=function(){return u===o?l:y(ka())},b}var Ta=Kr((function(e,t){return lr(e,1,t)})),Fa=Kr((function(e,t,n){return lr(e,ms(t)||0,n)}));function ja(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new Ce(i);var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],i=n.cache;if(i.has(o))return i.get(o);var a=e.apply(this,r);return n.cache=i.set(o,a)||i,a};return n.cache=new(ja.Cache||Vn),n}function Na(e){if("function"!=typeof e)throw new Ce(i);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}ja.Cache=Vn;var Ia=yo((function(e,t){var n=(t=1==t.length&&Ha(t[0])?Tt(t[0],Jt(ai())):Tt(mr(t,1),Jt(ai()))).length;return Kr((function(r){for(var o=-1,i=vn(r.length,n);++o<i;)r[o]=t[o].call(this,r[o]);return xt(e,this,r)}))})),La=Kr((function(e,t){var n=sn(t,ii(La));return Jo(e,l,o,t,n)})),Ma=Kr((function(e,t){var n=sn(t,ii(Ma));return Jo(e,64,o,t,n)})),Da=ei((function(e,t){return Jo(e,256,o,o,o,t)}));function Ba(e,t){return e===t||e!=e&&t!=t}var Ua=Wo(Ar),za=Wo((function(e,t){return e>=t})),$a=Pr(function(){return arguments}())?Pr:function(e){return es(e)&&je.call(e,"callee")&&!qe.call(e,"callee")},Ha=r.isArray,Wa=vt?Jt(vt):function(e){return es(e)&&xr(e)==T};function Va(e){return null!=e&&Qa(e.length)&&!Ya(e)}function qa(e){return es(e)&&Va(e)}var Ka=gt||ml,Ga=yt?Jt(yt):function(e){return es(e)&&xr(e)==y};function Ja(e){if(!es(e))return!1;var t=xr(e);return t==b||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!rs(e)}function Ya(e){if(!Za(e))return!1;var t=xr(e);return t==w||t==E||"[object AsyncFunction]"==t||"[object Proxy]"==t}function Xa(e){return"number"==typeof e&&e==ps(e)}function Qa(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=d}function Za(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function es(e){return null!=e&&"object"==typeof e}var ts=bt?Jt(bt):function(e){return es(e)&&fi(e)==S};function ns(e){return"number"==typeof e||es(e)&&xr(e)==x}function rs(e){if(!es(e)||xr(e)!=A)return!1;var t=We(e);if(null===t)return!0;var n=je.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&Fe.call(n)==Me}var os=wt?Jt(wt):function(e){return es(e)&&xr(e)==_},is=Et?Jt(Et):function(e){return es(e)&&fi(e)==C};function as(e){return"string"==typeof e||!Ha(e)&&es(e)&&xr(e)==O}function ss(e){return"symbol"==typeof e||es(e)&&xr(e)==P}var ls=St?Jt(St):function(e){return es(e)&&Qa(e.length)&&!!ot[xr(e)]},us=Wo(Ir),cs=Wo((function(e,t){return e<=t}));function ds(e){if(!e)return[];if(Va(e))return as(e)?dn(e):Co(e);if(Je&&e[Je])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[Je]());var t=fi(e);return(t==S?on:t==C?ln:Bs)(e)}function fs(e){return e?(e=ms(e))===c||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}function ps(e){var t=fs(e),n=t%1;return t==t?n?t-n:t:0}function hs(e){return e?ir(ps(e),0,p):0}function ms(e){if("number"==typeof e)return e;if(ss(e))return f;if(Za(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=Za(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Gt(e);var n=he.test(e);return n||ge.test(e)?lt(e.slice(2),n?2:8):pe.test(e)?f:+e}function gs(e){return Oo(e,Ts(e))}function vs(e){return null==e?"":ao(e)}var ys=Ro((function(e,t){if(Ei(t)||Va(t))Oo(t,Rs(t),e);else for(var n in t)je.call(t,n)&&Zn(e,n,t[n])})),bs=Ro((function(e,t){Oo(t,Ts(t),e)})),ws=Ro((function(e,t,n,r){Oo(t,Ts(t),e,r)})),Es=Ro((function(e,t,n,r){Oo(t,Rs(t),e,r)})),Ss=ei(or),xs=Kr((function(e,t){e=Ae(e);var n=-1,r=t.length,i=r>2?t[2]:o;for(i&&vi(t[0],t[1],i)&&(r=1);++n<r;)for(var a=t[n],s=Ts(a),l=-1,u=s.length;++l<u;){var c=s[l],d=e[c];(d===o||Ba(d,Re[c])&&!je.call(e,c))&&(e[c]=a[c])}return e})),As=Kr((function(e){return e.push(o,Xo),xt(js,o,e)}));function ks(e,t,n){var r=null==e?o:Er(e,t);return r===o?n:r}function _s(e,t){return null!=e&&pi(e,t,_r)}var Cs=Bo((function(e,t,n){null!=t&&"function"!=typeof t.toString&&(t=Le.call(t)),e[t]=n}),Zs(nl)),Os=Bo((function(e,t,n){null!=t&&"function"!=typeof t.toString&&(t=Le.call(t)),je.call(e,t)?e[t].push(n):e[t]=[n]}),ai),Ps=Kr(Or);function Rs(e){return Va(e)?Gn(e):Nr(e)}function Ts(e){return Va(e)?Gn(e,!0):function(e){if(!Za(e))return function(e){var t=[];if(null!=e)for(var n in Ae(e))t.push(n);return t}(e);var t=Ei(e),n=[];for(var r in e)("constructor"!=r||!t&&je.call(e,r))&&n.push(r);return n}(e)}var Fs=Ro((function(e,t,n){Br(e,t,n)})),js=Ro((function(e,t,n,r){Br(e,t,n,r)})),Ns=ei((function(e,t){var n={};if(null==e)return n;var r=!1;t=Tt(t,(function(t){return t=vo(t,e),r||(r=t.length>1),t})),Oo(e,ni(e),n),r&&(n=ar(n,7,Qo));for(var o=t.length;o--;)lo(n,t[o]);return n})),Is=ei((function(e,t){return null==e?{}:function(e,t){return $r(e,t,(function(t,n){return _s(e,n)}))}(e,t)}));function Ls(e,t){if(null==e)return{};var n=Tt(ni(e),(function(e){return[e]}));return t=ai(t),$r(e,n,(function(e,n){return t(e,n[0])}))}var Ms=Go(Rs),Ds=Go(Ts);function Bs(e){return null==e?[]:Yt(e,Rs(e))}var Us=No((function(e,t,n){return t=t.toLowerCase(),e+(n?zs(t):t)}));function zs(e){return Js(vs(e).toLowerCase())}function $s(e){return(e=vs(e))&&e.replace(ye,en).replace(Xe,"")}var Hs=No((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Ws=No((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Vs=jo("toLowerCase"),qs=No((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()})),Ks=No((function(e,t,n){return e+(n?" ":"")+Js(t)})),Gs=No((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Js=jo("toUpperCase");function Ys(e,t,n){return e=vs(e),(t=n?o:t)===o?function(e){return tt.test(e)}(e)?function(e){return e.match(Ze)||[]}(e):function(e){return e.match(le)||[]}(e):e.match(t)||[]}var Xs=Kr((function(e,t){try{return xt(e,o,t)}catch(e){return Ja(e)?e:new Ee(e)}})),Qs=ei((function(e,t){return kt(t,(function(t){t=Li(t),rr(e,t,Oa(e[t],e))})),e}));function Zs(e){return function(){return e}}var el=Mo(),tl=Mo(!0);function nl(e){return e}function rl(e){return jr("function"==typeof e?e:ar(e,1))}var ol=Kr((function(e,t){return function(n){return Or(n,e,t)}})),il=Kr((function(e,t){return function(n){return Or(e,n,t)}}));function al(e,t,n){var r=Rs(t),o=wr(t,r);null!=n||Za(t)&&(o.length||!r.length)||(n=t,t=e,e=this,o=wr(t,Rs(t)));var i=!(Za(n)&&"chain"in n&&!n.chain),a=Ya(e);return kt(o,(function(n){var r=t[n];e[n]=r,a&&(e.prototype[n]=function(){var t=this.__chain__;if(i||t){var n=e(this.__wrapped__);return(n.__actions__=Co(this.__actions__)).push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,Ft([this.value()],arguments))})})),e}function sl(){}var ll=zo(Tt),ul=zo(Ct),cl=zo(It);function dl(e){return yi(e)?Ht(Li(e)):function(e){return function(t){return Er(t,e)}}(e)}var fl=Ho(),pl=Ho(!0);function hl(){return[]}function ml(){return!1}var gl,vl=Uo((function(e,t){return e+t}),0),yl=qo("ceil"),bl=Uo((function(e,t){return e/t}),1),wl=qo("floor"),El=Uo((function(e,t){return e*t}),1),Sl=qo("round"),xl=Uo((function(e,t){return e-t}),0);return Dn.after=function(e,t){if("function"!=typeof t)throw new Ce(i);return e=ps(e),function(){if(--e<1)return t.apply(this,arguments)}},Dn.ary=_a,Dn.assign=ys,Dn.assignIn=bs,Dn.assignInWith=ws,Dn.assignWith=Es,Dn.at=Ss,Dn.before=Ca,Dn.bind=Oa,Dn.bindAll=Qs,Dn.bindKey=Pa,Dn.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return Ha(e)?e:[e]},Dn.chain=da,Dn.chunk=function(e,t,n){t=(n?vi(e,t,n):t===o)?1:gn(ps(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var a=0,s=0,l=r(ft(i/t));a<i;)l[s++]=eo(e,a,a+=t);return l},Dn.compact=function(e){for(var t=-1,n=null==e?0:e.length,r=0,o=[];++t<n;){var i=e[t];i&&(o[r++]=i)}return o},Dn.concat=function(){var e=arguments.length;if(!e)return[];for(var t=r(e-1),n=arguments[0],o=e;o--;)t[o-1]=arguments[o];return Ft(Ha(n)?Co(n):[n],mr(t,1))},Dn.cond=function(e){var t=null==e?0:e.length,n=ai();return e=t?Tt(e,(function(e){if("function"!=typeof e[1])throw new Ce(i);return[n(e[0]),e[1]]})):[],Kr((function(n){for(var r=-1;++r<t;){var o=e[r];if(xt(o[0],this,n))return xt(o[1],this,n)}}))},Dn.conforms=function(e){return function(e){var t=Rs(e);return function(n){return sr(n,e,t)}}(ar(e,1))},Dn.constant=Zs,Dn.countBy=ha,Dn.create=function(e,t){var n=Bn(e);return null==t?n:nr(n,t)},Dn.curry=function e(t,n,r){var i=Jo(t,8,o,o,o,o,o,n=r?o:n);return i.placeholder=e.placeholder,i},Dn.curryRight=function e(t,n,r){var i=Jo(t,16,o,o,o,o,o,n=r?o:n);return i.placeholder=e.placeholder,i},Dn.debounce=Ra,Dn.defaults=xs,Dn.defaultsDeep=As,Dn.defer=Ta,Dn.delay=Fa,Dn.difference=Bi,Dn.differenceBy=Ui,Dn.differenceWith=zi,Dn.drop=function(e,t,n){var r=null==e?0:e.length;return r?eo(e,(t=n||t===o?1:ps(t))<0?0:t,r):[]},Dn.dropRight=function(e,t,n){var r=null==e?0:e.length;return r?eo(e,0,(t=r-(t=n||t===o?1:ps(t)))<0?0:t):[]},Dn.dropRightWhile=function(e,t){return e&&e.length?co(e,ai(t,3),!0,!0):[]},Dn.dropWhile=function(e,t){return e&&e.length?co(e,ai(t,3),!0):[]},Dn.fill=function(e,t,n,r){var i=null==e?0:e.length;return i?(n&&"number"!=typeof n&&vi(e,t,n)&&(n=0,r=i),function(e,t,n,r){var i=e.length;for((n=ps(n))<0&&(n=-n>i?0:i+n),(r=r===o||r>i?i:ps(r))<0&&(r+=i),r=n>r?0:hs(r);n<r;)e[n++]=t;return e}(e,t,n,r)):[]},Dn.filter=function(e,t){return(Ha(e)?Ot:hr)(e,ai(t,3))},Dn.flatMap=function(e,t){return mr(Sa(e,t),1)},Dn.flatMapDeep=function(e,t){return mr(Sa(e,t),c)},Dn.flatMapDepth=function(e,t,n){return n=n===o?1:ps(n),mr(Sa(e,t),n)},Dn.flatten=Wi,Dn.flattenDeep=function(e){return null!=e&&e.length?mr(e,c):[]},Dn.flattenDepth=function(e,t){return null!=e&&e.length?mr(e,t=t===o?1:ps(t)):[]},Dn.flip=function(e){return Jo(e,512)},Dn.flow=el,Dn.flowRight=tl,Dn.fromPairs=function(e){for(var t=-1,n=null==e?0:e.length,r={};++t<n;){var o=e[t];r[o[0]]=o[1]}return r},Dn.functions=function(e){return null==e?[]:wr(e,Rs(e))},Dn.functionsIn=function(e){return null==e?[]:wr(e,Ts(e))},Dn.groupBy=ba,Dn.initial=function(e){return null!=e&&e.length?eo(e,0,-1):[]},Dn.intersection=qi,Dn.intersectionBy=Ki,Dn.intersectionWith=Gi,Dn.invert=Cs,Dn.invertBy=Os,Dn.invokeMap=wa,Dn.iteratee=rl,Dn.keyBy=Ea,Dn.keys=Rs,Dn.keysIn=Ts,Dn.map=Sa,Dn.mapKeys=function(e,t){var n={};return t=ai(t,3),yr(e,(function(e,r,o){rr(n,t(e,r,o),e)})),n},Dn.mapValues=function(e,t){var n={};return t=ai(t,3),yr(e,(function(e,r,o){rr(n,r,t(e,r,o))})),n},Dn.matches=function(e){return Mr(ar(e,1))},Dn.matchesProperty=function(e,t){return Dr(e,ar(t,1))},Dn.memoize=ja,Dn.merge=Fs,Dn.mergeWith=js,Dn.method=ol,Dn.methodOf=il,Dn.mixin=al,Dn.negate=Na,Dn.nthArg=function(e){return e=ps(e),Kr((function(t){return Ur(t,e)}))},Dn.omit=Ns,Dn.omitBy=function(e,t){return Ls(e,Na(ai(t)))},Dn.once=function(e){return Ca(2,e)},Dn.orderBy=function(e,t,n,r){return null==e?[]:(Ha(t)||(t=null==t?[]:[t]),Ha(n=r?o:n)||(n=null==n?[]:[n]),zr(e,t,n))},Dn.over=ll,Dn.overArgs=Ia,Dn.overEvery=ul,Dn.overSome=cl,Dn.partial=La,Dn.partialRight=Ma,Dn.partition=xa,Dn.pick=Is,Dn.pickBy=Ls,Dn.property=dl,Dn.propertyOf=function(e){return function(t){return null==e?o:Er(e,t)}},Dn.pull=Yi,Dn.pullAll=Xi,Dn.pullAllBy=function(e,t,n){return e&&e.length&&t&&t.length?Hr(e,t,ai(n,2)):e},Dn.pullAllWith=function(e,t,n){return e&&e.length&&t&&t.length?Hr(e,t,o,n):e},Dn.pullAt=Qi,Dn.range=fl,Dn.rangeRight=pl,Dn.rearg=Da,Dn.reject=function(e,t){return(Ha(e)?Ot:hr)(e,Na(ai(t,3)))},Dn.remove=function(e,t){var n=[];if(!e||!e.length)return n;var r=-1,o=[],i=e.length;for(t=ai(t,3);++r<i;){var a=e[r];t(a,r,e)&&(n.push(a),o.push(r))}return Wr(e,o),n},Dn.rest=function(e,t){if("function"!=typeof e)throw new Ce(i);return Kr(e,t=t===o?t:ps(t))},Dn.reverse=Zi,Dn.sampleSize=function(e,t,n){return t=(n?vi(e,t,n):t===o)?1:ps(t),(Ha(e)?Yn:Jr)(e,t)},Dn.set=function(e,t,n){return null==e?e:Yr(e,t,n)},Dn.setWith=function(e,t,n,r){return r="function"==typeof r?r:o,null==e?e:Yr(e,t,n,r)},Dn.shuffle=function(e){return(Ha(e)?Xn:Zr)(e)},Dn.slice=function(e,t,n){var r=null==e?0:e.length;return r?(n&&"number"!=typeof n&&vi(e,t,n)?(t=0,n=r):(t=null==t?0:ps(t),n=n===o?r:ps(n)),eo(e,t,n)):[]},Dn.sortBy=Aa,Dn.sortedUniq=function(e){return e&&e.length?oo(e):[]},Dn.sortedUniqBy=function(e,t){return e&&e.length?oo(e,ai(t,2)):[]},Dn.split=function(e,t,n){return n&&"number"!=typeof n&&vi(e,t,n)&&(t=n=o),(n=n===o?p:n>>>0)?(e=vs(e))&&("string"==typeof t||null!=t&&!os(t))&&!(t=ao(t))&&rn(e)?bo(dn(e),0,n):e.split(t,n):[]},Dn.spread=function(e,t){if("function"!=typeof e)throw new Ce(i);return t=null==t?0:gn(ps(t),0),Kr((function(n){var r=n[t],o=bo(n,0,t);return r&&Ft(o,r),xt(e,this,o)}))},Dn.tail=function(e){var t=null==e?0:e.length;return t?eo(e,1,t):[]},Dn.take=function(e,t,n){return e&&e.length?eo(e,0,(t=n||t===o?1:ps(t))<0?0:t):[]},Dn.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?eo(e,(t=r-(t=n||t===o?1:ps(t)))<0?0:t,r):[]},Dn.takeRightWhile=function(e,t){return e&&e.length?co(e,ai(t,3),!1,!0):[]},Dn.takeWhile=function(e,t){return e&&e.length?co(e,ai(t,3)):[]},Dn.tap=function(e,t){return t(e),e},Dn.throttle=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new Ce(i);return Za(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Ra(e,t,{leading:r,maxWait:t,trailing:o})},Dn.thru=fa,Dn.toArray=ds,Dn.toPairs=Ms,Dn.toPairsIn=Ds,Dn.toPath=function(e){return Ha(e)?Tt(e,Li):ss(e)?[e]:Co(Ii(vs(e)))},Dn.toPlainObject=gs,Dn.transform=function(e,t,n){var r=Ha(e),o=r||Ka(e)||ls(e);if(t=ai(t,4),null==n){var i=e&&e.constructor;n=o?r?new i:[]:Za(e)&&Ya(i)?Bn(We(e)):{}}return(o?kt:yr)(e,(function(e,r,o){return t(n,e,r,o)})),n},Dn.unary=function(e){return _a(e,1)},Dn.union=ea,Dn.unionBy=ta,Dn.unionWith=na,Dn.uniq=function(e){return e&&e.length?so(e):[]},Dn.uniqBy=function(e,t){return e&&e.length?so(e,ai(t,2)):[]},Dn.uniqWith=function(e,t){return t="function"==typeof t?t:o,e&&e.length?so(e,o,t):[]},Dn.unset=function(e,t){return null==e||lo(e,t)},Dn.unzip=ra,Dn.unzipWith=oa,Dn.update=function(e,t,n){return null==e?e:uo(e,t,go(n))},Dn.updateWith=function(e,t,n,r){return r="function"==typeof r?r:o,null==e?e:uo(e,t,go(n),r)},Dn.values=Bs,Dn.valuesIn=function(e){return null==e?[]:Yt(e,Ts(e))},Dn.without=ia,Dn.words=Ys,Dn.wrap=function(e,t){return La(go(t),e)},Dn.xor=aa,Dn.xorBy=sa,Dn.xorWith=la,Dn.zip=ua,Dn.zipObject=function(e,t){return ho(e||[],t||[],Zn)},Dn.zipObjectDeep=function(e,t){return ho(e||[],t||[],Yr)},Dn.zipWith=ca,Dn.entries=Ms,Dn.entriesIn=Ds,Dn.extend=bs,Dn.extendWith=ws,al(Dn,Dn),Dn.add=vl,Dn.attempt=Xs,Dn.camelCase=Us,Dn.capitalize=zs,Dn.ceil=yl,Dn.clamp=function(e,t,n){return n===o&&(n=t,t=o),n!==o&&(n=(n=ms(n))==n?n:0),t!==o&&(t=(t=ms(t))==t?t:0),ir(ms(e),t,n)},Dn.clone=function(e){return ar(e,4)},Dn.cloneDeep=function(e){return ar(e,5)},Dn.cloneDeepWith=function(e,t){return ar(e,5,t="function"==typeof t?t:o)},Dn.cloneWith=function(e,t){return ar(e,4,t="function"==typeof t?t:o)},Dn.conformsTo=function(e,t){return null==t||sr(e,t,Rs(t))},Dn.deburr=$s,Dn.defaultTo=function(e,t){return null==e||e!=e?t:e},Dn.divide=bl,Dn.endsWith=function(e,t,n){e=vs(e),t=ao(t);var r=e.length,i=n=n===o?r:ir(ps(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Dn.eq=Ba,Dn.escape=function(e){return(e=vs(e))&&G.test(e)?e.replace(q,tn):e},Dn.escapeRegExp=function(e){return(e=vs(e))&&ne.test(e)?e.replace(te,"\\$&"):e},Dn.every=function(e,t,n){var r=Ha(e)?Ct:fr;return n&&vi(e,t,n)&&(t=o),r(e,ai(t,3))},Dn.find=ma,Dn.findIndex=$i,Dn.findKey=function(e,t){return Mt(e,ai(t,3),yr)},Dn.findLast=ga,Dn.findLastIndex=Hi,Dn.findLastKey=function(e,t){return Mt(e,ai(t,3),br)},Dn.floor=wl,Dn.forEach=va,Dn.forEachRight=ya,Dn.forIn=function(e,t){return null==e?e:gr(e,ai(t,3),Ts)},Dn.forInRight=function(e,t){return null==e?e:vr(e,ai(t,3),Ts)},Dn.forOwn=function(e,t){return e&&yr(e,ai(t,3))},Dn.forOwnRight=function(e,t){return e&&br(e,ai(t,3))},Dn.get=ks,Dn.gt=Ua,Dn.gte=za,Dn.has=function(e,t){return null!=e&&pi(e,t,kr)},Dn.hasIn=_s,Dn.head=Vi,Dn.identity=nl,Dn.includes=function(e,t,n,r){e=Va(e)?e:Bs(e),n=n&&!r?ps(n):0;var o=e.length;return n<0&&(n=gn(o+n,0)),as(e)?n<=o&&e.indexOf(t,n)>-1:!!o&&Bt(e,t,n)>-1},Dn.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:ps(n);return o<0&&(o=gn(r+o,0)),Bt(e,t,o)},Dn.inRange=function(e,t,n){return t=fs(t),n===o?(n=t,t=0):n=fs(n),function(e,t,n){return e>=vn(t,n)&&e<gn(t,n)}(e=ms(e),t,n)},Dn.invoke=Ps,Dn.isArguments=$a,Dn.isArray=Ha,Dn.isArrayBuffer=Wa,Dn.isArrayLike=Va,Dn.isArrayLikeObject=qa,Dn.isBoolean=function(e){return!0===e||!1===e||es(e)&&xr(e)==v},Dn.isBuffer=Ka,Dn.isDate=Ga,Dn.isElement=function(e){return es(e)&&1===e.nodeType&&!rs(e)},Dn.isEmpty=function(e){if(null==e)return!0;if(Va(e)&&(Ha(e)||"string"==typeof e||"function"==typeof e.splice||Ka(e)||ls(e)||$a(e)))return!e.length;var t=fi(e);if(t==S||t==C)return!e.size;if(Ei(e))return!Nr(e).length;for(var n in e)if(je.call(e,n))return!1;return!0},Dn.isEqual=function(e,t){return Rr(e,t)},Dn.isEqualWith=function(e,t,n){var r=(n="function"==typeof n?n:o)?n(e,t):o;return r===o?Rr(e,t,o,n):!!r},Dn.isError=Ja,Dn.isFinite=function(e){return"number"==typeof e&&Lt(e)},Dn.isFunction=Ya,Dn.isInteger=Xa,Dn.isLength=Qa,Dn.isMap=ts,Dn.isMatch=function(e,t){return e===t||Tr(e,t,li(t))},Dn.isMatchWith=function(e,t,n){return n="function"==typeof n?n:o,Tr(e,t,li(t),n)},Dn.isNaN=function(e){return ns(e)&&e!=+e},Dn.isNative=function(e){if(wi(e))throw new Ee("Unsupported core-js use. Try https://npms.io/search?q=ponyfill.");return Fr(e)},Dn.isNil=function(e){return null==e},Dn.isNull=function(e){return null===e},Dn.isNumber=ns,Dn.isObject=Za,Dn.isObjectLike=es,Dn.isPlainObject=rs,Dn.isRegExp=os,Dn.isSafeInteger=function(e){return Xa(e)&&e>=-9007199254740991&&e<=d},Dn.isSet=is,Dn.isString=as,Dn.isSymbol=ss,Dn.isTypedArray=ls,Dn.isUndefined=function(e){return e===o},Dn.isWeakMap=function(e){return es(e)&&fi(e)==R},Dn.isWeakSet=function(e){return es(e)&&"[object WeakSet]"==xr(e)},Dn.join=function(e,t){return null==e?"":Wt.call(e,t)},Dn.kebabCase=Hs,Dn.last=Ji,Dn.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return n!==o&&(i=(i=ps(n))<0?gn(r+i,0):vn(i,r-1)),t==t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):Dt(e,zt,i,!0)},Dn.lowerCase=Ws,Dn.lowerFirst=Vs,Dn.lt=us,Dn.lte=cs,Dn.max=function(e){return e&&e.length?pr(e,nl,Ar):o},Dn.maxBy=function(e,t){return e&&e.length?pr(e,ai(t,2),Ar):o},Dn.mean=function(e){return $t(e,nl)},Dn.meanBy=function(e,t){return $t(e,ai(t,2))},Dn.min=function(e){return e&&e.length?pr(e,nl,Ir):o},Dn.minBy=function(e,t){return e&&e.length?pr(e,ai(t,2),Ir):o},Dn.stubArray=hl,Dn.stubFalse=ml,Dn.stubObject=function(){return{}},Dn.stubString=function(){return""},Dn.stubTrue=function(){return!0},Dn.multiply=El,Dn.nth=function(e,t){return e&&e.length?Ur(e,ps(t)):o},Dn.noConflict=function(){return dt._===this&&(dt._=De),this},Dn.noop=sl,Dn.now=ka,Dn.pad=function(e,t,n){e=vs(e);var r=(t=ps(t))?cn(e):0;if(!t||r>=t)return e;var o=(t-r)/2;return $o(pt(o),n)+e+$o(ft(o),n)},Dn.padEnd=function(e,t,n){e=vs(e);var r=(t=ps(t))?cn(e):0;return t&&r<t?e+$o(t-r,n):e},Dn.padStart=function(e,t,n){e=vs(e);var r=(t=ps(t))?cn(e):0;return t&&r<t?$o(t-r,n)+e:e},Dn.parseInt=function(e,t,n){return n||null==t?t=0:t&&(t=+t),bn(vs(e).replace(re,""),t||0)},Dn.random=function(e,t,n){if(n&&"boolean"!=typeof n&&vi(e,t,n)&&(t=n=o),n===o&&("boolean"==typeof t?(n=t,t=o):"boolean"==typeof e&&(n=e,e=o)),e===o&&t===o?(e=0,t=1):(e=fs(e),t===o?(t=e,e=0):t=fs(t)),e>t){var r=e;e=t,t=r}if(n||e%1||t%1){var i=wn();return vn(e+i*(t-e+st("1e-"+((i+"").length-1))),t)}return Vr(e,t)},Dn.reduce=function(e,t,n){var r=Ha(e)?jt:Vt,o=arguments.length<3;return r(e,ai(t,4),n,o,cr)},Dn.reduceRight=function(e,t,n){var r=Ha(e)?Nt:Vt,o=arguments.length<3;return r(e,ai(t,4),n,o,dr)},Dn.repeat=function(e,t,n){return t=(n?vi(e,t,n):t===o)?1:ps(t),qr(vs(e),t)},Dn.replace=function(){var e=arguments,t=vs(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Dn.result=function(e,t,n){var r=-1,i=(t=vo(t,e)).length;for(i||(i=1,e=o);++r<i;){var a=null==e?o:e[Li(t[r])];a===o&&(r=i,a=n),e=Ya(a)?a.call(e):a}return e},Dn.round=Sl,Dn.runInContext=e,Dn.sample=function(e){return(Ha(e)?Jn:Gr)(e)},Dn.size=function(e){if(null==e)return 0;if(Va(e))return as(e)?cn(e):e.length;var t=fi(e);return t==S||t==C?e.size:Nr(e).length},Dn.snakeCase=qs,Dn.some=function(e,t,n){var r=Ha(e)?It:to;return n&&vi(e,t,n)&&(t=o),r(e,ai(t,3))},Dn.sortedIndex=function(e,t){return no(e,t)},Dn.sortedIndexBy=function(e,t,n){return ro(e,t,ai(n,2))},Dn.sortedIndexOf=function(e,t){var n=null==e?0:e.length;if(n){var r=no(e,t);if(r<n&&Ba(e[r],t))return r}return-1},Dn.sortedLastIndex=function(e,t){return no(e,t,!0)},Dn.sortedLastIndexBy=function(e,t,n){return ro(e,t,ai(n,2),!0)},Dn.sortedLastIndexOf=function(e,t){if(null!=e&&e.length){var n=no(e,t,!0)-1;if(Ba(e[n],t))return n}return-1},Dn.startCase=Ks,Dn.startsWith=function(e,t,n){return e=vs(e),n=null==n?0:ir(ps(n),0,e.length),t=ao(t),e.slice(n,n+t.length)==t},Dn.subtract=xl,Dn.sum=function(e){return e&&e.length?qt(e,nl):0},Dn.sumBy=function(e,t){return e&&e.length?qt(e,ai(t,2)):0},Dn.template=function(e,t,n){var r=Dn.templateSettings;n&&vi(e,t,n)&&(t=o),e=vs(e),t=ws({},t,r,Yo);var i,a,s=ws({},t.imports,r.imports,Yo),l=Rs(s),u=Yt(s,l),c=0,d=t.interpolate||be,f="__p += '",p=ke((t.escape||be).source+"|"+d.source+"|"+(d===X?de:be).source+"|"+(t.evaluate||be).source+"|$","g"),h="//# sourceURL="+(je.call(t,"sourceURL")?(t.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++rt+"]")+"\n";e.replace(p,(function(t,n,r,o,s,l){return r||(r=o),f+=e.slice(c,l).replace(we,nn),n&&(i=!0,f+="' +\n__e("+n+") +\n'"),s&&(a=!0,f+="';\n"+s+";\n__p += '"),r&&(f+="' +\n((__t = ("+r+")) == null ? '' : __t) +\n'"),c=l+t.length,t})),f+="';\n";var m=je.call(t,"variable")&&t.variable;if(m){if(ue.test(m))throw new Ee("Invalid `variable` option passed into `_.template`")}else f="with (obj) {\n"+f+"\n}\n";f=(a?f.replace($,""):f).replace(H,"$1").replace(W,"$1;"),f="function("+(m||"obj")+") {\n"+(m?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(i?", __e = _.escape":"")+(a?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+f+"return __p\n}";var g=Xs((function(){return Se(l,h+"return "+f).apply(o,u)}));if(g.source=f,Ja(g))throw g;return g},Dn.times=function(e,t){if((e=ps(e))<1||e>d)return[];var n=p,r=vn(e,p);t=ai(t),e-=p;for(var o=Kt(r,t);++n<e;)t(n);return o},Dn.toFinite=fs,Dn.toInteger=ps,Dn.toLength=hs,Dn.toLower=function(e){return vs(e).toLowerCase()},Dn.toNumber=ms,Dn.toSafeInteger=function(e){return e?ir(ps(e),-9007199254740991,d):0===e?e:0},Dn.toString=vs,Dn.toUpper=function(e){return vs(e).toUpperCase()},Dn.trim=function(e,t,n){if((e=vs(e))&&(n||t===o))return Gt(e);if(!e||!(t=ao(t)))return e;var r=dn(e),i=dn(t);return bo(r,Qt(r,i),Zt(r,i)+1).join("")},Dn.trimEnd=function(e,t,n){if((e=vs(e))&&(n||t===o))return e.slice(0,fn(e)+1);if(!e||!(t=ao(t)))return e;var r=dn(e);return bo(r,0,Zt(r,dn(t))+1).join("")},Dn.trimStart=function(e,t,n){if((e=vs(e))&&(n||t===o))return e.replace(re,"");if(!e||!(t=ao(t)))return e;var r=dn(e);return bo(r,Qt(r,dn(t))).join("")},Dn.truncate=function(e,t){var n=30,r="...";if(Za(t)){var i="separator"in t?t.separator:i;n="length"in t?ps(t.length):n,r="omission"in t?ao(t.omission):r}var a=(e=vs(e)).length;if(rn(e)){var s=dn(e);a=s.length}if(n>=a)return e;var l=n-cn(r);if(l<1)return r;var u=s?bo(s,0,l).join(""):e.slice(0,l);if(i===o)return u+r;if(s&&(l+=u.length-l),os(i)){if(e.slice(l).search(i)){var c,d=u;for(i.global||(i=ke(i.source,vs(fe.exec(i))+"g")),i.lastIndex=0;c=i.exec(d);)var f=c.index;u=u.slice(0,f===o?l:f)}}else if(e.indexOf(ao(i),l)!=l){var p=u.lastIndexOf(i);p>-1&&(u=u.slice(0,p))}return u+r},Dn.unescape=function(e){return(e=vs(e))&&K.test(e)?e.replace(V,pn):e},Dn.uniqueId=function(e){var t=++Ne;return vs(e)+t},Dn.upperCase=Gs,Dn.upperFirst=Js,Dn.each=va,Dn.eachRight=ya,Dn.first=Vi,al(Dn,(gl={},yr(Dn,(function(e,t){je.call(Dn.prototype,t)||(gl[t]=e)})),gl),{chain:!1}),Dn.VERSION="4.17.21",kt(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Dn[e].placeholder=Dn})),kt(["drop","take"],(function(e,t){$n.prototype[e]=function(n){n=n===o?1:gn(ps(n),0);var r=this.__filtered__&&!t?new $n(this):this.clone();return r.__filtered__?r.__takeCount__=vn(n,r.__takeCount__):r.__views__.push({size:vn(n,p),type:e+(r.__dir__<0?"Right":"")}),r},$n.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),kt(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;$n.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:ai(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),kt(["head","last"],(function(e,t){var n="take"+(t?"Right":"");$n.prototype[e]=function(){return this[n](1).value()[0]}})),kt(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");$n.prototype[e]=function(){return this.__filtered__?new $n(this):this[n](1)}})),$n.prototype.compact=function(){return this.filter(nl)},$n.prototype.find=function(e){return this.filter(e).head()},$n.prototype.findLast=function(e){return this.reverse().find(e)},$n.prototype.invokeMap=Kr((function(e,t){return"function"==typeof e?new $n(this):this.map((function(n){return Or(n,e,t)}))})),$n.prototype.reject=function(e){return this.filter(Na(ai(e)))},$n.prototype.slice=function(e,t){e=ps(e);var n=this;return n.__filtered__&&(e>0||t<0)?new $n(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),t!==o&&(n=(t=ps(t))<0?n.dropRight(-t):n.take(t-e)),n)},$n.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},$n.prototype.toArray=function(){return this.take(p)},yr($n.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Dn[r?"take"+("last"==t?"Right":""):t],a=r||/^find/.test(t);i&&(Dn.prototype[t]=function(){var t=this.__wrapped__,s=r?[1]:arguments,l=t instanceof $n,u=s[0],c=l||Ha(t),d=function(e){var t=i.apply(Dn,Ft([e],s));return r&&f?t[0]:t};c&&n&&"function"==typeof u&&1!=u.length&&(l=c=!1);var f=this.__chain__,p=!!this.__actions__.length,h=a&&!f,m=l&&!p;if(!a&&c){t=m?t:new $n(this);var g=e.apply(t,s);return g.__actions__.push({func:fa,args:[d],thisArg:o}),new zn(g,f)}return h&&m?e.apply(this,s):(g=this.thru(d),h?r?g.value()[0]:g.value():g)})})),kt(["pop","push","shift","sort","splice","unshift"],(function(e){var t=Oe[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Dn.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var o=this.value();return t.apply(Ha(o)?o:[],e)}return this[n]((function(n){return t.apply(Ha(n)?n:[],e)}))}})),yr($n.prototype,(function(e,t){var n=Dn[t];if(n){var r=n.name+"";je.call(Pn,r)||(Pn[r]=[]),Pn[r].push({name:t,func:n})}})),Pn[Do(o,2).name]=[{name:"wrapper",func:o}],$n.prototype.clone=function(){var e=new $n(this.__wrapped__);return e.__actions__=Co(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=Co(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=Co(this.__views__),e},$n.prototype.reverse=function(){if(this.__filtered__){var e=new $n(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},$n.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=Ha(e),r=t<0,o=n?e.length:0,i=function(e,t,n){for(var r=-1,o=n.length;++r<o;){var i=n[r],a=i.size;switch(i.type){case"drop":e+=a;break;case"dropRight":t-=a;break;case"take":t=vn(t,e+a);break;case"takeRight":e=gn(e,t-a)}}return{start:e,end:t}}(0,o,this.__views__),a=i.start,s=i.end,l=s-a,u=r?s:a-1,c=this.__iteratees__,d=c.length,f=0,p=vn(l,this.__takeCount__);if(!n||!r&&o==l&&p==l)return fo(e,this.__actions__);var h=[];e:for(;l--&&f<p;){for(var m=-1,g=e[u+=t];++m<d;){var v=c[m],y=v.iteratee,b=v.type,w=y(g);if(2==b)g=w;else if(!w){if(1==b)continue e;break e}}h[f++]=g}return h},Dn.prototype.at=pa,Dn.prototype.chain=function(){return da(this)},Dn.prototype.commit=function(){return new zn(this.value(),this.__chain__)},Dn.prototype.next=function(){this.__values__===o&&(this.__values__=ds(this.value()));var e=this.__index__>=this.__values__.length;return{done:e,value:e?o:this.__values__[this.__index__++]}},Dn.prototype.plant=function(e){for(var t,n=this;n instanceof Un;){var r=Di(n);r.__index__=0,r.__values__=o,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Dn.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof $n){var t=e;return this.__actions__.length&&(t=new $n(this)),(t=t.reverse()).__actions__.push({func:fa,args:[Zi],thisArg:o}),new zn(t,this.__chain__)}return this.thru(Zi)},Dn.prototype.toJSON=Dn.prototype.valueOf=Dn.prototype.value=function(){return fo(this.__wrapped__,this.__actions__)},Dn.prototype.first=Dn.prototype.head,Je&&(Dn.prototype[Je]=function(){return this}),Dn}();dt._=hn,(r=function(){return hn}.call(t,n,t,e))===o||(e.exports=r)}.call(this)},2551:(e,t,n)=>{"use strict";var r=n(6540),o=n(9982);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n<arguments.length;n++)t+="&args[]="+encodeURIComponent(arguments[n]);return"Minified React error #"+e+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var a=new Set,s={};function l(e,t){u(e,t),u(e+"Capture",t)}function u(e,t){for(s[e]=t,e=0;e<t.length;e++)a.add(t[e])}var c=!("undefined"==typeof window||void 0===window.document||void 0===window.document.createElement),d=Object.prototype.hasOwnProperty,f=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,p={},h={};function m(e,t,n,r,o,i,a){this.acceptsBooleans=2===t||3===t||4===t,this.attributeName=r,this.attributeNamespace=o,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=i,this.removeEmptyString=a}var g={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach((function(e){g[e]=new m(e,0,!1,e,null,!1,!1)})),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach((function(e){var t=e[0];g[t]=new m(t,1,!1,e[1],null,!1,!1)})),["contentEditable","draggable","spellCheck","value"].forEach((function(e){g[e]=new m(e,2,!1,e.toLowerCase(),null,!1,!1)})),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach((function(e){g[e]=new m(e,2,!1,e,null,!1,!1)})),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach((function(e){g[e]=new m(e,3,!1,e.toLowerCase(),null,!1,!1)})),["checked","multiple","muted","selected"].forEach((function(e){g[e]=new m(e,3,!0,e,null,!1,!1)})),["capture","download"].forEach((function(e){g[e]=new m(e,4,!1,e,null,!1,!1)})),["cols","rows","size","span"].forEach((function(e){g[e]=new m(e,6,!1,e,null,!1,!1)})),["rowSpan","start"].forEach((function(e){g[e]=new m(e,5,!1,e.toLowerCase(),null,!1,!1)}));var v=/[\-:]([a-z])/g;function y(e){return e[1].toUpperCase()}function b(e,t,n,r){var o=g.hasOwnProperty(t)?g[t]:null;(null!==o?0!==o.type:r||!(2<t.length)||"o"!==t[0]&&"O"!==t[0]||"n"!==t[1]&&"N"!==t[1])&&(function(e,t,n,r){if(null==t||function(e,t,n,r){if(null!==n&&0===n.type)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return!r&&(null!==n?!n.acceptsBooleans:"data-"!==(e=e.toLowerCase().slice(0,5))&&"aria-"!==e);default:return!1}}(e,t,n,r))return!0;if(r)return!1;if(null!==n)switch(n.type){case 3:return!t;case 4:return!1===t;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}(t,n,o,r)&&(n=null),r||null===o?function(e){return!!d.call(h,e)||!d.call(p,e)&&(f.test(e)?h[e]=!0:(p[e]=!0,!1))}(t)&&(null===n?e.removeAttribute(t):e.setAttribute(t,""+n)):o.mustUseProperty?e[o.propertyName]=null===n?3!==o.type&&"":n:(t=o.attributeName,r=o.attributeNamespace,null===n?e.removeAttribute(t):(n=3===(o=o.type)||4===o&&!0===n?"":""+n,r?e.setAttributeNS(r,t,n):e.setAttribute(t,n))))}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach((function(e){var t=e.replace(v,y);g[t]=new m(t,1,!1,e,null,!1,!1)})),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach((function(e){var t=e.replace(v,y);g[t]=new m(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)})),["xml:base","xml:lang","xml:space"].forEach((function(e){var t=e.replace(v,y);g[t]=new m(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)})),["tabIndex","crossOrigin"].forEach((function(e){g[e]=new m(e,1,!1,e.toLowerCase(),null,!1,!1)})),g.xlinkHref=new m("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach((function(e){g[e]=new m(e,1,!1,e.toLowerCase(),null,!0,!0)}));var w=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,E=Symbol.for("react.element"),S=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),A=Symbol.for("react.strict_mode"),k=Symbol.for("react.profiler"),_=Symbol.for("react.provider"),C=Symbol.for("react.context"),O=Symbol.for("react.forward_ref"),P=Symbol.for("react.suspense"),R=Symbol.for("react.suspense_list"),T=Symbol.for("react.memo"),F=Symbol.for("react.lazy");Symbol.for("react.scope"),Symbol.for("react.debug_trace_mode");var j=Symbol.for("react.offscreen");Symbol.for("react.legacy_hidden"),Symbol.for("react.cache"),Symbol.for("react.tracing_marker");var N=Symbol.iterator;function I(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=N&&e[N]||e["@@iterator"])?e:null}var L,M=Object.assign;function D(e){if(void 0===L)try{throw Error()}catch(e){var t=e.stack.trim().match(/\n( *(at )?)/);L=t&&t[1]||""}return"\n"+L+e}var B=!1;function U(e,t){if(!e||B)return"";B=!0;var n=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(t)if(t=function(){throw Error()},Object.defineProperty(t.prototype,"props",{set:function(){throw Error()}}),"object"==typeof Reflect&&Reflect.construct){try{Reflect.construct(t,[])}catch(e){var r=e}Reflect.construct(e,[],t)}else{try{t.call()}catch(e){r=e}e.call(t.prototype)}else{try{throw Error()}catch(e){r=e}e()}}catch(t){if(t&&r&&"string"==typeof t.stack){for(var o=t.stack.split("\n"),i=r.stack.split("\n"),a=o.length-1,s=i.length-1;1<=a&&0<=s&&o[a]!==i[s];)s--;for(;1<=a&&0<=s;a--,s--)if(o[a]!==i[s]){if(1!==a||1!==s)do{if(a--,0>--s||o[a]!==i[s]){var l="\n"+o[a].replace(" at new "," at ");return e.displayName&&l.includes("<anonymous>")&&(l=l.replace("<anonymous>",e.displayName)),l}}while(1<=a&&0<=s);break}}}finally{B=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?D(e):""}function z(e){switch(e.tag){case 5:return D(e.type);case 16:return D("Lazy");case 13:return D("Suspense");case 19:return D("SuspenseList");case 0:case 2:case 15:return U(e.type,!1);case 11:return U(e.type.render,!1);case 1:return U(e.type,!0);default:return""}}function $(e){if(null==e)return null;if("function"==typeof e)return e.displayName||e.name||null;if("string"==typeof e)return e;switch(e){case x:return"Fragment";case S:return"Portal";case k:return"Profiler";case A:return"StrictMode";case P:return"Suspense";case R:return"SuspenseList"}if("object"==typeof e)switch(e.$$typeof){case C:return(e.displayName||"Context")+".Consumer";case _:return(e._context.displayName||"Context")+".Provider";case O:var t=e.render;return(e=e.displayName)||(e=""!==(e=t.displayName||t.name||"")?"ForwardRef("+e+")":"ForwardRef"),e;case T:return null!==(t=e.displayName||null)?t:$(e.type)||"Memo";case F:t=e._payload,e=e._init;try{return $(e(t))}catch(e){}}return null}function H(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=(e=t.render).displayName||e.name||"",t.displayName||(""!==e?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return $(t);case 8:return t===A?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"==typeof t)return t.displayName||t.name||null;if("string"==typeof t)return t}return null}function W(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":case"object":return e;default:return""}}function V(e){var t=e.type;return(e=e.nodeName)&&"input"===e.toLowerCase()&&("checkbox"===t||"radio"===t)}function q(e){e._valueTracker||(e._valueTracker=function(e){var t=V(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&void 0!==n&&"function"==typeof n.get&&"function"==typeof n.set){var o=n.get,i=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return o.call(this)},set:function(e){r=""+e,i.call(this,e)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(e){r=""+e},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}(e))}function K(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=V(e)?e.checked?"true":"false":e.value),(e=r)!==n&&(t.setValue(e),!0)}function G(e){if(void 0===(e=e||("undefined"!=typeof document?document:void 0)))return null;try{return e.activeElement||e.body}catch(t){return e.body}}function J(e,t){var n=t.checked;return M({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=n?n:e._wrapperState.initialChecked})}function Y(e,t){var n=null==t.defaultValue?"":t.defaultValue,r=null!=t.checked?t.checked:t.defaultChecked;n=W(null!=t.value?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function X(e,t){null!=(t=t.checked)&&b(e,"checked",t,!1)}function Q(e,t){X(e,t);var n=W(t.value),r=t.type;if(null!=n)"number"===r?(0===n&&""===e.value||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if("submit"===r||"reset"===r)return void e.removeAttribute("value");t.hasOwnProperty("value")?ee(e,t.type,n):t.hasOwnProperty("defaultValue")&&ee(e,t.type,W(t.defaultValue)),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked)}function Z(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!("submit"!==r&&"reset"!==r||void 0!==t.value&&null!==t.value))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}""!==(n=e.name)&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,""!==n&&(e.name=n)}function ee(e,t,n){"number"===t&&G(e.ownerDocument)===e||(null==n?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var te=Array.isArray;function ne(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o<n.length;o++)t["$"+n[o]]=!0;for(n=0;n<e.length;n++)o=t.hasOwnProperty("$"+e[n].value),e[n].selected!==o&&(e[n].selected=o),o&&r&&(e[n].defaultSelected=!0)}else{for(n=""+W(n),t=null,o=0;o<e.length;o++){if(e[o].value===n)return e[o].selected=!0,void(r&&(e[o].defaultSelected=!0));null!==t||e[o].disabled||(t=e[o])}null!==t&&(t.selected=!0)}}function re(e,t){if(null!=t.dangerouslySetInnerHTML)throw Error(i(91));return M({},t,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function oe(e,t){var n=t.value;if(null==n){if(n=t.children,t=t.defaultValue,null!=n){if(null!=t)throw Error(i(92));if(te(n)){if(1<n.length)throw Error(i(93));n=n[0]}t=n}null==t&&(t=""),n=t}e._wrapperState={initialValue:W(n)}}function ie(e,t){var n=W(t.value),r=W(t.defaultValue);null!=n&&((n=""+n)!==e.value&&(e.value=n),null==t.defaultValue&&e.defaultValue!==n&&(e.defaultValue=n)),null!=r&&(e.defaultValue=""+r)}function ae(e){var t=e.textContent;t===e._wrapperState.initialValue&&""!==t&&null!==t&&(e.value=t)}function se(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function le(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?se(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}var ue,ce,de=(ce=function(e,t){if("http://www.w3.org/2000/svg"!==e.namespaceURI||"innerHTML"in e)e.innerHTML=t;else{for((ue=ue||document.createElement("div")).innerHTML="<svg>"+t.valueOf().toString()+"</svg>",t=ue.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}},"undefined"!=typeof MSApp&&MSApp.execUnsafeLocalFunction?function(e,t,n,r){MSApp.execUnsafeLocalFunction((function(){return ce(e,t)}))}:ce);function fe(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}var pe={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},he=["Webkit","ms","Moz","O"];function me(e,t,n){return null==t||"boolean"==typeof t||""===t?"":n||"number"!=typeof t||0===t||pe.hasOwnProperty(e)&&pe[e]?(""+t).trim():t+"px"}function ge(e,t){for(var n in e=e.style,t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),o=me(n,t[n],r);"float"===n&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}Object.keys(pe).forEach((function(e){he.forEach((function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),pe[t]=pe[e]}))}));var ve=M({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ye(e,t){if(t){if(ve[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML))throw Error(i(137,e));if(null!=t.dangerouslySetInnerHTML){if(null!=t.children)throw Error(i(60));if("object"!=typeof t.dangerouslySetInnerHTML||!("__html"in t.dangerouslySetInnerHTML))throw Error(i(61))}if(null!=t.style&&"object"!=typeof t.style)throw Error(i(62))}}function be(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var we=null;function Ee(e){return(e=e.target||e.srcElement||window).correspondingUseElement&&(e=e.correspondingUseElement),3===e.nodeType?e.parentNode:e}var Se=null,xe=null,Ae=null;function ke(e){if(e=wo(e)){if("function"!=typeof Se)throw Error(i(280));var t=e.stateNode;t&&(t=So(t),Se(e.stateNode,e.type,t))}}function _e(e){xe?Ae?Ae.push(e):Ae=[e]:xe=e}function Ce(){if(xe){var e=xe,t=Ae;if(Ae=xe=null,ke(e),t)for(e=0;e<t.length;e++)ke(t[e])}}function Oe(e,t){return e(t)}function Pe(){}var Re=!1;function Te(e,t,n){if(Re)return e(t,n);Re=!0;try{return Oe(e,t,n)}finally{Re=!1,(null!==xe||null!==Ae)&&(Pe(),Ce())}}function Fe(e,t){var n=e.stateNode;if(null===n)return null;var r=So(n);if(null===r)return null;n=r[t];e:switch(t){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(r=!r.disabled)||(r=!("button"===(e=e.type)||"input"===e||"select"===e||"textarea"===e)),e=!r;break e;default:e=!1}if(e)return null;if(n&&"function"!=typeof n)throw Error(i(231,t,typeof n));return n}var je=!1;if(c)try{var Ne={};Object.defineProperty(Ne,"passive",{get:function(){je=!0}}),window.addEventListener("test",Ne,Ne),window.removeEventListener("test",Ne,Ne)}catch(ce){je=!1}function Ie(e,t,n,r,o,i,a,s,l){var u=Array.prototype.slice.call(arguments,3);try{t.apply(n,u)}catch(e){this.onError(e)}}var Le=!1,Me=null,De=!1,Be=null,Ue={onError:function(e){Le=!0,Me=e}};function ze(e,t,n,r,o,i,a,s,l){Le=!1,Me=null,Ie.apply(Ue,arguments)}function $e(e){var t=e,n=e;if(e.alternate)for(;t.return;)t=t.return;else{e=t;do{!!(4098&(t=e).flags)&&(n=t.return),e=t.return}while(e)}return 3===t.tag?n:null}function He(e){if(13===e.tag){var t=e.memoizedState;if(null===t&&null!==(e=e.alternate)&&(t=e.memoizedState),null!==t)return t.dehydrated}return null}function We(e){if($e(e)!==e)throw Error(i(188))}function Ve(e){return null!==(e=function(e){var t=e.alternate;if(!t){if(null===(t=$e(e)))throw Error(i(188));return t!==e?null:e}for(var n=e,r=t;;){var o=n.return;if(null===o)break;var a=o.alternate;if(null===a){if(null!==(r=o.return)){n=r;continue}break}if(o.child===a.child){for(a=o.child;a;){if(a===n)return We(o),e;if(a===r)return We(o),t;a=a.sibling}throw Error(i(188))}if(n.return!==r.return)n=o,r=a;else{for(var s=!1,l=o.child;l;){if(l===n){s=!0,n=o,r=a;break}if(l===r){s=!0,r=o,n=a;break}l=l.sibling}if(!s){for(l=a.child;l;){if(l===n){s=!0,n=a,r=o;break}if(l===r){s=!0,r=a,n=o;break}l=l.sibling}if(!s)throw Error(i(189))}}if(n.alternate!==r)throw Error(i(190))}if(3!==n.tag)throw Error(i(188));return n.stateNode.current===n?e:t}(e))?qe(e):null}function qe(e){if(5===e.tag||6===e.tag)return e;for(e=e.child;null!==e;){var t=qe(e);if(null!==t)return t;e=e.sibling}return null}var Ke=o.unstable_scheduleCallback,Ge=o.unstable_cancelCallback,Je=o.unstable_shouldYield,Ye=o.unstable_requestPaint,Xe=o.unstable_now,Qe=o.unstable_getCurrentPriorityLevel,Ze=o.unstable_ImmediatePriority,et=o.unstable_UserBlockingPriority,tt=o.unstable_NormalPriority,nt=o.unstable_LowPriority,rt=o.unstable_IdlePriority,ot=null,it=null,at=Math.clz32?Math.clz32:function(e){return 0===(e>>>=0)?32:31-(st(e)/lt|0)|0},st=Math.log,lt=Math.LN2,ut=64,ct=4194304;function dt(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return 4194240&e;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return 130023424&e;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ft(e,t){var n=e.pendingLanes;if(0===n)return 0;var r=0,o=e.suspendedLanes,i=e.pingedLanes,a=268435455&n;if(0!==a){var s=a&~o;0!==s?r=dt(s):0!=(i&=a)&&(r=dt(i))}else 0!=(a=n&~o)?r=dt(a):0!==i&&(r=dt(i));if(0===r)return 0;if(0!==t&&t!==r&&!(t&o)&&((o=r&-r)>=(i=t&-t)||16===o&&4194240&i))return t;if(4&r&&(r|=16&n),0!==(t=e.entangledLanes))for(e=e.entanglements,t&=r;0<t;)o=1<<(n=31-at(t)),r|=e[n],t&=~o;return r}function pt(e,t){switch(e){case 1:case 2:case 4:return t+250;case 8:case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;default:return-1}}function ht(e){return 0!=(e=-1073741825&e.pendingLanes)?e:1073741824&e?1073741824:0}function mt(){var e=ut;return!(4194240&(ut<<=1))&&(ut=64),e}function gt(e){for(var t=[],n=0;31>n;n++)t.push(e);return t}function vt(e,t,n){e.pendingLanes|=t,536870912!==t&&(e.suspendedLanes=0,e.pingedLanes=0),(e=e.eventTimes)[t=31-at(t)]=n}function yt(e,t){var n=e.entangledLanes|=t;for(e=e.entanglements;n;){var r=31-at(n),o=1<<r;o&t|e[r]&t&&(e[r]|=t),n&=~o}}var bt=0;function wt(e){return 1<(e&=-e)?4<e?268435455&e?16:536870912:4:1}var Et,St,xt,At,kt,_t=!1,Ct=[],Ot=null,Pt=null,Rt=null,Tt=new Map,Ft=new Map,jt=[],Nt="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split(" ");function It(e,t){switch(e){case"focusin":case"focusout":Ot=null;break;case"dragenter":case"dragleave":Pt=null;break;case"mouseover":case"mouseout":Rt=null;break;case"pointerover":case"pointerout":Tt.delete(t.pointerId);break;case"gotpointercapture":case"lostpointercapture":Ft.delete(t.pointerId)}}function Lt(e,t,n,r,o,i){return null===e||e.nativeEvent!==i?(e={blockedOn:t,domEventName:n,eventSystemFlags:r,nativeEvent:i,targetContainers:[o]},null!==t&&null!==(t=wo(t))&&St(t),e):(e.eventSystemFlags|=r,t=e.targetContainers,null!==o&&-1===t.indexOf(o)&&t.push(o),e)}function Mt(e){var t=bo(e.target);if(null!==t){var n=$e(t);if(null!==n)if(13===(t=n.tag)){if(null!==(t=He(n)))return e.blockedOn=t,void kt(e.priority,(function(){xt(n)}))}else if(3===t&&n.stateNode.current.memoizedState.isDehydrated)return void(e.blockedOn=3===n.tag?n.stateNode.containerInfo:null)}e.blockedOn=null}function Dt(e){if(null!==e.blockedOn)return!1;for(var t=e.targetContainers;0<t.length;){var n=Jt(e.domEventName,e.eventSystemFlags,t[0],e.nativeEvent);if(null!==n)return null!==(t=wo(n))&&St(t),e.blockedOn=n,!1;var r=new(n=e.nativeEvent).constructor(n.type,n);we=r,n.target.dispatchEvent(r),we=null,t.shift()}return!0}function Bt(e,t,n){Dt(e)&&n.delete(t)}function Ut(){_t=!1,null!==Ot&&Dt(Ot)&&(Ot=null),null!==Pt&&Dt(Pt)&&(Pt=null),null!==Rt&&Dt(Rt)&&(Rt=null),Tt.forEach(Bt),Ft.forEach(Bt)}function zt(e,t){e.blockedOn===t&&(e.blockedOn=null,_t||(_t=!0,o.unstable_scheduleCallback(o.unstable_NormalPriority,Ut)))}function $t(e){function t(t){return zt(t,e)}if(0<Ct.length){zt(Ct[0],e);for(var n=1;n<Ct.length;n++){var r=Ct[n];r.blockedOn===e&&(r.blockedOn=null)}}for(null!==Ot&&zt(Ot,e),null!==Pt&&zt(Pt,e),null!==Rt&&zt(Rt,e),Tt.forEach(t),Ft.forEach(t),n=0;n<jt.length;n++)(r=jt[n]).blockedOn===e&&(r.blockedOn=null);for(;0<jt.length&&null===(n=jt[0]).blockedOn;)Mt(n),null===n.blockedOn&&jt.shift()}var Ht=w.ReactCurrentBatchConfig,Wt=!0;function Vt(e,t,n,r){var o=bt,i=Ht.transition;Ht.transition=null;try{bt=1,Kt(e,t,n,r)}finally{bt=o,Ht.transition=i}}function qt(e,t,n,r){var o=bt,i=Ht.transition;Ht.transition=null;try{bt=4,Kt(e,t,n,r)}finally{bt=o,Ht.transition=i}}function Kt(e,t,n,r){if(Wt){var o=Jt(e,t,n,r);if(null===o)Wr(e,t,r,Gt,n),It(e,r);else if(function(e,t,n,r,o){switch(t){case"focusin":return Ot=Lt(Ot,e,t,n,r,o),!0;case"dragenter":return Pt=Lt(Pt,e,t,n,r,o),!0;case"mouseover":return Rt=Lt(Rt,e,t,n,r,o),!0;case"pointerover":var i=o.pointerId;return Tt.set(i,Lt(Tt.get(i)||null,e,t,n,r,o)),!0;case"gotpointercapture":return i=o.pointerId,Ft.set(i,Lt(Ft.get(i)||null,e,t,n,r,o)),!0}return!1}(o,e,t,n,r))r.stopPropagation();else if(It(e,r),4&t&&-1<Nt.indexOf(e)){for(;null!==o;){var i=wo(o);if(null!==i&&Et(i),null===(i=Jt(e,t,n,r))&&Wr(e,t,r,Gt,n),i===o)break;o=i}null!==o&&r.stopPropagation()}else Wr(e,t,r,null,n)}}var Gt=null;function Jt(e,t,n,r){if(Gt=null,null!==(e=bo(e=Ee(r))))if(null===(t=$e(e)))e=null;else if(13===(n=t.tag)){if(null!==(e=He(t)))return e;e=null}else if(3===n){if(t.stateNode.current.memoizedState.isDehydrated)return 3===t.tag?t.stateNode.containerInfo:null;e=null}else t!==e&&(e=null);return Gt=e,null}function Yt(e){switch(e){case"cancel":case"click":case"close":case"contextmenu":case"copy":case"cut":case"auxclick":case"dblclick":case"dragend":case"dragstart":case"drop":case"focusin":case"focusout":case"input":case"invalid":case"keydown":case"keypress":case"keyup":case"mousedown":case"mouseup":case"paste":case"pause":case"play":case"pointercancel":case"pointerdown":case"pointerup":case"ratechange":case"reset":case"resize":case"seeked":case"submit":case"touchcancel":case"touchend":case"touchstart":case"volumechange":case"change":case"selectionchange":case"textInput":case"compositionstart":case"compositionend":case"compositionupdate":case"beforeblur":case"afterblur":case"beforeinput":case"blur":case"fullscreenchange":case"focus":case"hashchange":case"popstate":case"select":case"selectstart":return 1;case"drag":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"mousemove":case"mouseout":case"mouseover":case"pointermove":case"pointerout":case"pointerover":case"scroll":case"toggle":case"touchmove":case"wheel":case"mouseenter":case"mouseleave":case"pointerenter":case"pointerleave":return 4;case"message":switch(Qe()){case Ze:return 1;case et:return 4;case tt:case nt:return 16;case rt:return 536870912;default:return 16}default:return 16}}var Xt=null,Qt=null,Zt=null;function en(){if(Zt)return Zt;var e,t,n=Qt,r=n.length,o="value"in Xt?Xt.value:Xt.textContent,i=o.length;for(e=0;e<r&&n[e]===o[e];e++);var a=r-e;for(t=1;t<=a&&n[r-t]===o[i-t];t++);return Zt=o.slice(e,1<t?1-t:void 0)}function tn(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,10===e&&(e=13),32<=e||13===e?e:0}function nn(){return!0}function rn(){return!1}function on(e){function t(t,n,r,o,i){for(var a in this._reactName=t,this._targetInst=r,this.type=n,this.nativeEvent=o,this.target=i,this.currentTarget=null,e)e.hasOwnProperty(a)&&(t=e[a],this[a]=t?t(o):o[a]);return this.isDefaultPrevented=(null!=o.defaultPrevented?o.defaultPrevented:!1===o.returnValue)?nn:rn,this.isPropagationStopped=rn,this}return M(t.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=nn)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=nn)},persist:function(){},isPersistent:nn}),t}var an,sn,ln,un={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},cn=on(un),dn=M({},un,{view:0,detail:0}),fn=on(dn),pn=M({},dn,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:kn,button:0,buttons:0,relatedTarget:function(e){return void 0===e.relatedTarget?e.fromElement===e.srcElement?e.toElement:e.fromElement:e.relatedTarget},movementX:function(e){return"movementX"in e?e.movementX:(e!==ln&&(ln&&"mousemove"===e.type?(an=e.screenX-ln.screenX,sn=e.screenY-ln.screenY):sn=an=0,ln=e),an)},movementY:function(e){return"movementY"in e?e.movementY:sn}}),hn=on(pn),mn=on(M({},pn,{dataTransfer:0})),gn=on(M({},dn,{relatedTarget:0})),vn=on(M({},un,{animationName:0,elapsedTime:0,pseudoElement:0})),yn=M({},un,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),bn=on(yn),wn=on(M({},un,{data:0})),En={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},Sn={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},xn={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function An(e){var t=this.nativeEvent;return t.getModifierState?t.getModifierState(e):!!(e=xn[e])&&!!t[e]}function kn(){return An}var _n=M({},dn,{key:function(e){if(e.key){var t=En[e.key]||e.key;if("Unidentified"!==t)return t}return"keypress"===e.type?13===(e=tn(e))?"Enter":String.fromCharCode(e):"keydown"===e.type||"keyup"===e.type?Sn[e.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:kn,charCode:function(e){return"keypress"===e.type?tn(e):0},keyCode:function(e){return"keydown"===e.type||"keyup"===e.type?e.keyCode:0},which:function(e){return"keypress"===e.type?tn(e):"keydown"===e.type||"keyup"===e.type?e.keyCode:0}}),Cn=on(_n),On=on(M({},pn,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0})),Pn=on(M({},dn,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:kn})),Rn=on(M({},un,{propertyName:0,elapsedTime:0,pseudoElement:0})),Tn=M({},pn,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:0,deltaMode:0}),Fn=on(Tn),jn=[9,13,27,32],Nn=c&&"CompositionEvent"in window,In=null;c&&"documentMode"in document&&(In=document.documentMode);var Ln=c&&"TextEvent"in window&&!In,Mn=c&&(!Nn||In&&8<In&&11>=In),Dn=String.fromCharCode(32),Bn=!1;function Un(e,t){switch(e){case"keyup":return-1!==jn.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function zn(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var $n=!1,Hn={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Wn(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return"input"===t?!!Hn[e.type]:"textarea"===t}function Vn(e,t,n,r){_e(r),0<(t=qr(t,"onChange")).length&&(n=new cn("onChange","change",null,n,r),e.push({event:n,listeners:t}))}var qn=null,Kn=null;function Gn(e){Dr(e,0)}function Jn(e){if(K(Eo(e)))return e}function Yn(e,t){if("change"===e)return t}var Xn=!1;if(c){var Qn;if(c){var Zn="oninput"in document;if(!Zn){var er=document.createElement("div");er.setAttribute("oninput","return;"),Zn="function"==typeof er.oninput}Qn=Zn}else Qn=!1;Xn=Qn&&(!document.documentMode||9<document.documentMode)}function tr(){qn&&(qn.detachEvent("onpropertychange",nr),Kn=qn=null)}function nr(e){if("value"===e.propertyName&&Jn(Kn)){var t=[];Vn(t,Kn,e,Ee(e)),Te(Gn,t)}}function rr(e,t,n){"focusin"===e?(tr(),Kn=n,(qn=t).attachEvent("onpropertychange",nr)):"focusout"===e&&tr()}function or(e){if("selectionchange"===e||"keyup"===e||"keydown"===e)return Jn(Kn)}function ir(e,t){if("click"===e)return Jn(t)}function ar(e,t){if("input"===e||"change"===e)return Jn(t)}var sr="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t};function lr(e,t){if(sr(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(r=0;r<n.length;r++){var o=n[r];if(!d.call(t,o)||!sr(e[o],t[o]))return!1}return!0}function ur(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function cr(e,t){var n,r=ur(e);for(e=0;r;){if(3===r.nodeType){if(n=e+r.textContent.length,e<=t&&n>=t)return{node:r,offset:t-e};e=n}e:{for(;r;){if(r.nextSibling){r=r.nextSibling;break e}r=r.parentNode}r=void 0}r=ur(r)}}function dr(e,t){return!(!e||!t)&&(e===t||(!e||3!==e.nodeType)&&(t&&3===t.nodeType?dr(e,t.parentNode):"contains"in e?e.contains(t):!!e.compareDocumentPosition&&!!(16&e.compareDocumentPosition(t))))}function fr(){for(var e=window,t=G();t instanceof e.HTMLIFrameElement;){try{var n="string"==typeof t.contentWindow.location.href}catch(e){n=!1}if(!n)break;t=G((e=t.contentWindow).document)}return t}function pr(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&("text"===e.type||"search"===e.type||"tel"===e.type||"url"===e.type||"password"===e.type)||"textarea"===t||"true"===e.contentEditable)}function hr(e){var t=fr(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&dr(n.ownerDocument.documentElement,n)){if(null!==r&&pr(n))if(t=r.start,void 0===(e=r.end)&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if((e=(t=n.ownerDocument||document)&&t.defaultView||window).getSelection){e=e.getSelection();var o=n.textContent.length,i=Math.min(r.start,o);r=void 0===r.end?i:Math.min(r.end,o),!e.extend&&i>r&&(o=r,r=i,i=o),o=cr(n,i);var a=cr(n,r);o&&a&&(1!==e.rangeCount||e.anchorNode!==o.node||e.anchorOffset!==o.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&((t=t.createRange()).setStart(o.node,o.offset),e.removeAllRanges(),i>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}for(t=[],e=n;e=e.parentNode;)1===e.nodeType&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for("function"==typeof n.focus&&n.focus(),n=0;n<t.length;n++)(e=t[n]).element.scrollLeft=e.left,e.element.scrollTop=e.top}}var mr=c&&"documentMode"in document&&11>=document.documentMode,gr=null,vr=null,yr=null,br=!1;function wr(e,t,n){var r=n.window===n?n.document:9===n.nodeType?n:n.ownerDocument;br||null==gr||gr!==G(r)||(r="selectionStart"in(r=gr)&&pr(r)?{start:r.selectionStart,end:r.selectionEnd}:{anchorNode:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection()).anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset},yr&&lr(yr,r)||(yr=r,0<(r=qr(vr,"onSelect")).length&&(t=new cn("onSelect","select",null,t,n),e.push({event:t,listeners:r}),t.target=gr)))}function Er(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n}var Sr={animationend:Er("Animation","AnimationEnd"),animationiteration:Er("Animation","AnimationIteration"),animationstart:Er("Animation","AnimationStart"),transitionend:Er("Transition","TransitionEnd")},xr={},Ar={};function kr(e){if(xr[e])return xr[e];if(!Sr[e])return e;var t,n=Sr[e];for(t in n)if(n.hasOwnProperty(t)&&t in Ar)return xr[e]=n[t];return e}c&&(Ar=document.createElement("div").style,"AnimationEvent"in window||(delete Sr.animationend.animation,delete Sr.animationiteration.animation,delete Sr.animationstart.animation),"TransitionEvent"in window||delete Sr.transitionend.transition);var _r=kr("animationend"),Cr=kr("animationiteration"),Or=kr("animationstart"),Pr=kr("transitionend"),Rr=new Map,Tr="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function Fr(e,t){Rr.set(e,t),l(t,[e])}for(var jr=0;jr<Tr.length;jr++){var Nr=Tr[jr];Fr(Nr.toLowerCase(),"on"+(Nr[0].toUpperCase()+Nr.slice(1)))}Fr(_r,"onAnimationEnd"),Fr(Cr,"onAnimationIteration"),Fr(Or,"onAnimationStart"),Fr("dblclick","onDoubleClick"),Fr("focusin","onFocus"),Fr("focusout","onBlur"),Fr(Pr,"onTransitionEnd"),u("onMouseEnter",["mouseout","mouseover"]),u("onMouseLeave",["mouseout","mouseover"]),u("onPointerEnter",["pointerout","pointerover"]),u("onPointerLeave",["pointerout","pointerover"]),l("onChange","change click focusin focusout input keydown keyup selectionchange".split(" ")),l("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" ")),l("onBeforeInput",["compositionend","keypress","textInput","paste"]),l("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" ")),l("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" ")),l("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var Ir="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Lr=new Set("cancel close invalid load scroll toggle".split(" ").concat(Ir));function Mr(e,t,n){var r=e.type||"unknown-event";e.currentTarget=n,function(e,t,n,r,o,a,s,l,u){if(ze.apply(this,arguments),Le){if(!Le)throw Error(i(198));var c=Me;Le=!1,Me=null,De||(De=!0,Be=c)}}(r,t,void 0,e),e.currentTarget=null}function Dr(e,t){t=!!(4&t);for(var n=0;n<e.length;n++){var r=e[n],o=r.event;r=r.listeners;e:{var i=void 0;if(t)for(var a=r.length-1;0<=a;a--){var s=r[a],l=s.instance,u=s.currentTarget;if(s=s.listener,l!==i&&o.isPropagationStopped())break e;Mr(o,s,u),i=l}else for(a=0;a<r.length;a++){if(l=(s=r[a]).instance,u=s.currentTarget,s=s.listener,l!==i&&o.isPropagationStopped())break e;Mr(o,s,u),i=l}}}if(De)throw e=Be,De=!1,Be=null,e}function Br(e,t){var n=t[go];void 0===n&&(n=t[go]=new Set);var r=e+"__bubble";n.has(r)||(Hr(t,e,2,!1),n.add(r))}function Ur(e,t,n){var r=0;t&&(r|=4),Hr(n,e,r,t)}var zr="_reactListening"+Math.random().toString(36).slice(2);function $r(e){if(!e[zr]){e[zr]=!0,a.forEach((function(t){"selectionchange"!==t&&(Lr.has(t)||Ur(t,!1,e),Ur(t,!0,e))}));var t=9===e.nodeType?e:e.ownerDocument;null===t||t[zr]||(t[zr]=!0,Ur("selectionchange",!1,t))}}function Hr(e,t,n,r){switch(Yt(t)){case 1:var o=Vt;break;case 4:o=qt;break;default:o=Kt}n=o.bind(null,t,n,e),o=void 0,!je||"touchstart"!==t&&"touchmove"!==t&&"wheel"!==t||(o=!0),r?void 0!==o?e.addEventListener(t,n,{capture:!0,passive:o}):e.addEventListener(t,n,!0):void 0!==o?e.addEventListener(t,n,{passive:o}):e.addEventListener(t,n,!1)}function Wr(e,t,n,r,o){var i=r;if(!(1&t||2&t||null===r))e:for(;;){if(null===r)return;var a=r.tag;if(3===a||4===a){var s=r.stateNode.containerInfo;if(s===o||8===s.nodeType&&s.parentNode===o)break;if(4===a)for(a=r.return;null!==a;){var l=a.tag;if((3===l||4===l)&&((l=a.stateNode.containerInfo)===o||8===l.nodeType&&l.parentNode===o))return;a=a.return}for(;null!==s;){if(null===(a=bo(s)))return;if(5===(l=a.tag)||6===l){r=i=a;continue e}s=s.parentNode}}r=r.return}Te((function(){var r=i,o=Ee(n),a=[];e:{var s=Rr.get(e);if(void 0!==s){var l=cn,u=e;switch(e){case"keypress":if(0===tn(n))break e;case"keydown":case"keyup":l=Cn;break;case"focusin":u="focus",l=gn;break;case"focusout":u="blur",l=gn;break;case"beforeblur":case"afterblur":l=gn;break;case"click":if(2===n.button)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":l=hn;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":l=mn;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":l=Pn;break;case _r:case Cr:case Or:l=vn;break;case Pr:l=Rn;break;case"scroll":l=fn;break;case"wheel":l=Fn;break;case"copy":case"cut":case"paste":l=bn;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":l=On}var c=!!(4&t),d=!c&&"scroll"===e,f=c?null!==s?s+"Capture":null:s;c=[];for(var p,h=r;null!==h;){var m=(p=h).stateNode;if(5===p.tag&&null!==m&&(p=m,null!==f&&null!=(m=Fe(h,f))&&c.push(Vr(h,m,p))),d)break;h=h.return}0<c.length&&(s=new l(s,u,null,n,o),a.push({event:s,listeners:c}))}}if(!(7&t)){if(l="mouseout"===e||"pointerout"===e,(!(s="mouseover"===e||"pointerover"===e)||n===we||!(u=n.relatedTarget||n.fromElement)||!bo(u)&&!u[mo])&&(l||s)&&(s=o.window===o?o:(s=o.ownerDocument)?s.defaultView||s.parentWindow:window,l?(l=r,null!==(u=(u=n.relatedTarget||n.toElement)?bo(u):null)&&(u!==(d=$e(u))||5!==u.tag&&6!==u.tag)&&(u=null)):(l=null,u=r),l!==u)){if(c=hn,m="onMouseLeave",f="onMouseEnter",h="mouse","pointerout"!==e&&"pointerover"!==e||(c=On,m="onPointerLeave",f="onPointerEnter",h="pointer"),d=null==l?s:Eo(l),p=null==u?s:Eo(u),(s=new c(m,h+"leave",l,n,o)).target=d,s.relatedTarget=p,m=null,bo(o)===r&&((c=new c(f,h+"enter",u,n,o)).target=p,c.relatedTarget=d,m=c),d=m,l&&u)e:{for(f=u,h=0,p=c=l;p;p=Kr(p))h++;for(p=0,m=f;m;m=Kr(m))p++;for(;0<h-p;)c=Kr(c),h--;for(;0<p-h;)f=Kr(f),p--;for(;h--;){if(c===f||null!==f&&c===f.alternate)break e;c=Kr(c),f=Kr(f)}c=null}else c=null;null!==l&&Gr(a,s,l,c,!1),null!==u&&null!==d&&Gr(a,d,u,c,!0)}if("select"===(l=(s=r?Eo(r):window).nodeName&&s.nodeName.toLowerCase())||"input"===l&&"file"===s.type)var g=Yn;else if(Wn(s))if(Xn)g=ar;else{g=or;var v=rr}else(l=s.nodeName)&&"input"===l.toLowerCase()&&("checkbox"===s.type||"radio"===s.type)&&(g=ir);switch(g&&(g=g(e,r))?Vn(a,g,n,o):(v&&v(e,s,r),"focusout"===e&&(v=s._wrapperState)&&v.controlled&&"number"===s.type&&ee(s,"number",s.value)),v=r?Eo(r):window,e){case"focusin":(Wn(v)||"true"===v.contentEditable)&&(gr=v,vr=r,yr=null);break;case"focusout":yr=vr=gr=null;break;case"mousedown":br=!0;break;case"contextmenu":case"mouseup":case"dragend":br=!1,wr(a,n,o);break;case"selectionchange":if(mr)break;case"keydown":case"keyup":wr(a,n,o)}var y;if(Nn)e:{switch(e){case"compositionstart":var b="onCompositionStart";break e;case"compositionend":b="onCompositionEnd";break e;case"compositionupdate":b="onCompositionUpdate";break e}b=void 0}else $n?Un(e,n)&&(b="onCompositionEnd"):"keydown"===e&&229===n.keyCode&&(b="onCompositionStart");b&&(Mn&&"ko"!==n.locale&&($n||"onCompositionStart"!==b?"onCompositionEnd"===b&&$n&&(y=en()):(Qt="value"in(Xt=o)?Xt.value:Xt.textContent,$n=!0)),0<(v=qr(r,b)).length&&(b=new wn(b,e,null,n,o),a.push({event:b,listeners:v}),(y||null!==(y=zn(n)))&&(b.data=y))),(y=Ln?function(e,t){switch(e){case"compositionend":return zn(t);case"keypress":return 32!==t.which?null:(Bn=!0,Dn);case"textInput":return(e=t.data)===Dn&&Bn?null:e;default:return null}}(e,n):function(e,t){if($n)return"compositionend"===e||!Nn&&Un(e,t)?(e=en(),Zt=Qt=Xt=null,$n=!1,e):null;switch(e){case"paste":default:return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1<t.char.length)return t.char;if(t.which)return String.fromCharCode(t.which)}return null;case"compositionend":return Mn&&"ko"!==t.locale?null:t.data}}(e,n))&&0<(r=qr(r,"onBeforeInput")).length&&(o=new wn("onBeforeInput","beforeinput",null,n,o),a.push({event:o,listeners:r}),o.data=y)}Dr(a,t)}))}function Vr(e,t,n){return{instance:e,listener:t,currentTarget:n}}function qr(e,t){for(var n=t+"Capture",r=[];null!==e;){var o=e,i=o.stateNode;5===o.tag&&null!==i&&(o=i,null!=(i=Fe(e,n))&&r.unshift(Vr(e,i,o)),null!=(i=Fe(e,t))&&r.push(Vr(e,i,o))),e=e.return}return r}function Kr(e){if(null===e)return null;do{e=e.return}while(e&&5!==e.tag);return e||null}function Gr(e,t,n,r,o){for(var i=t._reactName,a=[];null!==n&&n!==r;){var s=n,l=s.alternate,u=s.stateNode;if(null!==l&&l===r)break;5===s.tag&&null!==u&&(s=u,o?null!=(l=Fe(n,i))&&a.unshift(Vr(n,l,s)):o||null!=(l=Fe(n,i))&&a.push(Vr(n,l,s))),n=n.return}0!==a.length&&e.push({event:t,listeners:a})}var Jr=/\r\n?/g,Yr=/\u0000|\uFFFD/g;function Xr(e){return("string"==typeof e?e:""+e).replace(Jr,"\n").replace(Yr,"")}function Qr(e,t,n){if(t=Xr(t),Xr(e)!==t&&n)throw Error(i(425))}function Zr(){}var eo=null,to=null;function no(e,t){return"textarea"===e||"noscript"===e||"string"==typeof t.children||"number"==typeof t.children||"object"==typeof t.dangerouslySetInnerHTML&&null!==t.dangerouslySetInnerHTML&&null!=t.dangerouslySetInnerHTML.__html}var ro="function"==typeof setTimeout?setTimeout:void 0,oo="function"==typeof clearTimeout?clearTimeout:void 0,io="function"==typeof Promise?Promise:void 0,ao="function"==typeof queueMicrotask?queueMicrotask:void 0!==io?function(e){return io.resolve(null).then(e).catch(so)}:ro;function so(e){setTimeout((function(){throw e}))}function lo(e,t){var n=t,r=0;do{var o=n.nextSibling;if(e.removeChild(n),o&&8===o.nodeType)if("/$"===(n=o.data)){if(0===r)return e.removeChild(o),void $t(t);r--}else"$"!==n&&"$?"!==n&&"$!"!==n||r++;n=o}while(n);$t(t)}function uo(e){for(;null!=e;e=e.nextSibling){var t=e.nodeType;if(1===t||3===t)break;if(8===t){if("$"===(t=e.data)||"$!"===t||"$?"===t)break;if("/$"===t)return null}}return e}function co(e){e=e.previousSibling;for(var t=0;e;){if(8===e.nodeType){var n=e.data;if("$"===n||"$!"===n||"$?"===n){if(0===t)return e;t--}else"/$"===n&&t++}e=e.previousSibling}return null}var fo=Math.random().toString(36).slice(2),po="__reactFiber$"+fo,ho="__reactProps$"+fo,mo="__reactContainer$"+fo,go="__reactEvents$"+fo,vo="__reactListeners$"+fo,yo="__reactHandles$"+fo;function bo(e){var t=e[po];if(t)return t;for(var n=e.parentNode;n;){if(t=n[mo]||n[po]){if(n=t.alternate,null!==t.child||null!==n&&null!==n.child)for(e=co(e);null!==e;){if(n=e[po])return n;e=co(e)}return t}n=(e=n).parentNode}return null}function wo(e){return!(e=e[po]||e[mo])||5!==e.tag&&6!==e.tag&&13!==e.tag&&3!==e.tag?null:e}function Eo(e){if(5===e.tag||6===e.tag)return e.stateNode;throw Error(i(33))}function So(e){return e[ho]||null}var xo=[],Ao=-1;function ko(e){return{current:e}}function _o(e){0>Ao||(e.current=xo[Ao],xo[Ao]=null,Ao--)}function Co(e,t){Ao++,xo[Ao]=e.current,e.current=t}var Oo={},Po=ko(Oo),Ro=ko(!1),To=Oo;function Fo(e,t){var n=e.type.contextTypes;if(!n)return Oo;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o,i={};for(o in n)i[o]=t[o];return r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function jo(e){return null!=e.childContextTypes}function No(){_o(Ro),_o(Po)}function Io(e,t,n){if(Po.current!==Oo)throw Error(i(168));Co(Po,t),Co(Ro,n)}function Lo(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,"function"!=typeof r.getChildContext)return n;for(var o in r=r.getChildContext())if(!(o in t))throw Error(i(108,H(e)||"Unknown",o));return M({},n,r)}function Mo(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||Oo,To=Po.current,Co(Po,e),Co(Ro,Ro.current),!0}function Do(e,t,n){var r=e.stateNode;if(!r)throw Error(i(169));n?(e=Lo(e,t,To),r.__reactInternalMemoizedMergedChildContext=e,_o(Ro),_o(Po),Co(Po,e)):_o(Ro),Co(Ro,n)}var Bo=null,Uo=!1,zo=!1;function $o(e){null===Bo?Bo=[e]:Bo.push(e)}function Ho(){if(!zo&&null!==Bo){zo=!0;var e=0,t=bt;try{var n=Bo;for(bt=1;e<n.length;e++){var r=n[e];do{r=r(!0)}while(null!==r)}Bo=null,Uo=!1}catch(t){throw null!==Bo&&(Bo=Bo.slice(e+1)),Ke(Ze,Ho),t}finally{bt=t,zo=!1}}return null}var Wo=[],Vo=0,qo=null,Ko=0,Go=[],Jo=0,Yo=null,Xo=1,Qo="";function Zo(e,t){Wo[Vo++]=Ko,Wo[Vo++]=qo,qo=e,Ko=t}function ei(e,t,n){Go[Jo++]=Xo,Go[Jo++]=Qo,Go[Jo++]=Yo,Yo=e;var r=Xo;e=Qo;var o=32-at(r)-1;r&=~(1<<o),n+=1;var i=32-at(t)+o;if(30<i){var a=o-o%5;i=(r&(1<<a)-1).toString(32),r>>=a,o-=a,Xo=1<<32-at(t)+o|n<<o|r,Qo=i+e}else Xo=1<<i|n<<o|r,Qo=e}function ti(e){null!==e.return&&(Zo(e,1),ei(e,1,0))}function ni(e){for(;e===qo;)qo=Wo[--Vo],Wo[Vo]=null,Ko=Wo[--Vo],Wo[Vo]=null;for(;e===Yo;)Yo=Go[--Jo],Go[Jo]=null,Qo=Go[--Jo],Go[Jo]=null,Xo=Go[--Jo],Go[Jo]=null}var ri=null,oi=null,ii=!1,ai=null;function si(e,t){var n=Fu(5,null,null,0);n.elementType="DELETED",n.stateNode=t,n.return=e,null===(t=e.deletions)?(e.deletions=[n],e.flags|=16):t.push(n)}function li(e,t){switch(e.tag){case 5:var n=e.type;return null!==(t=1!==t.nodeType||n.toLowerCase()!==t.nodeName.toLowerCase()?null:t)&&(e.stateNode=t,ri=e,oi=uo(t.firstChild),!0);case 6:return null!==(t=""===e.pendingProps||3!==t.nodeType?null:t)&&(e.stateNode=t,ri=e,oi=null,!0);case 13:return null!==(t=8!==t.nodeType?null:t)&&(n=null!==Yo?{id:Xo,overflow:Qo}:null,e.memoizedState={dehydrated:t,treeContext:n,retryLane:1073741824},(n=Fu(18,null,null,0)).stateNode=t,n.return=e,e.child=n,ri=e,oi=null,!0);default:return!1}}function ui(e){return!(!(1&e.mode)||128&e.flags)}function ci(e){if(ii){var t=oi;if(t){var n=t;if(!li(e,t)){if(ui(e))throw Error(i(418));t=uo(n.nextSibling);var r=ri;t&&li(e,t)?si(r,n):(e.flags=-4097&e.flags|2,ii=!1,ri=e)}}else{if(ui(e))throw Error(i(418));e.flags=-4097&e.flags|2,ii=!1,ri=e}}}function di(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag&&13!==e.tag;)e=e.return;ri=e}function fi(e){if(e!==ri)return!1;if(!ii)return di(e),ii=!0,!1;var t;if((t=3!==e.tag)&&!(t=5!==e.tag)&&(t="head"!==(t=e.type)&&"body"!==t&&!no(e.type,e.memoizedProps)),t&&(t=oi)){if(ui(e))throw pi(),Error(i(418));for(;t;)si(e,t),t=uo(t.nextSibling)}if(di(e),13===e.tag){if(!(e=null!==(e=e.memoizedState)?e.dehydrated:null))throw Error(i(317));e:{for(e=e.nextSibling,t=0;e;){if(8===e.nodeType){var n=e.data;if("/$"===n){if(0===t){oi=uo(e.nextSibling);break e}t--}else"$"!==n&&"$!"!==n&&"$?"!==n||t++}e=e.nextSibling}oi=null}}else oi=ri?uo(e.stateNode.nextSibling):null;return!0}function pi(){for(var e=oi;e;)e=uo(e.nextSibling)}function hi(){oi=ri=null,ii=!1}function mi(e){null===ai?ai=[e]:ai.push(e)}var gi=w.ReactCurrentBatchConfig;function vi(e,t){if(e&&e.defaultProps){for(var n in t=M({},t),e=e.defaultProps)void 0===t[n]&&(t[n]=e[n]);return t}return t}var yi=ko(null),bi=null,wi=null,Ei=null;function Si(){Ei=wi=bi=null}function xi(e){var t=yi.current;_o(yi),e._currentValue=t}function Ai(e,t,n){for(;null!==e;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,null!==r&&(r.childLanes|=t)):null!==r&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function ki(e,t){bi=e,Ei=wi=null,null!==(e=e.dependencies)&&null!==e.firstContext&&(!!(e.lanes&t)&&(ws=!0),e.firstContext=null)}function _i(e){var t=e._currentValue;if(Ei!==e)if(e={context:e,memoizedValue:t,next:null},null===wi){if(null===bi)throw Error(i(308));wi=e,bi.dependencies={lanes:0,firstContext:e}}else wi=wi.next=e;return t}var Ci=null;function Oi(e){null===Ci?Ci=[e]:Ci.push(e)}function Pi(e,t,n,r){var o=t.interleaved;return null===o?(n.next=n,Oi(t)):(n.next=o.next,o.next=n),t.interleaved=n,Ri(e,r)}function Ri(e,t){e.lanes|=t;var n=e.alternate;for(null!==n&&(n.lanes|=t),n=e,e=e.return;null!==e;)e.childLanes|=t,null!==(n=e.alternate)&&(n.childLanes|=t),n=e,e=e.return;return 3===n.tag?n.stateNode:null}var Ti=!1;function Fi(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function ji(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Ni(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function Ii(e,t,n){var r=e.updateQueue;if(null===r)return null;if(r=r.shared,2&Pl){var o=r.pending;return null===o?t.next=t:(t.next=o.next,o.next=t),r.pending=t,Ri(e,n)}return null===(o=r.interleaved)?(t.next=t,Oi(r)):(t.next=o.next,o.next=t),r.interleaved=t,Ri(e,n)}function Li(e,t,n){if(null!==(t=t.updateQueue)&&(t=t.shared,4194240&n)){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,yt(e,n)}}function Mi(e,t){var n=e.updateQueue,r=e.alternate;if(null!==r&&n===(r=r.updateQueue)){var o=null,i=null;if(null!==(n=n.firstBaseUpdate)){do{var a={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};null===i?o=i=a:i=i.next=a,n=n.next}while(null!==n);null===i?o=i=t:i=i.next=t}else o=i=t;return n={baseState:r.baseState,firstBaseUpdate:o,lastBaseUpdate:i,shared:r.shared,effects:r.effects},void(e.updateQueue=n)}null===(e=n.lastBaseUpdate)?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function Di(e,t,n,r){var o=e.updateQueue;Ti=!1;var i=o.firstBaseUpdate,a=o.lastBaseUpdate,s=o.shared.pending;if(null!==s){o.shared.pending=null;var l=s,u=l.next;l.next=null,null===a?i=u:a.next=u,a=l;var c=e.alternate;null!==c&&(s=(c=c.updateQueue).lastBaseUpdate)!==a&&(null===s?c.firstBaseUpdate=u:s.next=u,c.lastBaseUpdate=l)}if(null!==i){var d=o.baseState;for(a=0,c=u=l=null,s=i;;){var f=s.lane,p=s.eventTime;if((r&f)===f){null!==c&&(c=c.next={eventTime:p,lane:0,tag:s.tag,payload:s.payload,callback:s.callback,next:null});e:{var h=e,m=s;switch(f=t,p=n,m.tag){case 1:if("function"==typeof(h=m.payload)){d=h.call(p,d,f);break e}d=h;break e;case 3:h.flags=-65537&h.flags|128;case 0:if(null==(f="function"==typeof(h=m.payload)?h.call(p,d,f):h))break e;d=M({},d,f);break e;case 2:Ti=!0}}null!==s.callback&&0!==s.lane&&(e.flags|=64,null===(f=o.effects)?o.effects=[s]:f.push(s))}else p={eventTime:p,lane:f,tag:s.tag,payload:s.payload,callback:s.callback,next:null},null===c?(u=c=p,l=d):c=c.next=p,a|=f;if(null===(s=s.next)){if(null===(s=o.shared.pending))break;s=(f=s).next,f.next=null,o.lastBaseUpdate=f,o.shared.pending=null}}if(null===c&&(l=d),o.baseState=l,o.firstBaseUpdate=u,o.lastBaseUpdate=c,null!==(t=o.shared.interleaved)){o=t;do{a|=o.lane,o=o.next}while(o!==t)}else null===i&&(o.shared.lanes=0);Ml|=a,e.lanes=a,e.memoizedState=d}}function Bi(e,t,n){if(e=t.effects,t.effects=null,null!==e)for(t=0;t<e.length;t++){var r=e[t],o=r.callback;if(null!==o){if(r.callback=null,r=n,"function"!=typeof o)throw Error(i(191,o));o.call(r)}}}var Ui=(new r.Component).refs;function zi(e,t,n,r){n=null==(n=n(r,t=e.memoizedState))?t:M({},t,n),e.memoizedState=n,0===e.lanes&&(e.updateQueue.baseState=n)}var $i={isMounted:function(e){return!!(e=e._reactInternals)&&$e(e)===e},enqueueSetState:function(e,t,n){e=e._reactInternals;var r=tu(),o=nu(e),i=Ni(r,o);i.payload=t,null!=n&&(i.callback=n),null!==(t=Ii(e,i,o))&&(ru(t,e,o,r),Li(t,e,o))},enqueueReplaceState:function(e,t,n){e=e._reactInternals;var r=tu(),o=nu(e),i=Ni(r,o);i.tag=1,i.payload=t,null!=n&&(i.callback=n),null!==(t=Ii(e,i,o))&&(ru(t,e,o,r),Li(t,e,o))},enqueueForceUpdate:function(e,t){e=e._reactInternals;var n=tu(),r=nu(e),o=Ni(n,r);o.tag=2,null!=t&&(o.callback=t),null!==(t=Ii(e,o,r))&&(ru(t,e,r,n),Li(t,e,r))}};function Hi(e,t,n,r,o,i,a){return"function"==typeof(e=e.stateNode).shouldComponentUpdate?e.shouldComponentUpdate(r,i,a):!(t.prototype&&t.prototype.isPureReactComponent&&lr(n,r)&&lr(o,i))}function Wi(e,t,n){var r=!1,o=Oo,i=t.contextType;return"object"==typeof i&&null!==i?i=_i(i):(o=jo(t)?To:Po.current,i=(r=null!=(r=t.contextTypes))?Fo(e,o):Oo),t=new t(n,i),e.memoizedState=null!==t.state&&void 0!==t.state?t.state:null,t.updater=$i,e.stateNode=t,t._reactInternals=e,r&&((e=e.stateNode).__reactInternalMemoizedUnmaskedChildContext=o,e.__reactInternalMemoizedMaskedChildContext=i),t}function Vi(e,t,n,r){e=t.state,"function"==typeof t.componentWillReceiveProps&&t.componentWillReceiveProps(n,r),"function"==typeof t.UNSAFE_componentWillReceiveProps&&t.UNSAFE_componentWillReceiveProps(n,r),t.state!==e&&$i.enqueueReplaceState(t,t.state,null)}function qi(e,t,n,r){var o=e.stateNode;o.props=n,o.state=e.memoizedState,o.refs=Ui,Fi(e);var i=t.contextType;"object"==typeof i&&null!==i?o.context=_i(i):(i=jo(t)?To:Po.current,o.context=Fo(e,i)),o.state=e.memoizedState,"function"==typeof(i=t.getDerivedStateFromProps)&&(zi(e,t,i,n),o.state=e.memoizedState),"function"==typeof t.getDerivedStateFromProps||"function"==typeof o.getSnapshotBeforeUpdate||"function"!=typeof o.UNSAFE_componentWillMount&&"function"!=typeof o.componentWillMount||(t=o.state,"function"==typeof o.componentWillMount&&o.componentWillMount(),"function"==typeof o.UNSAFE_componentWillMount&&o.UNSAFE_componentWillMount(),t!==o.state&&$i.enqueueReplaceState(o,o.state,null),Di(e,n,o,r),o.state=e.memoizedState),"function"==typeof o.componentDidMount&&(e.flags|=4194308)}function Ki(e,t,n){if(null!==(e=n.ref)&&"function"!=typeof e&&"object"!=typeof e){if(n._owner){if(n=n._owner){if(1!==n.tag)throw Error(i(309));var r=n.stateNode}if(!r)throw Error(i(147,e));var o=r,a=""+e;return null!==t&&null!==t.ref&&"function"==typeof t.ref&&t.ref._stringRef===a?t.ref:(t=function(e){var t=o.refs;t===Ui&&(t=o.refs={}),null===e?delete t[a]:t[a]=e},t._stringRef=a,t)}if("string"!=typeof e)throw Error(i(284));if(!n._owner)throw Error(i(290,e))}return e}function Gi(e,t){throw e=Object.prototype.toString.call(t),Error(i(31,"[object Object]"===e?"object with keys {"+Object.keys(t).join(", ")+"}":e))}function Ji(e){return(0,e._init)(e._payload)}function Yi(e){function t(t,n){if(e){var r=t.deletions;null===r?(t.deletions=[n],t.flags|=16):r.push(n)}}function n(n,r){if(!e)return null;for(;null!==r;)t(n,r),r=r.sibling;return null}function r(e,t){for(e=new Map;null!==t;)null!==t.key?e.set(t.key,t):e.set(t.index,t),t=t.sibling;return e}function o(e,t){return(e=Nu(e,t)).index=0,e.sibling=null,e}function a(t,n,r){return t.index=r,e?null!==(r=t.alternate)?(r=r.index)<n?(t.flags|=2,n):r:(t.flags|=2,n):(t.flags|=1048576,n)}function s(t){return e&&null===t.alternate&&(t.flags|=2),t}function l(e,t,n,r){return null===t||6!==t.tag?((t=Du(n,e.mode,r)).return=e,t):((t=o(t,n)).return=e,t)}function u(e,t,n,r){var i=n.type;return i===x?d(e,t,n.props.children,r,n.key):null!==t&&(t.elementType===i||"object"==typeof i&&null!==i&&i.$$typeof===F&&Ji(i)===t.type)?((r=o(t,n.props)).ref=Ki(e,t,n),r.return=e,r):((r=Iu(n.type,n.key,n.props,null,e.mode,r)).ref=Ki(e,t,n),r.return=e,r)}function c(e,t,n,r){return null===t||4!==t.tag||t.stateNode.containerInfo!==n.containerInfo||t.stateNode.implementation!==n.implementation?((t=Bu(n,e.mode,r)).return=e,t):((t=o(t,n.children||[])).return=e,t)}function d(e,t,n,r,i){return null===t||7!==t.tag?((t=Lu(n,e.mode,r,i)).return=e,t):((t=o(t,n)).return=e,t)}function f(e,t,n){if("string"==typeof t&&""!==t||"number"==typeof t)return(t=Du(""+t,e.mode,n)).return=e,t;if("object"==typeof t&&null!==t){switch(t.$$typeof){case E:return(n=Iu(t.type,t.key,t.props,null,e.mode,n)).ref=Ki(e,null,t),n.return=e,n;case S:return(t=Bu(t,e.mode,n)).return=e,t;case F:return f(e,(0,t._init)(t._payload),n)}if(te(t)||I(t))return(t=Lu(t,e.mode,n,null)).return=e,t;Gi(e,t)}return null}function p(e,t,n,r){var o=null!==t?t.key:null;if("string"==typeof n&&""!==n||"number"==typeof n)return null!==o?null:l(e,t,""+n,r);if("object"==typeof n&&null!==n){switch(n.$$typeof){case E:return n.key===o?u(e,t,n,r):null;case S:return n.key===o?c(e,t,n,r):null;case F:return p(e,t,(o=n._init)(n._payload),r)}if(te(n)||I(n))return null!==o?null:d(e,t,n,r,null);Gi(e,n)}return null}function h(e,t,n,r,o){if("string"==typeof r&&""!==r||"number"==typeof r)return l(t,e=e.get(n)||null,""+r,o);if("object"==typeof r&&null!==r){switch(r.$$typeof){case E:return u(t,e=e.get(null===r.key?n:r.key)||null,r,o);case S:return c(t,e=e.get(null===r.key?n:r.key)||null,r,o);case F:return h(e,t,n,(0,r._init)(r._payload),o)}if(te(r)||I(r))return d(t,e=e.get(n)||null,r,o,null);Gi(t,r)}return null}function m(o,i,s,l){for(var u=null,c=null,d=i,m=i=0,g=null;null!==d&&m<s.length;m++){d.index>m?(g=d,d=null):g=d.sibling;var v=p(o,d,s[m],l);if(null===v){null===d&&(d=g);break}e&&d&&null===v.alternate&&t(o,d),i=a(v,i,m),null===c?u=v:c.sibling=v,c=v,d=g}if(m===s.length)return n(o,d),ii&&Zo(o,m),u;if(null===d){for(;m<s.length;m++)null!==(d=f(o,s[m],l))&&(i=a(d,i,m),null===c?u=d:c.sibling=d,c=d);return ii&&Zo(o,m),u}for(d=r(o,d);m<s.length;m++)null!==(g=h(d,o,m,s[m],l))&&(e&&null!==g.alternate&&d.delete(null===g.key?m:g.key),i=a(g,i,m),null===c?u=g:c.sibling=g,c=g);return e&&d.forEach((function(e){return t(o,e)})),ii&&Zo(o,m),u}function g(o,s,l,u){var c=I(l);if("function"!=typeof c)throw Error(i(150));if(null==(l=c.call(l)))throw Error(i(151));for(var d=c=null,m=s,g=s=0,v=null,y=l.next();null!==m&&!y.done;g++,y=l.next()){m.index>g?(v=m,m=null):v=m.sibling;var b=p(o,m,y.value,u);if(null===b){null===m&&(m=v);break}e&&m&&null===b.alternate&&t(o,m),s=a(b,s,g),null===d?c=b:d.sibling=b,d=b,m=v}if(y.done)return n(o,m),ii&&Zo(o,g),c;if(null===m){for(;!y.done;g++,y=l.next())null!==(y=f(o,y.value,u))&&(s=a(y,s,g),null===d?c=y:d.sibling=y,d=y);return ii&&Zo(o,g),c}for(m=r(o,m);!y.done;g++,y=l.next())null!==(y=h(m,o,g,y.value,u))&&(e&&null!==y.alternate&&m.delete(null===y.key?g:y.key),s=a(y,s,g),null===d?c=y:d.sibling=y,d=y);return e&&m.forEach((function(e){return t(o,e)})),ii&&Zo(o,g),c}return function e(r,i,a,l){if("object"==typeof a&&null!==a&&a.type===x&&null===a.key&&(a=a.props.children),"object"==typeof a&&null!==a){switch(a.$$typeof){case E:e:{for(var u=a.key,c=i;null!==c;){if(c.key===u){if((u=a.type)===x){if(7===c.tag){n(r,c.sibling),(i=o(c,a.props.children)).return=r,r=i;break e}}else if(c.elementType===u||"object"==typeof u&&null!==u&&u.$$typeof===F&&Ji(u)===c.type){n(r,c.sibling),(i=o(c,a.props)).ref=Ki(r,c,a),i.return=r,r=i;break e}n(r,c);break}t(r,c),c=c.sibling}a.type===x?((i=Lu(a.props.children,r.mode,l,a.key)).return=r,r=i):((l=Iu(a.type,a.key,a.props,null,r.mode,l)).ref=Ki(r,i,a),l.return=r,r=l)}return s(r);case S:e:{for(c=a.key;null!==i;){if(i.key===c){if(4===i.tag&&i.stateNode.containerInfo===a.containerInfo&&i.stateNode.implementation===a.implementation){n(r,i.sibling),(i=o(i,a.children||[])).return=r,r=i;break e}n(r,i);break}t(r,i),i=i.sibling}(i=Bu(a,r.mode,l)).return=r,r=i}return s(r);case F:return e(r,i,(c=a._init)(a._payload),l)}if(te(a))return m(r,i,a,l);if(I(a))return g(r,i,a,l);Gi(r,a)}return"string"==typeof a&&""!==a||"number"==typeof a?(a=""+a,null!==i&&6===i.tag?(n(r,i.sibling),(i=o(i,a)).return=r,r=i):(n(r,i),(i=Du(a,r.mode,l)).return=r,r=i),s(r)):n(r,i)}}var Xi=Yi(!0),Qi=Yi(!1),Zi={},ea=ko(Zi),ta=ko(Zi),na=ko(Zi);function ra(e){if(e===Zi)throw Error(i(174));return e}function oa(e,t){switch(Co(na,t),Co(ta,e),Co(ea,Zi),e=t.nodeType){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:le(null,"");break;default:t=le(t=(e=8===e?t.parentNode:t).namespaceURI||null,e=e.tagName)}_o(ea),Co(ea,t)}function ia(){_o(ea),_o(ta),_o(na)}function aa(e){ra(na.current);var t=ra(ea.current),n=le(t,e.type);t!==n&&(Co(ta,e),Co(ea,n))}function sa(e){ta.current===e&&(_o(ea),_o(ta))}var la=ko(0);function ua(e){for(var t=e;null!==t;){if(13===t.tag){var n=t.memoizedState;if(null!==n&&(null===(n=n.dehydrated)||"$?"===n.data||"$!"===n.data))return t}else if(19===t.tag&&void 0!==t.memoizedProps.revealOrder){if(128&t.flags)return t}else if(null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var ca=[];function da(){for(var e=0;e<ca.length;e++)ca[e]._workInProgressVersionPrimary=null;ca.length=0}var fa=w.ReactCurrentDispatcher,pa=w.ReactCurrentBatchConfig,ha=0,ma=null,ga=null,va=null,ya=!1,ba=!1,wa=0,Ea=0;function Sa(){throw Error(i(321))}function xa(e,t){if(null===t)return!1;for(var n=0;n<t.length&&n<e.length;n++)if(!sr(e[n],t[n]))return!1;return!0}function Aa(e,t,n,r,o,a){if(ha=a,ma=t,t.memoizedState=null,t.updateQueue=null,t.lanes=0,fa.current=null===e||null===e.memoizedState?ss:ls,e=n(r,o),ba){a=0;do{if(ba=!1,wa=0,25<=a)throw Error(i(301));a+=1,va=ga=null,t.updateQueue=null,fa.current=us,e=n(r,o)}while(ba)}if(fa.current=as,t=null!==ga&&null!==ga.next,ha=0,va=ga=ma=null,ya=!1,t)throw Error(i(300));return e}function ka(){var e=0!==wa;return wa=0,e}function _a(){var e={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return null===va?ma.memoizedState=va=e:va=va.next=e,va}function Ca(){if(null===ga){var e=ma.alternate;e=null!==e?e.memoizedState:null}else e=ga.next;var t=null===va?ma.memoizedState:va.next;if(null!==t)va=t,ga=e;else{if(null===e)throw Error(i(310));e={memoizedState:(ga=e).memoizedState,baseState:ga.baseState,baseQueue:ga.baseQueue,queue:ga.queue,next:null},null===va?ma.memoizedState=va=e:va=va.next=e}return va}function Oa(e,t){return"function"==typeof t?t(e):t}function Pa(e){var t=Ca(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=ga,o=r.baseQueue,a=n.pending;if(null!==a){if(null!==o){var s=o.next;o.next=a.next,a.next=s}r.baseQueue=o=a,n.pending=null}if(null!==o){a=o.next,r=r.baseState;var l=s=null,u=null,c=a;do{var d=c.lane;if((ha&d)===d)null!==u&&(u=u.next={lane:0,action:c.action,hasEagerState:c.hasEagerState,eagerState:c.eagerState,next:null}),r=c.hasEagerState?c.eagerState:e(r,c.action);else{var f={lane:d,action:c.action,hasEagerState:c.hasEagerState,eagerState:c.eagerState,next:null};null===u?(l=u=f,s=r):u=u.next=f,ma.lanes|=d,Ml|=d}c=c.next}while(null!==c&&c!==a);null===u?s=r:u.next=l,sr(r,t.memoizedState)||(ws=!0),t.memoizedState=r,t.baseState=s,t.baseQueue=u,n.lastRenderedState=r}if(null!==(e=n.interleaved)){o=e;do{a=o.lane,ma.lanes|=a,Ml|=a,o=o.next}while(o!==e)}else null===o&&(n.lanes=0);return[t.memoizedState,n.dispatch]}function Ra(e){var t=Ca(),n=t.queue;if(null===n)throw Error(i(311));n.lastRenderedReducer=e;var r=n.dispatch,o=n.pending,a=t.memoizedState;if(null!==o){n.pending=null;var s=o=o.next;do{a=e(a,s.action),s=s.next}while(s!==o);sr(a,t.memoizedState)||(ws=!0),t.memoizedState=a,null===t.baseQueue&&(t.baseState=a),n.lastRenderedState=a}return[a,r]}function Ta(){}function Fa(e,t){var n=ma,r=Ca(),o=t(),a=!sr(r.memoizedState,o);if(a&&(r.memoizedState=o,ws=!0),r=r.queue,Wa(Ia.bind(null,n,r,e),[e]),r.getSnapshot!==t||a||null!==va&&1&va.memoizedState.tag){if(n.flags|=2048,Ba(9,Na.bind(null,n,r,o,t),void 0,null),null===Rl)throw Error(i(349));30&ha||ja(n,t,o)}return o}function ja(e,t,n){e.flags|=16384,e={getSnapshot:t,value:n},null===(t=ma.updateQueue)?(t={lastEffect:null,stores:null},ma.updateQueue=t,t.stores=[e]):null===(n=t.stores)?t.stores=[e]:n.push(e)}function Na(e,t,n,r){t.value=n,t.getSnapshot=r,La(t)&&Ma(e)}function Ia(e,t,n){return n((function(){La(t)&&Ma(e)}))}function La(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!sr(e,n)}catch(e){return!0}}function Ma(e){var t=Ri(e,1);null!==t&&ru(t,e,1,-1)}function Da(e){var t=_a();return"function"==typeof e&&(e=e()),t.memoizedState=t.baseState=e,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:Oa,lastRenderedState:e},t.queue=e,e=e.dispatch=ns.bind(null,ma,e),[t.memoizedState,e]}function Ba(e,t,n,r){return e={tag:e,create:t,destroy:n,deps:r,next:null},null===(t=ma.updateQueue)?(t={lastEffect:null,stores:null},ma.updateQueue=t,t.lastEffect=e.next=e):null===(n=t.lastEffect)?t.lastEffect=e.next=e:(r=n.next,n.next=e,e.next=r,t.lastEffect=e),e}function Ua(){return Ca().memoizedState}function za(e,t,n,r){var o=_a();ma.flags|=e,o.memoizedState=Ba(1|t,n,void 0,void 0===r?null:r)}function $a(e,t,n,r){var o=Ca();r=void 0===r?null:r;var i=void 0;if(null!==ga){var a=ga.memoizedState;if(i=a.destroy,null!==r&&xa(r,a.deps))return void(o.memoizedState=Ba(t,n,i,r))}ma.flags|=e,o.memoizedState=Ba(1|t,n,i,r)}function Ha(e,t){return za(8390656,8,e,t)}function Wa(e,t){return $a(2048,8,e,t)}function Va(e,t){return $a(4,2,e,t)}function qa(e,t){return $a(4,4,e,t)}function Ka(e,t){return"function"==typeof t?(e=e(),t(e),function(){t(null)}):null!=t?(e=e(),t.current=e,function(){t.current=null}):void 0}function Ga(e,t,n){return n=null!=n?n.concat([e]):null,$a(4,4,Ka.bind(null,t,e),n)}function Ja(){}function Ya(e,t){var n=Ca();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&xa(t,r[1])?r[0]:(n.memoizedState=[e,t],e)}function Xa(e,t){var n=Ca();t=void 0===t?null:t;var r=n.memoizedState;return null!==r&&null!==t&&xa(t,r[1])?r[0]:(e=e(),n.memoizedState=[e,t],e)}function Qa(e,t,n){return 21&ha?(sr(n,t)||(n=mt(),ma.lanes|=n,Ml|=n,e.baseState=!0),t):(e.baseState&&(e.baseState=!1,ws=!0),e.memoizedState=n)}function Za(e,t){var n=bt;bt=0!==n&&4>n?n:4,e(!0);var r=pa.transition;pa.transition={};try{e(!1),t()}finally{bt=n,pa.transition=r}}function es(){return Ca().memoizedState}function ts(e,t,n){var r=nu(e);n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},rs(e)?os(t,n):null!==(n=Pi(e,t,n,r))&&(ru(n,e,r,tu()),is(n,t,r))}function ns(e,t,n){var r=nu(e),o={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(rs(e))os(t,o);else{var i=e.alternate;if(0===e.lanes&&(null===i||0===i.lanes)&&null!==(i=t.lastRenderedReducer))try{var a=t.lastRenderedState,s=i(a,n);if(o.hasEagerState=!0,o.eagerState=s,sr(s,a)){var l=t.interleaved;return null===l?(o.next=o,Oi(t)):(o.next=l.next,l.next=o),void(t.interleaved=o)}}catch(e){}null!==(n=Pi(e,t,o,r))&&(ru(n,e,r,o=tu()),is(n,t,r))}}function rs(e){var t=e.alternate;return e===ma||null!==t&&t===ma}function os(e,t){ba=ya=!0;var n=e.pending;null===n?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function is(e,t,n){if(4194240&n){var r=t.lanes;n|=r&=e.pendingLanes,t.lanes=n,yt(e,n)}}var as={readContext:_i,useCallback:Sa,useContext:Sa,useEffect:Sa,useImperativeHandle:Sa,useInsertionEffect:Sa,useLayoutEffect:Sa,useMemo:Sa,useReducer:Sa,useRef:Sa,useState:Sa,useDebugValue:Sa,useDeferredValue:Sa,useTransition:Sa,useMutableSource:Sa,useSyncExternalStore:Sa,useId:Sa,unstable_isNewReconciler:!1},ss={readContext:_i,useCallback:function(e,t){return _a().memoizedState=[e,void 0===t?null:t],e},useContext:_i,useEffect:Ha,useImperativeHandle:function(e,t,n){return n=null!=n?n.concat([e]):null,za(4194308,4,Ka.bind(null,t,e),n)},useLayoutEffect:function(e,t){return za(4194308,4,e,t)},useInsertionEffect:function(e,t){return za(4,2,e,t)},useMemo:function(e,t){var n=_a();return t=void 0===t?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=_a();return t=void 0!==n?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=ts.bind(null,ma,e),[r.memoizedState,e]},useRef:function(e){return e={current:e},_a().memoizedState=e},useState:Da,useDebugValue:Ja,useDeferredValue:function(e){return _a().memoizedState=e},useTransition:function(){var e=Da(!1),t=e[0];return e=Za.bind(null,e[1]),_a().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=ma,o=_a();if(ii){if(void 0===n)throw Error(i(407));n=n()}else{if(n=t(),null===Rl)throw Error(i(349));30&ha||ja(r,t,n)}o.memoizedState=n;var a={value:n,getSnapshot:t};return o.queue=a,Ha(Ia.bind(null,r,a,e),[e]),r.flags|=2048,Ba(9,Na.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=_a(),t=Rl.identifierPrefix;if(ii){var n=Qo;t=":"+t+"R"+(n=(Xo&~(1<<32-at(Xo)-1)).toString(32)+n),0<(n=wa++)&&(t+="H"+n.toString(32)),t+=":"}else t=":"+t+"r"+(n=Ea++).toString(32)+":";return e.memoizedState=t},unstable_isNewReconciler:!1},ls={readContext:_i,useCallback:Ya,useContext:_i,useEffect:Wa,useImperativeHandle:Ga,useInsertionEffect:Va,useLayoutEffect:qa,useMemo:Xa,useReducer:Pa,useRef:Ua,useState:function(){return Pa(Oa)},useDebugValue:Ja,useDeferredValue:function(e){return Qa(Ca(),ga.memoizedState,e)},useTransition:function(){return[Pa(Oa)[0],Ca().memoizedState]},useMutableSource:Ta,useSyncExternalStore:Fa,useId:es,unstable_isNewReconciler:!1},us={readContext:_i,useCallback:Ya,useContext:_i,useEffect:Wa,useImperativeHandle:Ga,useInsertionEffect:Va,useLayoutEffect:qa,useMemo:Xa,useReducer:Ra,useRef:Ua,useState:function(){return Ra(Oa)},useDebugValue:Ja,useDeferredValue:function(e){var t=Ca();return null===ga?t.memoizedState=e:Qa(t,ga.memoizedState,e)},useTransition:function(){return[Ra(Oa)[0],Ca().memoizedState]},useMutableSource:Ta,useSyncExternalStore:Fa,useId:es,unstable_isNewReconciler:!1};function cs(e,t){try{var n="",r=t;do{n+=z(r),r=r.return}while(r);var o=n}catch(e){o="\nError generating stack: "+e.message+"\n"+e.stack}return{value:e,source:t,stack:o,digest:null}}function ds(e,t,n){return{value:e,source:null,stack:null!=n?n:null,digest:null!=t?t:null}}function fs(e,t){try{console.error(t.value)}catch(e){setTimeout((function(){throw e}))}}var ps="function"==typeof WeakMap?WeakMap:Map;function hs(e,t,n){(n=Ni(-1,n)).tag=3,n.payload={element:null};var r=t.value;return n.callback=function(){Vl||(Vl=!0,ql=r),fs(0,t)},n}function ms(e,t,n){(n=Ni(-1,n)).tag=3;var r=e.type.getDerivedStateFromError;if("function"==typeof r){var o=t.value;n.payload=function(){return r(o)},n.callback=function(){fs(0,t)}}var i=e.stateNode;return null!==i&&"function"==typeof i.componentDidCatch&&(n.callback=function(){fs(0,t),"function"!=typeof r&&(null===Kl?Kl=new Set([this]):Kl.add(this));var e=t.stack;this.componentDidCatch(t.value,{componentStack:null!==e?e:""})}),n}function gs(e,t,n){var r=e.pingCache;if(null===r){r=e.pingCache=new ps;var o=new Set;r.set(t,o)}else void 0===(o=r.get(t))&&(o=new Set,r.set(t,o));o.has(n)||(o.add(n),e=_u.bind(null,e,t,n),t.then(e,e))}function vs(e){do{var t;if((t=13===e.tag)&&(t=null===(t=e.memoizedState)||null!==t.dehydrated),t)return e;e=e.return}while(null!==e);return null}function ys(e,t,n,r,o){return 1&e.mode?(e.flags|=65536,e.lanes=o,e):(e===t?e.flags|=65536:(e.flags|=128,n.flags|=131072,n.flags&=-52805,1===n.tag&&(null===n.alternate?n.tag=17:((t=Ni(-1,1)).tag=2,Ii(n,t,1))),n.lanes|=1),e)}var bs=w.ReactCurrentOwner,ws=!1;function Es(e,t,n,r){t.child=null===e?Qi(t,null,n,r):Xi(t,e.child,n,r)}function Ss(e,t,n,r,o){n=n.render;var i=t.ref;return ki(t,o),r=Aa(e,t,n,r,i,o),n=ka(),null===e||ws?(ii&&n&&ti(t),t.flags|=1,Es(e,t,r,o),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Vs(e,t,o))}function xs(e,t,n,r,o){if(null===e){var i=n.type;return"function"!=typeof i||ju(i)||void 0!==i.defaultProps||null!==n.compare||void 0!==n.defaultProps?((e=Iu(n.type,null,r,t,t.mode,o)).ref=t.ref,e.return=t,t.child=e):(t.tag=15,t.type=i,As(e,t,i,r,o))}if(i=e.child,!(e.lanes&o)){var a=i.memoizedProps;if((n=null!==(n=n.compare)?n:lr)(a,r)&&e.ref===t.ref)return Vs(e,t,o)}return t.flags|=1,(e=Nu(i,r)).ref=t.ref,e.return=t,t.child=e}function As(e,t,n,r,o){if(null!==e){var i=e.memoizedProps;if(lr(i,r)&&e.ref===t.ref){if(ws=!1,t.pendingProps=r=i,!(e.lanes&o))return t.lanes=e.lanes,Vs(e,t,o);131072&e.flags&&(ws=!0)}}return Cs(e,t,n,r,o)}function ks(e,t,n){var r=t.pendingProps,o=r.children,i=null!==e?e.memoizedState:null;if("hidden"===r.mode)if(1&t.mode){if(!(1073741824&n))return e=null!==i?i.baseLanes|n:n,t.lanes=t.childLanes=1073741824,t.memoizedState={baseLanes:e,cachePool:null,transitions:null},t.updateQueue=null,Co(Nl,jl),jl|=e,null;t.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=null!==i?i.baseLanes:n,Co(Nl,jl),jl|=r}else t.memoizedState={baseLanes:0,cachePool:null,transitions:null},Co(Nl,jl),jl|=n;else null!==i?(r=i.baseLanes|n,t.memoizedState=null):r=n,Co(Nl,jl),jl|=r;return Es(e,t,o,n),t.child}function _s(e,t){var n=t.ref;(null===e&&null!==n||null!==e&&e.ref!==n)&&(t.flags|=512,t.flags|=2097152)}function Cs(e,t,n,r,o){var i=jo(n)?To:Po.current;return i=Fo(t,i),ki(t,o),n=Aa(e,t,n,r,i,o),r=ka(),null===e||ws?(ii&&r&&ti(t),t.flags|=1,Es(e,t,n,o),t.child):(t.updateQueue=e.updateQueue,t.flags&=-2053,e.lanes&=~o,Vs(e,t,o))}function Os(e,t,n,r,o){if(jo(n)){var i=!0;Mo(t)}else i=!1;if(ki(t,o),null===t.stateNode)Ws(e,t),Wi(t,n,r),qi(t,n,r,o),r=!0;else if(null===e){var a=t.stateNode,s=t.memoizedProps;a.props=s;var l=a.context,u=n.contextType;u="object"==typeof u&&null!==u?_i(u):Fo(t,u=jo(n)?To:Po.current);var c=n.getDerivedStateFromProps,d="function"==typeof c||"function"==typeof a.getSnapshotBeforeUpdate;d||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(s!==r||l!==u)&&Vi(t,a,r,u),Ti=!1;var f=t.memoizedState;a.state=f,Di(t,r,a,o),l=t.memoizedState,s!==r||f!==l||Ro.current||Ti?("function"==typeof c&&(zi(t,n,c,r),l=t.memoizedState),(s=Ti||Hi(t,n,s,r,f,l,u))?(d||"function"!=typeof a.UNSAFE_componentWillMount&&"function"!=typeof a.componentWillMount||("function"==typeof a.componentWillMount&&a.componentWillMount(),"function"==typeof a.UNSAFE_componentWillMount&&a.UNSAFE_componentWillMount()),"function"==typeof a.componentDidMount&&(t.flags|=4194308)):("function"==typeof a.componentDidMount&&(t.flags|=4194308),t.memoizedProps=r,t.memoizedState=l),a.props=r,a.state=l,a.context=u,r=s):("function"==typeof a.componentDidMount&&(t.flags|=4194308),r=!1)}else{a=t.stateNode,ji(e,t),s=t.memoizedProps,u=t.type===t.elementType?s:vi(t.type,s),a.props=u,d=t.pendingProps,f=a.context,l="object"==typeof(l=n.contextType)&&null!==l?_i(l):Fo(t,l=jo(n)?To:Po.current);var p=n.getDerivedStateFromProps;(c="function"==typeof p||"function"==typeof a.getSnapshotBeforeUpdate)||"function"!=typeof a.UNSAFE_componentWillReceiveProps&&"function"!=typeof a.componentWillReceiveProps||(s!==d||f!==l)&&Vi(t,a,r,l),Ti=!1,f=t.memoizedState,a.state=f,Di(t,r,a,o);var h=t.memoizedState;s!==d||f!==h||Ro.current||Ti?("function"==typeof p&&(zi(t,n,p,r),h=t.memoizedState),(u=Ti||Hi(t,n,u,r,f,h,l)||!1)?(c||"function"!=typeof a.UNSAFE_componentWillUpdate&&"function"!=typeof a.componentWillUpdate||("function"==typeof a.componentWillUpdate&&a.componentWillUpdate(r,h,l),"function"==typeof a.UNSAFE_componentWillUpdate&&a.UNSAFE_componentWillUpdate(r,h,l)),"function"==typeof a.componentDidUpdate&&(t.flags|=4),"function"==typeof a.getSnapshotBeforeUpdate&&(t.flags|=1024)):("function"!=typeof a.componentDidUpdate||s===e.memoizedProps&&f===e.memoizedState||(t.flags|=4),"function"!=typeof a.getSnapshotBeforeUpdate||s===e.memoizedProps&&f===e.memoizedState||(t.flags|=1024),t.memoizedProps=r,t.memoizedState=h),a.props=r,a.state=h,a.context=l,r=u):("function"!=typeof a.componentDidUpdate||s===e.memoizedProps&&f===e.memoizedState||(t.flags|=4),"function"!=typeof a.getSnapshotBeforeUpdate||s===e.memoizedProps&&f===e.memoizedState||(t.flags|=1024),r=!1)}return Ps(e,t,n,r,i,o)}function Ps(e,t,n,r,o,i){_s(e,t);var a=!!(128&t.flags);if(!r&&!a)return o&&Do(t,n,!1),Vs(e,t,i);r=t.stateNode,bs.current=t;var s=a&&"function"!=typeof n.getDerivedStateFromError?null:r.render();return t.flags|=1,null!==e&&a?(t.child=Xi(t,e.child,null,i),t.child=Xi(t,null,s,i)):Es(e,t,s,i),t.memoizedState=r.state,o&&Do(t,n,!0),t.child}function Rs(e){var t=e.stateNode;t.pendingContext?Io(0,t.pendingContext,t.pendingContext!==t.context):t.context&&Io(0,t.context,!1),oa(e,t.containerInfo)}function Ts(e,t,n,r,o){return hi(),mi(o),t.flags|=256,Es(e,t,n,r),t.child}var Fs,js,Ns,Is,Ls={dehydrated:null,treeContext:null,retryLane:0};function Ms(e){return{baseLanes:e,cachePool:null,transitions:null}}function Ds(e,t,n){var r,o=t.pendingProps,a=la.current,s=!1,l=!!(128&t.flags);if((r=l)||(r=(null===e||null!==e.memoizedState)&&!!(2&a)),r?(s=!0,t.flags&=-129):null!==e&&null===e.memoizedState||(a|=1),Co(la,1&a),null===e)return ci(t),null!==(e=t.memoizedState)&&null!==(e=e.dehydrated)?(1&t.mode?"$!"===e.data?t.lanes=8:t.lanes=1073741824:t.lanes=1,null):(l=o.children,e=o.fallback,s?(o=t.mode,s=t.child,l={mode:"hidden",children:l},1&o||null===s?s=Mu(l,o,0,null):(s.childLanes=0,s.pendingProps=l),e=Lu(e,o,n,null),s.return=t,e.return=t,s.sibling=e,t.child=s,t.child.memoizedState=Ms(n),t.memoizedState=Ls,e):Bs(t,l));if(null!==(a=e.memoizedState)&&null!==(r=a.dehydrated))return function(e,t,n,r,o,a,s){if(n)return 256&t.flags?(t.flags&=-257,Us(e,t,s,r=ds(Error(i(422))))):null!==t.memoizedState?(t.child=e.child,t.flags|=128,null):(a=r.fallback,o=t.mode,r=Mu({mode:"visible",children:r.children},o,0,null),(a=Lu(a,o,s,null)).flags|=2,r.return=t,a.return=t,r.sibling=a,t.child=r,1&t.mode&&Xi(t,e.child,null,s),t.child.memoizedState=Ms(s),t.memoizedState=Ls,a);if(!(1&t.mode))return Us(e,t,s,null);if("$!"===o.data){if(r=o.nextSibling&&o.nextSibling.dataset)var l=r.dgst;return r=l,Us(e,t,s,r=ds(a=Error(i(419)),r,void 0))}if(l=!!(s&e.childLanes),ws||l){if(null!==(r=Rl)){switch(s&-s){case 4:o=2;break;case 16:o=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:o=32;break;case 536870912:o=268435456;break;default:o=0}0!==(o=o&(r.suspendedLanes|s)?0:o)&&o!==a.retryLane&&(a.retryLane=o,Ri(e,o),ru(r,e,o,-1))}return gu(),Us(e,t,s,r=ds(Error(i(421))))}return"$?"===o.data?(t.flags|=128,t.child=e.child,t=Ou.bind(null,e),o._reactRetry=t,null):(e=a.treeContext,oi=uo(o.nextSibling),ri=t,ii=!0,ai=null,null!==e&&(Go[Jo++]=Xo,Go[Jo++]=Qo,Go[Jo++]=Yo,Xo=e.id,Qo=e.overflow,Yo=t),(t=Bs(t,r.children)).flags|=4096,t)}(e,t,l,o,r,a,n);if(s){s=o.fallback,l=t.mode,r=(a=e.child).sibling;var u={mode:"hidden",children:o.children};return 1&l||t.child===a?(o=Nu(a,u)).subtreeFlags=14680064&a.subtreeFlags:((o=t.child).childLanes=0,o.pendingProps=u,t.deletions=null),null!==r?s=Nu(r,s):(s=Lu(s,l,n,null)).flags|=2,s.return=t,o.return=t,o.sibling=s,t.child=o,o=s,s=t.child,l=null===(l=e.child.memoizedState)?Ms(n):{baseLanes:l.baseLanes|n,cachePool:null,transitions:l.transitions},s.memoizedState=l,s.childLanes=e.childLanes&~n,t.memoizedState=Ls,o}return e=(s=e.child).sibling,o=Nu(s,{mode:"visible",children:o.children}),!(1&t.mode)&&(o.lanes=n),o.return=t,o.sibling=null,null!==e&&(null===(n=t.deletions)?(t.deletions=[e],t.flags|=16):n.push(e)),t.child=o,t.memoizedState=null,o}function Bs(e,t){return(t=Mu({mode:"visible",children:t},e.mode,0,null)).return=e,e.child=t}function Us(e,t,n,r){return null!==r&&mi(r),Xi(t,e.child,null,n),(e=Bs(t,t.pendingProps.children)).flags|=2,t.memoizedState=null,e}function zs(e,t,n){e.lanes|=t;var r=e.alternate;null!==r&&(r.lanes|=t),Ai(e.return,t,n)}function $s(e,t,n,r,o){var i=e.memoizedState;null===i?e.memoizedState={isBackwards:t,rendering:null,renderingStartTime:0,last:r,tail:n,tailMode:o}:(i.isBackwards=t,i.rendering=null,i.renderingStartTime=0,i.last=r,i.tail=n,i.tailMode=o)}function Hs(e,t,n){var r=t.pendingProps,o=r.revealOrder,i=r.tail;if(Es(e,t,r.children,n),2&(r=la.current))r=1&r|2,t.flags|=128;else{if(null!==e&&128&e.flags)e:for(e=t.child;null!==e;){if(13===e.tag)null!==e.memoizedState&&zs(e,n,t);else if(19===e.tag)zs(e,n,t);else if(null!==e.child){e.child.return=e,e=e.child;continue}if(e===t)break e;for(;null===e.sibling;){if(null===e.return||e.return===t)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}r&=1}if(Co(la,r),1&t.mode)switch(o){case"forwards":for(n=t.child,o=null;null!==n;)null!==(e=n.alternate)&&null===ua(e)&&(o=n),n=n.sibling;null===(n=o)?(o=t.child,t.child=null):(o=n.sibling,n.sibling=null),$s(t,!1,o,n,i);break;case"backwards":for(n=null,o=t.child,t.child=null;null!==o;){if(null!==(e=o.alternate)&&null===ua(e)){t.child=o;break}e=o.sibling,o.sibling=n,n=o,o=e}$s(t,!0,n,null,i);break;case"together":$s(t,!1,null,null,void 0);break;default:t.memoizedState=null}else t.memoizedState=null;return t.child}function Ws(e,t){!(1&t.mode)&&null!==e&&(e.alternate=null,t.alternate=null,t.flags|=2)}function Vs(e,t,n){if(null!==e&&(t.dependencies=e.dependencies),Ml|=t.lanes,!(n&t.childLanes))return null;if(null!==e&&t.child!==e.child)throw Error(i(153));if(null!==t.child){for(n=Nu(e=t.child,e.pendingProps),t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,(n=n.sibling=Nu(e,e.pendingProps)).return=t;n.sibling=null}return t.child}function qs(e,t){if(!ii)switch(e.tailMode){case"hidden":t=e.tail;for(var n=null;null!==t;)null!==t.alternate&&(n=t),t=t.sibling;null===n?e.tail=null:n.sibling=null;break;case"collapsed":n=e.tail;for(var r=null;null!==n;)null!==n.alternate&&(r=n),n=n.sibling;null===r?t||null===e.tail?e.tail=null:e.tail.sibling=null:r.sibling=null}}function Ks(e){var t=null!==e.alternate&&e.alternate.child===e.child,n=0,r=0;if(t)for(var o=e.child;null!==o;)n|=o.lanes|o.childLanes,r|=14680064&o.subtreeFlags,r|=14680064&o.flags,o.return=e,o=o.sibling;else for(o=e.child;null!==o;)n|=o.lanes|o.childLanes,r|=o.subtreeFlags,r|=o.flags,o.return=e,o=o.sibling;return e.subtreeFlags|=r,e.childLanes=n,t}function Gs(e,t,n){var r=t.pendingProps;switch(ni(t),t.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ks(t),null;case 1:case 17:return jo(t.type)&&No(),Ks(t),null;case 3:return r=t.stateNode,ia(),_o(Ro),_o(Po),da(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),null!==e&&null!==e.child||(fi(t)?t.flags|=4:null===e||e.memoizedState.isDehydrated&&!(256&t.flags)||(t.flags|=1024,null!==ai&&(su(ai),ai=null))),js(e,t),Ks(t),null;case 5:sa(t);var o=ra(na.current);if(n=t.type,null!==e&&null!=t.stateNode)Ns(e,t,n,r,o),e.ref!==t.ref&&(t.flags|=512,t.flags|=2097152);else{if(!r){if(null===t.stateNode)throw Error(i(166));return Ks(t),null}if(e=ra(ea.current),fi(t)){r=t.stateNode,n=t.type;var a=t.memoizedProps;switch(r[po]=t,r[ho]=a,e=!!(1&t.mode),n){case"dialog":Br("cancel",r),Br("close",r);break;case"iframe":case"object":case"embed":Br("load",r);break;case"video":case"audio":for(o=0;o<Ir.length;o++)Br(Ir[o],r);break;case"source":Br("error",r);break;case"img":case"image":case"link":Br("error",r),Br("load",r);break;case"details":Br("toggle",r);break;case"input":Y(r,a),Br("invalid",r);break;case"select":r._wrapperState={wasMultiple:!!a.multiple},Br("invalid",r);break;case"textarea":oe(r,a),Br("invalid",r)}for(var l in ye(n,a),o=null,a)if(a.hasOwnProperty(l)){var u=a[l];"children"===l?"string"==typeof u?r.textContent!==u&&(!0!==a.suppressHydrationWarning&&Qr(r.textContent,u,e),o=["children",u]):"number"==typeof u&&r.textContent!==""+u&&(!0!==a.suppressHydrationWarning&&Qr(r.textContent,u,e),o=["children",""+u]):s.hasOwnProperty(l)&&null!=u&&"onScroll"===l&&Br("scroll",r)}switch(n){case"input":q(r),Z(r,a,!0);break;case"textarea":q(r),ae(r);break;case"select":case"option":break;default:"function"==typeof a.onClick&&(r.onclick=Zr)}r=o,t.updateQueue=r,null!==r&&(t.flags|=4)}else{l=9===o.nodeType?o:o.ownerDocument,"http://www.w3.org/1999/xhtml"===e&&(e=se(n)),"http://www.w3.org/1999/xhtml"===e?"script"===n?((e=l.createElement("div")).innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):"string"==typeof r.is?e=l.createElement(n,{is:r.is}):(e=l.createElement(n),"select"===n&&(l=e,r.multiple?l.multiple=!0:r.size&&(l.size=r.size))):e=l.createElementNS(e,n),e[po]=t,e[ho]=r,Fs(e,t,!1,!1),t.stateNode=e;e:{switch(l=be(n,r),n){case"dialog":Br("cancel",e),Br("close",e),o=r;break;case"iframe":case"object":case"embed":Br("load",e),o=r;break;case"video":case"audio":for(o=0;o<Ir.length;o++)Br(Ir[o],e);o=r;break;case"source":Br("error",e),o=r;break;case"img":case"image":case"link":Br("error",e),Br("load",e),o=r;break;case"details":Br("toggle",e),o=r;break;case"input":Y(e,r),o=J(e,r),Br("invalid",e);break;case"option":default:o=r;break;case"select":e._wrapperState={wasMultiple:!!r.multiple},o=M({},r,{value:void 0}),Br("invalid",e);break;case"textarea":oe(e,r),o=re(e,r),Br("invalid",e)}for(a in ye(n,o),u=o)if(u.hasOwnProperty(a)){var c=u[a];"style"===a?ge(e,c):"dangerouslySetInnerHTML"===a?null!=(c=c?c.__html:void 0)&&de(e,c):"children"===a?"string"==typeof c?("textarea"!==n||""!==c)&&fe(e,c):"number"==typeof c&&fe(e,""+c):"suppressContentEditableWarning"!==a&&"suppressHydrationWarning"!==a&&"autoFocus"!==a&&(s.hasOwnProperty(a)?null!=c&&"onScroll"===a&&Br("scroll",e):null!=c&&b(e,a,c,l))}switch(n){case"input":q(e),Z(e,r,!1);break;case"textarea":q(e),ae(e);break;case"option":null!=r.value&&e.setAttribute("value",""+W(r.value));break;case"select":e.multiple=!!r.multiple,null!=(a=r.value)?ne(e,!!r.multiple,a,!1):null!=r.defaultValue&&ne(e,!!r.multiple,r.defaultValue,!0);break;default:"function"==typeof o.onClick&&(e.onclick=Zr)}switch(n){case"button":case"input":case"select":case"textarea":r=!!r.autoFocus;break e;case"img":r=!0;break e;default:r=!1}}r&&(t.flags|=4)}null!==t.ref&&(t.flags|=512,t.flags|=2097152)}return Ks(t),null;case 6:if(e&&null!=t.stateNode)Is(e,t,e.memoizedProps,r);else{if("string"!=typeof r&&null===t.stateNode)throw Error(i(166));if(n=ra(na.current),ra(ea.current),fi(t)){if(r=t.stateNode,n=t.memoizedProps,r[po]=t,(a=r.nodeValue!==n)&&null!==(e=ri))switch(e.tag){case 3:Qr(r.nodeValue,n,!!(1&e.mode));break;case 5:!0!==e.memoizedProps.suppressHydrationWarning&&Qr(r.nodeValue,n,!!(1&e.mode))}a&&(t.flags|=4)}else(r=(9===n.nodeType?n:n.ownerDocument).createTextNode(r))[po]=t,t.stateNode=r}return Ks(t),null;case 13:if(_o(la),r=t.memoizedState,null===e||null!==e.memoizedState&&null!==e.memoizedState.dehydrated){if(ii&&null!==oi&&1&t.mode&&!(128&t.flags))pi(),hi(),t.flags|=98560,a=!1;else if(a=fi(t),null!==r&&null!==r.dehydrated){if(null===e){if(!a)throw Error(i(318));if(!(a=null!==(a=t.memoizedState)?a.dehydrated:null))throw Error(i(317));a[po]=t}else hi(),!(128&t.flags)&&(t.memoizedState=null),t.flags|=4;Ks(t),a=!1}else null!==ai&&(su(ai),ai=null),a=!0;if(!a)return 65536&t.flags?t:null}return 128&t.flags?(t.lanes=n,t):((r=null!==r)!=(null!==e&&null!==e.memoizedState)&&r&&(t.child.flags|=8192,1&t.mode&&(null===e||1&la.current?0===Il&&(Il=3):gu())),null!==t.updateQueue&&(t.flags|=4),Ks(t),null);case 4:return ia(),js(e,t),null===e&&$r(t.stateNode.containerInfo),Ks(t),null;case 10:return xi(t.type._context),Ks(t),null;case 19:if(_o(la),null===(a=t.memoizedState))return Ks(t),null;if(r=!!(128&t.flags),null===(l=a.rendering))if(r)qs(a,!1);else{if(0!==Il||null!==e&&128&e.flags)for(e=t.child;null!==e;){if(null!==(l=ua(e))){for(t.flags|=128,qs(a,!1),null!==(r=l.updateQueue)&&(t.updateQueue=r,t.flags|=4),t.subtreeFlags=0,r=n,n=t.child;null!==n;)e=r,(a=n).flags&=14680066,null===(l=a.alternate)?(a.childLanes=0,a.lanes=e,a.child=null,a.subtreeFlags=0,a.memoizedProps=null,a.memoizedState=null,a.updateQueue=null,a.dependencies=null,a.stateNode=null):(a.childLanes=l.childLanes,a.lanes=l.lanes,a.child=l.child,a.subtreeFlags=0,a.deletions=null,a.memoizedProps=l.memoizedProps,a.memoizedState=l.memoizedState,a.updateQueue=l.updateQueue,a.type=l.type,e=l.dependencies,a.dependencies=null===e?null:{lanes:e.lanes,firstContext:e.firstContext}),n=n.sibling;return Co(la,1&la.current|2),t.child}e=e.sibling}null!==a.tail&&Xe()>Hl&&(t.flags|=128,r=!0,qs(a,!1),t.lanes=4194304)}else{if(!r)if(null!==(e=ua(l))){if(t.flags|=128,r=!0,null!==(n=e.updateQueue)&&(t.updateQueue=n,t.flags|=4),qs(a,!0),null===a.tail&&"hidden"===a.tailMode&&!l.alternate&&!ii)return Ks(t),null}else 2*Xe()-a.renderingStartTime>Hl&&1073741824!==n&&(t.flags|=128,r=!0,qs(a,!1),t.lanes=4194304);a.isBackwards?(l.sibling=t.child,t.child=l):(null!==(n=a.last)?n.sibling=l:t.child=l,a.last=l)}return null!==a.tail?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=Xe(),t.sibling=null,n=la.current,Co(la,r?1&n|2:1&n),t):(Ks(t),null);case 22:case 23:return fu(),r=null!==t.memoizedState,null!==e&&null!==e.memoizedState!==r&&(t.flags|=8192),r&&1&t.mode?!!(1073741824&jl)&&(Ks(t),6&t.subtreeFlags&&(t.flags|=8192)):Ks(t),null;case 24:case 25:return null}throw Error(i(156,t.tag))}function Js(e,t){switch(ni(t),t.tag){case 1:return jo(t.type)&&No(),65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 3:return ia(),_o(Ro),_o(Po),da(),65536&(e=t.flags)&&!(128&e)?(t.flags=-65537&e|128,t):null;case 5:return sa(t),null;case 13:if(_o(la),null!==(e=t.memoizedState)&&null!==e.dehydrated){if(null===t.alternate)throw Error(i(340));hi()}return 65536&(e=t.flags)?(t.flags=-65537&e|128,t):null;case 19:return _o(la),null;case 4:return ia(),null;case 10:return xi(t.type._context),null;case 22:case 23:return fu(),null;default:return null}}Fs=function(e,t){for(var n=t.child;null!==n;){if(5===n.tag||6===n.tag)e.appendChild(n.stateNode);else if(4!==n.tag&&null!==n.child){n.child.return=n,n=n.child;continue}if(n===t)break;for(;null===n.sibling;){if(null===n.return||n.return===t)return;n=n.return}n.sibling.return=n.return,n=n.sibling}},js=function(){},Ns=function(e,t,n,r){var o=e.memoizedProps;if(o!==r){e=t.stateNode,ra(ea.current);var i,a=null;switch(n){case"input":o=J(e,o),r=J(e,r),a=[];break;case"select":o=M({},o,{value:void 0}),r=M({},r,{value:void 0}),a=[];break;case"textarea":o=re(e,o),r=re(e,r),a=[];break;default:"function"!=typeof o.onClick&&"function"==typeof r.onClick&&(e.onclick=Zr)}for(c in ye(n,r),n=null,o)if(!r.hasOwnProperty(c)&&o.hasOwnProperty(c)&&null!=o[c])if("style"===c){var l=o[c];for(i in l)l.hasOwnProperty(i)&&(n||(n={}),n[i]="")}else"dangerouslySetInnerHTML"!==c&&"children"!==c&&"suppressContentEditableWarning"!==c&&"suppressHydrationWarning"!==c&&"autoFocus"!==c&&(s.hasOwnProperty(c)?a||(a=[]):(a=a||[]).push(c,null));for(c in r){var u=r[c];if(l=null!=o?o[c]:void 0,r.hasOwnProperty(c)&&u!==l&&(null!=u||null!=l))if("style"===c)if(l){for(i in l)!l.hasOwnProperty(i)||u&&u.hasOwnProperty(i)||(n||(n={}),n[i]="");for(i in u)u.hasOwnProperty(i)&&l[i]!==u[i]&&(n||(n={}),n[i]=u[i])}else n||(a||(a=[]),a.push(c,n)),n=u;else"dangerouslySetInnerHTML"===c?(u=u?u.__html:void 0,l=l?l.__html:void 0,null!=u&&l!==u&&(a=a||[]).push(c,u)):"children"===c?"string"!=typeof u&&"number"!=typeof u||(a=a||[]).push(c,""+u):"suppressContentEditableWarning"!==c&&"suppressHydrationWarning"!==c&&(s.hasOwnProperty(c)?(null!=u&&"onScroll"===c&&Br("scroll",e),a||l===u||(a=[])):(a=a||[]).push(c,u))}n&&(a=a||[]).push("style",n);var c=a;(t.updateQueue=c)&&(t.flags|=4)}},Is=function(e,t,n,r){n!==r&&(t.flags|=4)};var Ys=!1,Xs=!1,Qs="function"==typeof WeakSet?WeakSet:Set,Zs=null;function el(e,t){var n=e.ref;if(null!==n)if("function"==typeof n)try{n(null)}catch(n){ku(e,t,n)}else n.current=null}function tl(e,t,n){try{n()}catch(n){ku(e,t,n)}}var nl=!1;function rl(e,t,n){var r=t.updateQueue;if(null!==(r=null!==r?r.lastEffect:null)){var o=r=r.next;do{if((o.tag&e)===e){var i=o.destroy;o.destroy=void 0,void 0!==i&&tl(t,n,i)}o=o.next}while(o!==r)}}function ol(e,t){if(null!==(t=null!==(t=t.updateQueue)?t.lastEffect:null)){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function il(e){var t=e.ref;if(null!==t){var n=e.stateNode;e.tag,e=n,"function"==typeof t?t(e):t.current=e}}function al(e){var t=e.alternate;null!==t&&(e.alternate=null,al(t)),e.child=null,e.deletions=null,e.sibling=null,5===e.tag&&null!==(t=e.stateNode)&&(delete t[po],delete t[ho],delete t[go],delete t[vo],delete t[yo]),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function sl(e){return 5===e.tag||3===e.tag||4===e.tag}function ll(e){e:for(;;){for(;null===e.sibling;){if(null===e.return||sl(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;5!==e.tag&&6!==e.tag&&18!==e.tag;){if(2&e.flags)continue e;if(null===e.child||4===e.tag)continue e;e.child.return=e,e=e.child}if(!(2&e.flags))return e.stateNode}}function ul(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?8===n.nodeType?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(8===n.nodeType?(t=n.parentNode).insertBefore(e,n):(t=n).appendChild(e),null!=(n=n._reactRootContainer)||null!==t.onclick||(t.onclick=Zr));else if(4!==r&&null!==(e=e.child))for(ul(e,t,n),e=e.sibling;null!==e;)ul(e,t,n),e=e.sibling}function cl(e,t,n){var r=e.tag;if(5===r||6===r)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(4!==r&&null!==(e=e.child))for(cl(e,t,n),e=e.sibling;null!==e;)cl(e,t,n),e=e.sibling}var dl=null,fl=!1;function pl(e,t,n){for(n=n.child;null!==n;)hl(e,t,n),n=n.sibling}function hl(e,t,n){if(it&&"function"==typeof it.onCommitFiberUnmount)try{it.onCommitFiberUnmount(ot,n)}catch(e){}switch(n.tag){case 5:Xs||el(n,t);case 6:var r=dl,o=fl;dl=null,pl(e,t,n),fl=o,null!==(dl=r)&&(fl?(e=dl,n=n.stateNode,8===e.nodeType?e.parentNode.removeChild(n):e.removeChild(n)):dl.removeChild(n.stateNode));break;case 18:null!==dl&&(fl?(e=dl,n=n.stateNode,8===e.nodeType?lo(e.parentNode,n):1===e.nodeType&&lo(e,n),$t(e)):lo(dl,n.stateNode));break;case 4:r=dl,o=fl,dl=n.stateNode.containerInfo,fl=!0,pl(e,t,n),dl=r,fl=o;break;case 0:case 11:case 14:case 15:if(!Xs&&null!==(r=n.updateQueue)&&null!==(r=r.lastEffect)){o=r=r.next;do{var i=o,a=i.destroy;i=i.tag,void 0!==a&&(2&i||4&i)&&tl(n,t,a),o=o.next}while(o!==r)}pl(e,t,n);break;case 1:if(!Xs&&(el(n,t),"function"==typeof(r=n.stateNode).componentWillUnmount))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(e){ku(n,t,e)}pl(e,t,n);break;case 21:pl(e,t,n);break;case 22:1&n.mode?(Xs=(r=Xs)||null!==n.memoizedState,pl(e,t,n),Xs=r):pl(e,t,n);break;default:pl(e,t,n)}}function ml(e){var t=e.updateQueue;if(null!==t){e.updateQueue=null;var n=e.stateNode;null===n&&(n=e.stateNode=new Qs),t.forEach((function(t){var r=Pu.bind(null,e,t);n.has(t)||(n.add(t),t.then(r,r))}))}}function gl(e,t){var n=t.deletions;if(null!==n)for(var r=0;r<n.length;r++){var o=n[r];try{var a=e,s=t,l=s;e:for(;null!==l;){switch(l.tag){case 5:dl=l.stateNode,fl=!1;break e;case 3:case 4:dl=l.stateNode.containerInfo,fl=!0;break e}l=l.return}if(null===dl)throw Error(i(160));hl(a,s,o),dl=null,fl=!1;var u=o.alternate;null!==u&&(u.return=null),o.return=null}catch(e){ku(o,t,e)}}if(12854&t.subtreeFlags)for(t=t.child;null!==t;)vl(t,e),t=t.sibling}function vl(e,t){var n=e.alternate,r=e.flags;switch(e.tag){case 0:case 11:case 14:case 15:if(gl(t,e),yl(e),4&r){try{rl(3,e,e.return),ol(3,e)}catch(t){ku(e,e.return,t)}try{rl(5,e,e.return)}catch(t){ku(e,e.return,t)}}break;case 1:gl(t,e),yl(e),512&r&&null!==n&&el(n,n.return);break;case 5:if(gl(t,e),yl(e),512&r&&null!==n&&el(n,n.return),32&e.flags){var o=e.stateNode;try{fe(o,"")}catch(t){ku(e,e.return,t)}}if(4&r&&null!=(o=e.stateNode)){var a=e.memoizedProps,s=null!==n?n.memoizedProps:a,l=e.type,u=e.updateQueue;if(e.updateQueue=null,null!==u)try{"input"===l&&"radio"===a.type&&null!=a.name&&X(o,a),be(l,s);var c=be(l,a);for(s=0;s<u.length;s+=2){var d=u[s],f=u[s+1];"style"===d?ge(o,f):"dangerouslySetInnerHTML"===d?de(o,f):"children"===d?fe(o,f):b(o,d,f,c)}switch(l){case"input":Q(o,a);break;case"textarea":ie(o,a);break;case"select":var p=o._wrapperState.wasMultiple;o._wrapperState.wasMultiple=!!a.multiple;var h=a.value;null!=h?ne(o,!!a.multiple,h,!1):p!==!!a.multiple&&(null!=a.defaultValue?ne(o,!!a.multiple,a.defaultValue,!0):ne(o,!!a.multiple,a.multiple?[]:"",!1))}o[ho]=a}catch(t){ku(e,e.return,t)}}break;case 6:if(gl(t,e),yl(e),4&r){if(null===e.stateNode)throw Error(i(162));o=e.stateNode,a=e.memoizedProps;try{o.nodeValue=a}catch(t){ku(e,e.return,t)}}break;case 3:if(gl(t,e),yl(e),4&r&&null!==n&&n.memoizedState.isDehydrated)try{$t(t.containerInfo)}catch(t){ku(e,e.return,t)}break;case 4:default:gl(t,e),yl(e);break;case 13:gl(t,e),yl(e),8192&(o=e.child).flags&&(a=null!==o.memoizedState,o.stateNode.isHidden=a,!a||null!==o.alternate&&null!==o.alternate.memoizedState||($l=Xe())),4&r&&ml(e);break;case 22:if(d=null!==n&&null!==n.memoizedState,1&e.mode?(Xs=(c=Xs)||d,gl(t,e),Xs=c):gl(t,e),yl(e),8192&r){if(c=null!==e.memoizedState,(e.stateNode.isHidden=c)&&!d&&1&e.mode)for(Zs=e,d=e.child;null!==d;){for(f=Zs=d;null!==Zs;){switch(h=(p=Zs).child,p.tag){case 0:case 11:case 14:case 15:rl(4,p,p.return);break;case 1:el(p,p.return);var m=p.stateNode;if("function"==typeof m.componentWillUnmount){r=p,n=p.return;try{t=r,m.props=t.memoizedProps,m.state=t.memoizedState,m.componentWillUnmount()}catch(e){ku(r,n,e)}}break;case 5:el(p,p.return);break;case 22:if(null!==p.memoizedState){Sl(f);continue}}null!==h?(h.return=p,Zs=h):Sl(f)}d=d.sibling}e:for(d=null,f=e;;){if(5===f.tag){if(null===d){d=f;try{o=f.stateNode,c?"function"==typeof(a=o.style).setProperty?a.setProperty("display","none","important"):a.display="none":(l=f.stateNode,s=null!=(u=f.memoizedProps.style)&&u.hasOwnProperty("display")?u.display:null,l.style.display=me("display",s))}catch(t){ku(e,e.return,t)}}}else if(6===f.tag){if(null===d)try{f.stateNode.nodeValue=c?"":f.memoizedProps}catch(t){ku(e,e.return,t)}}else if((22!==f.tag&&23!==f.tag||null===f.memoizedState||f===e)&&null!==f.child){f.child.return=f,f=f.child;continue}if(f===e)break e;for(;null===f.sibling;){if(null===f.return||f.return===e)break e;d===f&&(d=null),f=f.return}d===f&&(d=null),f.sibling.return=f.return,f=f.sibling}}break;case 19:gl(t,e),yl(e),4&r&&ml(e);case 21:}}function yl(e){var t=e.flags;if(2&t){try{e:{for(var n=e.return;null!==n;){if(sl(n)){var r=n;break e}n=n.return}throw Error(i(160))}switch(r.tag){case 5:var o=r.stateNode;32&r.flags&&(fe(o,""),r.flags&=-33),cl(e,ll(e),o);break;case 3:case 4:var a=r.stateNode.containerInfo;ul(e,ll(e),a);break;default:throw Error(i(161))}}catch(t){ku(e,e.return,t)}e.flags&=-3}4096&t&&(e.flags&=-4097)}function bl(e,t,n){Zs=e,wl(e,t,n)}function wl(e,t,n){for(var r=!!(1&e.mode);null!==Zs;){var o=Zs,i=o.child;if(22===o.tag&&r){var a=null!==o.memoizedState||Ys;if(!a){var s=o.alternate,l=null!==s&&null!==s.memoizedState||Xs;s=Ys;var u=Xs;if(Ys=a,(Xs=l)&&!u)for(Zs=o;null!==Zs;)l=(a=Zs).child,22===a.tag&&null!==a.memoizedState?xl(o):null!==l?(l.return=a,Zs=l):xl(o);for(;null!==i;)Zs=i,wl(i,t,n),i=i.sibling;Zs=o,Ys=s,Xs=u}El(e)}else 8772&o.subtreeFlags&&null!==i?(i.return=o,Zs=i):El(e)}}function El(e){for(;null!==Zs;){var t=Zs;if(8772&t.flags){var n=t.alternate;try{if(8772&t.flags)switch(t.tag){case 0:case 11:case 15:Xs||ol(5,t);break;case 1:var r=t.stateNode;if(4&t.flags&&!Xs)if(null===n)r.componentDidMount();else{var o=t.elementType===t.type?n.memoizedProps:vi(t.type,n.memoizedProps);r.componentDidUpdate(o,n.memoizedState,r.__reactInternalSnapshotBeforeUpdate)}var a=t.updateQueue;null!==a&&Bi(t,a,r);break;case 3:var s=t.updateQueue;if(null!==s){if(n=null,null!==t.child)switch(t.child.tag){case 5:case 1:n=t.child.stateNode}Bi(t,s,n)}break;case 5:var l=t.stateNode;if(null===n&&4&t.flags){n=l;var u=t.memoizedProps;switch(t.type){case"button":case"input":case"select":case"textarea":u.autoFocus&&n.focus();break;case"img":u.src&&(n.src=u.src)}}break;case 6:case 4:case 12:case 19:case 17:case 21:case 22:case 23:case 25:break;case 13:if(null===t.memoizedState){var c=t.alternate;if(null!==c){var d=c.memoizedState;if(null!==d){var f=d.dehydrated;null!==f&&$t(f)}}}break;default:throw Error(i(163))}Xs||512&t.flags&&il(t)}catch(e){ku(t,t.return,e)}}if(t===e){Zs=null;break}if(null!==(n=t.sibling)){n.return=t.return,Zs=n;break}Zs=t.return}}function Sl(e){for(;null!==Zs;){var t=Zs;if(t===e){Zs=null;break}var n=t.sibling;if(null!==n){n.return=t.return,Zs=n;break}Zs=t.return}}function xl(e){for(;null!==Zs;){var t=Zs;try{switch(t.tag){case 0:case 11:case 15:var n=t.return;try{ol(4,t)}catch(e){ku(t,n,e)}break;case 1:var r=t.stateNode;if("function"==typeof r.componentDidMount){var o=t.return;try{r.componentDidMount()}catch(e){ku(t,o,e)}}var i=t.return;try{il(t)}catch(e){ku(t,i,e)}break;case 5:var a=t.return;try{il(t)}catch(e){ku(t,a,e)}}}catch(e){ku(t,t.return,e)}if(t===e){Zs=null;break}var s=t.sibling;if(null!==s){s.return=t.return,Zs=s;break}Zs=t.return}}var Al,kl=Math.ceil,_l=w.ReactCurrentDispatcher,Cl=w.ReactCurrentOwner,Ol=w.ReactCurrentBatchConfig,Pl=0,Rl=null,Tl=null,Fl=0,jl=0,Nl=ko(0),Il=0,Ll=null,Ml=0,Dl=0,Bl=0,Ul=null,zl=null,$l=0,Hl=1/0,Wl=null,Vl=!1,ql=null,Kl=null,Gl=!1,Jl=null,Yl=0,Xl=0,Ql=null,Zl=-1,eu=0;function tu(){return 6&Pl?Xe():-1!==Zl?Zl:Zl=Xe()}function nu(e){return 1&e.mode?2&Pl&&0!==Fl?Fl&-Fl:null!==gi.transition?(0===eu&&(eu=mt()),eu):0!==(e=bt)?e:e=void 0===(e=window.event)?16:Yt(e.type):1}function ru(e,t,n,r){if(50<Xl)throw Xl=0,Ql=null,Error(i(185));vt(e,n,r),2&Pl&&e===Rl||(e===Rl&&(!(2&Pl)&&(Dl|=n),4===Il&&lu(e,Fl)),ou(e,r),1===n&&0===Pl&&!(1&t.mode)&&(Hl=Xe()+500,Uo&&Ho()))}function ou(e,t){var n=e.callbackNode;!function(e,t){for(var n=e.suspendedLanes,r=e.pingedLanes,o=e.expirationTimes,i=e.pendingLanes;0<i;){var a=31-at(i),s=1<<a,l=o[a];-1===l?s&n&&!(s&r)||(o[a]=pt(s,t)):l<=t&&(e.expiredLanes|=s),i&=~s}}(e,t);var r=ft(e,e===Rl?Fl:0);if(0===r)null!==n&&Ge(n),e.callbackNode=null,e.callbackPriority=0;else if(t=r&-r,e.callbackPriority!==t){if(null!=n&&Ge(n),1===t)0===e.tag?function(e){Uo=!0,$o(e)}(uu.bind(null,e)):$o(uu.bind(null,e)),ao((function(){!(6&Pl)&&Ho()})),n=null;else{switch(wt(r)){case 1:n=Ze;break;case 4:n=et;break;case 16:default:n=tt;break;case 536870912:n=rt}n=Ru(n,iu.bind(null,e))}e.callbackPriority=t,e.callbackNode=n}}function iu(e,t){if(Zl=-1,eu=0,6&Pl)throw Error(i(327));var n=e.callbackNode;if(xu()&&e.callbackNode!==n)return null;var r=ft(e,e===Rl?Fl:0);if(0===r)return null;if(30&r||r&e.expiredLanes||t)t=vu(e,r);else{t=r;var o=Pl;Pl|=2;var a=mu();for(Rl===e&&Fl===t||(Wl=null,Hl=Xe()+500,pu(e,t));;)try{bu();break}catch(t){hu(e,t)}Si(),_l.current=a,Pl=o,null!==Tl?t=0:(Rl=null,Fl=0,t=Il)}if(0!==t){if(2===t&&0!==(o=ht(e))&&(r=o,t=au(e,o)),1===t)throw n=Ll,pu(e,0),lu(e,r),ou(e,Xe()),n;if(6===t)lu(e,r);else{if(o=e.current.alternate,!(30&r||function(e){for(var t=e;;){if(16384&t.flags){var n=t.updateQueue;if(null!==n&&null!==(n=n.stores))for(var r=0;r<n.length;r++){var o=n[r],i=o.getSnapshot;o=o.value;try{if(!sr(i(),o))return!1}catch(e){return!1}}}if(n=t.child,16384&t.subtreeFlags&&null!==n)n.return=t,t=n;else{if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return!0;t=t.return}t.sibling.return=t.return,t=t.sibling}}return!0}(o)||(t=vu(e,r),2===t&&(a=ht(e),0!==a&&(r=a,t=au(e,a))),1!==t)))throw n=Ll,pu(e,0),lu(e,r),ou(e,Xe()),n;switch(e.finishedWork=o,e.finishedLanes=r,t){case 0:case 1:throw Error(i(345));case 2:case 5:Su(e,zl,Wl);break;case 3:if(lu(e,r),(130023424&r)===r&&10<(t=$l+500-Xe())){if(0!==ft(e,0))break;if(((o=e.suspendedLanes)&r)!==r){tu(),e.pingedLanes|=e.suspendedLanes&o;break}e.timeoutHandle=ro(Su.bind(null,e,zl,Wl),t);break}Su(e,zl,Wl);break;case 4:if(lu(e,r),(4194240&r)===r)break;for(t=e.eventTimes,o=-1;0<r;){var s=31-at(r);a=1<<s,(s=t[s])>o&&(o=s),r&=~a}if(r=o,10<(r=(120>(r=Xe()-r)?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*kl(r/1960))-r)){e.timeoutHandle=ro(Su.bind(null,e,zl,Wl),r);break}Su(e,zl,Wl);break;default:throw Error(i(329))}}}return ou(e,Xe()),e.callbackNode===n?iu.bind(null,e):null}function au(e,t){var n=Ul;return e.current.memoizedState.isDehydrated&&(pu(e,t).flags|=256),2!==(e=vu(e,t))&&(t=zl,zl=n,null!==t&&su(t)),e}function su(e){null===zl?zl=e:zl.push.apply(zl,e)}function lu(e,t){for(t&=~Bl,t&=~Dl,e.suspendedLanes|=t,e.pingedLanes&=~t,e=e.expirationTimes;0<t;){var n=31-at(t),r=1<<n;e[n]=-1,t&=~r}}function uu(e){if(6&Pl)throw Error(i(327));xu();var t=ft(e,0);if(!(1&t))return ou(e,Xe()),null;var n=vu(e,t);if(0!==e.tag&&2===n){var r=ht(e);0!==r&&(t=r,n=au(e,r))}if(1===n)throw n=Ll,pu(e,0),lu(e,t),ou(e,Xe()),n;if(6===n)throw Error(i(345));return e.finishedWork=e.current.alternate,e.finishedLanes=t,Su(e,zl,Wl),ou(e,Xe()),null}function cu(e,t){var n=Pl;Pl|=1;try{return e(t)}finally{0===(Pl=n)&&(Hl=Xe()+500,Uo&&Ho())}}function du(e){null!==Jl&&0===Jl.tag&&!(6&Pl)&&xu();var t=Pl;Pl|=1;var n=Ol.transition,r=bt;try{if(Ol.transition=null,bt=1,e)return e()}finally{bt=r,Ol.transition=n,!(6&(Pl=t))&&Ho()}}function fu(){jl=Nl.current,_o(Nl)}function pu(e,t){e.finishedWork=null,e.finishedLanes=0;var n=e.timeoutHandle;if(-1!==n&&(e.timeoutHandle=-1,oo(n)),null!==Tl)for(n=Tl.return;null!==n;){var r=n;switch(ni(r),r.tag){case 1:null!=(r=r.type.childContextTypes)&&No();break;case 3:ia(),_o(Ro),_o(Po),da();break;case 5:sa(r);break;case 4:ia();break;case 13:case 19:_o(la);break;case 10:xi(r.type._context);break;case 22:case 23:fu()}n=n.return}if(Rl=e,Tl=e=Nu(e.current,null),Fl=jl=t,Il=0,Ll=null,Bl=Dl=Ml=0,zl=Ul=null,null!==Ci){for(t=0;t<Ci.length;t++)if(null!==(r=(n=Ci[t]).interleaved)){n.interleaved=null;var o=r.next,i=n.pending;if(null!==i){var a=i.next;i.next=o,r.next=a}n.pending=r}Ci=null}return e}function hu(e,t){for(;;){var n=Tl;try{if(Si(),fa.current=as,ya){for(var r=ma.memoizedState;null!==r;){var o=r.queue;null!==o&&(o.pending=null),r=r.next}ya=!1}if(ha=0,va=ga=ma=null,ba=!1,wa=0,Cl.current=null,null===n||null===n.return){Il=1,Ll=t,Tl=null;break}e:{var a=e,s=n.return,l=n,u=t;if(t=Fl,l.flags|=32768,null!==u&&"object"==typeof u&&"function"==typeof u.then){var c=u,d=l,f=d.tag;if(!(1&d.mode||0!==f&&11!==f&&15!==f)){var p=d.alternate;p?(d.updateQueue=p.updateQueue,d.memoizedState=p.memoizedState,d.lanes=p.lanes):(d.updateQueue=null,d.memoizedState=null)}var h=vs(s);if(null!==h){h.flags&=-257,ys(h,s,l,0,t),1&h.mode&&gs(a,c,t),u=c;var m=(t=h).updateQueue;if(null===m){var g=new Set;g.add(u),t.updateQueue=g}else m.add(u);break e}if(!(1&t)){gs(a,c,t),gu();break e}u=Error(i(426))}else if(ii&&1&l.mode){var v=vs(s);if(null!==v){!(65536&v.flags)&&(v.flags|=256),ys(v,s,l,0,t),mi(cs(u,l));break e}}a=u=cs(u,l),4!==Il&&(Il=2),null===Ul?Ul=[a]:Ul.push(a),a=s;do{switch(a.tag){case 3:a.flags|=65536,t&=-t,a.lanes|=t,Mi(a,hs(0,u,t));break e;case 1:l=u;var y=a.type,b=a.stateNode;if(!(128&a.flags||"function"!=typeof y.getDerivedStateFromError&&(null===b||"function"!=typeof b.componentDidCatch||null!==Kl&&Kl.has(b)))){a.flags|=65536,t&=-t,a.lanes|=t,Mi(a,ms(a,l,t));break e}}a=a.return}while(null!==a)}Eu(n)}catch(e){t=e,Tl===n&&null!==n&&(Tl=n=n.return);continue}break}}function mu(){var e=_l.current;return _l.current=as,null===e?as:e}function gu(){0!==Il&&3!==Il&&2!==Il||(Il=4),null===Rl||!(268435455&Ml)&&!(268435455&Dl)||lu(Rl,Fl)}function vu(e,t){var n=Pl;Pl|=2;var r=mu();for(Rl===e&&Fl===t||(Wl=null,pu(e,t));;)try{yu();break}catch(t){hu(e,t)}if(Si(),Pl=n,_l.current=r,null!==Tl)throw Error(i(261));return Rl=null,Fl=0,Il}function yu(){for(;null!==Tl;)wu(Tl)}function bu(){for(;null!==Tl&&!Je();)wu(Tl)}function wu(e){var t=Al(e.alternate,e,jl);e.memoizedProps=e.pendingProps,null===t?Eu(e):Tl=t,Cl.current=null}function Eu(e){var t=e;do{var n=t.alternate;if(e=t.return,32768&t.flags){if(null!==(n=Js(n,t)))return n.flags&=32767,void(Tl=n);if(null===e)return Il=6,void(Tl=null);e.flags|=32768,e.subtreeFlags=0,e.deletions=null}else if(null!==(n=Gs(n,t,jl)))return void(Tl=n);if(null!==(t=t.sibling))return void(Tl=t);Tl=t=e}while(null!==t);0===Il&&(Il=5)}function Su(e,t,n){var r=bt,o=Ol.transition;try{Ol.transition=null,bt=1,function(e,t,n,r){do{xu()}while(null!==Jl);if(6&Pl)throw Error(i(327));n=e.finishedWork;var o=e.finishedLanes;if(null===n)return null;if(e.finishedWork=null,e.finishedLanes=0,n===e.current)throw Error(i(177));e.callbackNode=null,e.callbackPriority=0;var a=n.lanes|n.childLanes;if(function(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0<n;){var o=31-at(n),i=1<<o;t[o]=0,r[o]=-1,e[o]=-1,n&=~i}}(e,a),e===Rl&&(Tl=Rl=null,Fl=0),!(2064&n.subtreeFlags)&&!(2064&n.flags)||Gl||(Gl=!0,Ru(tt,(function(){return xu(),null}))),a=!!(15990&n.flags),15990&n.subtreeFlags||a){a=Ol.transition,Ol.transition=null;var s=bt;bt=1;var l=Pl;Pl|=4,Cl.current=null,function(e,t){if(eo=Wt,pr(e=fr())){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{var r=(n=(n=e.ownerDocument)&&n.defaultView||window).getSelection&&n.getSelection();if(r&&0!==r.rangeCount){n=r.anchorNode;var o=r.anchorOffset,a=r.focusNode;r=r.focusOffset;try{n.nodeType,a.nodeType}catch(e){n=null;break e}var s=0,l=-1,u=-1,c=0,d=0,f=e,p=null;t:for(;;){for(var h;f!==n||0!==o&&3!==f.nodeType||(l=s+o),f!==a||0!==r&&3!==f.nodeType||(u=s+r),3===f.nodeType&&(s+=f.nodeValue.length),null!==(h=f.firstChild);)p=f,f=h;for(;;){if(f===e)break t;if(p===n&&++c===o&&(l=s),p===a&&++d===r&&(u=s),null!==(h=f.nextSibling))break;p=(f=p).parentNode}f=h}n=-1===l||-1===u?null:{start:l,end:u}}else n=null}n=n||{start:0,end:0}}else n=null;for(to={focusedElem:e,selectionRange:n},Wt=!1,Zs=t;null!==Zs;)if(e=(t=Zs).child,1028&t.subtreeFlags&&null!==e)e.return=t,Zs=e;else for(;null!==Zs;){t=Zs;try{var m=t.alternate;if(1024&t.flags)switch(t.tag){case 0:case 11:case 15:case 5:case 6:case 4:case 17:break;case 1:if(null!==m){var g=m.memoizedProps,v=m.memoizedState,y=t.stateNode,b=y.getSnapshotBeforeUpdate(t.elementType===t.type?g:vi(t.type,g),v);y.__reactInternalSnapshotBeforeUpdate=b}break;case 3:var w=t.stateNode.containerInfo;1===w.nodeType?w.textContent="":9===w.nodeType&&w.documentElement&&w.removeChild(w.documentElement);break;default:throw Error(i(163))}}catch(e){ku(t,t.return,e)}if(null!==(e=t.sibling)){e.return=t.return,Zs=e;break}Zs=t.return}m=nl,nl=!1}(e,n),vl(n,e),hr(to),Wt=!!eo,to=eo=null,e.current=n,bl(n,e,o),Ye(),Pl=l,bt=s,Ol.transition=a}else e.current=n;if(Gl&&(Gl=!1,Jl=e,Yl=o),0===(a=e.pendingLanes)&&(Kl=null),function(e){if(it&&"function"==typeof it.onCommitFiberRoot)try{it.onCommitFiberRoot(ot,e,void 0,!(128&~e.current.flags))}catch(e){}}(n.stateNode),ou(e,Xe()),null!==t)for(r=e.onRecoverableError,n=0;n<t.length;n++)r((o=t[n]).value,{componentStack:o.stack,digest:o.digest});if(Vl)throw Vl=!1,e=ql,ql=null,e;!!(1&Yl)&&0!==e.tag&&xu(),1&(a=e.pendingLanes)?e===Ql?Xl++:(Xl=0,Ql=e):Xl=0,Ho()}(e,t,n,r)}finally{Ol.transition=o,bt=r}return null}function xu(){if(null!==Jl){var e=wt(Yl),t=Ol.transition,n=bt;try{if(Ol.transition=null,bt=16>e?16:e,null===Jl)var r=!1;else{if(e=Jl,Jl=null,Yl=0,6&Pl)throw Error(i(331));var o=Pl;for(Pl|=4,Zs=e.current;null!==Zs;){var a=Zs,s=a.child;if(16&Zs.flags){var l=a.deletions;if(null!==l){for(var u=0;u<l.length;u++){var c=l[u];for(Zs=c;null!==Zs;){var d=Zs;switch(d.tag){case 0:case 11:case 15:rl(8,d,a)}var f=d.child;if(null!==f)f.return=d,Zs=f;else for(;null!==Zs;){var p=(d=Zs).sibling,h=d.return;if(al(d),d===c){Zs=null;break}if(null!==p){p.return=h,Zs=p;break}Zs=h}}}var m=a.alternate;if(null!==m){var g=m.child;if(null!==g){m.child=null;do{var v=g.sibling;g.sibling=null,g=v}while(null!==g)}}Zs=a}}if(2064&a.subtreeFlags&&null!==s)s.return=a,Zs=s;else e:for(;null!==Zs;){if(2048&(a=Zs).flags)switch(a.tag){case 0:case 11:case 15:rl(9,a,a.return)}var y=a.sibling;if(null!==y){y.return=a.return,Zs=y;break e}Zs=a.return}}var b=e.current;for(Zs=b;null!==Zs;){var w=(s=Zs).child;if(2064&s.subtreeFlags&&null!==w)w.return=s,Zs=w;else e:for(s=b;null!==Zs;){if(2048&(l=Zs).flags)try{switch(l.tag){case 0:case 11:case 15:ol(9,l)}}catch(e){ku(l,l.return,e)}if(l===s){Zs=null;break e}var E=l.sibling;if(null!==E){E.return=l.return,Zs=E;break e}Zs=l.return}}if(Pl=o,Ho(),it&&"function"==typeof it.onPostCommitFiberRoot)try{it.onPostCommitFiberRoot(ot,e)}catch(e){}r=!0}return r}finally{bt=n,Ol.transition=t}}return!1}function Au(e,t,n){e=Ii(e,t=hs(0,t=cs(n,t),1),1),t=tu(),null!==e&&(vt(e,1,t),ou(e,t))}function ku(e,t,n){if(3===e.tag)Au(e,e,n);else for(;null!==t;){if(3===t.tag){Au(t,e,n);break}if(1===t.tag){var r=t.stateNode;if("function"==typeof t.type.getDerivedStateFromError||"function"==typeof r.componentDidCatch&&(null===Kl||!Kl.has(r))){t=Ii(t,e=ms(t,e=cs(n,e),1),1),e=tu(),null!==t&&(vt(t,1,e),ou(t,e));break}}t=t.return}}function _u(e,t,n){var r=e.pingCache;null!==r&&r.delete(t),t=tu(),e.pingedLanes|=e.suspendedLanes&n,Rl===e&&(Fl&n)===n&&(4===Il||3===Il&&(130023424&Fl)===Fl&&500>Xe()-$l?pu(e,0):Bl|=n),ou(e,t)}function Cu(e,t){0===t&&(1&e.mode?(t=ct,!(130023424&(ct<<=1))&&(ct=4194304)):t=1);var n=tu();null!==(e=Ri(e,t))&&(vt(e,t,n),ou(e,n))}function Ou(e){var t=e.memoizedState,n=0;null!==t&&(n=t.retryLane),Cu(e,n)}function Pu(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,o=e.memoizedState;null!==o&&(n=o.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(i(314))}null!==r&&r.delete(t),Cu(e,n)}function Ru(e,t){return Ke(e,t)}function Tu(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Fu(e,t,n,r){return new Tu(e,t,n,r)}function ju(e){return!(!(e=e.prototype)||!e.isReactComponent)}function Nu(e,t){var n=e.alternate;return null===n?((n=Fu(e.tag,t,e.key,e.mode)).elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=14680064&e.flags,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=null===t?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Iu(e,t,n,r,o,a){var s=2;if(r=e,"function"==typeof e)ju(e)&&(s=1);else if("string"==typeof e)s=5;else e:switch(e){case x:return Lu(n.children,o,a,t);case A:s=8,o|=8;break;case k:return(e=Fu(12,n,t,2|o)).elementType=k,e.lanes=a,e;case P:return(e=Fu(13,n,t,o)).elementType=P,e.lanes=a,e;case R:return(e=Fu(19,n,t,o)).elementType=R,e.lanes=a,e;case j:return Mu(n,o,a,t);default:if("object"==typeof e&&null!==e)switch(e.$$typeof){case _:s=10;break e;case C:s=9;break e;case O:s=11;break e;case T:s=14;break e;case F:s=16,r=null;break e}throw Error(i(130,null==e?e:typeof e,""))}return(t=Fu(s,n,t,o)).elementType=e,t.type=r,t.lanes=a,t}function Lu(e,t,n,r){return(e=Fu(7,e,r,t)).lanes=n,e}function Mu(e,t,n,r){return(e=Fu(22,e,r,t)).elementType=j,e.lanes=n,e.stateNode={isHidden:!1},e}function Du(e,t,n){return(e=Fu(6,e,null,t)).lanes=n,e}function Bu(e,t,n){return(t=Fu(4,null!==e.children?e.children:[],e.key,t)).lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Uu(e,t,n,r,o){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=gt(0),this.expirationTimes=gt(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=gt(0),this.identifierPrefix=r,this.onRecoverableError=o,this.mutableSourceEagerHydrationData=null}function zu(e,t,n,r,o,i,a,s,l){return e=new Uu(e,t,n,s,l),1===t?(t=1,!0===i&&(t|=8)):t=0,i=Fu(3,null,null,t),e.current=i,i.stateNode=e,i.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Fi(i),e}function $u(e){if(!e)return Oo;e:{if($e(e=e._reactInternals)!==e||1!==e.tag)throw Error(i(170));var t=e;do{switch(t.tag){case 3:t=t.stateNode.context;break e;case 1:if(jo(t.type)){t=t.stateNode.__reactInternalMemoizedMergedChildContext;break e}}t=t.return}while(null!==t);throw Error(i(171))}if(1===e.tag){var n=e.type;if(jo(n))return Lo(e,n,t)}return t}function Hu(e,t,n,r,o,i,a,s,l){return(e=zu(n,r,!0,e,0,i,0,s,l)).context=$u(null),n=e.current,(i=Ni(r=tu(),o=nu(n))).callback=null!=t?t:null,Ii(n,i,o),e.current.lanes=o,vt(e,o,r),ou(e,r),e}function Wu(e,t,n,r){var o=t.current,i=tu(),a=nu(o);return n=$u(n),null===t.context?t.context=n:t.pendingContext=n,(t=Ni(i,a)).payload={element:e},null!==(r=void 0===r?null:r)&&(t.callback=r),null!==(e=Ii(o,t,a))&&(ru(e,o,a,i),Li(e,o,a)),a}function Vu(e){return(e=e.current).child?(e.child.tag,e.child.stateNode):null}function qu(e,t){if(null!==(e=e.memoizedState)&&null!==e.dehydrated){var n=e.retryLane;e.retryLane=0!==n&&n<t?n:t}}function Ku(e,t){qu(e,t),(e=e.alternate)&&qu(e,t)}Al=function(e,t,n){if(null!==e)if(e.memoizedProps!==t.pendingProps||Ro.current)ws=!0;else{if(!(e.lanes&n||128&t.flags))return ws=!1,function(e,t,n){switch(t.tag){case 3:Rs(t),hi();break;case 5:aa(t);break;case 1:jo(t.type)&&Mo(t);break;case 4:oa(t,t.stateNode.containerInfo);break;case 10:var r=t.type._context,o=t.memoizedProps.value;Co(yi,r._currentValue),r._currentValue=o;break;case 13:if(null!==(r=t.memoizedState))return null!==r.dehydrated?(Co(la,1&la.current),t.flags|=128,null):n&t.child.childLanes?Ds(e,t,n):(Co(la,1&la.current),null!==(e=Vs(e,t,n))?e.sibling:null);Co(la,1&la.current);break;case 19:if(r=!!(n&t.childLanes),128&e.flags){if(r)return Hs(e,t,n);t.flags|=128}if(null!==(o=t.memoizedState)&&(o.rendering=null,o.tail=null,o.lastEffect=null),Co(la,la.current),r)break;return null;case 22:case 23:return t.lanes=0,ks(e,t,n)}return Vs(e,t,n)}(e,t,n);ws=!!(131072&e.flags)}else ws=!1,ii&&1048576&t.flags&&ei(t,Ko,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ws(e,t),e=t.pendingProps;var o=Fo(t,Po.current);ki(t,n),o=Aa(null,t,r,e,o,n);var a=ka();return t.flags|=1,"object"==typeof o&&null!==o&&"function"==typeof o.render&&void 0===o.$$typeof?(t.tag=1,t.memoizedState=null,t.updateQueue=null,jo(r)?(a=!0,Mo(t)):a=!1,t.memoizedState=null!==o.state&&void 0!==o.state?o.state:null,Fi(t),o.updater=$i,t.stateNode=o,o._reactInternals=t,qi(t,r,e,n),t=Ps(null,t,r,!0,a,n)):(t.tag=0,ii&&a&&ti(t),Es(null,t,o,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ws(e,t),e=t.pendingProps,r=(o=r._init)(r._payload),t.type=r,o=t.tag=function(e){if("function"==typeof e)return ju(e)?1:0;if(null!=e){if((e=e.$$typeof)===O)return 11;if(e===T)return 14}return 2}(r),e=vi(r,e),o){case 0:t=Cs(null,t,r,e,n);break e;case 1:t=Os(null,t,r,e,n);break e;case 11:t=Ss(null,t,r,e,n);break e;case 14:t=xs(null,t,r,vi(r.type,e),n);break e}throw Error(i(306,r,""))}return t;case 0:return r=t.type,o=t.pendingProps,Cs(e,t,r,o=t.elementType===r?o:vi(r,o),n);case 1:return r=t.type,o=t.pendingProps,Os(e,t,r,o=t.elementType===r?o:vi(r,o),n);case 3:e:{if(Rs(t),null===e)throw Error(i(387));r=t.pendingProps,o=(a=t.memoizedState).element,ji(e,t),Di(t,r,null,n);var s=t.memoizedState;if(r=s.element,a.isDehydrated){if(a={element:r,isDehydrated:!1,cache:s.cache,pendingSuspenseBoundaries:s.pendingSuspenseBoundaries,transitions:s.transitions},t.updateQueue.baseState=a,t.memoizedState=a,256&t.flags){t=Ts(e,t,r,n,o=cs(Error(i(423)),t));break e}if(r!==o){t=Ts(e,t,r,n,o=cs(Error(i(424)),t));break e}for(oi=uo(t.stateNode.containerInfo.firstChild),ri=t,ii=!0,ai=null,n=Qi(t,null,r,n),t.child=n;n;)n.flags=-3&n.flags|4096,n=n.sibling}else{if(hi(),r===o){t=Vs(e,t,n);break e}Es(e,t,r,n)}t=t.child}return t;case 5:return aa(t),null===e&&ci(t),r=t.type,o=t.pendingProps,a=null!==e?e.memoizedProps:null,s=o.children,no(r,o)?s=null:null!==a&&no(r,a)&&(t.flags|=32),_s(e,t),Es(e,t,s,n),t.child;case 6:return null===e&&ci(t),null;case 13:return Ds(e,t,n);case 4:return oa(t,t.stateNode.containerInfo),r=t.pendingProps,null===e?t.child=Xi(t,null,r,n):Es(e,t,r,n),t.child;case 11:return r=t.type,o=t.pendingProps,Ss(e,t,r,o=t.elementType===r?o:vi(r,o),n);case 7:return Es(e,t,t.pendingProps,n),t.child;case 8:case 12:return Es(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,o=t.pendingProps,a=t.memoizedProps,s=o.value,Co(yi,r._currentValue),r._currentValue=s,null!==a)if(sr(a.value,s)){if(a.children===o.children&&!Ro.current){t=Vs(e,t,n);break e}}else for(null!==(a=t.child)&&(a.return=t);null!==a;){var l=a.dependencies;if(null!==l){s=a.child;for(var u=l.firstContext;null!==u;){if(u.context===r){if(1===a.tag){(u=Ni(-1,n&-n)).tag=2;var c=a.updateQueue;if(null!==c){var d=(c=c.shared).pending;null===d?u.next=u:(u.next=d.next,d.next=u),c.pending=u}}a.lanes|=n,null!==(u=a.alternate)&&(u.lanes|=n),Ai(a.return,n,t),l.lanes|=n;break}u=u.next}}else if(10===a.tag)s=a.type===t.type?null:a.child;else if(18===a.tag){if(null===(s=a.return))throw Error(i(341));s.lanes|=n,null!==(l=s.alternate)&&(l.lanes|=n),Ai(s,n,t),s=a.sibling}else s=a.child;if(null!==s)s.return=a;else for(s=a;null!==s;){if(s===t){s=null;break}if(null!==(a=s.sibling)){a.return=s.return,s=a;break}s=s.return}a=s}Es(e,t,o.children,n),t=t.child}return t;case 9:return o=t.type,r=t.pendingProps.children,ki(t,n),r=r(o=_i(o)),t.flags|=1,Es(e,t,r,n),t.child;case 14:return o=vi(r=t.type,t.pendingProps),xs(e,t,r,o=vi(r.type,o),n);case 15:return As(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,o=t.pendingProps,o=t.elementType===r?o:vi(r,o),Ws(e,t),t.tag=1,jo(r)?(e=!0,Mo(t)):e=!1,ki(t,n),Wi(t,r,o),qi(t,r,o,n),Ps(null,t,r,!0,e,n);case 19:return Hs(e,t,n);case 22:return ks(e,t,n)}throw Error(i(156,t.tag))};var Gu="function"==typeof reportError?reportError:function(e){console.error(e)};function Ju(e){this._internalRoot=e}function Yu(e){this._internalRoot=e}function Xu(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType)}function Qu(e){return!(!e||1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType&&(8!==e.nodeType||" react-mount-point-unstable "!==e.nodeValue))}function Zu(){}function ec(e,t,n,r,o){var i=n._reactRootContainer;if(i){var a=i;if("function"==typeof o){var s=o;o=function(){var e=Vu(a);s.call(e)}}Wu(t,a,e,o)}else a=function(e,t,n,r,o){if(o){if("function"==typeof r){var i=r;r=function(){var e=Vu(a);i.call(e)}}var a=Hu(t,r,e,0,null,!1,0,"",Zu);return e._reactRootContainer=a,e[mo]=a.current,$r(8===e.nodeType?e.parentNode:e),du(),a}for(;o=e.lastChild;)e.removeChild(o);if("function"==typeof r){var s=r;r=function(){var e=Vu(l);s.call(e)}}var l=zu(e,0,!1,null,0,!1,0,"",Zu);return e._reactRootContainer=l,e[mo]=l.current,$r(8===e.nodeType?e.parentNode:e),du((function(){Wu(t,l,n,r)})),l}(n,t,e,o,r);return Vu(a)}Yu.prototype.render=Ju.prototype.render=function(e){var t=this._internalRoot;if(null===t)throw Error(i(409));Wu(e,t,null,null)},Yu.prototype.unmount=Ju.prototype.unmount=function(){var e=this._internalRoot;if(null!==e){this._internalRoot=null;var t=e.containerInfo;du((function(){Wu(null,e,null,null)})),t[mo]=null}},Yu.prototype.unstable_scheduleHydration=function(e){if(e){var t=At();e={blockedOn:null,target:e,priority:t};for(var n=0;n<jt.length&&0!==t&&t<jt[n].priority;n++);jt.splice(n,0,e),0===n&&Mt(e)}},Et=function(e){switch(e.tag){case 3:var t=e.stateNode;if(t.current.memoizedState.isDehydrated){var n=dt(t.pendingLanes);0!==n&&(yt(t,1|n),ou(t,Xe()),!(6&Pl)&&(Hl=Xe()+500,Ho()))}break;case 13:du((function(){var t=Ri(e,1);if(null!==t){var n=tu();ru(t,e,1,n)}})),Ku(e,1)}},St=function(e){if(13===e.tag){var t=Ri(e,134217728);null!==t&&ru(t,e,134217728,tu()),Ku(e,134217728)}},xt=function(e){if(13===e.tag){var t=nu(e),n=Ri(e,t);null!==n&&ru(n,e,t,tu()),Ku(e,t)}},At=function(){return bt},kt=function(e,t){var n=bt;try{return bt=e,t()}finally{bt=n}},Se=function(e,t,n){switch(t){case"input":if(Q(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;t<n.length;t++){var r=n[t];if(r!==e&&r.form===e.form){var o=So(r);if(!o)throw Error(i(90));K(r),Q(r,o)}}}break;case"textarea":ie(e,n);break;case"select":null!=(t=n.value)&&ne(e,!!n.multiple,t,!1)}},Oe=cu,Pe=du;var tc={usingClientEntryPoint:!1,Events:[wo,Eo,So,_e,Ce,cu]},nc={findFiberByHostInstance:bo,bundleType:0,version:"18.2.0",rendererPackageName:"react-dom"},rc={bundleType:nc.bundleType,version:nc.version,rendererPackageName:nc.rendererPackageName,rendererConfig:nc.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setErrorHandler:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:w.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return null===(e=Ve(e))?null:e.stateNode},findFiberByHostInstance:nc.findFiberByHostInstance||function(){return null},findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null,reconcilerVersion:"18.2.0-next-9e3b772b8-20220608"};if("undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__){var oc=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!oc.isDisabled&&oc.supportsFiber)try{ot=oc.inject(rc),it=oc}catch(ce){}}t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=tc,t.createPortal=function(e,t){var n=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!Xu(t))throw Error(i(200));return function(e,t,n){var r=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:S,key:null==r?null:""+r,children:e,containerInfo:t,implementation:n}}(e,t,null,n)},t.createRoot=function(e,t){if(!Xu(e))throw Error(i(299));var n=!1,r="",o=Gu;return null!=t&&(!0===t.unstable_strictMode&&(n=!0),void 0!==t.identifierPrefix&&(r=t.identifierPrefix),void 0!==t.onRecoverableError&&(o=t.onRecoverableError)),t=zu(e,1,!1,null,0,n,0,r,o),e[mo]=t.current,$r(8===e.nodeType?e.parentNode:e),new Ju(t)},t.findDOMNode=function(e){if(null==e)return null;if(1===e.nodeType)return e;var t=e._reactInternals;if(void 0===t){if("function"==typeof e.render)throw Error(i(188));throw e=Object.keys(e).join(","),Error(i(268,e))}return null===(e=Ve(t))?null:e.stateNode},t.flushSync=function(e){return du(e)},t.hydrate=function(e,t,n){if(!Qu(t))throw Error(i(200));return ec(null,e,t,!0,n)},t.hydrateRoot=function(e,t,n){if(!Xu(e))throw Error(i(405));var r=null!=n&&n.hydratedSources||null,o=!1,a="",s=Gu;if(null!=n&&(!0===n.unstable_strictMode&&(o=!0),void 0!==n.identifierPrefix&&(a=n.identifierPrefix),void 0!==n.onRecoverableError&&(s=n.onRecoverableError)),t=Hu(t,null,e,1,null!=n?n:null,o,0,a,s),e[mo]=t.current,$r(e),r)for(e=0;e<r.length;e++)o=(o=(n=r[e])._getVersion)(n._source),null==t.mutableSourceEagerHydrationData?t.mutableSourceEagerHydrationData=[n,o]:t.mutableSourceEagerHydrationData.push(n,o);return new Yu(t)},t.render=function(e,t,n){if(!Qu(t))throw Error(i(200));return ec(null,e,t,!1,n)},t.unmountComponentAtNode=function(e){if(!Qu(e))throw Error(i(40));return!!e._reactRootContainer&&(du((function(){ec(null,null,e,!1,(function(){e._reactRootContainer=null,e[mo]=null}))})),!0)},t.unstable_batchedUpdates=cu,t.unstable_renderSubtreeIntoContainer=function(e,t,n,r){if(!Qu(n))throw Error(i(200));if(null==e||void 0===e._reactInternals)throw Error(i(38));return ec(e,t,n,!1,r)},t.version="18.2.0-next-9e3b772b8-20220608"},2634:()=>{},2642:(e,t,n)=>{"use strict";var r=n(7720),o=Object.prototype.hasOwnProperty,i=Array.isArray,a={allowDots:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decoder:r.decode,delimiter:"&",depth:5,ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictNullHandling:!1},s=function(e){return e.replace(/&#(\d+);/g,(function(e,t){return String.fromCharCode(parseInt(t,10))}))},l=function(e,t){return e&&"string"==typeof e&&t.comma&&e.indexOf(",")>-1?e.split(","):e},u=function(e,t,n,r){if(e){var i=n.allowDots?e.replace(/\.([^.[]+)/g,"[$1]"):e,a=/(\[[^[\]]*])/g,s=n.depth>0&&/(\[[^[\]]*])/.exec(i),u=s?i.slice(0,s.index):i,c=[];if(u){if(!n.plainObjects&&o.call(Object.prototype,u)&&!n.allowPrototypes)return;c.push(u)}for(var d=0;n.depth>0&&null!==(s=a.exec(i))&&d<n.depth;){if(d+=1,!n.plainObjects&&o.call(Object.prototype,s[1].slice(1,-1))&&!n.allowPrototypes)return;c.push(s[1])}return s&&c.push("["+i.slice(s.index)+"]"),function(e,t,n,r){for(var o=r?t:l(t,n),i=e.length-1;i>=0;--i){var a,s=e[i];if("[]"===s&&n.parseArrays)a=[].concat(o);else{a=n.plainObjects?Object.create(null):{};var u="["===s.charAt(0)&&"]"===s.charAt(s.length-1)?s.slice(1,-1):s,c=parseInt(u,10);n.parseArrays||""!==u?!isNaN(c)&&s!==u&&String(c)===u&&c>=0&&n.parseArrays&&c<=n.arrayLimit?(a=[])[c]=o:"__proto__"!==u&&(a[u]=o):a={0:o}}o=a}return o}(c,t,n,r)}};e.exports=function(e,t){var n=function(e){if(!e)return a;if(null!==e.decoder&&void 0!==e.decoder&&"function"!=typeof e.decoder)throw new TypeError("Decoder has to be a function.");if(void 0!==e.charset&&"utf-8"!==e.charset&&"iso-8859-1"!==e.charset)throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var t=void 0===e.charset?a.charset:e.charset;return{allowDots:void 0===e.allowDots?a.allowDots:!!e.allowDots,allowPrototypes:"boolean"==typeof e.allowPrototypes?e.allowPrototypes:a.allowPrototypes,allowSparse:"boolean"==typeof e.allowSparse?e.allowSparse:a.allowSparse,arrayLimit:"number"==typeof e.arrayLimit?e.arrayLimit:a.arrayLimit,charset:t,charsetSentinel:"boolean"==typeof e.charsetSentinel?e.charsetSentinel:a.charsetSentinel,comma:"boolean"==typeof e.comma?e.comma:a.comma,decoder:"function"==typeof e.decoder?e.decoder:a.decoder,delimiter:"string"==typeof e.delimiter||r.isRegExp(e.delimiter)?e.delimiter:a.delimiter,depth:"number"==typeof e.depth||!1===e.depth?+e.depth:a.depth,ignoreQueryPrefix:!0===e.ignoreQueryPrefix,interpretNumericEntities:"boolean"==typeof e.interpretNumericEntities?e.interpretNumericEntities:a.interpretNumericEntities,parameterLimit:"number"==typeof e.parameterLimit?e.parameterLimit:a.parameterLimit,parseArrays:!1!==e.parseArrays,plainObjects:"boolean"==typeof e.plainObjects?e.plainObjects:a.plainObjects,strictNullHandling:"boolean"==typeof e.strictNullHandling?e.strictNullHandling:a.strictNullHandling}}(t);if(""===e||null==e)return n.plainObjects?Object.create(null):{};for(var c="string"==typeof e?function(e,t){var n,u={__proto__:null},c=t.ignoreQueryPrefix?e.replace(/^\?/,""):e,d=t.parameterLimit===1/0?void 0:t.parameterLimit,f=c.split(t.delimiter,d),p=-1,h=t.charset;if(t.charsetSentinel)for(n=0;n<f.length;++n)0===f[n].indexOf("utf8=")&&("utf8=%E2%9C%93"===f[n]?h="utf-8":"utf8=%26%2310003%3B"===f[n]&&(h="iso-8859-1"),p=n,n=f.length);for(n=0;n<f.length;++n)if(n!==p){var m,g,v=f[n],y=v.indexOf("]="),b=-1===y?v.indexOf("="):y+1;-1===b?(m=t.decoder(v,a.decoder,h,"key"),g=t.strictNullHandling?null:""):(m=t.decoder(v.slice(0,b),a.decoder,h,"key"),g=r.maybeMap(l(v.slice(b+1),t),(function(e){return t.decoder(e,a.decoder,h,"value")}))),g&&t.interpretNumericEntities&&"iso-8859-1"===h&&(g=s(g)),v.indexOf("[]=")>-1&&(g=i(g)?[g]:g),o.call(u,m)?u[m]=r.combine(u[m],g):u[m]=g}return u}(e,n):e,d=n.plainObjects?Object.create(null):{},f=Object.keys(c),p=0;p<f.length;++p){var h=f[p],m=u(h,c[h],n,"string"==typeof e);d=r.merge(d,m,n)}return!0===n.allowSparse?d:r.compact(d)}},2682:(e,t,n)=>{"use strict";var r=n(9600),o=Object.prototype.toString,i=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){if(!r(t))throw new TypeError("iterator must be a function");var a;arguments.length>=3&&(a=n),"[object Array]"===o.call(e)?function(e,t,n){for(var r=0,o=e.length;r<o;r++)i.call(e,r)&&(null==n?t(e[r],r,e):t.call(n,e[r],r,e))}(e,t,a):"string"==typeof e?function(e,t,n){for(var r=0,o=e.length;r<o;r++)null==n?t(e.charAt(r),r,e):t.call(n,e.charAt(r),r,e)}(e,t,a):function(e,t,n){for(var r in e)i.call(e,r)&&(null==n?t(e[r],r,e):t.call(n,e[r],r,e))}(e,t,a)}},2694:(e,t,n)=>{"use strict";var r=n(6925);function o(){}function i(){}i.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,i,a){if(a!==r){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:o};return n.PropTypes=n,n}},2702:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"}),"Refresh");t.A=a},2726:(e,t,n)=>{"use strict";function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function i(e,t,n){return(t=s(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,s(r.key),r)}}function s(e){var t=function(e){if("object"!=typeof e||null===e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:String(t)}var l=n(8287).Buffer,u=n(5340).inspect,c=u&&u.custom||"inspect";e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}var t,n;return t=e,(n=[{key:"push",value:function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length}},{key:"unshift",value:function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length}},{key:"shift",value:function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}}},{key:"clear",value:function(){this.head=this.tail=null,this.length=0}},{key:"join",value:function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n}},{key:"concat",value:function(e){if(0===this.length)return l.alloc(0);for(var t,n,r,o=l.allocUnsafe(e>>>0),i=this.head,a=0;i;)t=i.data,n=o,r=a,l.prototype.copy.call(t,n,r),a+=i.data.length,i=i.next;return o}},{key:"consume",value:function(e,t){var n;return e<this.head.data.length?(n=this.head.data.slice(0,e),this.head.data=this.head.data.slice(e)):n=e===this.head.data.length?this.shift():t?this._getString(e):this._getBuffer(e),n}},{key:"first",value:function(){return this.head.data}},{key:"_getString",value:function(e){var t=this.head,n=1,r=t.data;for(e-=r.length;t=t.next;){var o=t.data,i=e>o.length?o.length:e;if(i===o.length?r+=o:r+=o.slice(0,e),0==(e-=i)){i===o.length?(++n,t.next?this.head=t.next:this.head=this.tail=null):(this.head=t,t.data=o.slice(i));break}++n}return this.length-=n,r}},{key:"_getBuffer",value:function(e){var t=l.allocUnsafe(e),n=this.head,r=1;for(n.data.copy(t),e-=n.data.length;n=n.next;){var o=n.data,i=e>o.length?o.length:e;if(o.copy(t,t.length-e,0,i),0==(e-=i)){i===o.length?(++r,n.next?this.head=n.next:this.head=this.tail=null):(this.head=n,n.data=o.slice(i));break}++r}return this.length-=r,t}},{key:c,value:function(e,t){return u(this,o(o({},t),{},{depth:0,customInspect:!1}))}}])&&a(t.prototype,n),Object.defineProperty(t,"prototype",{writable:!1}),e}()},2765:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=(0,n(1574).A)()},2778:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(1529).A},2799:(e,t)=>{"use strict";Symbol.for("react.element"),Symbol.for("react.portal"),Symbol.for("react.fragment"),Symbol.for("react.strict_mode"),Symbol.for("react.profiler"),Symbol.for("react.provider"),Symbol.for("react.context"),Symbol.for("react.server_context"),Symbol.for("react.forward_ref"),Symbol.for("react.suspense"),Symbol.for("react.suspense_list"),Symbol.for("react.memo"),Symbol.for("react.lazy"),Symbol.for("react.offscreen");Symbol.for("react.module.reference")},2817:(e,t,n)=>{"use strict";var r=n(9550);t.sH=r.AuthenticationProvider,r.AuthenticationContext,t.km=r.useReactOidc;var o=n(3626);t.dZ=o.withOidcUser,t.zR=o.OidcSecure,o.withOidcSecure;var i=n(195);i.isRequireAuthentication,i.authenticateUser,i.signinSilent,t.cU=i.oidcLog,i.getUserManager,i.InMemoryWebStorage},2858:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});var r=n(7008),o=n(3951);const i=(0,r.A)(),a=function(e=i){return(0,o.A)(e)}},2861:(e,t,n)=>{var r=n(8287),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),a.prototype=Object.create(o.prototype),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},2955:(e,t,n)=>{"use strict";var r,o=n(5606);function i(e,t,n){return(t=function(e){var t=function(e){if("object"!=typeof e||null===e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:String(t)}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var a=n(6238),s=Symbol("lastResolve"),l=Symbol("lastReject"),u=Symbol("error"),c=Symbol("ended"),d=Symbol("lastPromise"),f=Symbol("handlePromise"),p=Symbol("stream");function h(e,t){return{value:e,done:t}}function m(e){var t=e[s];if(null!==t){var n=e[p].read();null!==n&&(e[d]=null,e[s]=null,e[l]=null,t(h(n,!1)))}}function g(e){o.nextTick(m,e)}var v=Object.getPrototypeOf((function(){})),y=Object.setPrototypeOf((i(r={get stream(){return this[p]},next:function(){var e=this,t=this[u];if(null!==t)return Promise.reject(t);if(this[c])return Promise.resolve(h(void 0,!0));if(this[p].destroyed)return new Promise((function(t,n){o.nextTick((function(){e[u]?n(e[u]):t(h(void 0,!0))}))}));var n,r=this[d];if(r)n=new Promise(function(e,t){return function(n,r){e.then((function(){t[c]?n(h(void 0,!0)):t[f](n,r)}),r)}}(r,this));else{var i=this[p].read();if(null!==i)return Promise.resolve(h(i,!1));n=new Promise(this[f])}return this[d]=n,n}},Symbol.asyncIterator,(function(){return this})),i(r,"return",(function(){var e=this;return new Promise((function(t,n){e[p].destroy(null,(function(e){e?n(e):t(h(void 0,!0))}))}))})),r),v);e.exports=function(e){var t,n=Object.create(y,(i(t={},p,{value:e,writable:!0}),i(t,s,{value:null,writable:!0}),i(t,l,{value:null,writable:!0}),i(t,u,{value:null,writable:!0}),i(t,c,{value:e._readableState.endEmitted,writable:!0}),i(t,f,{value:function(e,t){var r=n[p].read();r?(n[d]=null,n[s]=null,n[l]=null,e(h(r,!1))):(n[s]=e,n[l]=t)},writable:!0}),t));return n[d]=null,a(e,(function(e){if(e&&"ERR_STREAM_PREMATURE_CLOSE"!==e.code){var t=n[l];return null!==t&&(n[d]=null,n[s]=null,n[l]=null,t(e)),void(n[u]=e)}var r=n[s];null!==r&&(n[d]=null,n[s]=null,n[l]=null,r(h(void 0,!0))),n[c]=!0})),e.on("readable",g.bind(null,n)),n}},2987:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12 1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"}),"DeleteForever");t.A=a},2997:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(6735);t.Callback=r.default},3034:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(1547).A},3072:(e,t)=>{"use strict";var n="function"==typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,o=n?Symbol.for("react.portal"):60106,i=n?Symbol.for("react.fragment"):60107,a=n?Symbol.for("react.strict_mode"):60108,s=n?Symbol.for("react.profiler"):60114,l=n?Symbol.for("react.provider"):60109,u=n?Symbol.for("react.context"):60110,c=n?Symbol.for("react.async_mode"):60111,d=n?Symbol.for("react.concurrent_mode"):60111,f=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,h=n?Symbol.for("react.suspense_list"):60120,m=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,v=n?Symbol.for("react.block"):60121,y=n?Symbol.for("react.fundamental"):60117,b=n?Symbol.for("react.responder"):60118,w=n?Symbol.for("react.scope"):60119;function E(e){if("object"==typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case c:case d:case i:case s:case a:case p:return e;default:switch(e=e&&e.$$typeof){case u:case f:case g:case m:case l:return e;default:return t}}case o:return t}}}function S(e){return E(e)===d}t.AsyncMode=c,t.ConcurrentMode=d,t.ContextConsumer=u,t.ContextProvider=l,t.Element=r,t.ForwardRef=f,t.Fragment=i,t.Lazy=g,t.Memo=m,t.Portal=o,t.Profiler=s,t.StrictMode=a,t.Suspense=p,t.isAsyncMode=function(e){return S(e)||E(e)===c},t.isConcurrentMode=S,t.isContextConsumer=function(e){return E(e)===u},t.isContextProvider=function(e){return E(e)===l},t.isElement=function(e){return"object"==typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return E(e)===f},t.isFragment=function(e){return E(e)===i},t.isLazy=function(e){return E(e)===g},t.isMemo=function(e){return E(e)===m},t.isPortal=function(e){return E(e)===o},t.isProfiler=function(e){return E(e)===s},t.isStrictMode=function(e){return E(e)===a},t.isSuspense=function(e){return E(e)===p},t.isValidElementType=function(e){return"string"==typeof e||"function"==typeof e||e===i||e===d||e===s||e===a||e===p||e===h||"object"==typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===m||e.$$typeof===l||e.$$typeof===u||e.$$typeof===f||e.$$typeof===y||e.$$typeof===b||e.$$typeof===w||e.$$typeof===v)},t.typeOf=E},3093:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540));t.AuthenticationContext=o.default.createContext(null),t.useReactOidc=function(){var e=o.default.useContext(t.AuthenticationContext);return{isEnabled:e.isEnabled,login:e.login,logout:e.logout,oidcUser:e.oidcUser,events:e.events,signinSilent:e.signinSilent}}},3141:(e,t,n)=>{"use strict";var r=n(2861).Buffer,o=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function i(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===o||!o(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=l,this.end=u,t=4;break;case"utf8":this.fillLast=s,t=4;break;case"base64":this.text=c,this.end=d,t=3;break;default:return this.write=f,void(this.end=p)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e){var t=this.lastTotal-this.lastNeed,n=function(e,t){if(128!=(192&t[0]))return e.lastNeed=0,"ï¿½";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"ï¿½";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"ï¿½"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function l(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function u(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function c(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function d(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function f(e){return e.toString(this.encoding)}function p(e){return e&&e.length?this.write(e):""}t.I=i,i.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n<e.length?t?t+this.text(e,n):this.text(e,n):t||""},i.prototype.end=function(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+"ï¿½":t},i.prototype.text=function(e,t){var n=function(e,t,n){var r=t.length-1;if(r<n)return 0;var o=a(t[r]);return o>=0?(o>0&&(e.lastNeed=o-1),o):--r<n||-2===o?0:(o=a(t[r]))>=0?(o>0&&(e.lastNeed=o-2),o):--r<n||-2===o?0:(o=a(t[r]))>=0?(o>0&&(2===o?o=0:e.lastNeed=o-3),o):0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},i.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},3272:(e,t,n)=>{"use strict";n.d(t,{A:()=>a,Q:()=>o});var r=n(8168);function o(e){return null!==e&&"object"==typeof e&&e.constructor===Object}function i(e){if(!o(e))return e;const t={};return Object.keys(e).forEach((n=>{t[n]=i(e[n])})),t}function a(e,t,n={clone:!0}){const s=n.clone?(0,r.A)({},e):e;return o(e)&&o(t)&&Object.keys(t).forEach((r=>{"__proto__"!==r&&(o(t[r])&&r in e&&o(e[r])?s[r]=a(e[r],t[r],n):n.clone?s[r]=o(t[r])?i(t[r]):t[r]:s[r]=t[r])})),s}},3289:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"}),"Link");t.A=a},3341:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"}),"Notifications");t.A=a},3404:(e,t,n)=>{"use strict";e.exports=n(3072)},3419:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z"}),"PauseCircleFilled");t.A=a},3486:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==h(e)&&"function"!=typeof e)return{default:e};var t=c();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}return n.default=e,t&&t.set(e,n),n}(n(6540)),o=u(n(5556)),i=u(n(1578)),a=u(n(4525)),s=u(n(2444)),l=u(n(7932));function u(e){return e&&e.__esModule?e:{default:e}}function c(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return c=function(){return e},e}function d(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function f(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?d(Object(n),!0).forEach((function(t){b(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):d(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function p(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function h(e){return h="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},h(e)}function m(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function g(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function v(e){return v=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},v(e)}function y(e,t){return y=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},y(e,t)}function b(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var w={},E=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&y(e,t)}(c,e);var t,n,o,u=(o=c,function(){var e,t=v(o);if(function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}()){var n=v(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return function(e,t){return!t||"object"!==h(t)&&"function"!=typeof t?g(e):t}(this,e)});function c(e){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,c),b(g(t=u.call(this,e)),"storeInputReference",(function(e){null!==e&&(t.input=e);var n=t.props.inputProps.ref;n&&("function"==typeof n?n(e):"object"===h(n)&&Object.prototype.hasOwnProperty.call(n,"current")&&(n.current=e))})),b(g(t),"storeItemsContainerReference",(function(e){null!==e&&(t.itemsContainer=e)})),b(g(t),"onHighlightedItemChange",(function(e){t.highlightedItem=e})),b(g(t),"getItemId",(function(e,n){if(null===n)return null;var r=t.props.id,o=null===e?"":"section-".concat(e);return"react-autowhatever-".concat(r,"-").concat(o,"-item-").concat(n)})),b(g(t),"onFocus",(function(e){var n=t.props.inputProps;t.setState({isInputFocused:!0}),n.onFocus&&n.onFocus(e)})),b(g(t),"onBlur",(function(e){var n=t.props.inputProps;t.setState({isInputFocused:!1}),n.onBlur&&n.onBlur(e)})),b(g(t),"onKeyDown",(function(e){var n,r,o=t.props,i=o.inputProps,a=o.highlightedSectionIndex,s=o.highlightedItemIndex,l=e.keyCode;switch(l){case 40:case 38:var u=40===l?"next":"prev",c=(n=t.sectionIterator[u]([a,s]),r=2,function(e){if(Array.isArray(e))return e}(n)||function(e,t){if("undefined"!=typeof Symbol&&Symbol.iterator in Object(e)){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{r||null==s.return||s.return()}finally{if(o)throw i}}return n}}(n,r)||function(e,t){if(e){if("string"==typeof e)return p(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(n):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?p(e,t):void 0}}(n,r)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),d=c[0],f=c[1];i.onKeyDown(e,{newHighlightedSectionIndex:d,newHighlightedItemIndex:f});break;default:i.onKeyDown(e,{highlightedSectionIndex:a,highlightedItemIndex:s})}})),t.highlightedItem=null,t.state={isInputFocused:!1},t.setSectionsItems(e),t.setSectionIterator(e),t.setTheme(e),t}return t=c,(n=[{key:"componentDidMount",value:function(){this.ensureHighlightedItemIsVisible()}},{key:"UNSAFE_componentWillReceiveProps",value:function(e){e.items!==this.props.items&&this.setSectionsItems(e),e.items===this.props.items&&e.multiSection===this.props.multiSection||this.setSectionIterator(e),e.theme!==this.props.theme&&this.setTheme(e)}},{key:"componentDidUpdate",value:function(){this.ensureHighlightedItemIsVisible()}},{key:"setSectionsItems",value:function(e){e.multiSection&&(this.sectionsItems=e.items.map((function(t){return e.getSectionItems(t)})),this.sectionsLengths=this.sectionsItems.map((function(e){return e.length})),this.allSectionsAreEmpty=this.sectionsLengths.every((function(e){return 0===e})))}},{key:"setSectionIterator",value:function(e){this.sectionIterator=(0,i.default)({multiSection:e.multiSection,data:e.multiSection?this.sectionsLengths:e.items.length})}},{key:"setTheme",value:function(e){this.theme=(0,a.default)(e.theme)}},{key:"renderSections",value:function(){var e=this;if(this.allSectionsAreEmpty)return null;var t=this.theme,n=this.props,o=n.id,i=n.items,a=n.renderItem,u=n.renderItemData,c=n.renderSectionTitle,d=n.highlightedSectionIndex,f=n.highlightedItemIndex,p=n.itemProps;return i.map((function(n,i){var h="react-autowhatever-".concat(o,"-"),m="".concat(h,"section-").concat(i,"-"),g=0===i;return r.default.createElement("div",t("".concat(m,"container"),"sectionContainer",g&&"sectionContainerFirst"),r.default.createElement(s.default,{section:n,renderSectionTitle:c,theme:t,sectionKeyPrefix:m}),r.default.createElement(l.default,{items:e.sectionsItems[i],itemProps:p,renderItem:a,renderItemData:u,sectionIndex:i,highlightedItemIndex:d===i?f:null,onHighlightedItemChange:e.onHighlightedItemChange,getItemId:e.getItemId,theme:t,keyPrefix:h,ref:e.storeItemsListReference}))}))}},{key:"renderItems",value:function(){var e=this.props.items;if(0===e.length)return null;var t=this.theme,n=this.props,o=n.id,i=n.renderItem,a=n.renderItemData,s=n.highlightedSectionIndex,u=n.highlightedItemIndex,c=n.itemProps;return r.default.createElement(l.default,{items:e,itemProps:c,renderItem:i,renderItemData:a,highlightedItemIndex:null===s?u:null,onHighlightedItemChange:this.onHighlightedItemChange,getItemId:this.getItemId,theme:t,keyPrefix:"react-autowhatever-".concat(o,"-")})}},{key:"ensureHighlightedItemIsVisible",value:function(){var e=this.highlightedItem;if(e){var t=this.itemsContainer,n=e.offsetParent===t?e.offsetTop:e.offsetTop-t.offsetTop,r=t.scrollTop;n<r?r=n:n+e.offsetHeight>r+t.offsetHeight&&(r=n+e.offsetHeight-t.offsetHeight),r!==t.scrollTop&&(t.scrollTop=r)}}},{key:"render",value:function(){var e=this.theme,t=this.props,n=t.id,o=t.multiSection,i=t.renderInputComponent,a=t.renderItemsContainer,s=t.highlightedSectionIndex,l=t.highlightedItemIndex,u=this.state.isInputFocused,c=o?this.renderSections():this.renderItems(),d=null!==c,p=this.getItemId(s,l),h="react-autowhatever-".concat(n),m=f({role:"combobox","aria-haspopup":"listbox","aria-owns":h,"aria-expanded":d},e("react-autowhatever-".concat(n,"-container"),"container",d&&"containerOpen"),{},this.props.containerProps),g=i(f({type:"text",value:"",autoComplete:"off","aria-autocomplete":"list","aria-controls":h,"aria-activedescendant":p},e("react-autowhatever-".concat(n,"-input"),"input",d&&"inputOpen",u&&"inputFocused"),{},this.props.inputProps,{onFocus:this.onFocus,onBlur:this.onBlur,onKeyDown:this.props.inputProps.onKeyDown&&this.onKeyDown,ref:this.storeInputReference})),v=a({containerProps:f({id:h,role:"listbox"},e("react-autowhatever-".concat(n,"-items-container"),"itemsContainer",d&&"itemsContainerOpen"),{ref:this.storeItemsContainerReference}),children:c});return r.default.createElement("div",m,g,v)}}])&&m(t.prototype,n),c}(r.Component);t.default=E,b(E,"propTypes",{id:o.default.string,multiSection:o.default.bool,renderInputComponent:o.default.func,renderItemsContainer:o.default.func,items:o.default.array.isRequired,renderItem:o.default.func,renderItemData:o.default.object,renderSectionTitle:o.default.func,getSectionItems:o.default.func,containerProps:o.default.object,inputProps:o.default.object,itemProps:o.default.oneOfType([o.default.object,o.default.func]),highlightedSectionIndex:o.default.number,highlightedItemIndex:o.default.number,theme:o.default.oneOfType([o.default.object,o.default.array])}),b(E,"defaultProps",{id:"1",multiSection:!1,renderInputComponent:function(e){return r.default.createElement("input",e)},renderItemsContainer:function(e){var t=e.containerProps,n=e.children;return r.default.createElement("div",t,n)},renderItem:function(){throw new Error("`renderItem` must be provided")},renderItemData:w,renderSectionTitle:function(){throw new Error("`renderSectionTitle` must be provided")},getSectionItems:function(){throw new Error("`getSectionItems` must be provided")},containerProps:w,inputProps:w,itemProps:w,highlightedSectionIndex:null,highlightedItemIndex:null,theme:{container:"react-autowhatever__container",containerOpen:"react-autowhatever__container--open",input:"react-autowhatever__input",inputOpen:"react-autowhatever__input--open",inputFocused:"react-autowhatever__input--focused",itemsContainer:"react-autowhatever__items-container",itemsContainerOpen:"react-autowhatever__items-container--open",itemsList:"react-autowhatever__items-list",item:"react-autowhatever__item",itemFirst:"react-autowhatever__item--first",itemHighlighted:"react-autowhatever__item--highlighted",sectionContainer:"react-autowhatever__section-container",sectionContainerFirst:"react-autowhatever__section-container--first",sectionTitle:"react-autowhatever__section-title"}})},3541:(e,t,n)=>{"use strict";n.d(t,{A:()=>a});var r=n(4467),o=n(2765),i=n(8312);function a({props:e,name:t}){return(0,r.A)({props:e,name:t,defaultTheme:o.A,themeId:i.A})}},3542:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r={50:"#e8f5e9",100:"#c8e6c9",200:"#a5d6a7",300:"#81c784",400:"#66bb6a",500:"#4caf50",600:"#43a047",700:"#388e3c",800:"#2e7d32",900:"#1b5e20",A100:"#b9f6ca",A200:"#69f0ae",A400:"#00e676",A700:"#00c853"}},3544:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("circle",{cx:"12",cy:"12",r:"8"}),"FiberManualRecord");t.A=a},3571:(e,t,n)=>{"use strict";n.d(t,{A:()=>u});var r=n(3967),o=n(4620),i=n(6481),a=n(9452),s=n(8552);const l=function(){function e(e,t,n,o){const s={[e]:t,theme:n},l=o[e];if(!l)return{[e]:t};const{cssProperty:u=e,themeKey:c,transform:d,style:f}=l;if(null==t)return null;if("typography"===c&&"inherit"===t)return{[e]:t};const p=(0,i.Yn)(n,c)||{};return f?f(s):(0,a.NI)(s,t,(t=>{let n=(0,i.BO)(p,d,t);return t===n&&"string"==typeof t&&(n=(0,i.BO)(p,d,`${e}${"default"===t?"":(0,r.A)(t)}`,t)),!1===u?n:{[u]:n}}))}return function t(n){var r;const{sx:i,theme:l={}}=n||{};if(!i)return null;const u=null!=(r=l.unstable_sxConfig)?r:s.A;function c(n){let r=n;if("function"==typeof n)r=n(l);else if("object"!=typeof n)return n;if(!r)return null;const i=(0,a.EU)(l.breakpoints),s=Object.keys(i);let c=i;return Object.keys(r).forEach((n=>{const i="function"==typeof(s=r[n])?s(l):s;var s;if(null!=i)if("object"==typeof i)if(u[n])c=(0,o.A)(c,e(n,i,l,u));else{const e=(0,a.NI)({theme:l},i,(e=>({[n]:e})));!function(...e){const t=e.reduce(((e,t)=>e.concat(Object.keys(t))),[]),n=new Set(t);return e.every((e=>n.size===Object.keys(e).length))}(e,i)?c=(0,o.A)(c,e):c[n]=t({sx:i,theme:l})}else c=(0,o.A)(c,e(n,i,l,u))})),(0,a.vf)(s,c)}return Array.isArray(i)?i.map(c):c(i)}}();l.filterProps=["sx"];const u=l},3600:(e,t,n)=>{"use strict";e.exports=o;var r=n(4610);function o(e){if(!(this instanceof o))return new o(e);r.call(this,e)}n(6698)(o,r),o.prototype._transform=function(e,t,n){n(null,e)}},3626:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(7072);t.withOidcUser=r.withOidcUser;var o=n(8761);t.OidcSecure=o.default,t.withOidcSecure=o.withOidcSecure},3675:e=>{"use strict";var t=Object.prototype.propertyIsEnumerable;function n(e){var n=Object.getOwnPropertyNames(e);return Object.getOwnPropertySymbols&&(n=n.concat(Object.getOwnPropertySymbols(e))),n.filter((function(n){return t.call(e,n)}))}e.exports=Object.assign||function(e,t){for(var r,o,i=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),a=1;a<arguments.length;a++){r=arguments[a],o=n(Object(r));for(var s=0;s<o.length;s++)i[o[s]]=r[o[s]]}return i}},3726:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(8168);function o(e,t){const n=(0,r.A)({},t);return Object.keys(e).forEach((i=>{if(i.toString().match(/^(components|slots)$/))n[i]=(0,r.A)({},e[i],n[i]);else if(i.toString().match(/^(componentsProps|slotProps)$/)){const a=e[i]||{},s=t[i];n[i]={},s&&Object.keys(s)?a&&Object.keys(a)?(n[i]=(0,r.A)({},s),Object.keys(a).forEach((e=>{n[i][e]=o(a[e],s[e])}))):n[i]=s:n[i]=a}else void 0===n[i]&&(n[i]=e[i])})),n}},3738:e=>{function t(n){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(n)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},3749:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(4877).A},3951:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(6540),o=n(5684);const i=function(e=null){const t=r.useContext(o.T);return t&&(n=t,0!==Object.keys(n).length)?t:e;var n}},3967:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(9453);function o(e){if("string"!=typeof e)throw new Error((0,r.A)(7));return e.charAt(0).toUpperCase()+e.slice(1)}},4039:(e,t,n)=>{"use strict";var r="undefined"!=typeof Symbol&&Symbol,o=n(1333);e.exports=function(){return"function"==typeof r&&"function"==typeof Symbol&&"symbol"==typeof r("foo")&&"symbol"==typeof Symbol("bar")&&o()}},4146:(e,t,n)=>{"use strict";var r=n(3404),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},i={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},a={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},s={};function l(e){return r.isMemo(e)?a:s[e.$$typeof]||o}s[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},s[r.Memo]=a;var u=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(h){var o=p(n);o&&o!==h&&e(t,o,r)}var a=c(n);d&&(a=a.concat(d(n)));for(var s=l(t),m=l(n),g=0;g<a.length;++g){var v=a[g];if(!(i[v]||r&&r[v]||m&&m[v]||s&&s[v])){var y=f(n,v);try{u(t,v,y)}catch(e){}}}}return t}},4164:(e,t,n)=>{"use strict";function r(e){var t,n,o="";if("string"==typeof e||"number"==typeof e)o+=e;else if("object"==typeof e)if(Array.isArray(e)){var i=e.length;for(t=0;t<i;t++)e[t]&&(n=r(e[t]))&&(o&&(o+=" "),o+=n)}else for(n in e)e[n]&&(o&&(o+=" "),o+=n);return o}n.d(t,{A:()=>o});const o=function(){for(var e,t,n=0,o="",i=arguments.length;n<i;n++)(e=arguments[n])&&(t=r(e))&&(o&&(o+=" "),o+=t);return o}},4279:(e,t,n)=>{"use strict";n.d(t,{X4:()=>u,a:()=>d,e$:()=>c,eM:()=>l,tL:()=>f});var r=n(9453);function o(e,t=0,n=1){return Math.min(Math.max(t,e),n)}function i(e){if(e.type)return e;if("#"===e.charAt(0))return i(function(e){e=e.slice(1);const t=new RegExp(`.{1,${e.length>=6?2:1}}`,"g");let n=e.match(t);return n&&1===n[0].length&&(n=n.map((e=>e+e))),n?`rgb${4===n.length?"a":""}(${n.map(((e,t)=>t<3?parseInt(e,16):Math.round(parseInt(e,16)/255*1e3)/1e3)).join(", ")})`:""}(e));const t=e.indexOf("("),n=e.substring(0,t);if(-1===["rgb","rgba","hsl","hsla","color"].indexOf(n))throw new Error((0,r.A)(9,e));let o,a=e.substring(t+1,e.length-1);if("color"===n){if(a=a.split(" "),o=a.shift(),4===a.length&&"/"===a[3].charAt(0)&&(a[3]=a[3].slice(1)),-1===["srgb","display-p3","a98-rgb","prophoto-rgb","rec-2020"].indexOf(o))throw new Error((0,r.A)(10,o))}else a=a.split(",");return a=a.map((e=>parseFloat(e))),{type:n,values:a,colorSpace:o}}function a(e){const{type:t,colorSpace:n}=e;let{values:r}=e;return-1!==t.indexOf("rgb")?r=r.map(((e,t)=>t<3?parseInt(e,10):e)):-1!==t.indexOf("hsl")&&(r[1]=`${r[1]}%`,r[2]=`${r[2]}%`),r=-1!==t.indexOf("color")?`${n} ${r.join(" ")}`:`${r.join(", ")}`,`${t}(${r})`}function s(e){let t="hsl"===(e=i(e)).type||"hsla"===e.type?i(function(e){e=i(e);const{values:t}=e,n=t[0],r=t[1]/100,o=t[2]/100,s=r*Math.min(o,1-o),l=(e,t=(e+n/30)%12)=>o-s*Math.max(Math.min(t-3,9-t,1),-1);let u="rgb";const c=[Math.round(255*l(0)),Math.round(255*l(8)),Math.round(255*l(4))];return"hsla"===e.type&&(u+="a",c.push(t[3])),a({type:u,values:c})}(e)).values:e.values;return t=t.map((t=>("color"!==e.type&&(t/=255),t<=.03928?t/12.92:((t+.055)/1.055)**2.4))),Number((.2126*t[0]+.7152*t[1]+.0722*t[2]).toFixed(3))}function l(e,t){const n=s(e),r=s(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function u(e,t){return e=i(e),t=o(t),"rgb"!==e.type&&"hsl"!==e.type||(e.type+="a"),"color"===e.type?e.values[3]=`/${t}`:e.values[3]=t,a(e)}function c(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb")||-1!==e.type.indexOf("color"))for(let n=0;n<3;n+=1)e.values[n]*=1-t;return a(e)}function d(e,t){if(e=i(e),t=o(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(let n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;else if(-1!==e.type.indexOf("color"))for(let n=0;n<3;n+=1)e.values[n]+=(1-e.values[n])*t;return a(e)}function f(e,t=.15){return s(e)>.5?c(e,t):d(e,t)}},4363:(e,t,n)=>{"use strict";n(2799)},4373:e=>{e.exports=function(e){var t=Object(e),n=[];for(var r in t)n.unshift(r);return function e(){for(;n.length;)if((r=n.pop())in t)return e.value=r,e.done=!1,e;return e.done=!0,e}},e.exports.__esModule=!0,e.exports.default=e.exports},4467:(e,t,n)=>{"use strict";n.d(t,{A:()=>i});var r=n(7340),o=n(2858);function i({props:e,name:t,defaultTheme:n,themeId:i}){let a=(0,o.A)(n);return i&&(a=a[i]||a),(0,r.A)({theme:a,name:t,props:e})}},4525:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});function r(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}var o,i=(o=n(3675))&&o.__esModule?o:{default:o},a=function(e){return e};t.default=function(e){var t=function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&s.return&&s.return()}finally{if(o)throw i}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}(Array.isArray(e)&&2===e.length?e:[e,null],2),n=t[0],o=t[1];return function(e){for(var t=arguments.length,s=Array(t>1?t-1:0),l=1;l<t;l++)s[l-1]=arguments[l];var u=s.map((function(e){return n[e]})).filter(a);return"string"==typeof u[0]||"function"==typeof o?{key:e,className:o?o.apply(void 0,r(u)):u.join(" ")}:{key:e,style:i.default.apply(void 0,[{}].concat(r(u)))}}},e.exports=t.default},4606:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"}),"Sync");t.A=a},4610:(e,t,n)=>{"use strict";e.exports=c;var r=n(6048).F,o=r.ERR_METHOD_NOT_IMPLEMENTED,i=r.ERR_MULTIPLE_CALLBACK,a=r.ERR_TRANSFORM_ALREADY_TRANSFORMING,s=r.ERR_TRANSFORM_WITH_LENGTH_0,l=n(5382);function u(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(null===r)return this.emit("error",new i);n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var o=this._readableState;o.reading=!1,(o.needReadable||o.length<o.highWaterMark)&&this._read(o.highWaterMark)}function c(e){if(!(this instanceof c))return new c(e);l.call(this,e),this._transformState={afterTransform:u.bind(this),needTransform:!1,transforming:!1,writecb:null,writechunk:null,writeencoding:null},this._readableState.needReadable=!0,this._readableState.sync=!1,e&&("function"==typeof e.transform&&(this._transform=e.transform),"function"==typeof e.flush&&(this._flush=e.flush)),this.on("prefinish",d)}function d(){var e=this;"function"!=typeof this._flush||this._readableState.destroyed?f(this,null,null):this._flush((function(t,n){f(e,t,n)}))}function f(e,t,n){if(t)return e.emit("error",t);if(null!=n&&e.push(n),e._writableState.length)throw new s;if(e._transformState.transforming)throw new a;return e.push(null)}n(6698)(c,l),c.prototype.push=function(e,t){return this._transformState.needTransform=!1,l.prototype.push.call(this,e,t)},c.prototype._transform=function(e,t,n){n(new o("_transform()"))},c.prototype._write=function(e,t,n){var r=this._transformState;if(r.writecb=n,r.writechunk=e,r.writeencoding=t,!r.transforming){var o=this._readableState;(r.needTransform||o.needReadable||o.length<o.highWaterMark)&&this._read(o.highWaterMark)}},c.prototype._read=function(e){var t=this._transformState;null===t.writechunk||t.transforming?t.needTransform=!0:(t.transforming=!0,this._transform(t.writechunk,t.writeencoding,t.afterTransform))},c.prototype._destroy=function(e,t){l.prototype._destroy.call(this,e,(function(e){t(e)}))}},4620:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(3272);const o=function(e,t){return t?(0,r.A)(e,t,{clone:!1}):e}},4633:(e,t,n)=>{var r=n(5172),o=n(6993),i=n(5869),a=n(887),s=n(1791),l=n(4373),u=n(579);function c(){"use strict";var t=o(),n=t.m(c),d=(Object.getPrototypeOf?Object.getPrototypeOf(n):n.__proto__).constructor;function f(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===d||"GeneratorFunction"===(t.displayName||t.name))}var p={throw:1,return:2,break:3,continue:3};function h(e){var t,n;return function(r){t||(t={stop:function(){return n(r.a,2)},catch:function(){return r.v},abrupt:function(e,t){return n(r.a,p[e],t)},delegateYield:function(e,o,i){return t.resultName=o,n(r.d,u(e),i)},finish:function(e){return n(r.f,e)}},n=function(e,n,o){r.p=t.prev,r.n=t.next;try{return e(n,o)}finally{t.next=r.n}}),t.resultName&&(t[t.resultName]=r.v,t.resultName=void 0),t.sent=r.v,t.next=r.n;try{return e.call(this,t)}finally{r.p=t.prev,r.n=t.next}}}return(e.exports=c=function(){return{wrap:function(e,n,r,o){return t.w(h(e),n,r,o&&o.reverse())},isGeneratorFunction:f,mark:t.m,awrap:function(e,t){return new r(e,t)},AsyncIterator:s,async:function(e,t,n,r,o){return(f(t)?a:i)(h(e),t,n,r,o)},keys:l,values:u}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=c,e.exports.__esModule=!0,e.exports.default=e.exports},4634:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"}),"Replay");t.A=a},4643:(e,t,n)=>{function r(e){try{if(!n.g.localStorage)return!1}catch(e){return!1}var t=n.g.localStorage[e];return null!=t&&"true"===String(t).toLowerCase()}e.exports=function(e,t){if(r("noDeprecation"))return e;var n=!1;return function(){if(!n){if(r("throwDeprecation"))throw new Error(t);r("traceDeprecation")?console.trace(t):console.warn(t),n=!0}return e.apply(this,arguments)}}},4661:(e,t,n)=>{"use strict";var r;n.d(t,{A:()=>s});var o=n(6540);let i=0;const a=(r||(r=n.t(o,2)))["useId".toString()];function s(e){if(void 0!==a){const t=a();return null!=e?e:t}return function(e){const[t,n]=o.useState(e),r=e||t;return o.useEffect((()=>{null==t&&(i+=1,n(`mui-${i}`))}),[t]),r}(e)}},4680:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M9.4 16.6 4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0 4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"}),"Code");t.A=a},4691:function(e,t,n){"use strict";var r=this&&this.__spreadArrays||function(){for(var e=0,t=0,n=arguments.length;t<n;t++)e+=arguments[t].length;var r=Array(e),o=0;for(t=0;t<n;t++)for(var i=arguments[t],a=0,s=i.length;a<s;a++,o++)r[o]=i[a];return r};Object.defineProperty(t,"__esModule",{value:!0});var o=n(6405),i=o.Log.WARN,a=console;t.setLogger=function(e,t){var n=[o.Log.DEBUG,o.Log.INFO,o.Log.WARN,o.Log.ERROR,o.Log.NONE];if(-1===n.indexOf(e)){var r=n.join(", ");throw new RangeError("The log level must be one of "+r)}i=e,a=t,o.Log.level=e,o.Log.logger=t},t.oidcLog={debug:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];i>=o.Log.DEBUG&&a.debug.apply(a,r(["DEBUG [react-context-oidc] :"],e))},info:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];i>=o.Log.INFO&&a.info.apply(a,r(["INFO [react-context-oidc] :"],e))},warn:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];i>=o.Log.WARN&&a.warn.apply(a,r(["WARN [react-context-oidc] :"],e))},error:function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];i>=o.Log.ERROR&&a.error.apply(a,r(["ERROR [react-context-oidc] :"],e))},ERROR:o.Log.ERROR,WARN:o.Log.WARN,INFO:o.Log.INFO,NONE:o.Log.NONE,DEBUG:o.Log.DEBUG}},4705:(e,t,n)=>{"use strict";function r(e,t=166){let n;function r(...r){clearTimeout(n),n=setTimeout((()=>{e.apply(this,r)}),t)}return r.clear=()=>{clearTimeout(n)},r}n.d(t,{A:()=>r})},4720:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540)),i=r(n(1245)),a=n(8656);t.SessionLost=function(e){var t=e.onAuthenticate;return o.default.createElement("div",{className:"oidc-session-lost"},o.default.createElement("div",{className:"oidc-session-lost__container"},o.default.createElement("h1",{className:"oidc-session-lost__title"},"Session expirÃ©e"),o.default.createElement("p",{className:"oidc-session-lost__content"},"Votre session est expirÃ©e. Veuillez vous rÃ©-authentifier."),o.default.createElement("button",{className:"oidc-session-lost__button",type:"button",onClick:t},"RÃ©-authentifier")))},t.SessionLostContainer=function(e){var n=e.location,r=e.history,i=n.search.replace("?path=","");return o.default.createElement(t.SessionLost,{onAuthenticate:function(){a.authenticateUser(a.getUserManager(),n,r)(!0,i)}})},t.default=i.default(t.SessionLostContainer)},4736:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z"}),"Block");t.A=a},4755:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getPath=function(e){var t=function(e){var t=e.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/);return t&&{href:e,protocol:t[1],host:t[2],hostname:t[3],port:t[4],path:t[5],search:t[6],hash:t[7]}}(e),n=t.path,r=t.search,o=t.hash;return r&&(n+=r),o&&(n+=o),n}},4756:(e,t,n)=>{var r=n(4633)();e.exports=r;try{regeneratorRuntime=r}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=r:Function("r","regeneratorRuntime = r")(r)}},4765:e=>{"use strict";var t=String.prototype.replace,n=/%20/g,r="RFC3986";e.exports={default:r,formatters:{RFC1738:function(e){return t.call(e,n,"+")},RFC3986:function(e){return String(e)}},RFC1738:"RFC1738",RFC3986:r}},4807:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(6540)),a=o(n(5556)),s=n(347),l=n(4755),u=n(5448),c={notAuthenticated:a.default.elementType,notAuthorized:a.default.elementType,callbackComponent:a.default.elementType.isRequired,configuration:a.default.shape({redirect_uri:a.default.string.isRequired,silent_redirect_uri:a.default.string.isRequired}).isRequired,children:a.default.node},d=function(e){var t=e.notAuthenticated,n=e.notAuthorized,r=e.callbackComponent,o=e.sessionLost,a=e.configuration,c=e.children,d=i.useState(window.location.pathname),f=d[0],p=d[1],h=function(){return p(window.location.pathname)};i.useEffect((function(){return h(),window.addEventListener("popstate",h,!1),function(){return window.removeEventListener("popstate",h,!1)}}));var m=t||s.NotAuthenticated,g=n||s.NotAuthorized,v=o||s.SessionLost,y=l.getPath(a.silent_redirect_uri);switch(f){case l.getPath(a.redirect_uri):return i.default.createElement(r,null);case y:return i.default.createElement(u.SilentCallback,null);case"/authentication/not-authenticated":return i.default.createElement(m,null);case"/authentication/not-authorized":return i.default.createElement(g,null);case"/authentication/session-lost":return i.default.createElement(v,null);default:return i.default.createElement(i.default.Fragment,null,c)}};d.propTypes=c,d.defaultProps={notAuthenticated:null,notAuthorized:null},t.default=i.default.memo(d)},4848:(e,t,n)=>{"use strict";e.exports=n(1020)},4849:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540)),i=n(6405),a=n(8656),s=function(e){var t=e.logger;return o.useEffect((function(){new i.UserManager({}).signinSilentCallback(),t.info("callback silent signin successfull")})),o.default.createElement("div",null)};s.defaultProps={logger:a.oidcLog},t.default=s},4877:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(2325);function o(e){return(0,r.A)(e).defaultView||window}},4915:e=>{e.exports={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}},4967:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"m6 18 8.5-6L6 6v12zM16 6v12h2V6h-2z"}),"SkipNext");t.A=a},4994:e=>{e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.__esModule=!0,e.exports.default=e.exports},5157:e=>{e.exports=function(){throw new Error("Readable.from is not available in the browser")}},5172:e=>{e.exports=function(e,t){this.v=e,this.k=t},e.exports.__esModule=!0,e.exports.default=e.exports},5287:(e,t)=>{"use strict";var n=Symbol.for("react.element"),r=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),i=Symbol.for("react.strict_mode"),a=Symbol.for("react.profiler"),s=Symbol.for("react.provider"),l=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),f=Symbol.for("react.lazy"),p=Symbol.iterator,h={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},m=Object.assign,g={};function v(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||h}function y(){}function b(e,t,n){this.props=e,this.context=t,this.refs=g,this.updater=n||h}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},y.prototype=v.prototype;var w=b.prototype=new y;w.constructor=b,m(w,v.prototype),w.isPureReactComponent=!0;var E=Array.isArray,S=Object.prototype.hasOwnProperty,x={current:null},A={key:!0,ref:!0,__self:!0,__source:!0};function k(e,t,r){var o,i={},a=null,s=null;if(null!=t)for(o in void 0!==t.ref&&(s=t.ref),void 0!==t.key&&(a=""+t.key),t)S.call(t,o)&&!A.hasOwnProperty(o)&&(i[o]=t[o]);var l=arguments.length-2;if(1===l)i.children=r;else if(1<l){for(var u=Array(l),c=0;c<l;c++)u[c]=arguments[c+2];i.children=u}if(e&&e.defaultProps)for(o in l=e.defaultProps)void 0===i[o]&&(i[o]=l[o]);return{$$typeof:n,type:e,key:a,ref:s,props:i,_owner:x.current}}function _(e){return"object"==typeof e&&null!==e&&e.$$typeof===n}var C=/\/+/g;function O(e,t){return"object"==typeof e&&null!==e&&null!=e.key?function(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,(function(e){return t[e]}))}(""+e.key):t.toString(36)}function P(e,t,o,i,a){var s=typeof e;"undefined"!==s&&"boolean"!==s||(e=null);var l=!1;if(null===e)l=!0;else switch(s){case"string":case"number":l=!0;break;case"object":switch(e.$$typeof){case n:case r:l=!0}}if(l)return a=a(l=e),e=""===i?"."+O(l,0):i,E(a)?(o="",null!=e&&(o=e.replace(C,"$&/")+"/"),P(a,t,o,"",(function(e){return e}))):null!=a&&(_(a)&&(a=function(e,t){return{$$typeof:n,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}(a,o+(!a.key||l&&l.key===a.key?"":(""+a.key).replace(C,"$&/")+"/")+e)),t.push(a)),1;if(l=0,i=""===i?".":i+":",E(e))for(var u=0;u<e.length;u++){var c=i+O(s=e[u],u);l+=P(s,t,o,c,a)}else if(c=function(e){return null===e||"object"!=typeof e?null:"function"==typeof(e=p&&e[p]||e["@@iterator"])?e:null}(e),"function"==typeof c)for(e=c.call(e),u=0;!(s=e.next()).done;)l+=P(s=s.value,t,o,c=i+O(s,u++),a);else if("object"===s)throw t=String(e),Error("Objects are not valid as a React child (found: "+("[object Object]"===t?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return l}function R(e,t,n){if(null==e)return e;var r=[],o=0;return P(e,r,"","",(function(e){return t.call(n,e,o++)})),r}function T(e){if(-1===e._status){var t=e._result;(t=t()).then((function(t){0!==e._status&&-1!==e._status||(e._status=1,e._result=t)}),(function(t){0!==e._status&&-1!==e._status||(e._status=2,e._result=t)})),-1===e._status&&(e._status=0,e._result=t)}if(1===e._status)return e._result.default;throw e._result}var F={current:null},j={transition:null},N={ReactCurrentDispatcher:F,ReactCurrentBatchConfig:j,ReactCurrentOwner:x};t.Children={map:R,forEach:function(e,t,n){R(e,(function(){t.apply(this,arguments)}),n)},count:function(e){var t=0;return R(e,(function(){t++})),t},toArray:function(e){return R(e,(function(e){return e}))||[]},only:function(e){if(!_(e))throw Error("React.Children.only expected to receive a single React element child.");return e}},t.Component=v,t.Fragment=o,t.Profiler=a,t.PureComponent=b,t.StrictMode=i,t.Suspense=c,t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=N,t.cloneElement=function(e,t,r){if(null==e)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var o=m({},e.props),i=e.key,a=e.ref,s=e._owner;if(null!=t){if(void 0!==t.ref&&(a=t.ref,s=x.current),void 0!==t.key&&(i=""+t.key),e.type&&e.type.defaultProps)var l=e.type.defaultProps;for(u in t)S.call(t,u)&&!A.hasOwnProperty(u)&&(o[u]=void 0===t[u]&&void 0!==l?l[u]:t[u])}var u=arguments.length-2;if(1===u)o.children=r;else if(1<u){l=Array(u);for(var c=0;c<u;c++)l[c]=arguments[c+2];o.children=l}return{$$typeof:n,type:e.type,key:i,ref:a,props:o,_owner:s}},t.createContext=function(e){return(e={$$typeof:l,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null}).Provider={$$typeof:s,_context:e},e.Consumer=e},t.createElement=k,t.createFactory=function(e){var t=k.bind(null,e);return t.type=e,t},t.createRef=function(){return{current:null}},t.forwardRef=function(e){return{$$typeof:u,render:e}},t.isValidElement=_,t.lazy=function(e){return{$$typeof:f,_payload:{_status:-1,_result:e},_init:T}},t.memo=function(e,t){return{$$typeof:d,type:e,compare:void 0===t?null:t}},t.startTransition=function(e){var t=j.transition;j.transition={};try{e()}finally{j.transition=t}},t.unstable_act=function(){throw Error("act(...) is not supported in production builds of React.")},t.useCallback=function(e,t){return F.current.useCallback(e,t)},t.useContext=function(e){return F.current.useContext(e)},t.useDebugValue=function(){},t.useDeferredValue=function(e){return F.current.useDeferredValue(e)},t.useEffect=function(e,t){return F.current.useEffect(e,t)},t.useId=function(){return F.current.useId()},t.useImperativeHandle=function(e,t,n){return F.current.useImperativeHandle(e,t,n)},t.useInsertionEffect=function(e,t){return F.current.useInsertionEffect(e,t)},t.useLayoutEffect=function(e,t){return F.current.useLayoutEffect(e,t)},t.useMemo=function(e,t){return F.current.useMemo(e,t)},t.useReducer=function(e,t,n){return F.current.useReducer(e,t,n)},t.useRef=function(e){return F.current.useRef(e)},t.useState=function(e){return F.current.useState(e)},t.useSyncExternalStore=function(e,t,n){return F.current.useSyncExternalStore(e,t,n)},t.useTransition=function(){return F.current.useTransition()},t.version="18.2.0"},5291:(e,t,n)=>{"use strict";var r=n(6048).F.ERR_INVALID_OPT_VALUE;e.exports={getHighWaterMark:function(e,t,n,o){var i=function(e,t,n){return null!=e.highWaterMark?e.highWaterMark:t?e[n]:null}(t,o,n);if(null!=i){if(!isFinite(i)||Math.floor(i)!==i||i<0)throw new r(o?n:"highWaterMark",i);return Math.floor(i)}return e.objectMode?16:16384}}},5338:(e,t,n)=>{"use strict";var r=n(961);t.H=r.createRoot,r.hydrateRoot},5340:()=>{},5373:(e,t,n)=>{"use strict";var r=n(8636),o=n(2642),i=n(4765);e.exports={formats:i,parse:o,stringify:r}},5382:(e,t,n)=>{"use strict";var r=n(5606),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=c;var i=n(5412),a=n(6708);n(6698)(c,i);for(var s=o(a.prototype),l=0;l<s.length;l++){var u=s[l];c.prototype[u]||(c.prototype[u]=a.prototype[u])}function c(e){if(!(this instanceof c))return new c(e);i.call(this,e),a.call(this,e),this.allowHalfOpen=!0,e&&(!1===e.readable&&(this.readable=!1),!1===e.writable&&(this.writable=!1),!1===e.allowHalfOpen&&(this.allowHalfOpen=!1,this.once("end",d)))}function d(){this._writableState.ended||r.nextTick(f,this)}function f(e){e.end()}Object.defineProperty(c.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),Object.defineProperty(c.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(c.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(c.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&void 0!==this._writableState&&this._readableState.destroyed&&this._writableState.destroyed},set:function(e){void 0!==this._readableState&&void 0!==this._writableState&&(this._readableState.destroyed=e,this._writableState.destroyed=e)}})},5412:(e,t,n)=>{"use strict";var r,o=n(5606);e.exports=k,k.ReadableState=A,n(7007).EventEmitter;var i,a=function(e,t){return e.listeners(t).length},s=n(345),l=n(8287).Buffer,u=(void 0!==n.g?n.g:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{}).Uint8Array||function(){},c=n(9838);i=c&&c.debuglog?c.debuglog("stream"):function(){};var d,f,p,h=n(2726),m=n(5896),g=n(5291).getHighWaterMark,v=n(6048).F,y=v.ERR_INVALID_ARG_TYPE,b=v.ERR_STREAM_PUSH_AFTER_EOF,w=v.ERR_METHOD_NOT_IMPLEMENTED,E=v.ERR_STREAM_UNSHIFT_AFTER_END_EVENT;n(6698)(k,s);var S=m.errorOrDestroy,x=["error","close","destroy","pause","resume"];function A(e,t,o){r=r||n(5382),e=e||{},"boolean"!=typeof o&&(o=t instanceof r),this.objectMode=!!e.objectMode,o&&(this.objectMode=this.objectMode||!!e.readableObjectMode),this.highWaterMark=g(this,e,"readableHighWaterMark",o),this.buffer=new h,this.length=0,this.pipes=null,this.pipesCount=0,this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this.paused=!0,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.destroyed=!1,this.defaultEncoding=e.defaultEncoding||"utf8",this.awaitDrain=0,this.readingMore=!1,this.decoder=null,this.encoding=null,e.encoding&&(d||(d=n(3141).I),this.decoder=new d(e.encoding),this.encoding=e.encoding)}function k(e){if(r=r||n(5382),!(this instanceof k))return new k(e);var t=this instanceof r;this._readableState=new A(e,this,t),this.readable=!0,e&&("function"==typeof e.read&&(this._read=e.read),"function"==typeof e.destroy&&(this._destroy=e.destroy)),s.call(this)}function _(e,t,n,r,o){i("readableAddChunk",t);var a,s=e._readableState;if(null===t)s.reading=!1,function(e,t){if(i("onEofChunk"),!t.ended){if(t.decoder){var n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?R(e):(t.needReadable=!1,t.emittedReadable||(t.emittedReadable=!0,T(e)))}}(e,s);else if(o||(a=function(e,t){var n,r;return r=t,l.isBuffer(r)||r instanceof u||"string"==typeof t||void 0===t||e.objectMode||(n=new y("chunk",["string","Buffer","Uint8Array"],t)),n}(s,t)),a)S(e,a);else if(s.objectMode||t&&t.length>0)if("string"==typeof t||s.objectMode||Object.getPrototypeOf(t)===l.prototype||(t=function(e){return l.from(e)}(t)),r)s.endEmitted?S(e,new E):C(e,s,t,!0);else if(s.ended)S(e,new b);else{if(s.destroyed)return!1;s.reading=!1,s.decoder&&!n?(t=s.decoder.write(t),s.objectMode||0!==t.length?C(e,s,t,!1):F(e,s)):C(e,s,t,!1)}else r||(s.reading=!1,F(e,s));return!s.ended&&(s.length<s.highWaterMark||0===s.length)}function C(e,t,n,r){t.flowing&&0===t.length&&!t.sync?(t.awaitDrain=0,e.emit("data",n)):(t.length+=t.objectMode?1:n.length,r?t.buffer.unshift(n):t.buffer.push(n),t.needReadable&&R(e)),F(e,t)}Object.defineProperty(k.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._readableState&&this._readableState.destroyed},set:function(e){this._readableState&&(this._readableState.destroyed=e)}}),k.prototype.destroy=m.destroy,k.prototype._undestroy=m.undestroy,k.prototype._destroy=function(e,t){t(e)},k.prototype.push=function(e,t){var n,r=this._readableState;return r.objectMode?n=!0:"string"==typeof e&&((t=t||r.defaultEncoding)!==r.encoding&&(e=l.from(e,t),t=""),n=!0),_(this,e,t,!1,n)},k.prototype.unshift=function(e){return _(this,e,null,!0,!1)},k.prototype.isPaused=function(){return!1===this._readableState.flowing},k.prototype.setEncoding=function(e){d||(d=n(3141).I);var t=new d(e);this._readableState.decoder=t,this._readableState.encoding=this._readableState.decoder.encoding;for(var r=this._readableState.buffer.head,o="";null!==r;)o+=t.write(r.data),r=r.next;return this._readableState.buffer.clear(),""!==o&&this._readableState.buffer.push(o),this._readableState.length=o.length,this};var O=1073741824;function P(e,t){return e<=0||0===t.length&&t.ended?0:t.objectMode?1:e!=e?t.flowing&&t.length?t.buffer.head.data.length:t.length:(e>t.highWaterMark&&(t.highWaterMark=function(e){return e>=O?e=O:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function R(e){var t=e._readableState;i("emitReadable",t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(i("emitReadable",t.flowing),t.emittedReadable=!0,o.nextTick(T,e))}function T(e){var t=e._readableState;i("emitReadable_",t.destroyed,t.length,t.ended),t.destroyed||!t.length&&!t.ended||(e.emit("readable"),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,M(e)}function F(e,t){t.readingMore||(t.readingMore=!0,o.nextTick(j,e,t))}function j(e,t){for(;!t.reading&&!t.ended&&(t.length<t.highWaterMark||t.flowing&&0===t.length);){var n=t.length;if(i("maybeReadMore read 0"),e.read(0),n===t.length)break}t.readingMore=!1}function N(e){var t=e._readableState;t.readableListening=e.listenerCount("readable")>0,t.resumeScheduled&&!t.paused?t.flowing=!0:e.listenerCount("data")>0&&e.resume()}function I(e){i("readable nexttick read 0"),e.read(0)}function L(e,t){i("resume",t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit("resume"),M(e),t.flowing&&!t.reading&&e.read(0)}function M(e){var t=e._readableState;for(i("flow",t.flowing);t.flowing&&null!==e.read(););}function D(e,t){return 0===t.length?null:(t.objectMode?n=t.buffer.shift():!e||e>=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.first():t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n);var n}function B(e){var t=e._readableState;i("endReadable",t.endEmitted),t.endEmitted||(t.ended=!0,o.nextTick(U,t,e))}function U(e,t){if(i("endReadableNT",e.endEmitted,e.length),!e.endEmitted&&0===e.length&&(e.endEmitted=!0,t.readable=!1,t.emit("end"),e.autoDestroy)){var n=t._writableState;(!n||n.autoDestroy&&n.finished)&&t.destroy()}}function z(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1}k.prototype.read=function(e){i("read",e),e=parseInt(e,10);var t=this._readableState,n=e;if(0!==e&&(t.emittedReadable=!1),0===e&&t.needReadable&&((0!==t.highWaterMark?t.length>=t.highWaterMark:t.length>0)||t.ended))return i("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?B(this):R(this),null;if(0===(e=P(e,t))&&t.ended)return 0===t.length&&B(this),null;var r,o=t.needReadable;return i("need readable",o),(0===t.length||t.length-e<t.highWaterMark)&&i("length less than watermark",o=!0),t.ended||t.reading?i("reading or ended",o=!1):o&&(i("do read"),t.reading=!0,t.sync=!0,0===t.length&&(t.needReadable=!0),this._read(t.highWaterMark),t.sync=!1,t.reading||(e=P(n,t))),null===(r=e>0?D(e,t):null)?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.awaitDrain=0),0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&B(this)),null!==r&&this.emit("data",r),r},k.prototype._read=function(e){S(this,new w("_read()"))},k.prototype.pipe=function(e,t){var n=this,r=this._readableState;switch(r.pipesCount){case 0:r.pipes=e;break;case 1:r.pipes=[r.pipes,e];break;default:r.pipes.push(e)}r.pipesCount+=1,i("pipe count=%d opts=%j",r.pipesCount,t);var s=t&&!1===t.end||e===o.stdout||e===o.stderr?m:l;function l(){i("onend"),e.end()}r.endEmitted?o.nextTick(s):n.once("end",s),e.on("unpipe",(function t(o,a){i("onunpipe"),o===n&&a&&!1===a.hasUnpiped&&(a.hasUnpiped=!0,i("cleanup"),e.removeListener("close",p),e.removeListener("finish",h),e.removeListener("drain",u),e.removeListener("error",f),e.removeListener("unpipe",t),n.removeListener("end",l),n.removeListener("end",m),n.removeListener("data",d),c=!0,!r.awaitDrain||e._writableState&&!e._writableState.needDrain||u())}));var u=function(e){return function(){var t=e._readableState;i("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&a(e,"data")&&(t.flowing=!0,M(e))}}(n);e.on("drain",u);var c=!1;function d(t){i("ondata");var o=e.write(t);i("dest.write",o),!1===o&&((1===r.pipesCount&&r.pipes===e||r.pipesCount>1&&-1!==z(r.pipes,e))&&!c&&(i("false write response, pause",r.awaitDrain),r.awaitDrain++),n.pause())}function f(t){i("onerror",t),m(),e.removeListener("error",f),0===a(e,"error")&&S(e,t)}function p(){e.removeListener("finish",h),m()}function h(){i("onfinish"),e.removeListener("close",p),m()}function m(){i("unpipe"),n.unpipe(e)}return n.on("data",d),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?Array.isArray(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",f),e.once("close",p),e.once("finish",h),e.emit("pipe",n),r.flowing||(i("pipe resume"),n.resume()),e},k.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes||(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n)),this;if(!e){var r=t.pipes,o=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var i=0;i<o;i++)r[i].emit("unpipe",this,{hasUnpiped:!1});return this}var a=z(t.pipes,e);return-1===a||(t.pipes.splice(a,1),t.pipesCount-=1,1===t.pipesCount&&(t.pipes=t.pipes[0]),e.emit("unpipe",this,n)),this},k.prototype.on=function(e,t){var n=s.prototype.on.call(this,e,t),r=this._readableState;return"data"===e?(r.readableListening=this.listenerCount("readable")>0,!1!==r.flowing&&this.resume()):"readable"===e&&(r.endEmitted||r.readableListening||(r.readableListening=r.needReadable=!0,r.flowing=!1,r.emittedReadable=!1,i("on readable",r.length,r.reading),r.length?R(this):r.reading||o.nextTick(I,this))),n},k.prototype.addListener=k.prototype.on,k.prototype.removeListener=function(e,t){var n=s.prototype.removeListener.call(this,e,t);return"readable"===e&&o.nextTick(N,this),n},k.prototype.removeAllListeners=function(e){var t=s.prototype.removeAllListeners.apply(this,arguments);return"readable"!==e&&void 0!==e||o.nextTick(N,this),t},k.prototype.resume=function(){var e=this._readableState;return e.flowing||(i("resume"),e.flowing=!e.readableListening,function(e,t){t.resumeScheduled||(t.resumeScheduled=!0,o.nextTick(L,e,t))}(this,e)),e.paused=!1,this},k.prototype.pause=function(){return i("call pause flowing=%j",this._readableState.flowing),!1!==this._readableState.flowing&&(i("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState.paused=!0,this},k.prototype.wrap=function(e){var t=this,n=this._readableState,r=!1;for(var o in e.on("end",(function(){if(i("wrapped end"),n.decoder&&!n.ended){var e=n.decoder.end();e&&e.length&&t.push(e)}t.push(null)})),e.on("data",(function(o){i("wrapped data"),n.decoder&&(o=n.decoder.write(o)),n.objectMode&&null==o||(n.objectMode||o&&o.length)&&(t.push(o)||(r=!0,e.pause()))})),e)void 0===this[o]&&"function"==typeof e[o]&&(this[o]=function(t){return function(){return e[t].apply(e,arguments)}}(o));for(var a=0;a<x.length;a++)e.on(x[a],this.emit.bind(this,x[a]));return this._read=function(t){i("wrapped _read",t),r&&(r=!1,e.resume())},this},"function"==typeof Symbol&&(k.prototype[Symbol.asyncIterator]=function(){return void 0===f&&(f=n(2955)),f(this)}),Object.defineProperty(k.prototype,"readableHighWaterMark",{enumerable:!1,get:function(){return this._readableState.highWaterMark}}),Object.defineProperty(k.prototype,"readableBuffer",{enumerable:!1,get:function(){return this._readableState&&this._readableState.buffer}}),Object.defineProperty(k.prototype,"readableFlowing",{enumerable:!1,get:function(){return this._readableState.flowing},set:function(e){this._readableState&&(this._readableState.flowing=e)}}),k._fromList=D,Object.defineProperty(k.prototype,"readableLength",{enumerable:!1,get:function(){return this._readableState.length}}),"function"==typeof Symbol&&(k.from=function(e,t){return void 0===p&&(p=n(5157)),p(k,e,t)})},5448:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(4849);t.SilentCallback=r.default},5523:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M22 11h-4.17l3.24-3.24-1.41-1.42L15 11h-2V9l4.66-4.66-1.42-1.41L13 6.17V2h-2v4.17L7.76 2.93 6.34 4.34 11 9v2H9L4.34 6.34 2.93 7.76 6.17 11H2v2h4.17l-3.24 3.24 1.41 1.42L9 13h2v2l-4.66 4.66 1.42 1.41L11 17.83V22h2v-4.17l3.24 3.24 1.42-1.41L13 15v-2h2l4.66 4.66 1.41-1.42L17.83 13H22z"}),"AcUnit");t.A=a},5537:(e,t,n)=>{var r=n(8287).Buffer,o=n(5606),i=n(6688),a=n(6698),s=n(6917),l=n(8399),u=s.IncomingMessage,c=s.readyStates,d=e.exports=function(e){var t,n=this;l.Writable.call(n),n._opts=e,n._body=[],n._headers={},e.auth&&n.setHeader("Authorization","Basic "+r.from(e.auth).toString("base64")),Object.keys(e.headers).forEach((function(t){n.setHeader(t,e.headers[t])}));var o=!0;if("disable-fetch"===e.mode||"requestTimeout"in e&&!i.abortController)o=!1,t=!0;else if("prefer-streaming"===e.mode)t=!1;else if("allow-wrong-content-type"===e.mode)t=!i.overrideMimeType;else{if(e.mode&&"default"!==e.mode&&"prefer-fast"!==e.mode)throw new Error("Invalid value for opts.mode");t=!0}n._mode=function(e,t){return i.fetch&&t?"fetch":i.mozchunkedarraybuffer?"moz-chunked-arraybuffer":i.msstream?"ms-stream":i.arraybuffer&&e?"arraybuffer":"text"}(t,o),n._fetchTimer=null,n._socketTimeout=null,n._socketTimer=null,n.on("finish",(function(){n._onFinish()}))};a(d,l.Writable),d.prototype.setHeader=function(e,t){var n=e.toLowerCase();-1===f.indexOf(n)&&(this._headers[n]={name:e,value:t})},d.prototype.getHeader=function(e){var t=this._headers[e.toLowerCase()];return t?t.value:null},d.prototype.removeHeader=function(e){delete this._headers[e.toLowerCase()]},d.prototype._onFinish=function(){var e=this;if(!e._destroyed){var t=e._opts;"timeout"in t&&0!==t.timeout&&e.setTimeout(t.timeout);var r=e._headers,a=null;"GET"!==t.method&&"HEAD"!==t.method&&(a=new Blob(e._body,{type:(r["content-type"]||{}).value||""}));var s=[];if(Object.keys(r).forEach((function(e){var t=r[e].name,n=r[e].value;Array.isArray(n)?n.forEach((function(e){s.push([t,e])})):s.push([t,n])})),"fetch"===e._mode){var l=null;if(i.abortController){var u=new AbortController;l=u.signal,e._fetchAbortController=u,"requestTimeout"in t&&0!==t.requestTimeout&&(e._fetchTimer=n.g.setTimeout((function(){e.emit("requestTimeout"),e._fetchAbortController&&e._fetchAbortController.abort()}),t.requestTimeout))}n.g.fetch(e._opts.url,{method:e._opts.method,headers:s,body:a||void 0,mode:"cors",credentials:t.withCredentials?"include":"same-origin",signal:l}).then((function(t){e._fetchResponse=t,e._resetTimers(!1),e._connect()}),(function(t){e._resetTimers(!0),e._destroyed||e.emit("error",t)}))}else{var d=e._xhr=new n.g.XMLHttpRequest;try{d.open(e._opts.method,e._opts.url,!0)}catch(t){return void o.nextTick((function(){e.emit("error",t)}))}"responseType"in d&&(d.responseType=e._mode),"withCredentials"in d&&(d.withCredentials=!!t.withCredentials),"text"===e._mode&&"overrideMimeType"in d&&d.overrideMimeType("text/plain; charset=x-user-defined"),"requestTimeout"in t&&(d.timeout=t.requestTimeout,d.ontimeout=function(){e.emit("requestTimeout")}),s.forEach((function(e){d.setRequestHeader(e[0],e[1])})),e._response=null,d.onreadystatechange=function(){switch(d.readyState){case c.LOADING:case c.DONE:e._onXHRProgress()}},"moz-chunked-arraybuffer"===e._mode&&(d.onprogress=function(){e._onXHRProgress()}),d.onerror=function(){e._destroyed||(e._resetTimers(!0),e.emit("error",new Error("XHR error")))};try{d.send(a)}catch(t){return void o.nextTick((function(){e.emit("error",t)}))}}}},d.prototype._onXHRProgress=function(){var e=this;e._resetTimers(!1),function(e){try{var t=e.status;return null!==t&&0!==t}catch(e){return!1}}(e._xhr)&&!e._destroyed&&(e._response||e._connect(),e._response._onXHRProgress(e._resetTimers.bind(e)))},d.prototype._connect=function(){var e=this;e._destroyed||(e._response=new u(e._xhr,e._fetchResponse,e._mode,e._resetTimers.bind(e)),e._response.on("error",(function(t){e.emit("error",t)})),e.emit("response",e._response))},d.prototype._write=function(e,t,n){this._body.push(e),n()},d.prototype._resetTimers=function(e){var t=this;n.g.clearTimeout(t._socketTimer),t._socketTimer=null,e?(n.g.clearTimeout(t._fetchTimer),t._fetchTimer=null):t._socketTimeout&&(t._socketTimer=n.g.setTimeout((function(){t.emit("timeout")}),t._socketTimeout))},d.prototype.abort=d.prototype.destroy=function(e){var t=this;t._destroyed=!0,t._resetTimers(!0),t._response&&(t._response._destroyed=!0),t._xhr?t._xhr.abort():t._fetchAbortController&&t._fetchAbortController.abort(),e&&t.emit("error",e)},d.prototype.end=function(e,t,n){"function"==typeof e&&(n=e,e=void 0),l.Writable.prototype.end.call(this,e,t,n)},d.prototype.setTimeout=function(e,t){var n=this;t&&n.once("timeout",t),n._socketTimeout=e,n._resetTimers(!1)},d.prototype.flushHeaders=function(){},d.prototype.setNoDelay=function(){},d.prototype.setSocketKeepAlive=function(){};var f=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","cookie","cookie2","date","dnt","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","via"]},5546:e=>{function t(n,r,o,i){var a=Object.defineProperty;try{a({},"",{})}catch(n){a=0}e.exports=t=function(e,n,r,o){function i(n,r){t(e,n,(function(e){return this._invoke(n,r,e)}))}n?a?a(e,n,{value:r,enumerable:!o,configurable:!o,writable:!o}):e[n]=r:(i("next",0),i("throw",1),i("return",2))},e.exports.__esModule=!0,e.exports.default=e.exports,t(n,r,o,i)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},5556:(e,t,n)=>{e.exports=n(2694)()},5606:e=>{var t,n,r=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function i(){throw new Error("clearTimeout has not been defined")}function a(e){if(t===setTimeout)return setTimeout(e,0);if((t===o||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(n){try{return t.call(null,e,0)}catch(n){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:o}catch(e){t=o}try{n="function"==typeof clearTimeout?clearTimeout:i}catch(e){n=i}}();var s,l=[],u=!1,c=-1;function d(){u&&s&&(u=!1,s.length?l=s.concat(l):c=-1,l.length&&f())}function f(){if(!u){var e=a(d);u=!0;for(var t=l.length;t;){for(s=l,l=[];++c<t;)s&&s[c].run();c=-1,t=l.length}s=null,u=!1,function(e){if(n===clearTimeout)return clearTimeout(e);if((n===i||!n)&&clearTimeout)return n=clearTimeout,clearTimeout(e);try{return n(e)}catch(t){try{return n.call(null,e)}catch(t){return n.call(this,e)}}}(e)}}function p(e,t){this.fun=e,this.array=t}function h(){}r.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)t[n-1]=arguments[n];l.push(new p(e,t)),1!==l.length||u||a(f)},p.prototype.run=function(){this.fun.apply(null,this.array)},r.title="browser",r.browser=!0,r.env={},r.argv=[],r.version="",r.versions={},r.on=h,r.addListener=h,r.once=h,r.off=h,r.removeListener=h,r.removeAllListeners=h,r.emit=h,r.prependListener=h,r.prependOnceListener=h,r.listeners=function(e){return[]},r.binding=function(e){throw new Error("process.binding is not supported")},r.cwd=function(){return"/"},r.chdir=function(e){throw new Error("process.chdir is not supported")},r.umask=function(){return 0}},5659:(e,t,n)=>{"use strict";function r(e,t,n=void 0){const r={};return Object.keys(e).forEach((o=>{r[o]=e[o].reduce(((e,r)=>{if(r){const o=t(r);""!==o&&e.push(o),n&&n[r]&&e.push(n[r])}return e}),[]).join(" ")})),r}n.d(t,{A:()=>r})},5680:(e,t,n)=>{"use strict";var r=n(5767);e.exports=function(e){return!!r(e)}},5684:(e,t,n)=>{"use strict";n.d(t,{T:()=>l,i:()=>i,u:()=>u,w:()=>s});var r=n(6540),o=n(8966),i=(n(7308),n(1287),!0),a=r.createContext("undefined"!=typeof HTMLElement?(0,o.A)({key:"css"}):null);a.Provider;var s=function(e){return(0,r.forwardRef)((function(t,n){var o=(0,r.useContext)(a);return e(t,o,n)}))};i||(s=function(e){return function(t){var n=(0,r.useContext)(a);return null===n?(n=(0,o.A)({key:"css"}),r.createElement(a.Provider,{value:n},e(t,n))):e(t,n)}});var l=r.createContext({}),u=function(){return r.useContext(l)}},5697:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"}),"Sort");t.A=a},5767:(e,t,n)=>{"use strict";var r=n(2682),o=n(9209),i=n(487),a=n(8075),s=n(5795),l=a("Object.prototype.toString"),u=n(9092)(),c="undefined"==typeof globalThis?n.g:globalThis,d=o(),f=a("String.prototype.slice"),p=Object.getPrototypeOf,h=a("Array.prototype.indexOf",!0)||function(e,t){for(var n=0;n<e.length;n+=1)if(e[n]===t)return n;return-1},m={__proto__:null};r(d,u&&s&&p?function(e){var t=new c[e];if(Symbol.toStringTag in t){var n=p(t),r=s(n,Symbol.toStringTag);if(!r){var o=p(n);r=s(o,Symbol.toStringTag)}m["$"+e]=i(r.get)}}:function(e){var t=new c[e],n=t.slice||t.set;n&&(m["$"+e]=i(n))}),e.exports=function(e){if(!e||"object"!=typeof e)return!1;if(!u){var t=f(l(e),8,-1);return h(d,t)>-1?t:"Object"===t&&function(e){var t=!1;return r(m,(function(n,r){if(!t)try{n(e),t=f(r,1)}catch(e){}})),t}(e)}return s?function(e){var t=!1;return r(m,(function(n,r){if(!t)try{"$"+n(e)===r&&(t=f(r,1))}catch(e){}})),t}(e):null}},5795:(e,t,n)=>{"use strict";var r=n(453)("%Object.getOwnPropertyDescriptor%",!0);if(r)try{r([],"length")}catch(e){r=null}e.exports=r},5862:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(4807);t.OidcRoutes=r.default;var o=n(1245);t.withRouter=o.default,t.useHistory=o.useHistory},5869:(e,t,n)=>{var r=n(887);e.exports=function(e,t,n,o,i){var a=r(e,t,n,o,i);return a.next().then((function(e){return e.done?e.value:a.next()}))},e.exports.__esModule=!0,e.exports.default=e.exports},5878:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#f5f5f5",A200:"#eeeeee",A400:"#bdbdbd",A700:"#616161"}},5896:(e,t,n)=>{"use strict";var r=n(5606);function o(e,t){a(e,t),i(e)}function i(e){e._writableState&&!e._writableState.emitClose||e._readableState&&!e._readableState.emitClose||e.emit("close")}function a(e,t){e.emit("error",t)}e.exports={destroy:function(e,t){var n=this,s=this._readableState&&this._readableState.destroyed,l=this._writableState&&this._writableState.destroyed;return s||l?(t?t(e):e&&(this._writableState?this._writableState.errorEmitted||(this._writableState.errorEmitted=!0,r.nextTick(a,this,e)):r.nextTick(a,this,e)),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,(function(e){!t&&e?n._writableState?n._writableState.errorEmitted?r.nextTick(i,n):(n._writableState.errorEmitted=!0,r.nextTick(o,n,e)):r.nextTick(o,n,e):t?(r.nextTick(i,n),t(e)):r.nextTick(i,n)})),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finalCalled=!1,this._writableState.prefinished=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)},errorOrDestroy:function(e,t){var n=e._readableState,r=e._writableState;n&&n.autoDestroy||r&&r.autoDestroy?e.destroy(t):e.emit("error",t)}}},5897:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"}),"Info");t.A=a},5932:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M19.5 9.5c-1.03 0-1.9.62-2.29 1.5h-2.92c-.39-.88-1.26-1.5-2.29-1.5s-1.9.62-2.29 1.5H6.79c-.39-.88-1.26-1.5-2.29-1.5C3.12 9.5 2 10.62 2 12s1.12 2.5 2.5 2.5c1.03 0 1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5s1.9-.62 2.29-1.5h2.92c.39.88 1.26 1.5 2.29 1.5 1.38 0 2.5-1.12 2.5-2.5s-1.12-2.5-2.5-2.5z"}),"LinearScale");t.A=a},6048:e=>{"use strict";var t={};function n(e,n,r){r||(r=Error);var o=function(e){var t,r;function o(t,r,o){return e.call(this,function(e,t,r){return"string"==typeof n?n:n(e,t,r)}(t,r,o))||this}return r=e,(t=o).prototype=Object.create(r.prototype),t.prototype.constructor=t,t.__proto__=r,o}(r);o.prototype.name=r.name,o.prototype.code=e,t[e]=o}function r(e,t){if(Array.isArray(e)){var n=e.length;return e=e.map((function(e){return String(e)})),n>2?"one of ".concat(t," ").concat(e.slice(0,n-1).join(", "),", or ")+e[n-1]:2===n?"one of ".concat(t," ").concat(e[0]," or ").concat(e[1]):"of ".concat(t," ").concat(e[0])}return"of ".concat(t," ").concat(String(e))}n("ERR_INVALID_OPT_VALUE",(function(e,t){return'The value "'+t+'" is invalid for option "'+e+'"'}),TypeError),n("ERR_INVALID_ARG_TYPE",(function(e,t,n){var o,i,a,s,l;if("string"==typeof t&&(i="not ",t.substr(0,4)===i)?(o="must not be",t=t.replace(/^not /,"")):o="must be",function(e,t,n){return(void 0===n||n>e.length)&&(n=e.length),e.substring(n-9,n)===t}(e," argument"))a="The ".concat(e," ").concat(o," ").concat(r(t,"type"));else{var u=("number"!=typeof l&&(l=0),l+1>(s=e).length||-1===s.indexOf(".",l)?"argument":"property");a='The "'.concat(e,'" ').concat(u," ").concat(o," ").concat(r(t,"type"))}return a+". Received type ".concat(typeof n)}),TypeError),n("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF"),n("ERR_METHOD_NOT_IMPLEMENTED",(function(e){return"The "+e+" method is not implemented"})),n("ERR_STREAM_PREMATURE_CLOSE","Premature close"),n("ERR_STREAM_DESTROYED",(function(e){return"Cannot call "+e+" after a stream was destroyed"})),n("ERR_MULTIPLE_CALLBACK","Callback called multiple times"),n("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable"),n("ERR_STREAM_WRITE_AFTER_END","write after end"),n("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),n("ERR_UNKNOWN_ENCODING",(function(e){return"Unknown encoding: "+e}),TypeError),n("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event"),e.exports.F=t},6238:(e,t,n)=>{"use strict";var r=n(6048).F.ERR_STREAM_PREMATURE_CLOSE;function o(){}e.exports=function e(t,n,i){if("function"==typeof n)return e(t,null,n);n||(n={}),i=function(e){var t=!1;return function(){if(!t){t=!0;for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];e.apply(this,r)}}}(i||o);var a=n.readable||!1!==n.readable&&t.readable,s=n.writable||!1!==n.writable&&t.writable,l=function(){t.writable||c()},u=t._writableState&&t._writableState.finished,c=function(){s=!1,u=!0,a||i.call(t)},d=t._readableState&&t._readableState.endEmitted,f=function(){a=!1,d=!0,s||i.call(t)},p=function(e){i.call(t,e)},h=function(){var e;return a&&!d?(t._readableState&&t._readableState.ended||(e=new r),i.call(t,e)):s&&!u?(t._writableState&&t._writableState.ended||(e=new r),i.call(t,e)):void 0},m=function(){t.req.on("finish",c)};return function(e){return e.setHeader&&"function"==typeof e.abort}(t)?(t.on("complete",c),t.on("abort",h),t.req?m():t.on("request",m)):s&&!t._writableState&&(t.on("end",l),t.on("close",l)),t.on("end",f),t.on("finish",c),!1!==n.error&&t.on("error",p),t.on("close",h),function(){t.removeListener("complete",c),t.removeListener("abort",h),t.removeListener("request",m),t.req&&t.req.removeListener("finish",c),t.removeListener("end",l),t.removeListener("close",l),t.removeListener("finish",c),t.removeListener("end",f),t.removeListener("error",p),t.removeListener("close",h)}}},6248:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(2325).A},6269:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=o(n(6540));t.default=function(e,t){return function(n){return i.default.createElement(e,r({},n,t))}}},6288:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(6540);const o=function(e,t){var n,o;return r.isValidElement(e)&&-1!==t.indexOf(null!=(n=e.type.muiName)?n:null==(o=e.type)||null==(o=o._payload)||null==(o=o.value)?void 0:o.muiName)}},6289:(e,t,n)=>{"use strict";function r(e){var t=Object.create(null);return function(n){return void 0===t[n]&&(t[n]=e(n)),t[n]}}n.d(t,{A:()=>r})},6298:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"}),"Place");t.A=a},6314:e=>{"use strict";e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n="",r=void 0!==t[5];return t[4]&&(n+="@supports (".concat(t[4],") {")),t[2]&&(n+="@media ".concat(t[2]," {")),r&&(n+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),n+=e(t),r&&(n+="}"),t[2]&&(n+="}"),t[4]&&(n+="}"),n})).join("")},t.i=function(e,n,r,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var a={};if(r)for(var s=0;s<this.length;s++){var l=this[s][0];null!=l&&(a[l]=!0)}for(var u=0;u<e.length;u++){var c=[].concat(e[u]);r&&a[c[0]]||(void 0!==i&&(void 0===c[5]||(c[1]="@layer".concat(c[5].length>0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=i),n&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=n):c[2]=n),o&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=o):c[4]="".concat(o)),t.push(c))}},t}},6325:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"}),"Warning");t.A=a},6327:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6 19h4V5H6v14zm8-14v14h4V5h-4z"}),"Pause");t.A=a},6405:function(e){var t;t=function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=22)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o={debug:function(){},info:function(){},warn:function(){},error:function(){}},i=void 0,a=void 0;(t.Log=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.reset=function(){a=3,i=o},e.debug=function(){if(a>=4){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];i.debug.apply(i,Array.from(t))}},e.info=function(){if(a>=3){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];i.info.apply(i,Array.from(t))}},e.warn=function(){if(a>=2){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];i.warn.apply(i,Array.from(t))}},e.error=function(){if(a>=1){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];i.error.apply(i,Array.from(t))}},r(e,null,[{key:"NONE",get:function(){return 0}},{key:"ERROR",get:function(){return 1}},{key:"WARN",get:function(){return 2}},{key:"INFO",get:function(){return 3}},{key:"DEBUG",get:function(){return 4}},{key:"level",get:function(){return a},set:function(e){if(!(0<=e&&e<=4))throw new Error("Invalid log level");a=e}},{key:"logger",get:function(){return i},set:function(e){if(!e.debug&&e.info&&(e.debug=e.info),!(e.debug&&e.info&&e.warn&&e.error))throw new Error("Invalid logger");i=e}}]),e}()).reset()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o={setInterval:function(e){function t(t,n){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e,t){return setInterval(e,t)})),clearInterval:function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t}((function(e){return clearInterval(e)}))},i=!1,a=null;t.Global=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e._testing=function(){i=!0},e.setXMLHttpRequest=function(e){a=e},r(e,null,[{key:"location",get:function(){if(!i)return location}},{key:"localStorage",get:function(){if(!i&&"undefined"!=typeof window)return localStorage}},{key:"sessionStorage",get:function(){if(!i&&"undefined"!=typeof window)return sessionStorage}},{key:"XMLHttpRequest",get:function(){if(!i&&"undefined"!=typeof window)return a||XMLHttpRequest}},{key:"timer",get:function(){if(!i)return o}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.MetadataService=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(7),a=".well-known/openid-configuration";t.MetadataService=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.JsonService;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw o.Log.error("MetadataService: No settings passed to MetadataService"),new Error("settings");this._settings=t,this._jsonService=new n(["application/jwk-set+json"])}return e.prototype.resetSigningKeys=function(){this._settings=this._settings||{},this._settings.signingKeys=void 0},e.prototype.getMetadata=function(){var e=this;return this._settings.metadata?(o.Log.debug("MetadataService.getMetadata: Returning metadata from settings"),Promise.resolve(this._settings.metadata)):this.metadataUrl?(o.Log.debug("MetadataService.getMetadata: getting metadata from",this.metadataUrl),this._jsonService.getJson(this.metadataUrl).then((function(t){o.Log.debug("MetadataService.getMetadata: json received");var n=e._settings.metadataSeed||{};return e._settings.metadata=Object.assign({},n,t),e._settings.metadata}))):(o.Log.error("MetadataService.getMetadata: No authority or metadataUrl configured on settings"),Promise.reject(new Error("No authority or metadataUrl configured on settings")))},e.prototype.getIssuer=function(){return this._getMetadataProperty("issuer")},e.prototype.getAuthorizationEndpoint=function(){return this._getMetadataProperty("authorization_endpoint")},e.prototype.getUserInfoEndpoint=function(){return this._getMetadataProperty("userinfo_endpoint")},e.prototype.getTokenEndpoint=function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];return this._getMetadataProperty("token_endpoint",e)},e.prototype.getCheckSessionIframe=function(){return this._getMetadataProperty("check_session_iframe",!0)},e.prototype.getEndSessionEndpoint=function(){return this._getMetadataProperty("end_session_endpoint",!0)},e.prototype.getRevocationEndpoint=function(){return this._getMetadataProperty("revocation_endpoint",!0)},e.prototype.getKeysEndpoint=function(){return this._getMetadataProperty("jwks_uri",!0)},e.prototype._getMetadataProperty=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return o.Log.debug("MetadataService.getMetadataProperty for: "+e),this.getMetadata().then((function(n){if(o.Log.debug("MetadataService.getMetadataProperty: metadata recieved"),void 0===n[e]){if(!0===t)return void o.Log.warn("MetadataService.getMetadataProperty: Metadata does not contain optional property "+e);throw o.Log.error("MetadataService.getMetadataProperty: Metadata does not contain property "+e),new Error("Metadata does not contain property "+e)}return n[e]}))},e.prototype.getSigningKeys=function(){var e=this;return this._settings.signingKeys?(o.Log.debug("MetadataService.getSigningKeys: Returning signingKeys from settings"),Promise.resolve(this._settings.signingKeys)):this._getMetadataProperty("jwks_uri").then((function(t){return o.Log.debug("MetadataService.getSigningKeys: jwks_uri received",t),e._jsonService.getJson(t).then((function(t){if(o.Log.debug("MetadataService.getSigningKeys: key set received",t),!t.keys)throw o.Log.error("MetadataService.getSigningKeys: Missing keys on keyset"),new Error("Missing keys on keyset");return e._settings.signingKeys=t.keys,e._settings.signingKeys}))}))},r(e,[{key:"metadataUrl",get:function(){return this._metadataUrl||(this._settings.metadataUrl?this._metadataUrl=this._settings.metadataUrl:(this._metadataUrl=this._settings.authority,this._metadataUrl&&this._metadataUrl.indexOf(a)<0&&("/"!==this._metadataUrl[this._metadataUrl.length-1]&&(this._metadataUrl+="/"),this._metadataUrl+=a))),this._metadataUrl}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.UrlUtility=void 0;var r=n(0),o=n(1);t.UrlUtility=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.addQueryParam=function(e,t,n){return e.indexOf("?")<0&&(e+="?"),"?"!==e[e.length-1]&&(e+="&"),e+=encodeURIComponent(t),(e+="=")+encodeURIComponent(n)},e.parseUrlFragment=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"#",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:o.Global;"string"!=typeof e&&(e=n.location.href);var i=e.lastIndexOf(t);i>=0&&(e=e.substr(i+1)),"?"===t&&(i=e.indexOf("#"))>=0&&(e=e.substr(0,i));for(var a,s={},l=/([^&=]+)=([^&]*)/g,u=0;a=l.exec(e);)if(s[decodeURIComponent(a[1])]=decodeURIComponent(a[2].replace(/\+/g," ")),u++>50)return r.Log.error("UrlUtility.parseUrlFragment: response exceeded expected number of parameters",e),{error:"Response exceeded expected number of parameters"};for(var c in s)return s;return{}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.JoseUtil=void 0;var r=n(26),o=function(e){return e&&e.__esModule?e:{default:e}}(n(33));t.JoseUtil=(0,o.default)({jws:r.jws,KeyUtil:r.KeyUtil,X509:r.X509,crypto:r.crypto,hextob64u:r.hextob64u,b64tohex:r.b64tohex,AllowedSigningAlgs:r.AllowedSigningAlgs})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OidcClientSettings=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),i=n(0),a=n(23),s=n(6),l=n(24),u=n(2),c=".well-known/openid-configuration";t.OidcClientSettings=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.authority,o=t.metadataUrl,i=t.metadata,c=t.signingKeys,d=t.metadataSeed,f=t.client_id,p=t.client_secret,h=t.response_type,m=void 0===h?"id_token":h,g=t.scope,v=void 0===g?"openid":g,y=t.redirect_uri,b=t.post_logout_redirect_uri,w=t.client_authentication,E=void 0===w?"client_secret_post":w,S=t.prompt,x=t.display,A=t.max_age,k=t.ui_locales,_=t.acr_values,C=t.resource,O=t.response_mode,P=t.filterProtocolClaims,R=void 0===P||P,T=t.loadUserInfo,F=void 0===T||T,j=t.staleStateAge,N=void 0===j?900:j,I=t.clockSkew,L=void 0===I?300:I,M=t.clockService,D=void 0===M?new a.ClockService:M,B=t.userInfoJwtIssuer,U=void 0===B?"OP":B,z=t.mergeClaims,$=void 0!==z&&z,H=t.stateStore,W=void 0===H?new s.WebStorageStateStore:H,V=t.ResponseValidatorCtor,q=void 0===V?l.ResponseValidator:V,K=t.MetadataServiceCtor,G=void 0===K?u.MetadataService:K,J=t.extraQueryParams,Y=void 0===J?{}:J,X=t.extraTokenParams,Q=void 0===X?{}:X;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._authority=n,this._metadataUrl=o,this._metadata=i,this._metadataSeed=d,this._signingKeys=c,this._client_id=f,this._client_secret=p,this._response_type=m,this._scope=v,this._redirect_uri=y,this._post_logout_redirect_uri=b,this._client_authentication=E,this._prompt=S,this._display=x,this._max_age=A,this._ui_locales=k,this._acr_values=_,this._resource=C,this._response_mode=O,this._filterProtocolClaims=!!R,this._loadUserInfo=!!F,this._staleStateAge=N,this._clockSkew=L,this._clockService=D,this._userInfoJwtIssuer=U,this._mergeClaims=!!$,this._stateStore=W,this._validator=new q(this),this._metadataService=new G(this),this._extraQueryParams="object"===(void 0===Y?"undefined":r(Y))?Y:{},this._extraTokenParams="object"===(void 0===Q?"undefined":r(Q))?Q:{}}return e.prototype.getEpochTime=function(){return this._clockService.getEpochTime()},o(e,[{key:"client_id",get:function(){return this._client_id},set:function(e){if(this._client_id)throw i.Log.error("OidcClientSettings.set_client_id: client_id has already been assigned."),new Error("client_id has already been assigned.");this._client_id=e}},{key:"client_secret",get:function(){return this._client_secret}},{key:"response_type",get:function(){return this._response_type}},{key:"scope",get:function(){return this._scope}},{key:"redirect_uri",get:function(){return this._redirect_uri}},{key:"post_logout_redirect_uri",get:function(){return this._post_logout_redirect_uri}},{key:"client_authentication",get:function(){return this._client_authentication}},{key:"prompt",get:function(){return this._prompt}},{key:"display",get:function(){return this._display}},{key:"max_age",get:function(){return this._max_age}},{key:"ui_locales",get:function(){return this._ui_locales}},{key:"acr_values",get:function(){return this._acr_values}},{key:"resource",get:function(){return this._resource}},{key:"response_mode",get:function(){return this._response_mode}},{key:"authority",get:function(){return this._authority},set:function(e){if(this._authority)throw i.Log.error("OidcClientSettings.set_authority: authority has already been assigned."),new Error("authority has already been assigned.");this._authority=e}},{key:"metadataUrl",get:function(){return this._metadataUrl||(this._metadataUrl=this.authority,this._metadataUrl&&this._metadataUrl.indexOf(c)<0&&("/"!==this._metadataUrl[this._metadataUrl.length-1]&&(this._metadataUrl+="/"),this._metadataUrl+=c)),this._metadataUrl}},{key:"metadata",get:function(){return this._metadata},set:function(e){this._metadata=e}},{key:"metadataSeed",get:function(){return this._metadataSeed},set:function(e){this._metadataSeed=e}},{key:"signingKeys",get:function(){return this._signingKeys},set:function(e){this._signingKeys=e}},{key:"filterProtocolClaims",get:function(){return this._filterProtocolClaims}},{key:"loadUserInfo",get:function(){return this._loadUserInfo}},{key:"staleStateAge",get:function(){return this._staleStateAge}},{key:"clockSkew",get:function(){return this._clockSkew}},{key:"userInfoJwtIssuer",get:function(){return this._userInfoJwtIssuer}},{key:"mergeClaims",get:function(){return this._mergeClaims}},{key:"stateStore",get:function(){return this._stateStore}},{key:"validator",get:function(){return this._validator}},{key:"metadataService",get:function(){return this._metadataService}},{key:"extraQueryParams",get:function(){return this._extraQueryParams},set:function(e){"object"===(void 0===e?"undefined":r(e))?this._extraQueryParams=e:this._extraQueryParams={}}},{key:"extraTokenParams",get:function(){return this._extraTokenParams},set:function(e){"object"===(void 0===e?"undefined":r(e))?this._extraTokenParams=e:this._extraTokenParams={}}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.WebStorageStateStore=void 0;var r=n(0),o=n(1);t.WebStorageStateStore=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.prefix,r=void 0===n?"oidc.":n,i=t.store,a=void 0===i?o.Global.localStorage:i;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._store=a,this._prefix=r}return e.prototype.set=function(e,t){return r.Log.debug("WebStorageStateStore.set",e),e=this._prefix+e,this._store.setItem(e,t),Promise.resolve()},e.prototype.get=function(e){r.Log.debug("WebStorageStateStore.get",e),e=this._prefix+e;var t=this._store.getItem(e);return Promise.resolve(t)},e.prototype.remove=function(e){r.Log.debug("WebStorageStateStore.remove",e),e=this._prefix+e;var t=this._store.getItem(e);return this._store.removeItem(e),Promise.resolve(t)},e.prototype.getAllKeys=function(){r.Log.debug("WebStorageStateStore.getAllKeys");for(var e=[],t=0;t<this._store.length;t++){var n=this._store.key(t);0===n.indexOf(this._prefix)&&e.push(n.substr(this._prefix.length))}return Promise.resolve(e)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.JsonService=void 0;var r=n(0),o=n(1);t.JsonService=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:o.Global.XMLHttpRequest,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),t&&Array.isArray(t)?this._contentTypes=t.slice():this._contentTypes=[],this._contentTypes.push("application/json"),r&&this._contentTypes.push("application/jwt"),this._XMLHttpRequest=n,this._jwtHandler=r}return e.prototype.getJson=function(e,t){var n=this;if(!e)throw r.Log.error("JsonService.getJson: No url passed"),new Error("url");return r.Log.debug("JsonService.getJson, url: ",e),new Promise((function(o,i){var a=new n._XMLHttpRequest;a.open("GET",e);var s=n._contentTypes,l=n._jwtHandler;a.onload=function(){if(r.Log.debug("JsonService.getJson: HTTP response received, status",a.status),200===a.status){var t=a.getResponseHeader("Content-Type");if(t){var n=s.find((function(e){if(t.startsWith(e))return!0}));if("application/jwt"==n)return void l(a).then(o,i);if(n)try{return void o(JSON.parse(a.responseText))}catch(e){return r.Log.error("JsonService.getJson: Error parsing JSON response",e.message),void i(e)}}i(Error("Invalid response Content-Type: "+t+", from URL: "+e))}else i(Error(a.statusText+" ("+a.status+")"))},a.onerror=function(){r.Log.error("JsonService.getJson: network error"),i(Error("Network Error"))},t&&(r.Log.debug("JsonService.getJson: token passed, setting Authorization header"),a.setRequestHeader("Authorization","Bearer "+t)),a.send()}))},e.prototype.postForm=function(e,t,n){var o=this;if(!e)throw r.Log.error("JsonService.postForm: No url passed"),new Error("url");return r.Log.debug("JsonService.postForm, url: ",e),new Promise((function(i,a){var s=new o._XMLHttpRequest;s.open("POST",e);var l=o._contentTypes;s.onload=function(){if(r.Log.debug("JsonService.postForm: HTTP response received, status",s.status),200!==s.status){if(400===s.status&&(n=s.getResponseHeader("Content-Type"))&&l.find((function(e){if(n.startsWith(e))return!0})))try{var t=JSON.parse(s.responseText);if(t&&t.error)return r.Log.error("JsonService.postForm: Error from server: ",t.error),void a(new Error(t.error))}catch(e){return r.Log.error("JsonService.postForm: Error parsing JSON response",e.message),void a(e)}a(Error(s.statusText+" ("+s.status+")"))}else{var n;if((n=s.getResponseHeader("Content-Type"))&&l.find((function(e){if(n.startsWith(e))return!0})))try{return void i(JSON.parse(s.responseText))}catch(e){return r.Log.error("JsonService.postForm: Error parsing JSON response",e.message),void a(e)}a(Error("Invalid response Content-Type: "+n+", from URL: "+e))}},s.onerror=function(){r.Log.error("JsonService.postForm: network error"),a(Error("Network Error"))};var u="";for(var c in t){var d=t[c];d&&(u.length>0&&(u+="&"),u+=encodeURIComponent(c),u+="=",u+=encodeURIComponent(d))}s.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),void 0!==n&&s.setRequestHeader("Authorization","Basic "+btoa(n)),s.send(u)}))},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SigninRequest=void 0;var r=n(0),o=n(3),i=n(13);t.SigninRequest=function(){function e(t){var n=t.url,a=t.client_id,s=t.redirect_uri,l=t.response_type,u=t.scope,c=t.authority,d=t.data,f=t.prompt,p=t.display,h=t.max_age,m=t.ui_locales,g=t.id_token_hint,v=t.login_hint,y=t.acr_values,b=t.resource,w=t.response_mode,E=t.request,S=t.request_uri,x=t.extraQueryParams,A=t.request_type,k=t.client_secret,_=t.extraTokenParams,C=t.skipUserInfo;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!n)throw r.Log.error("SigninRequest.ctor: No url passed"),new Error("url");if(!a)throw r.Log.error("SigninRequest.ctor: No client_id passed"),new Error("client_id");if(!s)throw r.Log.error("SigninRequest.ctor: No redirect_uri passed"),new Error("redirect_uri");if(!l)throw r.Log.error("SigninRequest.ctor: No response_type passed"),new Error("response_type");if(!u)throw r.Log.error("SigninRequest.ctor: No scope passed"),new Error("scope");if(!c)throw r.Log.error("SigninRequest.ctor: No authority passed"),new Error("authority");var O=e.isOidc(l),P=e.isCode(l);w||(w=e.isCode(l)?"query":null),this.state=new i.SigninState({nonce:O,data:d,client_id:a,authority:c,redirect_uri:s,code_verifier:P,request_type:A,response_mode:w,client_secret:k,scope:u,extraTokenParams:_,skipUserInfo:C}),n=o.UrlUtility.addQueryParam(n,"client_id",a),n=o.UrlUtility.addQueryParam(n,"redirect_uri",s),n=o.UrlUtility.addQueryParam(n,"response_type",l),n=o.UrlUtility.addQueryParam(n,"scope",u),n=o.UrlUtility.addQueryParam(n,"state",this.state.id),O&&(n=o.UrlUtility.addQueryParam(n,"nonce",this.state.nonce)),P&&(n=o.UrlUtility.addQueryParam(n,"code_challenge",this.state.code_challenge),n=o.UrlUtility.addQueryParam(n,"code_challenge_method","S256"));var R={prompt:f,display:p,max_age:h,ui_locales:m,id_token_hint:g,login_hint:v,acr_values:y,resource:b,request:E,request_uri:S,response_mode:w};for(var T in R)R[T]&&(n=o.UrlUtility.addQueryParam(n,T,R[T]));for(var F in x)n=o.UrlUtility.addQueryParam(n,F,x[F]);this.url=n}return e.isOidc=function(e){return!!e.split(/\s+/g).filter((function(e){return"id_token"===e}))[0]},e.isOAuth=function(e){return!!e.split(/\s+/g).filter((function(e){return"token"===e}))[0]},e.isCode=function(e){return!!e.split(/\s+/g).filter((function(e){return"code"===e}))[0]},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.State=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=function(e){return e&&e.__esModule?e:{default:e}}(n(14));t.State=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.id,r=t.data,o=t.created,a=t.request_type;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._id=n||(0,i.default)(),this._data=r,this._created="number"==typeof o&&o>0?o:parseInt(Date.now()/1e3),this._request_type=a}return e.prototype.toStorageString=function(){return o.Log.debug("State.toStorageString"),JSON.stringify({id:this.id,data:this.data,created:this.created,request_type:this.request_type})},e.fromStorageString=function(t){return o.Log.debug("State.fromStorageString"),new e(JSON.parse(t))},e.clearStaleState=function(t,n){var r=Date.now()/1e3-n;return t.getAllKeys().then((function(n){o.Log.debug("State.clearStaleState: got keys",n);for(var i=[],a=function(a){var s=n[a];l=t.get(s).then((function(n){var i=!1;if(n)try{var a=e.fromStorageString(n);o.Log.debug("State.clearStaleState: got item from key: ",s,a.created),a.created<=r&&(i=!0)}catch(e){o.Log.error("State.clearStaleState: Error parsing state for key",s,e.message),i=!0}else o.Log.debug("State.clearStaleState: no item in storage for key: ",s),i=!0;if(i)return o.Log.debug("State.clearStaleState: removed item for key: ",s),t.remove(s)})),i.push(l)},s=0;s<n.length;s++){var l;a(s)}return o.Log.debug("State.clearStaleState: waiting on promise count:",i.length),Promise.all(i)}))},r(e,[{key:"id",get:function(){return this._id}},{key:"data",get:function(){return this._data}},{key:"created",get:function(){return this._created}},{key:"request_type",get:function(){return this._request_type}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OidcClient=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(5),a=n(12),s=n(8),l=n(34),u=n(35),c=n(36),d=n(13),f=n(9);t.OidcClient=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),t instanceof i.OidcClientSettings?this._settings=t:this._settings=new i.OidcClientSettings(t)}return e.prototype.createSigninRequest=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.response_type,r=t.scope,i=t.redirect_uri,a=t.data,l=t.state,u=t.prompt,c=t.display,d=t.max_age,f=t.ui_locales,p=t.id_token_hint,h=t.login_hint,m=t.acr_values,g=t.resource,v=t.request,y=t.request_uri,b=t.response_mode,w=t.extraQueryParams,E=t.extraTokenParams,S=t.request_type,x=t.skipUserInfo,A=arguments[1];o.Log.debug("OidcClient.createSigninRequest");var k=this._settings.client_id;n=n||this._settings.response_type,r=r||this._settings.scope,i=i||this._settings.redirect_uri,u=u||this._settings.prompt,c=c||this._settings.display,d=d||this._settings.max_age,f=f||this._settings.ui_locales,m=m||this._settings.acr_values,g=g||this._settings.resource,b=b||this._settings.response_mode,w=w||this._settings.extraQueryParams,E=E||this._settings.extraTokenParams;var _=this._settings.authority;return s.SigninRequest.isCode(n)&&"code"!==n?Promise.reject(new Error("OpenID Connect hybrid flow is not supported")):this._metadataService.getAuthorizationEndpoint().then((function(t){o.Log.debug("OidcClient.createSigninRequest: Received authorization endpoint",t);var C=new s.SigninRequest({url:t,client_id:k,redirect_uri:i,response_type:n,scope:r,data:a||l,authority:_,prompt:u,display:c,max_age:d,ui_locales:f,id_token_hint:p,login_hint:h,acr_values:m,resource:g,request:v,request_uri:y,extraQueryParams:w,extraTokenParams:E,request_type:S,response_mode:b,client_secret:e._settings.client_secret,skipUserInfo:x}),O=C.state;return(A=A||e._stateStore).set(O.id,O.toStorageString()).then((function(){return C}))}))},e.prototype.readSigninResponseState=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];o.Log.debug("OidcClient.readSigninResponseState");var r="query"===this._settings.response_mode||!this._settings.response_mode&&s.SigninRequest.isCode(this._settings.response_type)?"?":"#",i=new l.SigninResponse(e,r);return i.state?(t=t||this._stateStore,(n?t.remove.bind(t):t.get.bind(t))(i.state).then((function(e){if(!e)throw o.Log.error("OidcClient.readSigninResponseState: No matching state found in storage"),new Error("No matching state found in storage");return{state:d.SigninState.fromStorageString(e),response:i}}))):(o.Log.error("OidcClient.readSigninResponseState: No state in response"),Promise.reject(new Error("No state in response")))},e.prototype.processSigninResponse=function(e,t){var n=this;return o.Log.debug("OidcClient.processSigninResponse"),this.readSigninResponseState(e,t,!0).then((function(e){var t=e.state,r=e.response;return o.Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response"),n._validator.validateSigninResponse(t,r)}))},e.prototype.createSignoutRequest=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.id_token_hint,r=t.data,i=t.state,a=t.post_logout_redirect_uri,s=t.extraQueryParams,l=t.request_type,c=arguments[1];return o.Log.debug("OidcClient.createSignoutRequest"),a=a||this._settings.post_logout_redirect_uri,s=s||this._settings.extraQueryParams,this._metadataService.getEndSessionEndpoint().then((function(t){if(!t)throw o.Log.error("OidcClient.createSignoutRequest: No end session endpoint url returned"),new Error("no end session endpoint");o.Log.debug("OidcClient.createSignoutRequest: Received end session endpoint",t);var d=new u.SignoutRequest({url:t,id_token_hint:n,post_logout_redirect_uri:a,data:r||i,extraQueryParams:s,request_type:l}),f=d.state;return f&&(o.Log.debug("OidcClient.createSignoutRequest: Signout request has state to persist"),(c=c||e._stateStore).set(f.id,f.toStorageString())),d}))},e.prototype.readSignoutResponseState=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];o.Log.debug("OidcClient.readSignoutResponseState");var r=new c.SignoutResponse(e);if(!r.state)return o.Log.debug("OidcClient.readSignoutResponseState: No state in response"),r.error?(o.Log.warn("OidcClient.readSignoutResponseState: Response was error: ",r.error),Promise.reject(new a.ErrorResponse(r))):Promise.resolve({state:void 0,response:r});var i=r.state;return t=t||this._stateStore,(n?t.remove.bind(t):t.get.bind(t))(i).then((function(e){if(!e)throw o.Log.error("OidcClient.readSignoutResponseState: No matching state found in storage"),new Error("No matching state found in storage");return{state:f.State.fromStorageString(e),response:r}}))},e.prototype.processSignoutResponse=function(e,t){var n=this;return o.Log.debug("OidcClient.processSignoutResponse"),this.readSignoutResponseState(e,t,!0).then((function(e){var t=e.state,r=e.response;return t?(o.Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response"),n._validator.validateSignoutResponse(t,r)):(o.Log.debug("OidcClient.processSignoutResponse: No state from storage; skipping validating response"),r)}))},e.prototype.clearStaleState=function(e){return o.Log.debug("OidcClient.clearStaleState"),e=e||this._stateStore,f.State.clearStaleState(e,this.settings.staleStateAge)},r(e,[{key:"_stateStore",get:function(){return this.settings.stateStore}},{key:"_validator",get:function(){return this.settings.validator}},{key:"_metadataService",get:function(){return this.settings.metadataService}},{key:"settings",get:function(){return this._settings}},{key:"metadataService",get:function(){return this._metadataService}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TokenClient=void 0;var r=n(7),o=n(2),i=n(0);t.TokenClient=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r.JsonService,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:o.MetadataService;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw i.Log.error("TokenClient.ctor: No settings passed"),new Error("settings");this._settings=t,this._jsonService=new n,this._metadataService=new a(this._settings)}return e.prototype.exchangeCode=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(t=Object.assign({},t)).grant_type=t.grant_type||"authorization_code",t.client_id=t.client_id||this._settings.client_id,t.client_secret=t.client_secret||this._settings.client_secret,t.redirect_uri=t.redirect_uri||this._settings.redirect_uri;var n=void 0,r=t._client_authentication||this._settings._client_authentication;return delete t._client_authentication,t.code?t.redirect_uri?t.code_verifier?t.client_id?t.client_secret||"client_secret_basic"!=r?("client_secret_basic"==r&&(n=t.client_id+":"+t.client_secret,delete t.client_id,delete t.client_secret),this._metadataService.getTokenEndpoint(!1).then((function(r){return i.Log.debug("TokenClient.exchangeCode: Received token endpoint"),e._jsonService.postForm(r,t,n).then((function(e){return i.Log.debug("TokenClient.exchangeCode: response received"),e}))}))):(i.Log.error("TokenClient.exchangeCode: No client_secret passed"),Promise.reject(new Error("A client_secret is required"))):(i.Log.error("TokenClient.exchangeCode: No client_id passed"),Promise.reject(new Error("A client_id is required"))):(i.Log.error("TokenClient.exchangeCode: No code_verifier passed"),Promise.reject(new Error("A code_verifier is required"))):(i.Log.error("TokenClient.exchangeCode: No redirect_uri passed"),Promise.reject(new Error("A redirect_uri is required"))):(i.Log.error("TokenClient.exchangeCode: No code passed"),Promise.reject(new Error("A code is required")))},e.prototype.exchangeRefreshToken=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(t=Object.assign({},t)).grant_type=t.grant_type||"refresh_token",t.client_id=t.client_id||this._settings.client_id,t.client_secret=t.client_secret||this._settings.client_secret;var n=void 0,r=t._client_authentication||this._settings._client_authentication;return delete t._client_authentication,t.refresh_token?t.client_id?("client_secret_basic"==r&&(n=t.client_id+":"+t.client_secret,delete t.client_id,delete t.client_secret),this._metadataService.getTokenEndpoint(!1).then((function(r){return i.Log.debug("TokenClient.exchangeRefreshToken: Received token endpoint"),e._jsonService.postForm(r,t,n).then((function(e){return i.Log.debug("TokenClient.exchangeRefreshToken: response received"),e}))}))):(i.Log.error("TokenClient.exchangeRefreshToken: No client_id passed"),Promise.reject(new Error("A client_id is required"))):(i.Log.error("TokenClient.exchangeRefreshToken: No refresh_token passed"),Promise.reject(new Error("A refresh_token is required")))},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ErrorResponse=void 0;var r=n(0);t.ErrorResponse=function(e){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=n.error,i=n.error_description,a=n.error_uri,s=n.state,l=n.session_state;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),!o)throw r.Log.error("No error passed to ErrorResponse"),new Error("error");var u=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,i||o));return u.name="ErrorResponse",u.error=o,u.error_description=i,u.error_uri=a,u.state=s,u.session_state=l,u}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t}(Error)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SigninState=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(9),a=n(4),s=function(e){return e&&e.__esModule?e:{default:e}}(n(14));t.SigninState=function(e){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.nonce,o=n.authority,i=n.client_id,l=n.redirect_uri,u=n.code_verifier,c=n.response_mode,d=n.client_secret,f=n.scope,p=n.extraTokenParams,h=n.skipUserInfo;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var m=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,arguments[0]));if(!0===r?m._nonce=(0,s.default)():r&&(m._nonce=r),!0===u?m._code_verifier=(0,s.default)()+(0,s.default)()+(0,s.default)():u&&(m._code_verifier=u),m.code_verifier){var g=a.JoseUtil.hashString(m.code_verifier,"SHA256");m._code_challenge=a.JoseUtil.hexToBase64Url(g)}return m._redirect_uri=l,m._authority=o,m._client_id=i,m._response_mode=c,m._client_secret=d,m._scope=f,m._extraTokenParams=p,m._skipUserInfo=h,m}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.toStorageString=function(){return o.Log.debug("SigninState.toStorageString"),JSON.stringify({id:this.id,data:this.data,created:this.created,request_type:this.request_type,nonce:this.nonce,code_verifier:this.code_verifier,redirect_uri:this.redirect_uri,authority:this.authority,client_id:this.client_id,response_mode:this.response_mode,client_secret:this.client_secret,scope:this.scope,extraTokenParams:this.extraTokenParams,skipUserInfo:this.skipUserInfo})},t.fromStorageString=function(e){return o.Log.debug("SigninState.fromStorageString"),new t(JSON.parse(e))},r(t,[{key:"nonce",get:function(){return this._nonce}},{key:"authority",get:function(){return this._authority}},{key:"client_id",get:function(){return this._client_id}},{key:"redirect_uri",get:function(){return this._redirect_uri}},{key:"code_verifier",get:function(){return this._code_verifier}},{key:"code_challenge",get:function(){return this._code_challenge}},{key:"response_mode",get:function(){return this._response_mode}},{key:"client_secret",get:function(){return this._client_secret}},{key:"scope",get:function(){return this._scope}},{key:"extraTokenParams",get:function(){return this._extraTokenParams}},{key:"skipUserInfo",get:function(){return this._skipUserInfo}}]),t}(i.State)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return("undefined"!=r&&null!==r&&void 0!==r.getRandomValues?o:i)().replace(/-/g,"")};var r="undefined"!=typeof window?window.crypto||window.msCrypto:null;function o(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(function(e){return(e^r.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)}))}function i(){return([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(function(e){return(e^16*Math.random()>>e/4).toString(16)}))}e.exports=t.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.User=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0);t.User=function(){function e(t){var n=t.id_token,r=t.session_state,o=t.access_token,i=t.refresh_token,a=t.token_type,s=t.scope,l=t.profile,u=t.expires_at,c=t.state;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.id_token=n,this.session_state=r,this.access_token=o,this.refresh_token=i,this.token_type=a,this.scope=s,this.profile=l,this.expires_at=u,this.state=c}return e.prototype.toStorageString=function(){return o.Log.debug("User.toStorageString"),JSON.stringify({id_token:this.id_token,session_state:this.session_state,access_token:this.access_token,refresh_token:this.refresh_token,token_type:this.token_type,scope:this.scope,profile:this.profile,expires_at:this.expires_at})},e.fromStorageString=function(t){return o.Log.debug("User.fromStorageString"),new e(JSON.parse(t))},r(e,[{key:"expires_in",get:function(){if(this.expires_at){var e=parseInt(Date.now()/1e3);return this.expires_at-e}},set:function(e){var t=parseInt(e);if("number"==typeof t&&t>0){var n=parseInt(Date.now()/1e3);this.expires_at=n+t}}},{key:"expired",get:function(){var e=this.expires_in;if(void 0!==e)return e<=0}},{key:"scopes",get:function(){return(this.scope||"").split(" ")}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AccessTokenEvents=void 0;var r=n(0),o=n(46);t.AccessTokenEvents=function(){function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.accessTokenExpiringNotificationTime,r=void 0===n?60:n,i=t.accessTokenExpiringTimer,a=void 0===i?new o.Timer("Access token expiring"):i,s=t.accessTokenExpiredTimer,l=void 0===s?new o.Timer("Access token expired"):s;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._accessTokenExpiringNotificationTime=r,this._accessTokenExpiring=a,this._accessTokenExpired=l}return e.prototype.load=function(e){if(e.access_token&&void 0!==e.expires_in){var t=e.expires_in;if(r.Log.debug("AccessTokenEvents.load: access token present, remaining duration:",t),t>0){var n=t-this._accessTokenExpiringNotificationTime;n<=0&&(n=1),r.Log.debug("AccessTokenEvents.load: registering expiring timer in:",n),this._accessTokenExpiring.init(n)}else r.Log.debug("AccessTokenEvents.load: canceling existing expiring timer becase we're past expiration."),this._accessTokenExpiring.cancel();var o=t+1;r.Log.debug("AccessTokenEvents.load: registering expired timer in:",o),this._accessTokenExpired.init(o)}else this._accessTokenExpiring.cancel(),this._accessTokenExpired.cancel()},e.prototype.unload=function(){r.Log.debug("AccessTokenEvents.unload: canceling existing access token timers"),this._accessTokenExpiring.cancel(),this._accessTokenExpired.cancel()},e.prototype.addAccessTokenExpiring=function(e){this._accessTokenExpiring.addHandler(e)},e.prototype.removeAccessTokenExpiring=function(e){this._accessTokenExpiring.removeHandler(e)},e.prototype.addAccessTokenExpired=function(e){this._accessTokenExpired.addHandler(e)},e.prototype.removeAccessTokenExpired=function(e){this._accessTokenExpired.removeHandler(e)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Event=void 0;var r=n(0);t.Event=function(){function e(t){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._name=t,this._callbacks=[]}return e.prototype.addHandler=function(e){this._callbacks.push(e)},e.prototype.removeHandler=function(e){var t=this._callbacks.findIndex((function(t){return t===e}));t>=0&&this._callbacks.splice(t,1)},e.prototype.raise=function(){r.Log.debug("Event: Raising event: "+this._name);for(var e=0;e<this._callbacks.length;e++){var t;(t=this._callbacks)[e].apply(t,arguments)}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SessionMonitor=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(19),a=n(1);t.SessionMonitor=function(){function e(t){var n=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.CheckSessionIFrame,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:a.Global.timer;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw o.Log.error("SessionMonitor.ctor: No user manager passed to SessionMonitor"),new Error("userManager");this._userManager=t,this._CheckSessionIFrameCtor=r,this._timer=s,this._userManager.events.addUserLoaded(this._start.bind(this)),this._userManager.events.addUserUnloaded(this._stop.bind(this)),Promise.resolve(this._userManager.getUser().then((function(e){e?n._start(e):n._settings.monitorAnonymousSession&&n._userManager.querySessionStatus().then((function(e){var t={session_state:e.session_state};e.sub&&e.sid&&(t.profile={sub:e.sub,sid:e.sid}),n._start(t)})).catch((function(e){o.Log.error("SessionMonitor ctor: error from querySessionStatus:",e.message)}))})).catch((function(e){o.Log.error("SessionMonitor ctor: error from getUser:",e.message)})))}return e.prototype._start=function(e){var t=this,n=e.session_state;n&&(e.profile?(this._sub=e.profile.sub,this._sid=e.profile.sid,o.Log.debug("SessionMonitor._start: session_state:",n,", sub:",this._sub)):(this._sub=void 0,this._sid=void 0,o.Log.debug("SessionMonitor._start: session_state:",n,", anonymous user")),this._checkSessionIFrame?this._checkSessionIFrame.start(n):this._metadataService.getCheckSessionIframe().then((function(e){if(e){o.Log.debug("SessionMonitor._start: Initializing check session iframe");var r=t._client_id,i=t._checkSessionInterval,a=t._stopCheckSessionOnError;t._checkSessionIFrame=new t._CheckSessionIFrameCtor(t._callback.bind(t),r,e,i,a),t._checkSessionIFrame.load().then((function(){t._checkSessionIFrame.start(n)}))}else o.Log.warn("SessionMonitor._start: No check session iframe found in the metadata")})).catch((function(e){o.Log.error("SessionMonitor._start: Error from getCheckSessionIframe:",e.message)})))},e.prototype._stop=function(){var e=this;if(this._sub=void 0,this._sid=void 0,this._checkSessionIFrame&&(o.Log.debug("SessionMonitor._stop"),this._checkSessionIFrame.stop()),this._settings.monitorAnonymousSession)var t=this._timer.setInterval((function(){e._timer.clearInterval(t),e._userManager.querySessionStatus().then((function(t){var n={session_state:t.session_state};t.sub&&t.sid&&(n.profile={sub:t.sub,sid:t.sid}),e._start(n)})).catch((function(e){o.Log.error("SessionMonitor: error from querySessionStatus:",e.message)}))}),1e3)},e.prototype._callback=function(){var e=this;this._userManager.querySessionStatus().then((function(t){var n=!0;t?t.sub===e._sub?(n=!1,e._checkSessionIFrame.start(t.session_state),t.sid===e._sid?o.Log.debug("SessionMonitor._callback: Same sub still logged in at OP, restarting check session iframe; session_state:",t.session_state):(o.Log.debug("SessionMonitor._callback: Same sub still logged in at OP, session state has changed, restarting check session iframe; session_state:",t.session_state),e._userManager.events._raiseUserSessionChanged())):o.Log.debug("SessionMonitor._callback: Different subject signed into OP:",t.sub):o.Log.debug("SessionMonitor._callback: Subject no longer signed into OP"),n&&(e._sub?(o.Log.debug("SessionMonitor._callback: SessionMonitor._callback; raising signed out event"),e._userManager.events._raiseUserSignedOut()):(o.Log.debug("SessionMonitor._callback: SessionMonitor._callback; raising signed in event"),e._userManager.events._raiseUserSignedIn()))})).catch((function(t){e._sub&&(o.Log.debug("SessionMonitor._callback: Error calling queryCurrentSigninSession; raising signed out event",t.message),e._userManager.events._raiseUserSignedOut())}))},r(e,[{key:"_settings",get:function(){return this._userManager.settings}},{key:"_metadataService",get:function(){return this._userManager.metadataService}},{key:"_client_id",get:function(){return this._settings.client_id}},{key:"_checkSessionInterval",get:function(){return this._settings.checkSessionInterval}},{key:"_stopCheckSessionOnError",get:function(){return this._settings.stopCheckSessionOnError}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CheckSessionIFrame=void 0;var r=n(0);t.CheckSessionIFrame=function(){function e(t,n,r,o){var i=!(arguments.length>4&&void 0!==arguments[4])||arguments[4];(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,e),this._callback=t,this._client_id=n,this._url=r,this._interval=o||2e3,this._stopOnError=i;var a=r.indexOf("/",r.indexOf("//")+2);this._frame_origin=r.substr(0,a),this._frame=window.document.createElement("iframe"),this._frame.style.visibility="hidden",this._frame.style.position="absolute",this._frame.style.display="none",this._frame.width=0,this._frame.height=0,this._frame.src=r}return e.prototype.load=function(){var e=this;return new Promise((function(t){e._frame.onload=function(){t()},window.document.body.appendChild(e._frame),e._boundMessageEvent=e._message.bind(e),window.addEventListener("message",e._boundMessageEvent,!1)}))},e.prototype._message=function(e){e.origin===this._frame_origin&&e.source===this._frame.contentWindow&&("error"===e.data?(r.Log.error("CheckSessionIFrame: error message from check session op iframe"),this._stopOnError&&this.stop()):"changed"===e.data?(r.Log.debug("CheckSessionIFrame: changed message from check session op iframe"),this.stop(),this._callback()):r.Log.debug("CheckSessionIFrame: "+e.data+" message from check session op iframe"))},e.prototype.start=function(e){var t=this;if(this._session_state!==e){r.Log.debug("CheckSessionIFrame.start"),this.stop(),this._session_state=e;var n=function(){t._frame.contentWindow.postMessage(t._client_id+" "+t._session_state,t._frame_origin)};n(),this._timer=window.setInterval(n,this._interval)}},e.prototype.stop=function(){this._session_state=null,this._timer&&(r.Log.debug("CheckSessionIFrame.stop"),window.clearInterval(this._timer),this._timer=null)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TokenRevocationClient=void 0;var r=n(0),o=n(2),i=n(1);t.TokenRevocationClient=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.Global.XMLHttpRequest,a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:o.MetadataService;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw r.Log.error("TokenRevocationClient.ctor: No settings provided"),new Error("No settings provided.");this._settings=t,this._XMLHttpRequestCtor=n,this._metadataService=new a(this._settings)}return e.prototype.revoke=function(e,t){var n=this,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"access_token";if(!e)throw r.Log.error("TokenRevocationClient.revoke: No token provided"),new Error("No token provided.");if("access_token"!==o&&"refresh_token"!=o)throw r.Log.error("TokenRevocationClient.revoke: Invalid token type"),new Error("Invalid token type.");return this._metadataService.getRevocationEndpoint().then((function(i){if(i){r.Log.debug("TokenRevocationClient.revoke: Revoking "+o);var a=n._settings.client_id,s=n._settings.client_secret;return n._revoke(i,a,s,e,o)}if(t)throw r.Log.error("TokenRevocationClient.revoke: Revocation not supported"),new Error("Revocation not supported")}))},e.prototype._revoke=function(e,t,n,o,i){var a=this;return new Promise((function(s,l){var u=new a._XMLHttpRequestCtor;u.open("POST",e),u.onload=function(){r.Log.debug("TokenRevocationClient.revoke: HTTP response received, status",u.status),200===u.status?s():l(Error(u.statusText+" ("+u.status+")"))},u.onerror=function(){r.Log.debug("TokenRevocationClient.revoke: Network Error."),l("Network Error")};var c="client_id="+encodeURIComponent(t);n&&(c+="&client_secret="+encodeURIComponent(n)),c+="&token_type_hint="+encodeURIComponent(i),c+="&token="+encodeURIComponent(o),u.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),u.send(c)}))},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CordovaPopupWindow=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0);t.CordovaPopupWindow=function(){function e(t){var n=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._promise=new Promise((function(e,t){n._resolve=e,n._reject=t})),this.features=t.popupWindowFeatures||"location=no,toolbar=no,zoom=no",this.target=t.popupWindowTarget||"_blank",this.redirect_uri=t.startUrl,o.Log.debug("CordovaPopupWindow.ctor: redirect_uri: "+this.redirect_uri)}return e.prototype._isInAppBrowserInstalled=function(e){return["cordova-plugin-inappbrowser","cordova-plugin-inappbrowser.inappbrowser","org.apache.cordova.inappbrowser"].some((function(t){return e.hasOwnProperty(t)}))},e.prototype.navigate=function(e){if(e&&e.url){if(!window.cordova)return this._error("cordova is undefined");var t=window.cordova.require("cordova/plugin_list").metadata;if(!1===this._isInAppBrowserInstalled(t))return this._error("InAppBrowser plugin not found");this._popup=cordova.InAppBrowser.open(e.url,this.target,this.features),this._popup?(o.Log.debug("CordovaPopupWindow.navigate: popup successfully created"),this._exitCallbackEvent=this._exitCallback.bind(this),this._loadStartCallbackEvent=this._loadStartCallback.bind(this),this._popup.addEventListener("exit",this._exitCallbackEvent,!1),this._popup.addEventListener("loadstart",this._loadStartCallbackEvent,!1)):this._error("Error opening popup window")}else this._error("No url provided");return this.promise},e.prototype._loadStartCallback=function(e){0===e.url.indexOf(this.redirect_uri)&&this._success({url:e.url})},e.prototype._exitCallback=function(e){this._error(e)},e.prototype._success=function(e){this._cleanup(),o.Log.debug("CordovaPopupWindow: Successful response from cordova popup window"),this._resolve(e)},e.prototype._error=function(e){this._cleanup(),o.Log.error(e),this._reject(new Error(e))},e.prototype.close=function(){this._cleanup()},e.prototype._cleanup=function(){this._popup&&(o.Log.debug("CordovaPopupWindow: cleaning up popup"),this._popup.removeEventListener("exit",this._exitCallbackEvent,!1),this._popup.removeEventListener("loadstart",this._loadStartCallbackEvent,!1),this._popup.close()),this._popup=null},r(e,[{key:"promise",get:function(){return this._promise}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(0),o=n(10),i=n(5),a=n(6),s=n(37),l=n(38),u=n(16),c=n(2),d=n(48),f=n(49),p=n(19),h=n(20),m=n(18),g=n(1),v=n(15),y=n(50);t.default={Version:y.Version,Log:r.Log,OidcClient:o.OidcClient,OidcClientSettings:i.OidcClientSettings,WebStorageStateStore:a.WebStorageStateStore,InMemoryWebStorage:s.InMemoryWebStorage,UserManager:l.UserManager,AccessTokenEvents:u.AccessTokenEvents,MetadataService:c.MetadataService,CordovaPopupNavigator:d.CordovaPopupNavigator,CordovaIFrameNavigator:f.CordovaIFrameNavigator,CheckSessionIFrame:p.CheckSessionIFrame,TokenRevocationClient:h.TokenRevocationClient,SessionMonitor:m.SessionMonitor,Global:g.Global,User:v.User},e.exports=t.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ClockService=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.getEpochTime=function(){return Promise.resolve(Date.now()/1e3|0)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseValidator=void 0;var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=n(0),i=n(2),a=n(25),s=n(11),l=n(12),u=n(4),c=["nonce","at_hash","iat","nbf","exp","aud","iss","c_hash"];t.ResponseValidator=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.MetadataService,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:a.UserInfoService,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:u.JoseUtil,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:s.TokenClient;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw o.Log.error("ResponseValidator.ctor: No settings passed to ResponseValidator"),new Error("settings");this._settings=t,this._metadataService=new n(this._settings),this._userInfoService=new r(this._settings),this._joseUtil=l,this._tokenClient=new c(this._settings)}return e.prototype.validateSigninResponse=function(e,t){var n=this;return o.Log.debug("ResponseValidator.validateSigninResponse"),this._processSigninParams(e,t).then((function(t){return o.Log.debug("ResponseValidator.validateSigninResponse: state processed"),n._validateTokens(e,t).then((function(t){return o.Log.debug("ResponseValidator.validateSigninResponse: tokens validated"),n._processClaims(e,t).then((function(e){return o.Log.debug("ResponseValidator.validateSigninResponse: claims processed"),e}))}))}))},e.prototype.validateSignoutResponse=function(e,t){return e.id!==t.state?(o.Log.error("ResponseValidator.validateSignoutResponse: State does not match"),Promise.reject(new Error("State does not match"))):(o.Log.debug("ResponseValidator.validateSignoutResponse: state validated"),t.state=e.data,t.error?(o.Log.warn("ResponseValidator.validateSignoutResponse: Response was error",t.error),Promise.reject(new l.ErrorResponse(t))):Promise.resolve(t))},e.prototype._processSigninParams=function(e,t){if(e.id!==t.state)return o.Log.error("ResponseValidator._processSigninParams: State does not match"),Promise.reject(new Error("State does not match"));if(!e.client_id)return o.Log.error("ResponseValidator._processSigninParams: No client_id on state"),Promise.reject(new Error("No client_id on state"));if(!e.authority)return o.Log.error("ResponseValidator._processSigninParams: No authority on state"),Promise.reject(new Error("No authority on state"));if(this._settings.authority){if(this._settings.authority&&this._settings.authority!==e.authority)return o.Log.error("ResponseValidator._processSigninParams: authority mismatch on settings vs. signin state"),Promise.reject(new Error("authority mismatch on settings vs. signin state"))}else this._settings.authority=e.authority;if(this._settings.client_id){if(this._settings.client_id&&this._settings.client_id!==e.client_id)return o.Log.error("ResponseValidator._processSigninParams: client_id mismatch on settings vs. signin state"),Promise.reject(new Error("client_id mismatch on settings vs. signin state"))}else this._settings.client_id=e.client_id;return o.Log.debug("ResponseValidator._processSigninParams: state validated"),t.state=e.data,t.error?(o.Log.warn("ResponseValidator._processSigninParams: Response was error",t.error),Promise.reject(new l.ErrorResponse(t))):e.nonce&&!t.id_token?(o.Log.error("ResponseValidator._processSigninParams: Expecting id_token in response"),Promise.reject(new Error("No id_token in response"))):!e.nonce&&t.id_token?(o.Log.error("ResponseValidator._processSigninParams: Not expecting id_token in response"),Promise.reject(new Error("Unexpected id_token in response"))):e.code_verifier&&!t.code?(o.Log.error("ResponseValidator._processSigninParams: Expecting code in response"),Promise.reject(new Error("No code in response"))):!e.code_verifier&&t.code?(o.Log.error("ResponseValidator._processSigninParams: Not expecting code in response"),Promise.reject(new Error("Unexpected code in response"))):(t.scope||(t.scope=e.scope),Promise.resolve(t))},e.prototype._processClaims=function(e,t){var n=this;if(t.isOpenIdConnect){if(o.Log.debug("ResponseValidator._processClaims: response is OIDC, processing claims"),t.profile=this._filterProtocolClaims(t.profile),!0!==e.skipUserInfo&&this._settings.loadUserInfo&&t.access_token)return o.Log.debug("ResponseValidator._processClaims: loading user info"),this._userInfoService.getClaims(t.access_token).then((function(e){return o.Log.debug("ResponseValidator._processClaims: user info claims received from user info endpoint"),e.sub!==t.profile.sub?(o.Log.error("ResponseValidator._processClaims: sub from user info endpoint does not match sub in id_token"),Promise.reject(new Error("sub from user info endpoint does not match sub in id_token"))):(t.profile=n._mergeClaims(t.profile,e),o.Log.debug("ResponseValidator._processClaims: user info claims received, updated profile:",t.profile),t)}));o.Log.debug("ResponseValidator._processClaims: not loading user info")}else o.Log.debug("ResponseValidator._processClaims: response is not OIDC, not processing claims");return Promise.resolve(t)},e.prototype._mergeClaims=function(e,t){var n=Object.assign({},e);for(var o in t){var i=t[o];Array.isArray(i)||(i=[i]);for(var a=0;a<i.length;a++){var s=i[a];n[o]?Array.isArray(n[o])?n[o].indexOf(s)<0&&n[o].push(s):n[o]!==s&&("object"===(void 0===s?"undefined":r(s))&&this._settings.mergeClaims?n[o]=this._mergeClaims(n[o],s):n[o]=[n[o],s]):n[o]=s}}return n},e.prototype._filterProtocolClaims=function(e){o.Log.debug("ResponseValidator._filterProtocolClaims, incoming claims:",e);var t=Object.assign({},e);return this._settings._filterProtocolClaims?(c.forEach((function(e){delete t[e]})),o.Log.debug("ResponseValidator._filterProtocolClaims: protocol claims filtered",t)):o.Log.debug("ResponseValidator._filterProtocolClaims: protocol claims not filtered"),t},e.prototype._validateTokens=function(e,t){return t.code?(o.Log.debug("ResponseValidator._validateTokens: Validating code"),this._processCode(e,t)):t.id_token?t.access_token?(o.Log.debug("ResponseValidator._validateTokens: Validating id_token and access_token"),this._validateIdTokenAndAccessToken(e,t)):(o.Log.debug("ResponseValidator._validateTokens: Validating id_token"),this._validateIdToken(e,t)):(o.Log.debug("ResponseValidator._validateTokens: No code to process or id_token to validate"),Promise.resolve(t))},e.prototype._processCode=function(e,t){var n=this,i={client_id:e.client_id,client_secret:e.client_secret,code:t.code,redirect_uri:e.redirect_uri,code_verifier:e.code_verifier};return e.extraTokenParams&&"object"===r(e.extraTokenParams)&&Object.assign(i,e.extraTokenParams),this._tokenClient.exchangeCode(i).then((function(r){for(var i in r)t[i]=r[i];return t.id_token?(o.Log.debug("ResponseValidator._processCode: token response successful, processing id_token"),n._validateIdTokenAttributes(e,t)):(o.Log.debug("ResponseValidator._processCode: token response successful, returning response"),t)}))},e.prototype._validateIdTokenAttributes=function(e,t){var n=this;return this._metadataService.getIssuer().then((function(r){var i=e.client_id,a=n._settings.clockSkew;return o.Log.debug("ResponseValidator._validateIdTokenAttributes: Validaing JWT attributes; using clock skew (in seconds) of: ",a),n._settings.getEpochTime().then((function(s){return n._joseUtil.validateJwtAttributes(t.id_token,r,i,a,s).then((function(n){return e.nonce&&e.nonce!==n.nonce?(o.Log.error("ResponseValidator._validateIdTokenAttributes: Invalid nonce in id_token"),Promise.reject(new Error("Invalid nonce in id_token"))):n.sub?(t.profile=n,t):(o.Log.error("ResponseValidator._validateIdTokenAttributes: No sub present in id_token"),Promise.reject(new Error("No sub present in id_token")))}))}))}))},e.prototype._validateIdTokenAndAccessToken=function(e,t){var n=this;return this._validateIdToken(e,t).then((function(e){return n._validateAccessToken(e)}))},e.prototype._getSigningKeyForJwt=function(e){var t=this;return this._metadataService.getSigningKeys().then((function(n){var r=e.header.kid;if(!n)return o.Log.error("ResponseValidator._validateIdToken: No signing keys from metadata"),Promise.reject(new Error("No signing keys from metadata"));o.Log.debug("ResponseValidator._validateIdToken: Received signing keys");var i=void 0;if(r)i=n.filter((function(e){return e.kid===r}))[0];else{if((n=t._filterByAlg(n,e.header.alg)).length>1)return o.Log.error("ResponseValidator._validateIdToken: No kid found in id_token and more than one key found in metadata"),Promise.reject(new Error("No kid found in id_token and more than one key found in metadata"));i=n[0]}return Promise.resolve(i)}))},e.prototype._getSigningKeyForJwtWithSingleRetry=function(e){var t=this;return this._getSigningKeyForJwt(e).then((function(n){return n?Promise.resolve(n):(t._metadataService.resetSigningKeys(),t._getSigningKeyForJwt(e))}))},e.prototype._validateIdToken=function(e,t){var n=this;if(!e.nonce)return o.Log.error("ResponseValidator._validateIdToken: No nonce on state"),Promise.reject(new Error("No nonce on state"));var r=this._joseUtil.parseJwt(t.id_token);return r&&r.header&&r.payload?e.nonce!==r.payload.nonce?(o.Log.error("ResponseValidator._validateIdToken: Invalid nonce in id_token"),Promise.reject(new Error("Invalid nonce in id_token"))):this._metadataService.getIssuer().then((function(i){return o.Log.debug("ResponseValidator._validateIdToken: Received issuer"),n._getSigningKeyForJwtWithSingleRetry(r).then((function(a){if(!a)return o.Log.error("ResponseValidator._validateIdToken: No key matching kid or alg found in signing keys"),Promise.reject(new Error("No key matching kid or alg found in signing keys"));var s=e.client_id,l=n._settings.clockSkew;return o.Log.debug("ResponseValidator._validateIdToken: Validaing JWT; using clock skew (in seconds) of: ",l),n._joseUtil.validateJwt(t.id_token,a,i,s,l).then((function(){return o.Log.debug("ResponseValidator._validateIdToken: JWT validation successful"),r.payload.sub?(t.profile=r.payload,t):(o.Log.error("ResponseValidator._validateIdToken: No sub present in id_token"),Promise.reject(new Error("No sub present in id_token")))}))}))})):(o.Log.error("ResponseValidator._validateIdToken: Failed to parse id_token",r),Promise.reject(new Error("Failed to parse id_token")))},e.prototype._filterByAlg=function(e,t){var n=null;if(t.startsWith("RS"))n="RSA";else if(t.startsWith("PS"))n="PS";else{if(!t.startsWith("ES"))return o.Log.debug("ResponseValidator._filterByAlg: alg not supported: ",t),[];n="EC"}return o.Log.debug("ResponseValidator._filterByAlg: Looking for keys that match kty: ",n),e=e.filter((function(e){return e.kty===n})),o.Log.debug("ResponseValidator._filterByAlg: Number of keys that match kty: ",n,e.length),e},e.prototype._validateAccessToken=function(e){if(!e.profile)return o.Log.error("ResponseValidator._validateAccessToken: No profile loaded from id_token"),Promise.reject(new Error("No profile loaded from id_token"));if(!e.profile.at_hash)return o.Log.error("ResponseValidator._validateAccessToken: No at_hash in id_token"),Promise.reject(new Error("No at_hash in id_token"));if(!e.id_token)return o.Log.error("ResponseValidator._validateAccessToken: No id_token"),Promise.reject(new Error("No id_token"));var t=this._joseUtil.parseJwt(e.id_token);if(!t||!t.header)return o.Log.error("ResponseValidator._validateAccessToken: Failed to parse id_token",t),Promise.reject(new Error("Failed to parse id_token"));var n=t.header.alg;if(!n||5!==n.length)return o.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:",n),Promise.reject(new Error("Unsupported alg: "+n));var r=n.substr(2,3);if(!r)return o.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:",n,r),Promise.reject(new Error("Unsupported alg: "+n));if(256!==(r=parseInt(r))&&384!==r&&512!==r)return o.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:",n,r),Promise.reject(new Error("Unsupported alg: "+n));var i="sha"+r,a=this._joseUtil.hashString(e.access_token,i);if(!a)return o.Log.error("ResponseValidator._validateAccessToken: access_token hash failed:",i),Promise.reject(new Error("Failed to validate at_hash"));var s=a.substr(0,a.length/2),l=this._joseUtil.hexToBase64Url(s);return l!==e.profile.at_hash?(o.Log.error("ResponseValidator._validateAccessToken: Failed to validate at_hash",l,e.profile.at_hash),Promise.reject(new Error("Failed to validate at_hash"))):(o.Log.debug("ResponseValidator._validateAccessToken: success"),Promise.resolve(e))},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.UserInfoService=void 0;var r=n(7),o=n(2),i=n(0),a=n(4);t.UserInfoService=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:r.JsonService,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:o.MetadataService,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:a.JoseUtil;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!t)throw i.Log.error("UserInfoService.ctor: No settings passed"),new Error("settings");this._settings=t,this._jsonService=new n(void 0,void 0,this._getClaimsFromJwt.bind(this)),this._metadataService=new s(this._settings),this._joseUtil=l}return e.prototype.getClaims=function(e){var t=this;return e?this._metadataService.getUserInfoEndpoint().then((function(n){return i.Log.debug("UserInfoService.getClaims: received userinfo url",n),t._jsonService.getJson(n,e).then((function(e){return i.Log.debug("UserInfoService.getClaims: claims received",e),e}))})):(i.Log.error("UserInfoService.getClaims: No token passed"),Promise.reject(new Error("A token is required")))},e.prototype._getClaimsFromJwt=function e(t){var n=this;try{var r=this._joseUtil.parseJwt(t.responseText);if(!r||!r.header||!r.payload)return i.Log.error("UserInfoService._getClaimsFromJwt: Failed to parse JWT",r),Promise.reject(new Error("Failed to parse id_token"));var o=r.header.kid,a=void 0;switch(this._settings.userInfoJwtIssuer){case"OP":a=this._metadataService.getIssuer();break;case"ANY":a=Promise.resolve(r.payload.iss);break;default:a=Promise.resolve(this._settings.userInfoJwtIssuer)}return a.then((function(e){return i.Log.debug("UserInfoService._getClaimsFromJwt: Received issuer:"+e),n._metadataService.getSigningKeys().then((function(a){if(!a)return i.Log.error("UserInfoService._getClaimsFromJwt: No signing keys from metadata"),Promise.reject(new Error("No signing keys from metadata"));i.Log.debug("UserInfoService._getClaimsFromJwt: Received signing keys");var s=void 0;if(o)s=a.filter((function(e){return e.kid===o}))[0];else{if((a=n._filterByAlg(a,r.header.alg)).length>1)return i.Log.error("UserInfoService._getClaimsFromJwt: No kid found in id_token and more than one key found in metadata"),Promise.reject(new Error("No kid found in id_token and more than one key found in metadata"));s=a[0]}if(!s)return i.Log.error("UserInfoService._getClaimsFromJwt: No key matching kid or alg found in signing keys"),Promise.reject(new Error("No key matching kid or alg found in signing keys"));var l=n._settings.client_id,u=n._settings.clockSkew;return i.Log.debug("UserInfoService._getClaimsFromJwt: Validaing JWT; using clock skew (in seconds) of: ",u),n._joseUtil.validateJwt(t.responseText,s,e,l,u,void 0,!0).then((function(){return i.Log.debug("UserInfoService._getClaimsFromJwt: JWT validation successful"),r.payload}))}))}))}catch(e){return i.Log.error("UserInfoService._getClaimsFromJwt: Error parsing JWT response",e.message),void reject(e)}},e.prototype._filterByAlg=function(e,t){var n=null;if(t.startsWith("RS"))n="RSA";else if(t.startsWith("PS"))n="PS";else{if(!t.startsWith("ES"))return i.Log.debug("UserInfoService._filterByAlg: alg not supported: ",t),[];n="EC"}return i.Log.debug("UserInfoService._filterByAlg: Looking for keys that match kty: ",n),e=e.filter((function(e){return e.kty===n})),i.Log.debug("UserInfoService._filterByAlg: Number of keys that match kty: ",n,e.length),e},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.AllowedSigningAlgs=t.b64tohex=t.hextob64u=t.crypto=t.X509=t.KeyUtil=t.jws=void 0;var r=n(27);t.jws=r.jws,t.KeyUtil=r.KEYUTIL,t.X509=r.X509,t.crypto=r.crypto,t.hextob64u=r.hextob64u,t.b64tohex=r.b64tohex,t.AllowedSigningAlgs=["RS256","RS384","RS512","PS256","PS384","PS512","ES256","ES384","ES512"]},function(e,t,n){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0});var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r={userAgent:!1},o={};if(void 0===i)var i={};i.lang={extend:function(e,t,n){if(!t||!e)throw new Error("YAHOO.lang.extend failed, please check that all dependencies are included.");var o=function(){};if(o.prototype=t.prototype,e.prototype=new o,e.prototype.constructor=e,e.superclass=t.prototype,t.prototype.constructor==Object.prototype.constructor&&(t.prototype.constructor=t),n){var i;for(i in n)e.prototype[i]=n[i];var a=function(){},s=["toString","valueOf"];try{/MSIE/.test(r.userAgent)&&(a=function(e,t){for(i=0;i<s.length;i+=1){var n=s[i],r=t[n];"function"==typeof r&&r!=Object.prototype[n]&&(e[n]=r)}})}catch(e){}a(e.prototype,n)}}};var a,s,l,u,c,d,f,p,h,m,g,v=v||(a=Math,l=(s={}).lib={},u=l.Base=function(){function e(){}return{extend:function(t){e.prototype=this;var n=new e;return t&&n.mixIn(t),n.hasOwnProperty("init")||(n.init=function(){n.$super.init.apply(this,arguments)}),n.init.prototype=n,n.$super=this,n},create:function(){var e=this.extend();return e.init.apply(e,arguments),e},init:function(){},mixIn:function(e){for(var t in e)e.hasOwnProperty(t)&&(this[t]=e[t]);e.hasOwnProperty("toString")&&(this.toString=e.toString)},clone:function(){return this.init.prototype.extend(this)}}}(),c=l.WordArray=u.extend({init:function(e,t){e=this.words=e||[],this.sigBytes=null!=t?t:4*e.length},toString:function(e){return(e||f).stringify(this)},concat:function(e){var t=this.words,n=e.words,r=this.sigBytes,o=e.sigBytes;if(this.clamp(),r%4)for(var i=0;i<o;i++){var a=n[i>>>2]>>>24-i%4*8&255;t[r+i>>>2]|=a<<24-(r+i)%4*8}else for(i=0;i<o;i+=4)t[r+i>>>2]=n[i>>>2];return this.sigBytes+=o,this},clamp:function(){var e=this.words,t=this.sigBytes;e[t>>>2]&=4294967295<<32-t%4*8,e.length=a.ceil(t/4)},clone:function(){var e=u.clone.call(this);return e.words=this.words.slice(0),e},random:function(e){for(var t=[],n=0;n<e;n+=4)t.push(4294967296*a.random()|0);return new c.init(t,e)}}),d=s.enc={},f=d.Hex={stringify:function(e){for(var t=e.words,n=e.sigBytes,r=[],o=0;o<n;o++){var i=t[o>>>2]>>>24-o%4*8&255;r.push((i>>>4).toString(16)),r.push((15&i).toString(16))}return r.join("")},parse:function(e){for(var t=e.length,n=[],r=0;r<t;r+=2)n[r>>>3]|=parseInt(e.substr(r,2),16)<<24-r%8*4;return new c.init(n,t/2)}},p=d.Latin1={stringify:function(e){for(var t=e.words,n=e.sigBytes,r=[],o=0;o<n;o++){var i=t[o>>>2]>>>24-o%4*8&255;r.push(String.fromCharCode(i))}return r.join("")},parse:function(e){for(var t=e.length,n=[],r=0;r<t;r++)n[r>>>2]|=(255&e.charCodeAt(r))<<24-r%4*8;return new c.init(n,t)}},h=d.Utf8={stringify:function(e){try{return decodeURIComponent(escape(p.stringify(e)))}catch(e){throw new Error("Malformed UTF-8 data")}},parse:function(e){return p.parse(unescape(encodeURIComponent(e)))}},m=l.BufferedBlockAlgorithm=u.extend({reset:function(){this._data=new c.init,this._nDataBytes=0},_append:function(e){"string"==typeof e&&(e=h.parse(e)),this._data.concat(e),this._nDataBytes+=e.sigBytes},_process:function(e){var t=this._data,n=t.words,r=t.sigBytes,o=this.blockSize,i=r/(4*o),s=(i=e?a.ceil(i):a.max((0|i)-this._minBufferSize,0))*o,l=a.min(4*s,r);if(s){for(var u=0;u<s;u+=o)this._doProcessBlock(n,u);var d=n.splice(0,s);t.sigBytes-=l}return new c.init(d,l)},clone:function(){var e=u.clone.call(this);return e._data=this._data.clone(),e},_minBufferSize:0}),l.Hasher=m.extend({cfg:u.extend(),init:function(e){this.cfg=this.cfg.extend(e),this.reset()},reset:function(){m.reset.call(this),this._doReset()},update:function(e){return this._append(e),this._process(),this},finalize:function(e){return e&&this._append(e),this._doFinalize()},blockSize:16,_createHelper:function(e){return function(t,n){return new e.init(n).finalize(t)}},_createHmacHelper:function(e){return function(t,n){return new g.HMAC.init(e,n).finalize(t)}}}),g=s.algo={},s);!function(){var e,t=(e=v).lib,n=t.Base,r=t.WordArray;(e=e.x64={}).Word=n.extend({init:function(e,t){this.high=e,this.low=t}}),e.WordArray=n.extend({init:function(e,t){e=this.words=e||[],this.sigBytes=null!=t?t:8*e.length},toX32:function(){for(var e=this.words,t=e.length,n=[],o=0;o<t;o++){var i=e[o];n.push(i.high),n.push(i.low)}return r.create(n,this.sigBytes)},clone:function(){for(var e=n.clone.call(this),t=e.words=this.words.slice(0),r=t.length,o=0;o<r;o++)t[o]=t[o].clone();return e}})}(),function(){var e=v,t=e.lib.WordArray;e.enc.Base64={stringify:function(e){var t=e.words,n=e.sigBytes,r=this._map;e.clamp(),e=[];for(var o=0;o<n;o+=3)for(var i=(t[o>>>2]>>>24-o%4*8&255)<<16|(t[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|t[o+2>>>2]>>>24-(o+2)%4*8&255,a=0;4>a&&o+.75*a<n;a++)e.push(r.charAt(i>>>6*(3-a)&63));if(t=r.charAt(64))for(;e.length%4;)e.push(t);return e.join("")},parse:function(e){var n=e.length,r=this._map;(o=r.charAt(64))&&-1!=(o=e.indexOf(o))&&(n=o);for(var o=[],i=0,a=0;a<n;a++)if(a%4){var s=r.indexOf(e.charAt(a-1))<<a%4*2,l=r.indexOf(e.charAt(a))>>>6-a%4*2;o[i>>>2]|=(s|l)<<24-i%4*8,i++}return t.create(o,i)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}}(),function(e){for(var t=v,n=(o=t.lib).WordArray,r=o.Hasher,o=t.algo,i=[],a=[],s=function(e){return 4294967296*(e-(0|e))|0},l=2,u=0;64>u;){var c;e:{c=l;for(var d=e.sqrt(c),f=2;f<=d;f++)if(!(c%f)){c=!1;break e}c=!0}c&&(8>u&&(i[u]=s(e.pow(l,.5))),a[u]=s(e.pow(l,1/3)),u++),l++}var p=[];o=o.SHA256=r.extend({_doReset:function(){this._hash=new n.init(i.slice(0))},_doProcessBlock:function(e,t){for(var n=this._hash.words,r=n[0],o=n[1],i=n[2],s=n[3],l=n[4],u=n[5],c=n[6],d=n[7],f=0;64>f;f++){if(16>f)p[f]=0|e[t+f];else{var h=p[f-15],m=p[f-2];p[f]=((h<<25|h>>>7)^(h<<14|h>>>18)^h>>>3)+p[f-7]+((m<<15|m>>>17)^(m<<13|m>>>19)^m>>>10)+p[f-16]}h=d+((l<<26|l>>>6)^(l<<21|l>>>11)^(l<<7|l>>>25))+(l&u^~l&c)+a[f]+p[f],m=((r<<30|r>>>2)^(r<<19|r>>>13)^(r<<10|r>>>22))+(r&o^r&i^o&i),d=c,c=u,u=l,l=s+h|0,s=i,i=o,o=r,r=h+m|0}n[0]=n[0]+r|0,n[1]=n[1]+o|0,n[2]=n[2]+i|0,n[3]=n[3]+s|0,n[4]=n[4]+l|0,n[5]=n[5]+u|0,n[6]=n[6]+c|0,n[7]=n[7]+d|0},_doFinalize:function(){var t=this._data,n=t.words,r=8*this._nDataBytes,o=8*t.sigBytes;return n[o>>>5]|=128<<24-o%32,n[14+(o+64>>>9<<4)]=e.floor(r/4294967296),n[15+(o+64>>>9<<4)]=r,t.sigBytes=4*n.length,this._process(),this._hash},clone:function(){var e=r.clone.call(this);return e._hash=this._hash.clone(),e}}),t.SHA256=r._createHelper(o),t.HmacSHA256=r._createHmacHelper(o)}(Math),function(){function e(){return r.create.apply(r,arguments)}for(var t=v,n=t.lib.Hasher,r=(i=t.x64).Word,o=i.WordArray,i=t.algo,a=[e(1116352408,3609767458),e(1899447441,602891725),e(3049323471,3964484399),e(3921009573,2173295548),e(961987163,4081628472),e(1508970993,3053834265),e(2453635748,2937671579),e(2870763221,3664609560),e(3624381080,2734883394),e(310598401,1164996542),e(607225278,1323610764),e(1426881987,3590304994),e(1925078388,4068182383),e(2162078206,991336113),e(2614888103,633803317),e(3248222580,3479774868),e(3835390401,2666613458),e(4022224774,944711139),e(264347078,2341262773),e(604807628,2007800933),e(770255983,1495990901),e(1249150122,1856431235),e(1555081692,3175218132),e(1996064986,2198950837),e(2554220882,3999719339),e(2821834349,766784016),e(2952996808,2566594879),e(3210313671,3203337956),e(3336571891,1034457026),e(3584528711,2466948901),e(113926993,3758326383),e(338241895,168717936),e(666307205,1188179964),e(773529912,1546045734),e(1294757372,1522805485),e(1396182291,2643833823),e(1695183700,2343527390),e(1986661051,1014477480),e(2177026350,1206759142),e(2456956037,344077627),e(2730485921,1290863460),e(2820302411,3158454273),e(3259730800,3505952657),e(3345764771,106217008),e(3516065817,3606008344),e(3600352804,1432725776),e(4094571909,1467031594),e(275423344,851169720),e(430227734,3100823752),e(506948616,1363258195),e(659060556,3750685593),e(883997877,3785050280),e(958139571,3318307427),e(1322822218,3812723403),e(1537002063,2003034995),e(1747873779,3602036899),e(1955562222,1575990012),e(2024104815,1125592928),e(2227730452,2716904306),e(2361852424,442776044),e(2428436474,593698344),e(2756734187,3733110249),e(3204031479,2999351573),e(3329325298,3815920427),e(3391569614,3928383900),e(3515267271,566280711),e(3940187606,3454069534),e(4118630271,4000239992),e(116418474,1914138554),e(174292421,2731055270),e(289380356,3203993006),e(460393269,320620315),e(685471733,587496836),e(852142971,1086792851),e(1017036298,365543100),e(1126000580,2618297676),e(1288033470,3409855158),e(1501505948,4234509866),e(1607167915,987167468),e(1816402316,1246189591)],s=[],l=0;80>l;l++)s[l]=e();i=i.SHA512=n.extend({_doReset:function(){this._hash=new o.init([new r.init(1779033703,4089235720),new r.init(3144134277,2227873595),new r.init(1013904242,4271175723),new r.init(2773480762,1595750129),new r.init(1359893119,2917565137),new r.init(2600822924,725511199),new r.init(528734635,4215389547),new r.init(1541459225,327033209)])},_doProcessBlock:function(e,t){for(var n=(d=this._hash.words)[0],r=d[1],o=d[2],i=d[3],l=d[4],u=d[5],c=d[6],d=d[7],f=n.high,p=n.low,h=r.high,m=r.low,g=o.high,v=o.low,y=i.high,b=i.low,w=l.high,E=l.low,S=u.high,x=u.low,A=c.high,k=c.low,_=d.high,C=d.low,O=f,P=p,R=h,T=m,F=g,j=v,N=y,I=b,L=w,M=E,D=S,B=x,U=A,z=k,$=_,H=C,W=0;80>W;W++){var V=s[W];if(16>W)var q=V.high=0|e[t+2*W],K=V.low=0|e[t+2*W+1];else{q=((K=(q=s[W-15]).high)>>>1|(G=q.low)<<31)^(K>>>8|G<<24)^K>>>7;var G=(G>>>1|K<<31)^(G>>>8|K<<24)^(G>>>7|K<<25),J=((K=(J=s[W-2]).high)>>>19|(Y=J.low)<<13)^(K<<3|Y>>>29)^K>>>6,Y=(Y>>>19|K<<13)^(Y<<3|K>>>29)^(Y>>>6|K<<26),X=(K=s[W-7]).high,Q=(Z=s[W-16]).high,Z=Z.low;q=(q=(q=q+X+((K=G+K.low)>>>0<G>>>0?1:0))+J+((K+=Y)>>>0<Y>>>0?1:0))+Q+((K+=Z)>>>0<Z>>>0?1:0),V.high=q,V.low=K}X=L&D^~L&U,Z=M&B^~M&z,V=O&R^O&F^R&F;var ee=P&T^P&j^T&j,te=(G=(O>>>28|P<<4)^(O<<30|P>>>2)^(O<<25|P>>>7),J=(P>>>28|O<<4)^(P<<30|O>>>2)^(P<<25|O>>>7),(Y=a[W]).high),ne=Y.low;Q=$+((L>>>14|M<<18)^(L>>>18|M<<14)^(L<<23|M>>>9))+((Y=H+((M>>>14|L<<18)^(M>>>18|L<<14)^(M<<23|L>>>9)))>>>0<H>>>0?1:0),$=U,H=z,U=D,z=B,D=L,B=M,L=N+(Q=(Q=(Q=Q+X+((Y+=Z)>>>0<Z>>>0?1:0))+te+((Y+=ne)>>>0<ne>>>0?1:0))+q+((Y+=K)>>>0<K>>>0?1:0))+((M=I+Y|0)>>>0<I>>>0?1:0)|0,N=F,I=j,F=R,j=T,R=O,T=P,O=Q+(V=G+V+((K=J+ee)>>>0<J>>>0?1:0))+((P=Y+K|0)>>>0<Y>>>0?1:0)|0}p=n.low=p+P,n.high=f+O+(p>>>0<P>>>0?1:0),m=r.low=m+T,r.high=h+R+(m>>>0<T>>>0?1:0),v=o.low=v+j,o.high=g+F+(v>>>0<j>>>0?1:0),b=i.low=b+I,i.high=y+N+(b>>>0<I>>>0?1:0),E=l.low=E+M,l.high=w+L+(E>>>0<M>>>0?1:0),x=u.low=x+B,u.high=S+D+(x>>>0<B>>>0?1:0),k=c.low=k+z,c.high=A+U+(k>>>0<z>>>0?1:0),C=d.low=C+H,d.high=_+$+(C>>>0<H>>>0?1:0)},_doFinalize:function(){var e=this._data,t=e.words,n=8*this._nDataBytes,r=8*e.sigBytes;return t[r>>>5]|=128<<24-r%32,t[30+(r+128>>>10<<5)]=Math.floor(n/4294967296),t[31+(r+128>>>10<<5)]=n,e.sigBytes=4*t.length,this._process(),this._hash.toX32()},clone:function(){var e=n.clone.call(this);return e._hash=this._hash.clone(),e},blockSize:32}),t.SHA512=n._createHelper(i),t.HmacSHA512=n._createHmacHelper(i)}(),function(){var e=v,t=(o=e.x64).Word,n=o.WordArray,r=(o=e.algo).SHA512,o=o.SHA384=r.extend({_doReset:function(){this._hash=new n.init([new t.init(3418070365,3238371032),new t.init(1654270250,914150663),new t.init(2438529370,812702999),new t.init(355462360,4144912697),new t.init(1731405415,4290775857),new t.init(2394180231,1750603025),new t.init(3675008525,1694076839),new t.init(1203062813,3204075428)])},_doFinalize:function(){var e=r._doFinalize.call(this);return e.sigBytes-=16,e}});e.SHA384=r._createHelper(o),e.HmacSHA384=r._createHmacHelper(o)}();var y,b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function w(e){var t,n,r="";for(t=0;t+3<=e.length;t+=3)n=parseInt(e.substring(t,t+3),16),r+=b.charAt(n>>6)+b.charAt(63&n);for(t+1==e.length?(n=parseInt(e.substring(t,t+1),16),r+=b.charAt(n<<2)):t+2==e.length&&(n=parseInt(e.substring(t,t+2),16),r+=b.charAt(n>>2)+b.charAt((3&n)<<4));(3&r.length)>0;)r+="=";return r}function E(e){var t,n,r,o="",i=0;for(t=0;t<e.length&&"="!=e.charAt(t);++t)(r=b.indexOf(e.charAt(t)))<0||(0==i?(o+=O(r>>2),n=3&r,i=1):1==i?(o+=O(n<<2|r>>4),n=15&r,i=2):2==i?(o+=O(n),o+=O(r>>2),n=3&r,i=3):(o+=O(n<<2|r>>4),o+=O(15&r),i=0));return 1==i&&(o+=O(n<<2)),o}function S(e){var t,n=E(e),r=new Array;for(t=0;2*t<n.length;++t)r[t]=parseInt(n.substring(2*t,2*t+2),16);return r}function x(e,t,n){null!=e&&("number"==typeof e?this.fromNumber(e,t,n):null==t&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function A(){return new x(null)}"Microsoft Internet Explorer"==r.appName?(x.prototype.am=function(e,t,n,r,o,i){for(var a=32767&t,s=t>>15;--i>=0;){var l=32767&this[e],u=this[e++]>>15,c=s*l+u*a;o=((l=a*l+((32767&c)<<15)+n[r]+(1073741823&o))>>>30)+(c>>>15)+s*u+(o>>>30),n[r++]=1073741823&l}return o},y=30):"Netscape"!=r.appName?(x.prototype.am=function(e,t,n,r,o,i){for(;--i>=0;){var a=t*this[e++]+n[r]+o;o=Math.floor(a/67108864),n[r++]=67108863&a}return o},y=26):(x.prototype.am=function(e,t,n,r,o,i){for(var a=16383&t,s=t>>14;--i>=0;){var l=16383&this[e],u=this[e++]>>14,c=s*l+u*a;o=((l=a*l+((16383&c)<<14)+n[r]+o)>>28)+(c>>14)+s*u,n[r++]=268435455&l}return o},y=28),x.prototype.DB=y,x.prototype.DM=(1<<y)-1,x.prototype.DV=1<<y,x.prototype.FV=Math.pow(2,52),x.prototype.F1=52-y,x.prototype.F2=2*y-52;var k,_,C=new Array;for(k="0".charCodeAt(0),_=0;_<=9;++_)C[k++]=_;for(k="a".charCodeAt(0),_=10;_<36;++_)C[k++]=_;for(k="A".charCodeAt(0),_=10;_<36;++_)C[k++]=_;function O(e){return"0123456789abcdefghijklmnopqrstuvwxyz".charAt(e)}function P(e,t){var n=C[e.charCodeAt(t)];return null==n?-1:n}function R(e){var t=A();return t.fromInt(e),t}function T(e){var t,n=1;return 0!=(t=e>>>16)&&(e=t,n+=16),0!=(t=e>>8)&&(e=t,n+=8),0!=(t=e>>4)&&(e=t,n+=4),0!=(t=e>>2)&&(e=t,n+=2),0!=(t=e>>1)&&(e=t,n+=1),n}function F(e){this.m=e}function j(e){this.m=e,this.mp=e.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<<e.DB-15)-1,this.mt2=2*e.t}function N(e,t){return e&t}function I(e,t){return e|t}function L(e,t){return e^t}function M(e,t){return e&~t}function D(e){if(0==e)return-1;var t=0;return!(65535&e)&&(e>>=16,t+=16),!(255&e)&&(e>>=8,t+=8),!(15&e)&&(e>>=4,t+=4),!(3&e)&&(e>>=2,t+=2),!(1&e)&&++t,t}function B(e){for(var t=0;0!=e;)e&=e-1,++t;return t}function U(){}function z(e){return e}function $(e){this.r2=A(),this.q3=A(),x.ONE.dlShiftTo(2*e.t,this.r2),this.mu=this.r2.divide(e),this.m=e}F.prototype.convert=function(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e},F.prototype.revert=function(e){return e},F.prototype.reduce=function(e){e.divRemTo(this.m,null,e)},F.prototype.mulTo=function(e,t,n){e.multiplyTo(t,n),this.reduce(n)},F.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},j.prototype.convert=function(e){var t=A();return e.abs().dlShiftTo(this.m.t,t),t.divRemTo(this.m,null,t),e.s<0&&t.compareTo(x.ZERO)>0&&this.m.subTo(t,t),t},j.prototype.revert=function(e){var t=A();return e.copyTo(t),this.reduce(t),t},j.prototype.reduce=function(e){for(;e.t<=this.mt2;)e[e.t++]=0;for(var t=0;t<this.m.t;++t){var n=32767&e[t],r=n*this.mpl+((n*this.mph+(e[t]>>15)*this.mpl&this.um)<<15)&e.DM;for(e[n=t+this.m.t]+=this.m.am(0,r,e,t,0,this.m.t);e[n]>=e.DV;)e[n]-=e.DV,e[++n]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)},j.prototype.mulTo=function(e,t,n){e.multiplyTo(t,n),this.reduce(n)},j.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)},x.prototype.copyTo=function(e){for(var t=this.t-1;t>=0;--t)e[t]=this[t];e.t=this.t,e.s=this.s},x.prototype.fromInt=function(e){this.t=1,this.s=e<0?-1:0,e>0?this[0]=e:e<-1?this[0]=e+this.DV:this.t=0},x.prototype.fromString=function(e,t){var n;if(16==t)n=4;else if(8==t)n=3;else if(256==t)n=8;else if(2==t)n=1;else if(32==t)n=5;else{if(4!=t)return void this.fromRadix(e,t);n=2}this.t=0,this.s=0;for(var r=e.length,o=!1,i=0;--r>=0;){var a=8==n?255&e[r]:P(e,r);a<0?"-"==e.charAt(r)&&(o=!0):(o=!1,0==i?this[this.t++]=a:i+n>this.DB?(this[this.t-1]|=(a&(1<<this.DB-i)-1)<<i,this[this.t++]=a>>this.DB-i):this[this.t-1]|=a<<i,(i+=n)>=this.DB&&(i-=this.DB))}8==n&&!!(128&e[0])&&(this.s=-1,i>0&&(this[this.t-1]|=(1<<this.DB-i)-1<<i)),this.clamp(),o&&x.ZERO.subTo(this,this)},x.prototype.clamp=function(){for(var e=this.s&this.DM;this.t>0&&this[this.t-1]==e;)--this.t},x.prototype.dlShiftTo=function(e,t){var n;for(n=this.t-1;n>=0;--n)t[n+e]=this[n];for(n=e-1;n>=0;--n)t[n]=0;t.t=this.t+e,t.s=this.s},x.prototype.drShiftTo=function(e,t){for(var n=e;n<this.t;++n)t[n-e]=this[n];t.t=Math.max(this.t-e,0),t.s=this.s},x.prototype.lShiftTo=function(e,t){var n,r=e%this.DB,o=this.DB-r,i=(1<<o)-1,a=Math.floor(e/this.DB),s=this.s<<r&this.DM;for(n=this.t-1;n>=0;--n)t[n+a+1]=this[n]>>o|s,s=(this[n]&i)<<r;for(n=a-1;n>=0;--n)t[n]=0;t[a]=s,t.t=this.t+a+1,t.s=this.s,t.clamp()},x.prototype.rShiftTo=function(e,t){t.s=this.s;var n=Math.floor(e/this.DB);if(n>=this.t)t.t=0;else{var r=e%this.DB,o=this.DB-r,i=(1<<r)-1;t[0]=this[n]>>r;for(var a=n+1;a<this.t;++a)t[a-n-1]|=(this[a]&i)<<o,t[a-n]=this[a]>>r;r>0&&(t[this.t-n-1]|=(this.s&i)<<o),t.t=this.t-n,t.clamp()}},x.prototype.subTo=function(e,t){for(var n=0,r=0,o=Math.min(e.t,this.t);n<o;)r+=this[n]-e[n],t[n++]=r&this.DM,r>>=this.DB;if(e.t<this.t){for(r-=e.s;n<this.t;)r+=this[n],t[n++]=r&this.DM,r>>=this.DB;r+=this.s}else{for(r+=this.s;n<e.t;)r-=e[n],t[n++]=r&this.DM,r>>=this.DB;r-=e.s}t.s=r<0?-1:0,r<-1?t[n++]=this.DV+r:r>0&&(t[n++]=r),t.t=n,t.clamp()},x.prototype.multiplyTo=function(e,t){var n=this.abs(),r=e.abs(),o=n.t;for(t.t=o+r.t;--o>=0;)t[o]=0;for(o=0;o<r.t;++o)t[o+n.t]=n.am(0,r[o],t,o,0,n.t);t.s=0,t.clamp(),this.s!=e.s&&x.ZERO.subTo(t,t)},x.prototype.squareTo=function(e){for(var t=this.abs(),n=e.t=2*t.t;--n>=0;)e[n]=0;for(n=0;n<t.t-1;++n){var r=t.am(n,t[n],e,2*n,0,1);(e[n+t.t]+=t.am(n+1,2*t[n],e,2*n+1,r,t.t-n-1))>=t.DV&&(e[n+t.t]-=t.DV,e[n+t.t+1]=1)}e.t>0&&(e[e.t-1]+=t.am(n,t[n],e,2*n,0,1)),e.s=0,e.clamp()},x.prototype.divRemTo=function(e,t,n){var r=e.abs();if(!(r.t<=0)){var o=this.abs();if(o.t<r.t)return null!=t&&t.fromInt(0),void(null!=n&&this.copyTo(n));null==n&&(n=A());var i=A(),a=this.s,s=e.s,l=this.DB-T(r[r.t-1]);l>0?(r.lShiftTo(l,i),o.lShiftTo(l,n)):(r.copyTo(i),o.copyTo(n));var u=i.t,c=i[u-1];if(0!=c){var d=c*(1<<this.F1)+(u>1?i[u-2]>>this.F2:0),f=this.FV/d,p=(1<<this.F1)/d,h=1<<this.F2,m=n.t,g=m-u,v=null==t?A():t;for(i.dlShiftTo(g,v),n.compareTo(v)>=0&&(n[n.t++]=1,n.subTo(v,n)),x.ONE.dlShiftTo(u,v),v.subTo(i,i);i.t<u;)i[i.t++]=0;for(;--g>=0;){var y=n[--m]==c?this.DM:Math.floor(n[m]*f+(n[m-1]+h)*p);if((n[m]+=i.am(0,y,n,g,0,u))<y)for(i.dlShiftTo(g,v),n.subTo(v,n);n[m]<--y;)n.subTo(v,n)}null!=t&&(n.drShiftTo(u,t),a!=s&&x.ZERO.subTo(t,t)),n.t=u,n.clamp(),l>0&&n.rShiftTo(l,n),a<0&&x.ZERO.subTo(n,n)}}},x.prototype.invDigit=function(){if(this.t<1)return 0;var e=this[0];if(!(1&e))return 0;var t=3&e;return(t=(t=(t=(t=t*(2-(15&e)*t)&15)*(2-(255&e)*t)&255)*(2-((65535&e)*t&65535))&65535)*(2-e*t%this.DV)%this.DV)>0?this.DV-t:-t},x.prototype.isEven=function(){return 0==(this.t>0?1&this[0]:this.s)},x.prototype.exp=function(e,t){if(e>4294967295||e<1)return x.ONE;var n=A(),r=A(),o=t.convert(this),i=T(e)-1;for(o.copyTo(n);--i>=0;)if(t.sqrTo(n,r),(e&1<<i)>0)t.mulTo(r,o,n);else{var a=n;n=r,r=a}return t.revert(n)},x.prototype.toString=function(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(16==e)t=4;else if(8==e)t=3;else if(2==e)t=1;else if(32==e)t=5;else{if(4!=e)return this.toRadix(e);t=2}var n,r=(1<<t)-1,o=!1,i="",a=this.t,s=this.DB-a*this.DB%t;if(a-- >0)for(s<this.DB&&(n=this[a]>>s)>0&&(o=!0,i=O(n));a>=0;)s<t?(n=(this[a]&(1<<s)-1)<<t-s,n|=this[--a]>>(s+=this.DB-t)):(n=this[a]>>(s-=t)&r,s<=0&&(s+=this.DB,--a)),n>0&&(o=!0),o&&(i+=O(n));return o?i:"0"},x.prototype.negate=function(){var e=A();return x.ZERO.subTo(this,e),e},x.prototype.abs=function(){return this.s<0?this.negate():this},x.prototype.compareTo=function(e){var t=this.s-e.s;if(0!=t)return t;var n=this.t;if(0!=(t=n-e.t))return this.s<0?-t:t;for(;--n>=0;)if(0!=(t=this[n]-e[n]))return t;return 0},x.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+T(this[this.t-1]^this.s&this.DM)},x.prototype.mod=function(e){var t=A();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(x.ZERO)>0&&e.subTo(t,t),t},x.prototype.modPowInt=function(e,t){var n;return n=e<256||t.isEven()?new F(t):new j(t),this.exp(e,n)},x.ZERO=R(0),x.ONE=R(1),U.prototype.convert=z,U.prototype.revert=z,U.prototype.mulTo=function(e,t,n){e.multiplyTo(t,n)},U.prototype.sqrTo=function(e,t){e.squareTo(t)},$.prototype.convert=function(e){if(e.s<0||e.t>2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=A();return e.copyTo(t),this.reduce(t),t},$.prototype.revert=function(e){return e},$.prototype.reduce=function(e){for(e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);e.compareTo(this.r2)<0;)e.dAddOffset(1,this.m.t+1);for(e.subTo(this.r2,e);e.compareTo(this.m)>=0;)e.subTo(this.m,e)},$.prototype.mulTo=function(e,t,n){e.multiplyTo(t,n),this.reduce(n)},$.prototype.sqrTo=function(e,t){e.squareTo(t),this.reduce(t)};var H,W,V,q=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997],K=(1<<26)/q[q.length-1];function G(){this.i=0,this.j=0,this.S=new Array}function J(){!function(e){W[V++]^=255&e,W[V++]^=e>>8&255,W[V++]^=e>>16&255,W[V++]^=e>>24&255,V>=256&&(V-=256)}((new Date).getTime())}if(x.prototype.chunkSize=function(e){return Math.floor(Math.LN2*this.DB/Math.log(e))},x.prototype.toRadix=function(e){if(null==e&&(e=10),0==this.signum()||e<2||e>36)return"0";var t=this.chunkSize(e),n=Math.pow(e,t),r=R(n),o=A(),i=A(),a="";for(this.divRemTo(r,o,i);o.signum()>0;)a=(n+i.intValue()).toString(e).substr(1)+a,o.divRemTo(r,o,i);return i.intValue().toString(e)+a},x.prototype.fromRadix=function(e,t){this.fromInt(0),null==t&&(t=10);for(var n=this.chunkSize(t),r=Math.pow(t,n),o=!1,i=0,a=0,s=0;s<e.length;++s){var l=P(e,s);l<0?"-"==e.charAt(s)&&0==this.signum()&&(o=!0):(a=t*a+l,++i>=n&&(this.dMultiply(r),this.dAddOffset(a,0),i=0,a=0))}i>0&&(this.dMultiply(Math.pow(t,i)),this.dAddOffset(a,0)),o&&x.ZERO.subTo(this,this)},x.prototype.fromNumber=function(e,t,n){if("number"==typeof t)if(e<2)this.fromInt(1);else for(this.fromNumber(e,n),this.testBit(e-1)||this.bitwiseTo(x.ONE.shiftLeft(e-1),I,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(t);)this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(x.ONE.shiftLeft(e-1),this);else{var r=new Array,o=7&e;r.length=1+(e>>3),t.nextBytes(r),o>0?r[0]&=(1<<o)-1:r[0]=0,this.fromString(r,256)}},x.prototype.bitwiseTo=function(e,t,n){var r,o,i=Math.min(e.t,this.t);for(r=0;r<i;++r)n[r]=t(this[r],e[r]);if(e.t<this.t){for(o=e.s&this.DM,r=i;r<this.t;++r)n[r]=t(this[r],o);n.t=this.t}else{for(o=this.s&this.DM,r=i;r<e.t;++r)n[r]=t(o,e[r]);n.t=e.t}n.s=t(this.s,e.s),n.clamp()},x.prototype.changeBit=function(e,t){var n=x.ONE.shiftLeft(e);return this.bitwiseTo(n,t,n),n},x.prototype.addTo=function(e,t){for(var n=0,r=0,o=Math.min(e.t,this.t);n<o;)r+=this[n]+e[n],t[n++]=r&this.DM,r>>=this.DB;if(e.t<this.t){for(r+=e.s;n<this.t;)r+=this[n],t[n++]=r&this.DM,r>>=this.DB;r+=this.s}else{for(r+=this.s;n<e.t;)r+=e[n],t[n++]=r&this.DM,r>>=this.DB;r+=e.s}t.s=r<0?-1:0,r>0?t[n++]=r:r<-1&&(t[n++]=this.DV+r),t.t=n,t.clamp()},x.prototype.dMultiply=function(e){this[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()},x.prototype.dAddOffset=function(e,t){if(0!=e){for(;this.t<=t;)this[this.t++]=0;for(this[t]+=e;this[t]>=this.DV;)this[t]-=this.DV,++t>=this.t&&(this[this.t++]=0),++this[t]}},x.prototype.multiplyLowerTo=function(e,t,n){var r,o=Math.min(this.t+e.t,t);for(n.s=0,n.t=o;o>0;)n[--o]=0;for(r=n.t-this.t;o<r;++o)n[o+this.t]=this.am(0,e[o],n,o,0,this.t);for(r=Math.min(e.t,t);o<r;++o)this.am(0,e[o],n,o,0,t-o);n.clamp()},x.prototype.multiplyUpperTo=function(e,t,n){--t;var r=n.t=this.t+e.t-t;for(n.s=0;--r>=0;)n[r]=0;for(r=Math.max(t-this.t,0);r<e.t;++r)n[this.t+r-t]=this.am(t-r,e[r],n,0,0,this.t+r-t);n.clamp(),n.drShiftTo(1,n)},x.prototype.modInt=function(e){if(e<=0)return 0;var t=this.DV%e,n=this.s<0?e-1:0;if(this.t>0)if(0==t)n=this[0]%e;else for(var r=this.t-1;r>=0;--r)n=(t*n+this[r])%e;return n},x.prototype.millerRabin=function(e){var t=this.subtract(x.ONE),n=t.getLowestSetBit();if(n<=0)return!1;var r=t.shiftRight(n);(e=e+1>>1)>q.length&&(e=q.length);for(var o=A(),i=0;i<e;++i){o.fromInt(q[Math.floor(Math.random()*q.length)]);var a=o.modPow(r,this);if(0!=a.compareTo(x.ONE)&&0!=a.compareTo(t)){for(var s=1;s++<n&&0!=a.compareTo(t);)if(0==(a=a.modPowInt(2,this)).compareTo(x.ONE))return!1;if(0!=a.compareTo(t))return!1}}return!0},x.prototype.clone=function(){var e=A();return this.copyTo(e),e},x.prototype.intValue=function(){if(this.s<0){if(1==this.t)return this[0]-this.DV;if(0==this.t)return-1}else{if(1==this.t)return this[0];if(0==this.t)return 0}return(this[1]&(1<<32-this.DB)-1)<<this.DB|this[0]},x.prototype.byteValue=function(){return 0==this.t?this.s:this[0]<<24>>24},x.prototype.shortValue=function(){return 0==this.t?this.s:this[0]<<16>>16},x.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this[0]<=0?0:1},x.prototype.toByteArray=function(){var e=this.t,t=new Array;t[0]=this.s;var n,r=this.DB-e*this.DB%8,o=0;if(e-- >0)for(r<this.DB&&(n=this[e]>>r)!=(this.s&this.DM)>>r&&(t[o++]=n|this.s<<this.DB-r);e>=0;)r<8?(n=(this[e]&(1<<r)-1)<<8-r,n|=this[--e]>>(r+=this.DB-8)):(n=this[e]>>(r-=8)&255,r<=0&&(r+=this.DB,--e)),!!(128&n)&&(n|=-256),0==o&&(128&this.s)!=(128&n)&&++o,(o>0||n!=this.s)&&(t[o++]=n);return t},x.prototype.equals=function(e){return 0==this.compareTo(e)},x.prototype.min=function(e){return this.compareTo(e)<0?this:e},x.prototype.max=function(e){return this.compareTo(e)>0?this:e},x.prototype.and=function(e){var t=A();return this.bitwiseTo(e,N,t),t},x.prototype.or=function(e){var t=A();return this.bitwiseTo(e,I,t),t},x.prototype.xor=function(e){var t=A();return this.bitwiseTo(e,L,t),t},x.prototype.andNot=function(e){var t=A();return this.bitwiseTo(e,M,t),t},x.prototype.not=function(){for(var e=A(),t=0;t<this.t;++t)e[t]=this.DM&~this[t];return e.t=this.t,e.s=~this.s,e},x.prototype.shiftLeft=function(e){var t=A();return e<0?this.rShiftTo(-e,t):this.lShiftTo(e,t),t},x.prototype.shiftRight=function(e){var t=A();return e<0?this.lShiftTo(-e,t):this.rShiftTo(e,t),t},x.prototype.getLowestSetBit=function(){for(var e=0;e<this.t;++e)if(0!=this[e])return e*this.DB+D(this[e]);return this.s<0?this.t*this.DB:-1},x.prototype.bitCount=function(){for(var e=0,t=this.s&this.DM,n=0;n<this.t;++n)e+=B(this[n]^t);return e},x.prototype.testBit=function(e){var t=Math.floor(e/this.DB);return t>=this.t?0!=this.s:!!(this[t]&1<<e%this.DB)},x.prototype.setBit=function(e){return this.changeBit(e,I)},x.prototype.clearBit=function(e){return this.changeBit(e,M)},x.prototype.flipBit=function(e){return this.changeBit(e,L)},x.prototype.add=function(e){var t=A();return this.addTo(e,t),t},x.prototype.subtract=function(e){var t=A();return this.subTo(e,t),t},x.prototype.multiply=function(e){var t=A();return this.multiplyTo(e,t),t},x.prototype.divide=function(e){var t=A();return this.divRemTo(e,t,null),t},x.prototype.remainder=function(e){var t=A();return this.divRemTo(e,null,t),t},x.prototype.divideAndRemainder=function(e){var t=A(),n=A();return this.divRemTo(e,t,n),new Array(t,n)},x.prototype.modPow=function(e,t){var n,r,o=e.bitLength(),i=R(1);if(o<=0)return i;n=o<18?1:o<48?3:o<144?4:o<768?5:6,r=o<8?new F(t):t.isEven()?new $(t):new j(t);var a=new Array,s=3,l=n-1,u=(1<<n)-1;if(a[1]=r.convert(this),n>1){var c=A();for(r.sqrTo(a[1],c);s<=u;)a[s]=A(),r.mulTo(c,a[s-2],a[s]),s+=2}var d,f,p=e.t-1,h=!0,m=A();for(o=T(e[p])-1;p>=0;){for(o>=l?d=e[p]>>o-l&u:(d=(e[p]&(1<<o+1)-1)<<l-o,p>0&&(d|=e[p-1]>>this.DB+o-l)),s=n;!(1&d);)d>>=1,--s;if((o-=s)<0&&(o+=this.DB,--p),h)a[d].copyTo(i),h=!1;else{for(;s>1;)r.sqrTo(i,m),r.sqrTo(m,i),s-=2;s>0?r.sqrTo(i,m):(f=i,i=m,m=f),r.mulTo(m,a[d],i)}for(;p>=0&&!(e[p]&1<<o);)r.sqrTo(i,m),f=i,i=m,m=f,--o<0&&(o=this.DB-1,--p)}return r.revert(i)},x.prototype.modInverse=function(e){var t=e.isEven();if(this.isEven()&&t||0==e.signum())return x.ZERO;for(var n=e.clone(),r=this.clone(),o=R(1),i=R(0),a=R(0),s=R(1);0!=n.signum();){for(;n.isEven();)n.rShiftTo(1,n),t?(o.isEven()&&i.isEven()||(o.addTo(this,o),i.subTo(e,i)),o.rShiftTo(1,o)):i.isEven()||i.subTo(e,i),i.rShiftTo(1,i);for(;r.isEven();)r.rShiftTo(1,r),t?(a.isEven()&&s.isEven()||(a.addTo(this,a),s.subTo(e,s)),a.rShiftTo(1,a)):s.isEven()||s.subTo(e,s),s.rShiftTo(1,s);n.compareTo(r)>=0?(n.subTo(r,n),t&&o.subTo(a,o),i.subTo(s,i)):(r.subTo(n,r),t&&a.subTo(o,a),s.subTo(i,s))}return 0!=r.compareTo(x.ONE)?x.ZERO:s.compareTo(e)>=0?s.subtract(e):s.signum()<0?(s.addTo(e,s),s.signum()<0?s.add(e):s):s},x.prototype.pow=function(e){return this.exp(e,new U)},x.prototype.gcd=function(e){var t=this.s<0?this.negate():this.clone(),n=e.s<0?e.negate():e.clone();if(t.compareTo(n)<0){var r=t;t=n,n=r}var o=t.getLowestSetBit(),i=n.getLowestSetBit();if(i<0)return t;for(o<i&&(i=o),i>0&&(t.rShiftTo(i,t),n.rShiftTo(i,n));t.signum()>0;)(o=t.getLowestSetBit())>0&&t.rShiftTo(o,t),(o=n.getLowestSetBit())>0&&n.rShiftTo(o,n),t.compareTo(n)>=0?(t.subTo(n,t),t.rShiftTo(1,t)):(n.subTo(t,n),n.rShiftTo(1,n));return i>0&&n.lShiftTo(i,n),n},x.prototype.isProbablePrime=function(e){var t,n=this.abs();if(1==n.t&&n[0]<=q[q.length-1]){for(t=0;t<q.length;++t)if(n[0]==q[t])return!0;return!1}if(n.isEven())return!1;for(t=1;t<q.length;){for(var r=q[t],o=t+1;o<q.length&&r<K;)r*=q[o++];for(r=n.modInt(r);t<o;)if(r%q[t++]==0)return!1}return n.millerRabin(e)},x.prototype.square=function(){var e=A();return this.squareTo(e),e},G.prototype.init=function(e){var t,n,r;for(t=0;t<256;++t)this.S[t]=t;for(n=0,t=0;t<256;++t)n=n+this.S[t]+e[t%e.length]&255,r=this.S[t],this.S[t]=this.S[n],this.S[n]=r;this.i=0,this.j=0},G.prototype.next=function(){var e;return this.i=this.i+1&255,this.j=this.j+this.S[this.i]&255,e=this.S[this.i],this.S[this.i]=this.S[this.j],this.S[this.j]=e,this.S[e+this.S[this.i]&255]},null==W){var Y;if(W=new Array,V=0,void 0!==o&&(void 0!==o.crypto||void 0!==o.msCrypto)){var X=o.crypto||o.msCrypto;if(X.getRandomValues){var Q=new Uint8Array(32);for(X.getRandomValues(Q),Y=0;Y<32;++Y)W[V++]=Q[Y]}else if("Netscape"==r.appName&&r.appVersion<"5"){var Z=o.crypto.random(32);for(Y=0;Y<Z.length;++Y)W[V++]=255&Z.charCodeAt(Y)}}for(;V<256;)Y=Math.floor(65536*Math.random()),W[V++]=Y>>>8,W[V++]=255&Y;V=0,J()}function ee(){if(null==H){for(J(),(H=new G).init(W),V=0;V<W.length;++V)W[V]=0;V=0}return H.next()}function te(){}function ne(e,t){return new x(e,t)}function re(e,t,n){for(var r="",o=0;r.length<t;)r+=n(String.fromCharCode.apply(String,e.concat([(4278190080&o)>>24,(16711680&o)>>16,(65280&o)>>8,255&o]))),o+=1;return r}function oe(){this.n=null,this.e=0,this.d=null,this.p=null,this.q=null,this.dmp1=null,this.dmq1=null,this.coeff=null}function ie(e,t){this.x=t,this.q=e}function ae(e,t,n,r){this.curve=e,this.x=t,this.y=n,this.z=null==r?x.ONE:r,this.zinv=null}function se(e,t,n){this.q=e,this.a=this.fromBigInteger(t),this.b=this.fromBigInteger(n),this.infinity=new ae(this,null,null)}te.prototype.nextBytes=function(e){var t;for(t=0;t<e.length;++t)e[t]=ee()},oe.prototype.doPublic=function(e){return e.modPowInt(this.e,this.n)},oe.prototype.setPublic=function(e,t){if(this.isPublic=!0,this.isPrivate=!1,"string"!=typeof e)this.n=e,this.e=t;else{if(!(null!=e&&null!=t&&e.length>0&&t.length>0))throw"Invalid RSA public key";this.n=ne(e,16),this.e=parseInt(t,16)}},oe.prototype.encrypt=function(e){var t=function(e,t){if(t<e.length+11)throw"Message too long for RSA";for(var n=new Array,r=e.length-1;r>=0&&t>0;){var o=e.charCodeAt(r--);o<128?n[--t]=o:o>127&&o<2048?(n[--t]=63&o|128,n[--t]=o>>6|192):(n[--t]=63&o|128,n[--t]=o>>6&63|128,n[--t]=o>>12|224)}n[--t]=0;for(var i=new te,a=new Array;t>2;){for(a[0]=0;0==a[0];)i.nextBytes(a);n[--t]=a[0]}return n[--t]=2,n[--t]=0,new x(n)}(e,this.n.bitLength()+7>>3);if(null==t)return null;var n=this.doPublic(t);if(null==n)return null;var r=n.toString(16);return 1&r.length?"0"+r:r},oe.prototype.encryptOAEP=function(e,t,n){var r=function(e,t,n,r){var o=ue.crypto.MessageDigest,i=ue.crypto.Util,a=null;if(n||(n="sha1"),"string"==typeof n&&(a=o.getCanonicalAlgName(n),r=o.getHashLength(a),n=function(e){return xe(i.hashHex(Ae(e),a))}),e.length+2*r+2>t)throw"Message too long for RSA";var s,l="";for(s=0;s<t-e.length-2*r-2;s+=1)l+="\0";var u=n("")+l+""+e,c=new Array(r);(new te).nextBytes(c);var d=re(c,u.length,n),f=[];for(s=0;s<u.length;s+=1)f[s]=u.charCodeAt(s)^d.charCodeAt(s);var p=re(f,c.length,n),h=[0];for(s=0;s<c.length;s+=1)h[s+1]=c[s]^p.charCodeAt(s);return new x(h.concat(f))}(e,this.n.bitLength()+7>>3,t,n);if(null==r)return null;var o=this.doPublic(r);if(null==o)return null;var i=o.toString(16);return 1&i.length?"0"+i:i},oe.prototype.type="RSA",ie.prototype.equals=function(e){return e==this||this.q.equals(e.q)&&this.x.equals(e.x)},ie.prototype.toBigInteger=function(){return this.x},ie.prototype.negate=function(){return new ie(this.q,this.x.negate().mod(this.q))},ie.prototype.add=function(e){return new ie(this.q,this.x.add(e.toBigInteger()).mod(this.q))},ie.prototype.subtract=function(e){return new ie(this.q,this.x.subtract(e.toBigInteger()).mod(this.q))},ie.prototype.multiply=function(e){return new ie(this.q,this.x.multiply(e.toBigInteger()).mod(this.q))},ie.prototype.square=function(){return new ie(this.q,this.x.square().mod(this.q))},ie.prototype.divide=function(e){return new ie(this.q,this.x.multiply(e.toBigInteger().modInverse(this.q)).mod(this.q))},ae.prototype.getX=function(){return null==this.zinv&&(this.zinv=this.z.modInverse(this.curve.q)),this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q))},ae.prototype.getY=function(){return null==this.zinv&&(this.zinv=this.z.modInverse(this.curve.q)),this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q))},ae.prototype.equals=function(e){return e==this||(this.isInfinity()?e.isInfinity():e.isInfinity()?this.isInfinity():!!e.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(e.z)).mod(this.curve.q).equals(x.ZERO)&&e.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(e.z)).mod(this.curve.q).equals(x.ZERO))},ae.prototype.isInfinity=function(){return null==this.x&&null==this.y||this.z.equals(x.ZERO)&&!this.y.toBigInteger().equals(x.ZERO)},ae.prototype.negate=function(){return new ae(this.curve,this.x,this.y.negate(),this.z)},ae.prototype.add=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;var t=e.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(e.z)).mod(this.curve.q),n=e.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(e.z)).mod(this.curve.q);if(x.ZERO.equals(n))return x.ZERO.equals(t)?this.twice():this.curve.getInfinity();var r=new x("3"),o=this.x.toBigInteger(),i=this.y.toBigInteger(),a=(e.x.toBigInteger(),e.y.toBigInteger(),n.square()),s=a.multiply(n),l=o.multiply(a),u=t.square().multiply(this.z),c=u.subtract(l.shiftLeft(1)).multiply(e.z).subtract(s).multiply(n).mod(this.curve.q),d=l.multiply(r).multiply(t).subtract(i.multiply(s)).subtract(u.multiply(t)).multiply(e.z).add(t.multiply(s)).mod(this.curve.q),f=s.multiply(this.z).multiply(e.z).mod(this.curve.q);return new ae(this.curve,this.curve.fromBigInteger(c),this.curve.fromBigInteger(d),f)},ae.prototype.twice=function(){if(this.isInfinity())return this;if(0==this.y.toBigInteger().signum())return this.curve.getInfinity();var e=new x("3"),t=this.x.toBigInteger(),n=this.y.toBigInteger(),r=n.multiply(this.z),o=r.multiply(n).mod(this.curve.q),i=this.curve.a.toBigInteger(),a=t.square().multiply(e);x.ZERO.equals(i)||(a=a.add(this.z.square().multiply(i)));var s=(a=a.mod(this.curve.q)).square().subtract(t.shiftLeft(3).multiply(o)).shiftLeft(1).multiply(r).mod(this.curve.q),l=a.multiply(e).multiply(t).subtract(o.shiftLeft(1)).shiftLeft(2).multiply(o).subtract(a.square().multiply(a)).mod(this.curve.q),u=r.square().multiply(r).shiftLeft(3).mod(this.curve.q);return new ae(this.curve,this.curve.fromBigInteger(s),this.curve.fromBigInteger(l),u)},ae.prototype.multiply=function(e){if(this.isInfinity())return this;if(0==e.signum())return this.curve.getInfinity();var t,n=e,r=n.multiply(new x("3")),o=this.negate(),i=this,a=this.curve.q.subtract(e),s=a.multiply(new x("3")),l=new ae(this.curve,this.x,this.y),u=l.negate();for(t=r.bitLength()-2;t>0;--t){i=i.twice();var c=r.testBit(t);c!=n.testBit(t)&&(i=i.add(c?this:o))}for(t=s.bitLength()-2;t>0;--t){l=l.twice();var d=s.testBit(t);d!=a.testBit(t)&&(l=l.add(d?l:u))}return i},ae.prototype.multiplyTwo=function(e,t,n){var r;r=e.bitLength()>n.bitLength()?e.bitLength()-1:n.bitLength()-1;for(var o=this.curve.getInfinity(),i=this.add(t);r>=0;)o=o.twice(),e.testBit(r)?o=n.testBit(r)?o.add(i):o.add(this):n.testBit(r)&&(o=o.add(t)),--r;return o},se.prototype.getQ=function(){return this.q},se.prototype.getA=function(){return this.a},se.prototype.getB=function(){return this.b},se.prototype.equals=function(e){return e==this||this.q.equals(e.q)&&this.a.equals(e.a)&&this.b.equals(e.b)},se.prototype.getInfinity=function(){return this.infinity},se.prototype.fromBigInteger=function(e){return new ie(this.q,e)},se.prototype.decodePointHex=function(e){switch(parseInt(e.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:default:return null;case 4:case 6:case 7:var t=(e.length-2)/2,n=e.substr(2,t),r=e.substr(t+2,t);return new ae(this,this.fromBigInteger(new x(n,16)),this.fromBigInteger(new x(r,16)))}},ie.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)},ae.prototype.getEncoded=function(e){var t=function(e,t){var n=e.toByteArrayUnsigned();if(t<n.length)n=n.slice(n.length-t);else for(;t>n.length;)n.unshift(0);return n},n=this.getX().toBigInteger(),r=this.getY().toBigInteger(),o=t(n,32);return e?r.isEven()?o.unshift(2):o.unshift(3):(o.unshift(4),o=o.concat(t(r,32))),o},ae.decodeFrom=function(e,t){t[0];var n=t.length-1,r=t.slice(1,1+n/2),o=t.slice(1+n/2,1+n);r.unshift(0),o.unshift(0);var i=new x(r),a=new x(o);return new ae(e,e.fromBigInteger(i),e.fromBigInteger(a))},ae.decodeFromHex=function(e,t){t.substr(0,2);var n=t.length-2,r=t.substr(2,n/2),o=t.substr(2+n/2,n/2),i=new x(r,16),a=new x(o,16);return new ae(e,e.fromBigInteger(i),e.fromBigInteger(a))},ae.prototype.add2D=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;if(this.x.equals(e.x))return this.y.equals(e.y)?this.twice():this.curve.getInfinity();var t=e.x.subtract(this.x),n=e.y.subtract(this.y).divide(t),r=n.square().subtract(this.x).subtract(e.x),o=n.multiply(this.x.subtract(r)).subtract(this.y);return new ae(this.curve,r,o)},ae.prototype.twice2D=function(){if(this.isInfinity())return this;if(0==this.y.toBigInteger().signum())return this.curve.getInfinity();var e=this.curve.fromBigInteger(x.valueOf(2)),t=this.curve.fromBigInteger(x.valueOf(3)),n=this.x.square().multiply(t).add(this.curve.a).divide(this.y.multiply(e)),r=n.square().subtract(this.x.multiply(e)),o=n.multiply(this.x.subtract(r)).subtract(this.y);return new ae(this.curve,r,o)},ae.prototype.multiply2D=function(e){if(this.isInfinity())return this;if(0==e.signum())return this.curve.getInfinity();var t,n=e,r=n.multiply(new x("3")),o=this.negate(),i=this;for(t=r.bitLength()-2;t>0;--t){i=i.twice();var a=r.testBit(t);a!=n.testBit(t)&&(i=i.add2D(a?this:o))}return i},ae.prototype.isOnCurve=function(){var e=this.getX().toBigInteger(),t=this.getY().toBigInteger(),n=this.curve.getA().toBigInteger(),r=this.curve.getB().toBigInteger(),o=this.curve.getQ(),i=t.multiply(t).mod(o),a=e.multiply(e).multiply(e).add(n.multiply(e)).add(r).mod(o);return i.equals(a)},ae.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"},ae.prototype.validate=function(){var e=this.curve.getQ();if(this.isInfinity())throw new Error("Point is at infinity.");var t=this.getX().toBigInteger(),n=this.getY().toBigInteger();if(t.compareTo(x.ONE)<0||t.compareTo(e.subtract(x.ONE))>0)throw new Error("x coordinate out of bounds");if(n.compareTo(x.ONE)<0||n.compareTo(e.subtract(x.ONE))>0)throw new Error("y coordinate out of bounds");if(!this.isOnCurve())throw new Error("Point is not on the curve.");if(this.multiply(e).isInfinity())throw new Error("Point is not a scalar multiple of G.");return!0};var le=function(){var e=new RegExp('(?:false|true|null|[\\{\\}\\[\\]]|(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)|(?:"(?:[^\\0-\\x08\\x0a-\\x1f"\\\\]|\\\\(?:["/\\\\bfnrt]|u[0-9A-Fa-f]{4}))*"))',"g"),t=new RegExp("\\\\(?:([^u])|u(.{4}))","g"),r={'"':'"',"/":"/","\\":"\\",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"};function o(e,t,n){return t?r[t]:String.fromCharCode(parseInt(n,16))}var i=new String(""),a=Object.hasOwnProperty;return function(r,s){var l,u,c=r.match(e),d=c[0],f=!1;"{"===d?l={}:"["===d?l=[]:(l=[],f=!0);for(var p=[l],h=1-f,m=c.length;h<m;++h){var g;switch((d=c[h]).charCodeAt(0)){default:(g=p[0])[u||g.length]=+d,u=void 0;break;case 34:if(-1!==(d=d.substring(1,d.length-1)).indexOf("\\")&&(d=d.replace(t,o)),g=p[0],!u){if(!(g instanceof Array)){u=d||i;break}u=g.length}g[u]=d,u=void 0;break;case 91:g=p[0],p.unshift(g[u||g.length]=[]),u=void 0;break;case 93:case 125:p.shift();break;case 102:(g=p[0])[u||g.length]=!1,u=void 0;break;case 110:(g=p[0])[u||g.length]=null,u=void 0;break;case 116:(g=p[0])[u||g.length]=!0,u=void 0;break;case 123:g=p[0],p.unshift(g[u||g.length]={}),u=void 0}}if(f){if(1!==p.length)throw new Error;l=l[0]}else if(p.length)throw new Error;return s&&(l=function e(t,r){var o=t[r];if(o&&"object"===(void 0===o?"undefined":n(o))){var i=null;for(var l in o)if(a.call(o,l)&&o!==t){var u=e(o,l);void 0!==u?o[l]=u:(i||(i=[]),i.push(l))}if(i)for(var c=i.length;--c>=0;)delete o[i[c]]}return s.call(t,r,o)}({"":l},"")),l}}();void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.asn1&&ue.asn1||(ue.asn1={}),ue.asn1.ASN1Util=new function(){this.integerToByteHex=function(e){var t=e.toString(16);return t.length%2==1&&(t="0"+t),t},this.bigIntToMinTwosComplementsHex=function(e){var t=e.toString(16);if("-"!=t.substr(0,1))t.length%2==1?t="0"+t:t.match(/^[0-7]/)||(t="00"+t);else{var n=t.substr(1).length;n%2==1?n+=1:t.match(/^[0-7]/)||(n+=2);for(var r="",o=0;o<n;o++)r+="f";t=new x(r,16).xor(e).add(x.ONE).toString(16).replace(/^-/,"")}return t},this.getPEMStringFromHex=function(e,t){return Oe(e,t)},this.newObject=function(e){var t=ue.asn1,n=t.ASN1Object,r=t.DERBoolean,o=t.DERInteger,i=t.DERBitString,a=t.DEROctetString,s=t.DERNull,l=t.DERObjectIdentifier,u=t.DEREnumerated,c=t.DERUTF8String,d=t.DERNumericString,f=t.DERPrintableString,p=t.DERTeletexString,h=t.DERIA5String,m=t.DERUTCTime,g=t.DERGeneralizedTime,v=t.DERVisibleString,y=t.DERBMPString,b=t.DERSequence,w=t.DERSet,E=t.DERTaggedObject,S=t.ASN1Util.newObject;if(e instanceof t.ASN1Object)return e;var x=Object.keys(e);if(1!=x.length)throw new Error("key of param shall be only one.");var A=x[0];if(-1==":asn1:bool:int:bitstr:octstr:null:oid:enum:utf8str:numstr:prnstr:telstr:ia5str:utctime:gentime:visstr:bmpstr:seq:set:tag:".indexOf(":"+A+":"))throw new Error("undefined key: "+A);if("bool"==A)return new r(e[A]);if("int"==A)return new o(e[A]);if("bitstr"==A)return new i(e[A]);if("octstr"==A)return new a(e[A]);if("null"==A)return new s(e[A]);if("oid"==A)return new l(e[A]);if("enum"==A)return new u(e[A]);if("utf8str"==A)return new c(e[A]);if("numstr"==A)return new d(e[A]);if("prnstr"==A)return new f(e[A]);if("telstr"==A)return new p(e[A]);if("ia5str"==A)return new h(e[A]);if("utctime"==A)return new m(e[A]);if("gentime"==A)return new g(e[A]);if("visstr"==A)return new v(e[A]);if("bmpstr"==A)return new y(e[A]);if("asn1"==A)return new n(e[A]);if("seq"==A){for(var k=e[A],_=[],C=0;C<k.length;C++){var O=S(k[C]);_.push(O)}return new b({array:_})}if("set"==A){for(k=e[A],_=[],C=0;C<k.length;C++)O=S(k[C]),_.push(O);return new w({array:_})}if("tag"==A){var P=e[A];if("[object Array]"===Object.prototype.toString.call(P)&&3==P.length){var R=S(P[2]);return new E({tag:P[0],explicit:P[1],obj:R})}return new E(P)}},this.jsonToASN1HEX=function(e){return this.newObject(e).getEncodedHex()}},ue.asn1.ASN1Util.oidHexToInt=function(e){for(var t="",n=parseInt(e.substr(0,2),16),r=(t=Math.floor(n/40)+"."+n%40,""),o=2;o<e.length;o+=2){var i=("00000000"+parseInt(e.substr(o,2),16).toString(2)).slice(-8);r+=i.substr(1,7),"0"==i.substr(0,1)&&(t=t+"."+new x(r,2).toString(10),r="")}return t},ue.asn1.ASN1Util.oidIntToHex=function(e){var t=function(e){var t=e.toString(16);return 1==t.length&&(t="0"+t),t},n=function(e){var n="",r=new x(e,10).toString(2),o=7-r.length%7;7==o&&(o=0);for(var i="",a=0;a<o;a++)i+="0";for(r=i+r,a=0;a<r.length-1;a+=7){var s=r.substr(a,7);a!=r.length-7&&(s="1"+s),n+=t(parseInt(s,2))}return n};if(!e.match(/^[0-9.]+$/))throw"malformed oid string: "+e;var r="",o=e.split("."),i=40*parseInt(o[0])+parseInt(o[1]);r+=t(i),o.splice(0,2);for(var a=0;a<o.length;a++)r+=n(o[a]);return r},ue.asn1.ASN1Object=function(e){this.params=null,this.getLengthHexFromValue=function(){if(void 0===this.hV||null==this.hV)throw new Error("this.hV is null or undefined");if(this.hV.length%2==1)throw new Error("value hex must be even length: n=0,v="+this.hV);var e=this.hV.length/2,t=e.toString(16);if(t.length%2==1&&(t="0"+t),e<128)return t;var n=t.length/2;if(n>15)throw"ASN.1 length too long to represent by 8x: n = "+e.toString(16);return(128+n).toString(16)+t},this.getEncodedHex=function(){return(null==this.hTLV||this.isModified)&&(this.hV=this.getFreshValueHex(),this.hL=this.getLengthHexFromValue(),this.hTLV=this.hT+this.hL+this.hV,this.isModified=!1),this.hTLV},this.getValueHex=function(){return this.getEncodedHex(),this.hV},this.getFreshValueHex=function(){return""},this.setByParam=function(e){this.params=e},null!=e&&null!=e.tlv&&(this.hTLV=e.tlv,this.isModified=!1)},ue.asn1.DERAbstractString=function(e){ue.asn1.DERAbstractString.superclass.constructor.call(this),this.getString=function(){return this.s},this.setString=function(e){this.hTLV=null,this.isModified=!0,this.s=e,this.hV=Ee(this.s).toLowerCase()},this.setStringHex=function(e){this.hTLV=null,this.isModified=!0,this.s=null,this.hV=e},this.getFreshValueHex=function(){return this.hV},void 0!==e&&("string"==typeof e?this.setString(e):void 0!==e.str?this.setString(e.str):void 0!==e.hex&&this.setStringHex(e.hex))},i.lang.extend(ue.asn1.DERAbstractString,ue.asn1.ASN1Object),ue.asn1.DERAbstractTime=function(e){ue.asn1.DERAbstractTime.superclass.constructor.call(this),this.localDateToUTC=function(e){var t=e.getTime()+6e4*e.getTimezoneOffset();return new Date(t)},this.formatDate=function(e,t,n){var r=this.zeroPadding,o=this.localDateToUTC(e),i=String(o.getFullYear());"utc"==t&&(i=i.substr(2,2));var a=i+r(String(o.getMonth()+1),2)+r(String(o.getDate()),2)+r(String(o.getHours()),2)+r(String(o.getMinutes()),2)+r(String(o.getSeconds()),2);if(!0===n){var s=o.getMilliseconds();if(0!=s){var l=r(String(s),3);a=a+"."+(l=l.replace(/[0]+$/,""))}}return a+"Z"},this.zeroPadding=function(e,t){return e.length>=t?e:new Array(t-e.length+1).join("0")+e},this.getString=function(){return this.s},this.setString=function(e){this.hTLV=null,this.isModified=!0,this.s=e,this.hV=ge(e)},this.setByDateValue=function(e,t,n,r,o,i){var a=new Date(Date.UTC(e,t-1,n,r,o,i,0));this.setByDate(a)},this.getFreshValueHex=function(){return this.hV}},i.lang.extend(ue.asn1.DERAbstractTime,ue.asn1.ASN1Object),ue.asn1.DERAbstractStructured=function(e){ue.asn1.DERAbstractString.superclass.constructor.call(this),this.setByASN1ObjectArray=function(e){this.hTLV=null,this.isModified=!0,this.asn1Array=e},this.appendASN1Object=function(e){this.hTLV=null,this.isModified=!0,this.asn1Array.push(e)},this.asn1Array=new Array,void 0!==e&&void 0!==e.array&&(this.asn1Array=e.array)},i.lang.extend(ue.asn1.DERAbstractStructured,ue.asn1.ASN1Object),ue.asn1.DERBoolean=function(e){ue.asn1.DERBoolean.superclass.constructor.call(this),this.hT="01",this.hTLV=0==e?"010100":"0101ff"},i.lang.extend(ue.asn1.DERBoolean,ue.asn1.ASN1Object),ue.asn1.DERInteger=function(e){ue.asn1.DERInteger.superclass.constructor.call(this),this.hT="02",this.setByBigInteger=function(e){this.hTLV=null,this.isModified=!0,this.hV=ue.asn1.ASN1Util.bigIntToMinTwosComplementsHex(e)},this.setByInteger=function(e){var t=new x(String(e),10);this.setByBigInteger(t)},this.setValueHex=function(e){this.hV=e},this.getFreshValueHex=function(){return this.hV},void 0!==e&&(void 0!==e.bigint?this.setByBigInteger(e.bigint):void 0!==e.int?this.setByInteger(e.int):"number"==typeof e?this.setByInteger(e):void 0!==e.hex&&this.setValueHex(e.hex))},i.lang.extend(ue.asn1.DERInteger,ue.asn1.ASN1Object),ue.asn1.DERBitString=function(e){if(void 0!==e&&void 0!==e.obj){var t=ue.asn1.ASN1Util.newObject(e.obj);e.hex="00"+t.getEncodedHex()}ue.asn1.DERBitString.superclass.constructor.call(this),this.hT="03",this.setHexValueIncludingUnusedBits=function(e){this.hTLV=null,this.isModified=!0,this.hV=e},this.setUnusedBitsAndHexValue=function(e,t){if(e<0||7<e)throw"unused bits shall be from 0 to 7: u = "+e;var n="0"+e;this.hTLV=null,this.isModified=!0,this.hV=n+t},this.setByBinaryString=function(e){var t=8-(e=e.replace(/0+$/,"")).length%8;8==t&&(t=0);for(var n=0;n<=t;n++)e+="0";var r="";for(n=0;n<e.length-1;n+=8){var o=e.substr(n,8),i=parseInt(o,2).toString(16);1==i.length&&(i="0"+i),r+=i}this.hTLV=null,this.isModified=!0,this.hV="0"+t+r},this.setByBooleanArray=function(e){for(var t="",n=0;n<e.length;n++)1==e[n]?t+="1":t+="0";this.setByBinaryString(t)},this.newFalseArray=function(e){for(var t=new Array(e),n=0;n<e;n++)t[n]=!1;return t},this.getFreshValueHex=function(){return this.hV},void 0!==e&&("string"==typeof e&&e.toLowerCase().match(/^[0-9a-f]+$/)?this.setHexValueIncludingUnusedBits(e):void 0!==e.hex?this.setHexValueIncludingUnusedBits(e.hex):void 0!==e.bin?this.setByBinaryString(e.bin):void 0!==e.array&&this.setByBooleanArray(e.array))},i.lang.extend(ue.asn1.DERBitString,ue.asn1.ASN1Object),ue.asn1.DEROctetString=function(e){if(void 0!==e&&void 0!==e.obj){var t=ue.asn1.ASN1Util.newObject(e.obj);e.hex=t.getEncodedHex()}ue.asn1.DEROctetString.superclass.constructor.call(this,e),this.hT="04"},i.lang.extend(ue.asn1.DEROctetString,ue.asn1.DERAbstractString),ue.asn1.DERNull=function(){ue.asn1.DERNull.superclass.constructor.call(this),this.hT="05",this.hTLV="0500"},i.lang.extend(ue.asn1.DERNull,ue.asn1.ASN1Object),ue.asn1.DERObjectIdentifier=function(e){ue.asn1.DERObjectIdentifier.superclass.constructor.call(this),this.hT="06",this.setValueHex=function(e){this.hTLV=null,this.isModified=!0,this.s=null,this.hV=e},this.setValueOidString=function(e){var t=function(e){var t=function(e){var t=e.toString(16);return 1==t.length&&(t="0"+t),t},n=function(e){var n="",r=parseInt(e,10).toString(2),o=7-r.length%7;7==o&&(o=0);for(var i="",a=0;a<o;a++)i+="0";for(r=i+r,a=0;a<r.length-1;a+=7){var s=r.substr(a,7);a!=r.length-7&&(s="1"+s),n+=t(parseInt(s,2))}return n};try{if(!e.match(/^[0-9.]+$/))return null;var r="",o=e.split("."),i=40*parseInt(o[0],10)+parseInt(o[1],10);r+=t(i),o.splice(0,2);for(var a=0;a<o.length;a++)r+=n(o[a]);return r}catch(e){return null}}(e);if(null==t)throw new Error("malformed oid string: "+e);this.hTLV=null,this.isModified=!0,this.s=null,this.hV=t},this.setValueName=function(e){var t=ue.asn1.x509.OID.name2oid(e);if(""===t)throw new Error("DERObjectIdentifier oidName undefined: "+e);this.setValueOidString(t)},this.setValueNameOrOid=function(e){e.match(/^[0-2].[0-9.]+$/)?this.setValueOidString(e):this.setValueName(e)},this.getFreshValueHex=function(){return this.hV},this.setByParam=function(e){"string"==typeof e?this.setValueNameOrOid(e):void 0!==e.oid?this.setValueNameOrOid(e.oid):void 0!==e.name?this.setValueNameOrOid(e.name):void 0!==e.hex&&this.setValueHex(e.hex)},void 0!==e&&this.setByParam(e)},i.lang.extend(ue.asn1.DERObjectIdentifier,ue.asn1.ASN1Object),ue.asn1.DEREnumerated=function(e){ue.asn1.DEREnumerated.superclass.constructor.call(this),this.hT="0a",this.setByBigInteger=function(e){this.hTLV=null,this.isModified=!0,this.hV=ue.asn1.ASN1Util.bigIntToMinTwosComplementsHex(e)},this.setByInteger=function(e){var t=new x(String(e),10);this.setByBigInteger(t)},this.setValueHex=function(e){this.hV=e},this.getFreshValueHex=function(){return this.hV},void 0!==e&&(void 0!==e.int?this.setByInteger(e.int):"number"==typeof e?this.setByInteger(e):void 0!==e.hex&&this.setValueHex(e.hex))},i.lang.extend(ue.asn1.DEREnumerated,ue.asn1.ASN1Object),ue.asn1.DERUTF8String=function(e){ue.asn1.DERUTF8String.superclass.constructor.call(this,e),this.hT="0c"},i.lang.extend(ue.asn1.DERUTF8String,ue.asn1.DERAbstractString),ue.asn1.DERNumericString=function(e){ue.asn1.DERNumericString.superclass.constructor.call(this,e),this.hT="12"},i.lang.extend(ue.asn1.DERNumericString,ue.asn1.DERAbstractString),ue.asn1.DERPrintableString=function(e){ue.asn1.DERPrintableString.superclass.constructor.call(this,e),this.hT="13"},i.lang.extend(ue.asn1.DERPrintableString,ue.asn1.DERAbstractString),ue.asn1.DERTeletexString=function(e){ue.asn1.DERTeletexString.superclass.constructor.call(this,e),this.hT="14"},i.lang.extend(ue.asn1.DERTeletexString,ue.asn1.DERAbstractString),ue.asn1.DERIA5String=function(e){ue.asn1.DERIA5String.superclass.constructor.call(this,e),this.hT="16"},i.lang.extend(ue.asn1.DERIA5String,ue.asn1.DERAbstractString),ue.asn1.DERVisibleString=function(e){ue.asn1.DERIA5String.superclass.constructor.call(this,e),this.hT="1a"},i.lang.extend(ue.asn1.DERVisibleString,ue.asn1.DERAbstractString),ue.asn1.DERBMPString=function(e){ue.asn1.DERBMPString.superclass.constructor.call(this,e),this.hT="1e"},i.lang.extend(ue.asn1.DERBMPString,ue.asn1.DERAbstractString),ue.asn1.DERUTCTime=function(e){ue.asn1.DERUTCTime.superclass.constructor.call(this,e),this.hT="17",this.setByDate=function(e){this.hTLV=null,this.isModified=!0,this.date=e,this.s=this.formatDate(this.date,"utc"),this.hV=ge(this.s)},this.getFreshValueHex=function(){return void 0===this.date&&void 0===this.s&&(this.date=new Date,this.s=this.formatDate(this.date,"utc"),this.hV=ge(this.s)),this.hV},void 0!==e&&(void 0!==e.str?this.setString(e.str):"string"==typeof e&&e.match(/^[0-9]{12}Z$/)?this.setString(e):void 0!==e.hex?this.setStringHex(e.hex):void 0!==e.date&&this.setByDate(e.date))},i.lang.extend(ue.asn1.DERUTCTime,ue.asn1.DERAbstractTime),ue.asn1.DERGeneralizedTime=function(e){ue.asn1.DERGeneralizedTime.superclass.constructor.call(this,e),this.hT="18",this.withMillis=!1,this.setByDate=function(e){this.hTLV=null,this.isModified=!0,this.date=e,this.s=this.formatDate(this.date,"gen",this.withMillis),this.hV=ge(this.s)},this.getFreshValueHex=function(){return void 0===this.date&&void 0===this.s&&(this.date=new Date,this.s=this.formatDate(this.date,"gen",this.withMillis),this.hV=ge(this.s)),this.hV},void 0!==e&&(void 0!==e.str?this.setString(e.str):"string"==typeof e&&e.match(/^[0-9]{14}Z$/)?this.setString(e):void 0!==e.hex?this.setStringHex(e.hex):void 0!==e.date&&this.setByDate(e.date),!0===e.millis&&(this.withMillis=!0))},i.lang.extend(ue.asn1.DERGeneralizedTime,ue.asn1.DERAbstractTime),ue.asn1.DERSequence=function(e){ue.asn1.DERSequence.superclass.constructor.call(this,e),this.hT="30",this.getFreshValueHex=function(){for(var e="",t=0;t<this.asn1Array.length;t++)e+=this.asn1Array[t].getEncodedHex();return this.hV=e,this.hV}},i.lang.extend(ue.asn1.DERSequence,ue.asn1.DERAbstractStructured),ue.asn1.DERSet=function(e){ue.asn1.DERSet.superclass.constructor.call(this,e),this.hT="31",this.sortFlag=!0,this.getFreshValueHex=function(){for(var e=new Array,t=0;t<this.asn1Array.length;t++){var n=this.asn1Array[t];e.push(n.getEncodedHex())}return 1==this.sortFlag&&e.sort(),this.hV=e.join(""),this.hV},void 0!==e&&void 0!==e.sortflag&&0==e.sortflag&&(this.sortFlag=!1)},i.lang.extend(ue.asn1.DERSet,ue.asn1.DERAbstractStructured),ue.asn1.DERTaggedObject=function(e){ue.asn1.DERTaggedObject.superclass.constructor.call(this);var t=ue.asn1;this.hT="a0",this.hV="",this.isExplicit=!0,this.asn1Object=null,this.setASN1Object=function(e,t,n){this.hT=t,this.isExplicit=e,this.asn1Object=n,this.isExplicit?(this.hV=this.asn1Object.getEncodedHex(),this.hTLV=null,this.isModified=!0):(this.hV=null,this.hTLV=n.getEncodedHex(),this.hTLV=this.hTLV.replace(/^../,t),this.isModified=!1)},this.getFreshValueHex=function(){return this.hV},this.setByParam=function(e){null!=e.tag&&(this.hT=e.tag),null!=e.explicit&&(this.isExplicit=e.explicit),null!=e.tage&&(this.hT=e.tage,this.isExplicit=!0),null!=e.tagi&&(this.hT=e.tagi,this.isExplicit=!1),null!=e.obj&&(e.obj instanceof t.ASN1Object?(this.asn1Object=e.obj,this.setASN1Object(this.isExplicit,this.hT,this.asn1Object)):"object"==n(e.obj)&&(this.asn1Object=t.ASN1Util.newObject(e.obj),this.setASN1Object(this.isExplicit,this.hT,this.asn1Object)))},null!=e&&this.setByParam(e)},i.lang.extend(ue.asn1.DERTaggedObject,ue.asn1.ASN1Object);var ue,ce,de,fe=new function(){};function pe(e){for(var t=new Array,n=0;n<e.length;n++)t[n]=e.charCodeAt(n);return t}function he(e){for(var t="",n=0;n<e.length;n++)t+=String.fromCharCode(e[n]);return t}function me(e){for(var t="",n=0;n<e.length;n++){var r=e[n].toString(16);1==r.length&&(r="0"+r),t+=r}return t}function ge(e){return me(pe(e))}function ve(e){return(e=(e=e.replace(/\=/g,"")).replace(/\+/g,"-")).replace(/\//g,"_")}function ye(e){return e.length%4==2?e+="==":e.length%4==3&&(e+="="),(e=e.replace(/-/g,"+")).replace(/_/g,"/")}function be(e){return e.length%2==1&&(e="0"+e),ve(w(e))}function we(e){return E(ye(e))}function Ee(e){return Fe(Me(e))}function Se(e){return decodeURIComponent(je(e))}function xe(e){for(var t="",n=0;n<e.length-1;n+=2)t+=String.fromCharCode(parseInt(e.substr(n,2),16));return t}function Ae(e){for(var t="",n=0;n<e.length;n++)t+=("0"+e.charCodeAt(n).toString(16)).slice(-2);return t}function ke(e){return w(e)}function _e(e){return ke(e).replace(/(.{64})/g,"$1\r\n").replace(/\r\n$/,"")}function Ce(e){return E(e.replace(/[^0-9A-Za-z\/+=]*/g,""))}function Oe(e,t){return"-----BEGIN "+t+"-----\r\n"+_e(e)+"\r\n-----END "+t+"-----\r\n"}function Pe(e,t){if(-1==e.indexOf("-----BEGIN "))throw"can't find PEM header: "+t;return Ce(e=void 0!==t?(e=e.replace(new RegExp("^[^]*-----BEGIN "+t+"-----"),"")).replace(new RegExp("-----END "+t+"-----[^]*$"),""):(e=e.replace(/^[^]*-----BEGIN [^-]+-----/,"")).replace(/-----END [^-]+-----[^]*$/,""))}function Re(e){var t,n,r,o,i,a,s,l,u,c,d;if(d=e.match(/^(\d{2}|\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(|\.\d+)Z$/))return l=d[1],t=parseInt(l),2===l.length&&(50<=t&&t<100?t=1900+t:0<=t&&t<50&&(t=2e3+t)),n=parseInt(d[2])-1,r=parseInt(d[3]),o=parseInt(d[4]),i=parseInt(d[5]),a=parseInt(d[6]),s=0,""!==(u=d[7])&&(c=(u.substr(1)+"00").substr(0,3),s=parseInt(c)),Date.UTC(t,n,r,o,i,a,s);throw"unsupported zulu format: "+e}function Te(e){return~~(Re(e)/1e3)}function Fe(e){return e.replace(/%/g,"")}function je(e){return e.replace(/(..)/g,"%$1")}function Ne(e){var t="malformed IPv6 address";if(!e.match(/^[0-9A-Fa-f:]+$/))throw t;var n=(e=e.toLowerCase()).split(":").length-1;if(n<2)throw t;var r=":".repeat(7-n+2),o=(e=e.replace("::",r)).split(":");if(8!=o.length)throw t;for(var i=0;i<8;i++)o[i]=("0000"+o[i]).slice(-4);return o.join("")}function Ie(e){if(!e.match(/^[0-9A-Fa-f]{32}$/))throw"malformed IPv6 address octet";for(var t=(e=e.toLowerCase()).match(/.{1,4}/g),n=0;n<8;n++)t[n]=t[n].replace(/^0+/,""),""==t[n]&&(t[n]="0");var r=(e=":"+t.join(":")+":").match(/:(0:){2,}/g);if(null===r)return e.slice(1,-1);var o="";for(n=0;n<r.length;n++)r[n].length>o.length&&(o=r[n]);return(e=e.replace(o,"::")).slice(1,-1)}function Le(e){var t="malformed hex value";if(!e.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/))throw t;if(8!=e.length)return 32==e.length?Ie(e):e;try{return parseInt(e.substr(0,2),16)+"."+parseInt(e.substr(2,2),16)+"."+parseInt(e.substr(4,2),16)+"."+parseInt(e.substr(6,2),16)}catch(e){throw t}}function Me(e){for(var t=encodeURIComponent(e),n="",r=0;r<t.length;r++)"%"==t[r]?(n+=t.substr(r,3),r+=2):n=n+"%"+ge(t[r]);return n}function De(e){return!(e.length%2!=0||!e.match(/^[0-9a-f]+$/)&&!e.match(/^[0-9A-F]+$/))}function Be(e){return e.length%2==1?"0"+e:e.substr(0,1)>"7"?"00"+e:e}fe.getLblen=function(e,t){if("8"!=e.substr(t+2,1))return 1;var n=parseInt(e.substr(t+3,1));return 0==n?-1:0<n&&n<10?n+1:-2},fe.getL=function(e,t){var n=fe.getLblen(e,t);return n<1?"":e.substr(t+2,2*n)},fe.getVblen=function(e,t){var n;return""==(n=fe.getL(e,t))?-1:("8"===n.substr(0,1)?new x(n.substr(2),16):new x(n,16)).intValue()},fe.getVidx=function(e,t){var n=fe.getLblen(e,t);return n<0?n:t+2*(n+1)},fe.getV=function(e,t){var n=fe.getVidx(e,t),r=fe.getVblen(e,t);return e.substr(n,2*r)},fe.getTLV=function(e,t){return e.substr(t,2)+fe.getL(e,t)+fe.getV(e,t)},fe.getTLVblen=function(e,t){return 2+2*fe.getLblen(e,t)+2*fe.getVblen(e,t)},fe.getNextSiblingIdx=function(e,t){return fe.getVidx(e,t)+2*fe.getVblen(e,t)},fe.getChildIdx=function(e,t){var n,r,o,i=fe,a=[];n=i.getVidx(e,t),r=2*i.getVblen(e,t),"03"==e.substr(t,2)&&(n+=2,r-=2),o=0;for(var s=n;o<=r;){var l=i.getTLVblen(e,s);if((o+=l)<=r&&a.push(s),s+=l,o>=r)break}return a},fe.getNthChildIdx=function(e,t,n){return fe.getChildIdx(e,t)[n]},fe.getIdxbyList=function(e,t,n,r){var o,i,a=fe;return 0==n.length?void 0!==r&&e.substr(t,2)!==r?-1:t:(o=n.shift())>=(i=a.getChildIdx(e,t)).length?-1:a.getIdxbyList(e,i[o],n,r)},fe.getIdxbyListEx=function(e,t,n,r){var o,i,a=fe;if(0==n.length)return void 0!==r&&e.substr(t,2)!==r?-1:t;o=n.shift(),i=a.getChildIdx(e,t);for(var s=0,l=0;l<i.length;l++){var u=e.substr(i[l],2);if("number"==typeof o&&!a.isContextTag(u)&&s==o||"string"==typeof o&&a.isContextTag(u,o))return a.getIdxbyListEx(e,i[l],n,r);a.isContextTag(u)||s++}return-1},fe.getTLVbyList=function(e,t,n,r){var o=fe,i=o.getIdxbyList(e,t,n,r);return-1==i||i>=e.length?null:o.getTLV(e,i)},fe.getTLVbyListEx=function(e,t,n,r){var o=fe,i=o.getIdxbyListEx(e,t,n,r);return-1==i?null:o.getTLV(e,i)},fe.getVbyList=function(e,t,n,r,o){var i,a,s=fe;return-1==(i=s.getIdxbyList(e,t,n,r))||i>=e.length?null:(a=s.getV(e,i),!0===o&&(a=a.substr(2)),a)},fe.getVbyListEx=function(e,t,n,r,o){var i,a,s=fe;return-1==(i=s.getIdxbyListEx(e,t,n,r))?null:(a=s.getV(e,i),"03"==e.substr(i,2)&&!1!==o&&(a=a.substr(2)),a)},fe.getInt=function(e,t,n){null==n&&(n=-1);try{var r=e.substr(t,2);if("02"!=r&&"03"!=r)return n;var o=fe.getV(e,t);return"02"==r?parseInt(o,16):function(e){try{var t=e.substr(0,2);if("00"==t)return parseInt(e.substr(2),16);var n=parseInt(t,16),r=e.substr(2),o=parseInt(r,16).toString(2);return"0"==o&&(o="00000000"),o=o.slice(0,0-n),parseInt(o,2)}catch(e){return-1}}(o)}catch(e){return n}},fe.getOID=function(e,t,n){null==n&&(n=null);try{return"06"!=e.substr(t,2)?n:function(e){if(!De(e))return null;try{var t=[],n=e.substr(0,2),r=parseInt(n,16);t[0]=new String(Math.floor(r/40)),t[1]=new String(r%40);for(var o=e.substr(2),i=[],a=0;a<o.length/2;a++)i.push(parseInt(o.substr(2*a,2),16));var s=[],l="";for(a=0;a<i.length;a++)128&i[a]?l+=Ue((127&i[a]).toString(2),7):(l+=Ue((127&i[a]).toString(2),7),s.push(new String(parseInt(l,2))),l="");var u=t.join(".");return s.length>0&&(u=u+"."+s.join(".")),u}catch(e){return null}}(fe.getV(e,t))}catch(e){return n}},fe.getOIDName=function(e,t,n){null==n&&(n=null);try{var r=fe.getOID(e,t,n);if(r==n)return n;var o=ue.asn1.x509.OID.oid2name(r);return""==o?r:o}catch(e){return n}},fe.getString=function(e,t,n){null==n&&(n=null);try{return xe(fe.getV(e,t))}catch(e){return n}},fe.hextooidstr=function(e){var t=function(e,t){return e.length>=t?e:new Array(t-e.length+1).join("0")+e},n=[],r=e.substr(0,2),o=parseInt(r,16);n[0]=new String(Math.floor(o/40)),n[1]=new String(o%40);for(var i=e.substr(2),a=[],s=0;s<i.length/2;s++)a.push(parseInt(i.substr(2*s,2),16));var l=[],u="";for(s=0;s<a.length;s++)128&a[s]?u+=t((127&a[s]).toString(2),7):(u+=t((127&a[s]).toString(2),7),l.push(new String(parseInt(u,2))),u="");var c=n.join(".");return l.length>0&&(c=c+"."+l.join(".")),c},fe.dump=function(e,t,n,r){var o=fe,i=o.getV,a=o.dump,s=o.getChildIdx,l=e;e instanceof ue.asn1.ASN1Object&&(l=e.getEncodedHex());var u=function(e,t){return e.length<=2*t?e:e.substr(0,t)+"..(total "+e.length/2+"bytes).."+e.substr(e.length-t,t)};void 0===t&&(t={ommit_long_octet:32}),void 0===n&&(n=0),void 0===r&&(r="");var c,d=t.ommit_long_octet;if("01"==(c=l.substr(n,2)))return"00"==(f=i(l,n))?r+"BOOLEAN FALSE\n":r+"BOOLEAN TRUE\n";if("02"==c)return r+"INTEGER "+u(f=i(l,n),d)+"\n";if("03"==c){var f=i(l,n);return o.isASN1HEX(f.substr(2))?(E=r+"BITSTRING, encapsulates\n")+a(f.substr(2),t,0,r+"  "):r+"BITSTRING "+u(f,d)+"\n"}if("04"==c)return f=i(l,n),o.isASN1HEX(f)?(E=r+"OCTETSTRING, encapsulates\n")+a(f,t,0,r+"  "):r+"OCTETSTRING "+u(f,d)+"\n";if("05"==c)return r+"NULL\n";if("06"==c){var p=i(l,n),h=ue.asn1.ASN1Util.oidHexToInt(p),m=ue.asn1.x509.OID.oid2name(h),g=h.replace(/\./g," ");return""!=m?r+"ObjectIdentifier "+m+" ("+g+")\n":r+"ObjectIdentifier ("+g+")\n"}if("0a"==c)return r+"ENUMERATED "+parseInt(i(l,n))+"\n";if("0c"==c)return r+"UTF8String '"+Se(i(l,n))+"'\n";if("13"==c)return r+"PrintableString '"+Se(i(l,n))+"'\n";if("14"==c)return r+"TeletexString '"+Se(i(l,n))+"'\n";if("16"==c)return r+"IA5String '"+Se(i(l,n))+"'\n";if("17"==c)return r+"UTCTime "+Se(i(l,n))+"\n";if("18"==c)return r+"GeneralizedTime "+Se(i(l,n))+"\n";if("1a"==c)return r+"VisualString '"+Se(i(l,n))+"'\n";if("1e"==c)return r+"BMPString '"+Se(i(l,n))+"'\n";if("30"==c){if("3000"==l.substr(n,4))return r+"SEQUENCE {}\n";E=r+"SEQUENCE\n";var v=t;if((2==(w=s(l,n)).length||3==w.length)&&"06"==l.substr(w[0],2)&&"04"==l.substr(w[w.length-1],2)){m=o.oidname(i(l,w[0]));var y=JSON.parse(JSON.stringify(t));y.x509ExtName=m,v=y}for(var b=0;b<w.length;b++)E+=a(l,v,w[b],r+"  ");return E}if("31"==c){E=r+"SET\n";var w=s(l,n);for(b=0;b<w.length;b++)E+=a(l,t,w[b],r+"  ");return E}if(128&(c=parseInt(c,16))){var E,S=31&c;if(32&c){for(E=r+"["+S+"]\n",w=s(l,n),b=0;b<w.length;b++)E+=a(l,t,w[b],r+"  ");return E}return f=i(l,n),fe.isASN1HEX(f)?(E=r+"["+S+"]\n")+a(f,t,0,r+"  "):(("68747470"==f.substr(0,8)||"subjectAltName"===t.x509ExtName&&2==S)&&(f=Se(f)),r+"["+S+"] "+f+"\n")}return r+"UNKNOWN("+c+") "+i(l,n)+"\n"},fe.isContextTag=function(e,t){var n,r;e=e.toLowerCase();try{n=parseInt(e,16)}catch(e){return-1}if(void 0===t)return 128==(192&n);try{return null!=t.match(/^\[[0-9]+\]$/)&&!((r=parseInt(t.substr(1,t.length-1),10))>31)&&128==(192&n)&&(31&n)==r}catch(e){return!1}},fe.isASN1HEX=function(e){var t=fe;if(e.length%2==1)return!1;var n=t.getVblen(e,0),r=e.substr(0,2),o=t.getL(e,0);return e.length-r.length-o.length==2*n},fe.checkStrictDER=function(e,t,n,r,o){var i=fe;if(void 0===n){if("string"!=typeof e)throw new Error("not hex string");if(e=e.toLowerCase(),!ue.lang.String.isHex(e))throw new Error("not hex string");n=e.length,o=(r=e.length/2)<128?1:Math.ceil(r.toString(16))+1}if(i.getL(e,t).length>2*o)throw new Error("L of TLV too long: idx="+t);var a=i.getVblen(e,t);if(a>r)throw new Error("value of L too long than hex: idx="+t);var s=i.getTLV(e,t),l=s.length-2-i.getL(e,t).length;if(l!==2*a)throw new Error("V string length and L's value not the same:"+l+"/"+2*a);if(0===t&&e.length!=s.length)throw new Error("total length and TLV length unmatch:"+e.length+"!="+s.length);var u=e.substr(t,2);if("02"===u){var c=i.getVidx(e,t);if("00"==e.substr(c,2)&&e.charCodeAt(c+2)<56)throw new Error("not least zeros for DER INTEGER")}if(32&parseInt(u,16)){for(var d=i.getVblen(e,t),f=0,p=i.getChildIdx(e,t),h=0;h<p.length;h++)f+=i.getTLV(e,p[h]).length,i.checkStrictDER(e,p[h],n,r,o);if(2*d!=f)throw new Error("sum of children's TLV length and L unmatch: "+2*d+"!="+f)}},fe.oidname=function(e){var t=ue.asn1;ue.lang.String.isHex(e)&&(e=t.ASN1Util.oidHexToInt(e));var n=t.x509.OID.oid2name(e);return""===n&&(n=e),n},void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.lang&&ue.lang||(ue.lang={}),ue.lang.String=function(){},"function"==typeof e?(t.utf8tob64u=ce=function(t){return ve(e.from(t,"utf8").toString("base64"))},t.b64utoutf8=de=function(t){return e.from(ye(t),"base64").toString("utf8")}):(t.utf8tob64u=ce=function(e){return be(Fe(Me(e)))},t.b64utoutf8=de=function(e){return decodeURIComponent(je(we(e)))}),ue.lang.String.isInteger=function(e){return!!e.match(/^[0-9]+$/)||!!e.match(/^-[0-9]+$/)},ue.lang.String.isHex=function(e){return De(e)},ue.lang.String.isBase64=function(e){return!(!(e=e.replace(/\s+/g,"")).match(/^[0-9A-Za-z+\/]+={0,3}$/)||e.length%4!=0)},ue.lang.String.isBase64URL=function(e){return!e.match(/[+/=]/)&&(e=ye(e),ue.lang.String.isBase64(e))},ue.lang.String.isIntegerArray=function(e){return!!(e=e.replace(/\s+/g,"")).match(/^\[[0-9,]+\]$/)},ue.lang.String.isPrintable=function(e){return null!==e.match(/^[0-9A-Za-z '()+,-./:=?]*$/)},ue.lang.String.isIA5=function(e){return null!==e.match(/^[\x20-\x21\x23-\x7f]*$/)},ue.lang.String.isMail=function(e){return null!==e.match(/^[A-Za-z0-9]{1}[A-Za-z0-9_.-]*@{1}[A-Za-z0-9_.-]{1,}\.[A-Za-z0-9]{1,}$/)};var Ue=function(e,t,n){return null==n&&(n="0"),e.length>=t?e:new Array(t-e.length+1).join(n)+e};void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.crypto&&ue.crypto||(ue.crypto={}),ue.crypto.Util=new function(){this.DIGESTINFOHEAD={sha1:"3021300906052b0e03021a05000414",sha224:"302d300d06096086480165030402040500041c",sha256:"3031300d060960864801650304020105000420",sha384:"3041300d060960864801650304020205000430",sha512:"3051300d060960864801650304020305000440",md2:"3020300c06082a864886f70d020205000410",md5:"3020300c06082a864886f70d020505000410",ripemd160:"3021300906052b2403020105000414"},this.DEFAULTPROVIDER={md5:"cryptojs",sha1:"cryptojs",sha224:"cryptojs",sha256:"cryptojs",sha384:"cryptojs",sha512:"cryptojs",ripemd160:"cryptojs",hmacmd5:"cryptojs",hmacsha1:"cryptojs",hmacsha224:"cryptojs",hmacsha256:"cryptojs",hmacsha384:"cryptojs",hmacsha512:"cryptojs",hmacripemd160:"cryptojs",MD5withRSA:"cryptojs/jsrsa",SHA1withRSA:"cryptojs/jsrsa",SHA224withRSA:"cryptojs/jsrsa",SHA256withRSA:"cryptojs/jsrsa",SHA384withRSA:"cryptojs/jsrsa",SHA512withRSA:"cryptojs/jsrsa",RIPEMD160withRSA:"cryptojs/jsrsa",MD5withECDSA:"cryptojs/jsrsa",SHA1withECDSA:"cryptojs/jsrsa",SHA224withECDSA:"cryptojs/jsrsa",SHA256withECDSA:"cryptojs/jsrsa",SHA384withECDSA:"cryptojs/jsrsa",SHA512withECDSA:"cryptojs/jsrsa",RIPEMD160withECDSA:"cryptojs/jsrsa",SHA1withDSA:"cryptojs/jsrsa",SHA224withDSA:"cryptojs/jsrsa",SHA256withDSA:"cryptojs/jsrsa",MD5withRSAandMGF1:"cryptojs/jsrsa",SHAwithRSAandMGF1:"cryptojs/jsrsa",SHA1withRSAandMGF1:"cryptojs/jsrsa",SHA224withRSAandMGF1:"cryptojs/jsrsa",SHA256withRSAandMGF1:"cryptojs/jsrsa",SHA384withRSAandMGF1:"cryptojs/jsrsa",SHA512withRSAandMGF1:"cryptojs/jsrsa",RIPEMD160withRSAandMGF1:"cryptojs/jsrsa"},this.CRYPTOJSMESSAGEDIGESTNAME={md5:v.algo.MD5,sha1:v.algo.SHA1,sha224:v.algo.SHA224,sha256:v.algo.SHA256,sha384:v.algo.SHA384,sha512:v.algo.SHA512,ripemd160:v.algo.RIPEMD160},this.getDigestInfoHex=function(e,t){if(void 0===this.DIGESTINFOHEAD[t])throw"alg not supported in Util.DIGESTINFOHEAD: "+t;return this.DIGESTINFOHEAD[t]+e},this.getPaddedDigestInfoHex=function(e,t,n){var r=this.getDigestInfoHex(e,t),o=n/4;if(r.length+22>o)throw"key is too short for SigAlg: keylen="+n+","+t;for(var i="00"+r,a="",s=o-4-i.length,l=0;l<s;l+=2)a+="ff";return"0001"+a+i},this.hashString=function(e,t){return new ue.crypto.MessageDigest({alg:t}).digestString(e)},this.hashHex=function(e,t){return new ue.crypto.MessageDigest({alg:t}).digestHex(e)},this.sha1=function(e){return this.hashString(e,"sha1")},this.sha256=function(e){return this.hashString(e,"sha256")},this.sha256Hex=function(e){return this.hashHex(e,"sha256")},this.sha512=function(e){return this.hashString(e,"sha512")},this.sha512Hex=function(e){return this.hashHex(e,"sha512")},this.isKey=function(e){return e instanceof oe||e instanceof ue.crypto.DSA||e instanceof ue.crypto.ECDSA}},ue.crypto.Util.md5=function(e){return new ue.crypto.MessageDigest({alg:"md5",prov:"cryptojs"}).digestString(e)},ue.crypto.Util.ripemd160=function(e){return new ue.crypto.MessageDigest({alg:"ripemd160",prov:"cryptojs"}).digestString(e)},ue.crypto.Util.SECURERANDOMGEN=new te,ue.crypto.Util.getRandomHexOfNbytes=function(e){var t=new Array(e);return ue.crypto.Util.SECURERANDOMGEN.nextBytes(t),me(t)},ue.crypto.Util.getRandomBigIntegerOfNbytes=function(e){return new x(ue.crypto.Util.getRandomHexOfNbytes(e),16)},ue.crypto.Util.getRandomHexOfNbits=function(e){var t=e%8,n=new Array((e-t)/8+1);return ue.crypto.Util.SECURERANDOMGEN.nextBytes(n),n[0]=(255<<t&255^255)&n[0],me(n)},ue.crypto.Util.getRandomBigIntegerOfNbits=function(e){return new x(ue.crypto.Util.getRandomHexOfNbits(e),16)},ue.crypto.Util.getRandomBigIntegerZeroToMax=function(e){for(var t=e.bitLength();;){var n=ue.crypto.Util.getRandomBigIntegerOfNbits(t);if(-1!=e.compareTo(n))return n}},ue.crypto.Util.getRandomBigIntegerMinToMax=function(e,t){var n=e.compareTo(t);if(1==n)throw"biMin is greater than biMax";if(0==n)return e;var r=t.subtract(e);return ue.crypto.Util.getRandomBigIntegerZeroToMax(r).add(e)},ue.crypto.MessageDigest=function(e){this.setAlgAndProvider=function(e,t){if(null!==(e=ue.crypto.MessageDigest.getCanonicalAlgName(e))&&void 0===t&&(t=ue.crypto.Util.DEFAULTPROVIDER[e]),-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(e)&&"cryptojs"==t){try{this.md=ue.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[e].create()}catch(t){throw"setAlgAndProvider hash alg set fail alg="+e+"/"+t}this.updateString=function(e){this.md.update(e)},this.updateHex=function(e){var t=v.enc.Hex.parse(e);this.md.update(t)},this.digest=function(){return this.md.finalize().toString(v.enc.Hex)},this.digestString=function(e){return this.updateString(e),this.digest()},this.digestHex=function(e){return this.updateHex(e),this.digest()}}if(-1!=":sha256:".indexOf(e)&&"sjcl"==t){try{this.md=new sjcl.hash.sha256}catch(t){throw"setAlgAndProvider hash alg set fail alg="+e+"/"+t}this.updateString=function(e){this.md.update(e)},this.updateHex=function(e){var t=sjcl.codec.hex.toBits(e);this.md.update(t)},this.digest=function(){var e=this.md.finalize();return sjcl.codec.hex.fromBits(e)},this.digestString=function(e){return this.updateString(e),this.digest()},this.digestHex=function(e){return this.updateHex(e),this.digest()}}},this.updateString=function(e){throw"updateString(str) not supported for this alg/prov: "+this.algName+"/"+this.provName},this.updateHex=function(e){throw"updateHex(hex) not supported for this alg/prov: "+this.algName+"/"+this.provName},this.digest=function(){throw"digest() not supported for this alg/prov: "+this.algName+"/"+this.provName},this.digestString=function(e){throw"digestString(str) not supported for this alg/prov: "+this.algName+"/"+this.provName},this.digestHex=function(e){throw"digestHex(hex) not supported for this alg/prov: "+this.algName+"/"+this.provName},void 0!==e&&void 0!==e.alg&&(this.algName=e.alg,void 0===e.prov&&(this.provName=ue.crypto.Util.DEFAULTPROVIDER[this.algName]),this.setAlgAndProvider(this.algName,this.provName))},ue.crypto.MessageDigest.getCanonicalAlgName=function(e){return"string"==typeof e&&(e=(e=e.toLowerCase()).replace(/-/,"")),e},ue.crypto.MessageDigest.getHashLength=function(e){var t=ue.crypto.MessageDigest,n=t.getCanonicalAlgName(e);if(void 0===t.HASHLENGTH[n])throw"not supported algorithm: "+e;return t.HASHLENGTH[n]},ue.crypto.MessageDigest.HASHLENGTH={md5:16,sha1:20,sha224:28,sha256:32,sha384:48,sha512:64,ripemd160:20},ue.crypto.Mac=function(e){this.setAlgAndProvider=function(e,t){if(null==(e=e.toLowerCase())&&(e="hmacsha1"),"hmac"!=(e=e.toLowerCase()).substr(0,4))throw"setAlgAndProvider unsupported HMAC alg: "+e;void 0===t&&(t=ue.crypto.Util.DEFAULTPROVIDER[e]),this.algProv=e+"/"+t;var n=e.substr(4);if(-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(n)&&"cryptojs"==t){try{var r=ue.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[n];this.mac=v.algo.HMAC.create(r,this.pass)}catch(e){throw"setAlgAndProvider hash alg set fail hashAlg="+n+"/"+e}this.updateString=function(e){this.mac.update(e)},this.updateHex=function(e){var t=v.enc.Hex.parse(e);this.mac.update(t)},this.doFinal=function(){return this.mac.finalize().toString(v.enc.Hex)},this.doFinalString=function(e){return this.updateString(e),this.doFinal()},this.doFinalHex=function(e){return this.updateHex(e),this.doFinal()}}},this.updateString=function(e){throw"updateString(str) not supported for this alg/prov: "+this.algProv},this.updateHex=function(e){throw"updateHex(hex) not supported for this alg/prov: "+this.algProv},this.doFinal=function(){throw"digest() not supported for this alg/prov: "+this.algProv},this.doFinalString=function(e){throw"digestString(str) not supported for this alg/prov: "+this.algProv},this.doFinalHex=function(e){throw"digestHex(hex) not supported for this alg/prov: "+this.algProv},this.setPassword=function(e){if("string"==typeof e){var t=e;return e.length%2!=1&&e.match(/^[0-9A-Fa-f]+$/)||(t=Ae(e)),void(this.pass=v.enc.Hex.parse(t))}if("object"!=(void 0===e?"undefined":n(e)))throw"KJUR.crypto.Mac unsupported password type: "+e;if(t=null,void 0!==e.hex){if(e.hex.length%2!=0||!e.hex.match(/^[0-9A-Fa-f]+$/))throw"Mac: wrong hex password: "+e.hex;t=e.hex}if(void 0!==e.utf8&&(t=Ee(e.utf8)),void 0!==e.rstr&&(t=Ae(e.rstr)),void 0!==e.b64&&(t=E(e.b64)),void 0!==e.b64u&&(t=we(e.b64u)),null==t)throw"KJUR.crypto.Mac unsupported password type: "+e;this.pass=v.enc.Hex.parse(t)},void 0!==e&&(void 0!==e.pass&&this.setPassword(e.pass),void 0!==e.alg&&(this.algName=e.alg,void 0===e.prov&&(this.provName=ue.crypto.Util.DEFAULTPROVIDER[this.algName]),this.setAlgAndProvider(this.algName,this.provName)))},ue.crypto.Signature=function(e){var t=null;if(this._setAlgNames=function(){var e=this.algName.match(/^(.+)with(.+)$/);e&&(this.mdAlgName=e[1].toLowerCase(),this.pubkeyAlgName=e[2].toLowerCase(),"rsaandmgf1"==this.pubkeyAlgName&&"sha"==this.mdAlgName&&(this.mdAlgName="sha1"))},this._zeroPaddingOfSignature=function(e,t){for(var n="",r=t/4-e.length,o=0;o<r;o++)n+="0";return n+e},this.setAlgAndProvider=function(e,t){if(this._setAlgNames(),"cryptojs/jsrsa"!=t)throw new Error("provider not supported: "+t);if(-1!=":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(this.mdAlgName)){try{this.md=new ue.crypto.MessageDigest({alg:this.mdAlgName})}catch(e){throw new Error("setAlgAndProvider hash alg set fail alg="+this.mdAlgName+"/"+e)}this.init=function(e,t){var n=null;try{n=void 0===t?ze.getKey(e):ze.getKey(e,t)}catch(e){throw"init failed:"+e}if(!0===n.isPrivate)this.prvKey=n,this.state="SIGN";else{if(!0!==n.isPublic)throw"init failed.:"+n;this.pubKey=n,this.state="VERIFY"}},this.updateString=function(e){this.md.updateString(e)},this.updateHex=function(e){this.md.updateHex(e)},this.sign=function(){if(this.sHashHex=this.md.digest(),void 0===this.prvKey&&void 0!==this.ecprvhex&&void 0!==this.eccurvename&&void 0!==ue.crypto.ECDSA&&(this.prvKey=new ue.crypto.ECDSA({curve:this.eccurvename,prv:this.ecprvhex})),this.prvKey instanceof oe&&"rsaandmgf1"===this.pubkeyAlgName)this.hSign=this.prvKey.signWithMessageHashPSS(this.sHashHex,this.mdAlgName,this.pssSaltLen);else if(this.prvKey instanceof oe&&"rsa"===this.pubkeyAlgName)this.hSign=this.prvKey.signWithMessageHash(this.sHashHex,this.mdAlgName);else if(this.prvKey instanceof ue.crypto.ECDSA)this.hSign=this.prvKey.signWithMessageHash(this.sHashHex);else{if(!(this.prvKey instanceof ue.crypto.DSA))throw"Signature: unsupported private key alg: "+this.pubkeyAlgName;this.hSign=this.prvKey.signWithMessageHash(this.sHashHex)}return this.hSign},this.signString=function(e){return this.updateString(e),this.sign()},this.signHex=function(e){return this.updateHex(e),this.sign()},this.verify=function(e){if(this.sHashHex=this.md.digest(),void 0===this.pubKey&&void 0!==this.ecpubhex&&void 0!==this.eccurvename&&void 0!==ue.crypto.ECDSA&&(this.pubKey=new ue.crypto.ECDSA({curve:this.eccurvename,pub:this.ecpubhex})),this.pubKey instanceof oe&&"rsaandmgf1"===this.pubkeyAlgName)return this.pubKey.verifyWithMessageHashPSS(this.sHashHex,e,this.mdAlgName,this.pssSaltLen);if(this.pubKey instanceof oe&&"rsa"===this.pubkeyAlgName)return this.pubKey.verifyWithMessageHash(this.sHashHex,e);if(void 0!==ue.crypto.ECDSA&&this.pubKey instanceof ue.crypto.ECDSA)return this.pubKey.verifyWithMessageHash(this.sHashHex,e);if(void 0!==ue.crypto.DSA&&this.pubKey instanceof ue.crypto.DSA)return this.pubKey.verifyWithMessageHash(this.sHashHex,e);throw"Signature: unsupported public key alg: "+this.pubkeyAlgName}}},this.init=function(e,t){throw"init(key, pass) not supported for this alg:prov="+this.algProvName},this.updateString=function(e){throw"updateString(str) not supported for this alg:prov="+this.algProvName},this.updateHex=function(e){throw"updateHex(hex) not supported for this alg:prov="+this.algProvName},this.sign=function(){throw"sign() not supported for this alg:prov="+this.algProvName},this.signString=function(e){throw"digestString(str) not supported for this alg:prov="+this.algProvName},this.signHex=function(e){throw"digestHex(hex) not supported for this alg:prov="+this.algProvName},this.verify=function(e){throw"verify(hSigVal) not supported for this alg:prov="+this.algProvName},this.initParams=e,void 0!==e&&(void 0!==e.alg&&(this.algName=e.alg,void 0===e.prov?this.provName=ue.crypto.Util.DEFAULTPROVIDER[this.algName]:this.provName=e.prov,this.algProvName=this.algName+":"+this.provName,this.setAlgAndProvider(this.algName,this.provName),this._setAlgNames()),void 0!==e.psssaltlen&&(this.pssSaltLen=e.psssaltlen),void 0!==e.prvkeypem)){if(void 0!==e.prvkeypas)throw"both prvkeypem and prvkeypas parameters not supported";try{t=ze.getKey(e.prvkeypem),this.init(t)}catch(e){throw"fatal error to load pem private key: "+e}}},ue.crypto.Cipher=function(e){},ue.crypto.Cipher.encrypt=function(e,t,n){if(t instanceof oe&&t.isPublic){var r=ue.crypto.Cipher.getAlgByKeyAndName(t,n);if("RSA"===r)return t.encrypt(e);if("RSAOAEP"===r)return t.encryptOAEP(e,"sha1");var o=r.match(/^RSAOAEP(\d+)$/);if(null!==o)return t.encryptOAEP(e,"sha"+o[1]);throw"Cipher.encrypt: unsupported algorithm for RSAKey: "+n}throw"Cipher.encrypt: unsupported key or algorithm"},ue.crypto.Cipher.decrypt=function(e,t,n){if(t instanceof oe&&t.isPrivate){var r=ue.crypto.Cipher.getAlgByKeyAndName(t,n);if("RSA"===r)return t.decrypt(e);if("RSAOAEP"===r)return t.decryptOAEP(e,"sha1");var o=r.match(/^RSAOAEP(\d+)$/);if(null!==o)return t.decryptOAEP(e,"sha"+o[1]);throw"Cipher.decrypt: unsupported algorithm for RSAKey: "+n}throw"Cipher.decrypt: unsupported key or algorithm"},ue.crypto.Cipher.getAlgByKeyAndName=function(e,t){if(e instanceof oe){if(-1!=":RSA:RSAOAEP:RSAOAEP224:RSAOAEP256:RSAOAEP384:RSAOAEP512:".indexOf(t))return t;if(null==t)return"RSA";throw"getAlgByKeyAndName: not supported algorithm name for RSAKey: "+t}throw"getAlgByKeyAndName: not supported algorithm name: "+t},ue.crypto.OID=new function(){this.oidhex2name={"2a864886f70d010101":"rsaEncryption","2a8648ce3d0201":"ecPublicKey","2a8648ce380401":"dsa","2a8648ce3d030107":"secp256r1","2b8104001f":"secp192k1","2b81040021":"secp224r1","2b8104000a":"secp256k1","2b81040023":"secp521r1","2b81040022":"secp384r1","2a8648ce380403":"SHA1withDSA","608648016503040301":"SHA224withDSA","608648016503040302":"SHA256withDSA"}},void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.crypto&&ue.crypto||(ue.crypto={}),ue.crypto.ECDSA=function(e){var t=Error,r=x,o=ae,i=ue.crypto.ECDSA,a=ue.crypto.ECParameterDB,s=i.getName,l=fe,u=l.getVbyListEx,c=l.isASN1HEX,d=new te;this.type="EC",this.isPrivate=!1,this.isPublic=!1,this.getBigRandom=function(e){return new r(e.bitLength(),d).mod(e.subtract(r.ONE)).add(r.ONE)},this.setNamedCurve=function(e){this.ecparams=a.getByName(e),this.prvKeyHex=null,this.pubKeyHex=null,this.curveName=e},this.setPrivateKeyHex=function(e){this.isPrivate=!0,this.prvKeyHex=e},this.setPublicKeyHex=function(e){this.isPublic=!0,this.pubKeyHex=e},this.getPublicKeyXYHex=function(){var e=this.pubKeyHex;if("04"!==e.substr(0,2))throw"this method supports uncompressed format(04) only";var t=this.ecparams.keylen/4;if(e.length!==2+2*t)throw"malformed public key hex length";var n={};return n.x=e.substr(2,t),n.y=e.substr(2+t),n},this.getShortNISTPCurveName=function(){var e=this.curveName;return"secp256r1"===e||"NIST P-256"===e||"P-256"===e||"prime256v1"===e?"P-256":"secp384r1"===e||"NIST P-384"===e||"P-384"===e?"P-384":null},this.generateKeyPairHex=function(){var e=this.ecparams.n,t=this.getBigRandom(e),n=this.ecparams.G.multiply(t),r=n.getX().toBigInteger(),o=n.getY().toBigInteger(),i=this.ecparams.keylen/4,a=("0000000000"+t.toString(16)).slice(-i),s="04"+("0000000000"+r.toString(16)).slice(-i)+("0000000000"+o.toString(16)).slice(-i);return this.setPrivateKeyHex(a),this.setPublicKeyHex(s),{ecprvhex:a,ecpubhex:s}},this.signWithMessageHash=function(e){return this.signHex(e,this.prvKeyHex)},this.signHex=function(e,t){var n=new r(t,16),o=this.ecparams.n,a=new r(e.substring(0,this.ecparams.keylen/4),16);do{var s=this.getBigRandom(o),l=this.ecparams.G.multiply(s).getX().toBigInteger().mod(o)}while(l.compareTo(r.ZERO)<=0);var u=s.modInverse(o).multiply(a.add(n.multiply(l))).mod(o);return i.biRSSigToASN1Sig(l,u)},this.sign=function(e,t){var n=t,o=this.ecparams.n,i=r.fromByteArrayUnsigned(e);do{var a=this.getBigRandom(o),s=this.ecparams.G.multiply(a).getX().toBigInteger().mod(o)}while(s.compareTo(x.ZERO)<=0);var l=a.modInverse(o).multiply(i.add(n.multiply(s))).mod(o);return this.serializeSig(s,l)},this.verifyWithMessageHash=function(e,t){return this.verifyHex(e,t,this.pubKeyHex)},this.verifyHex=function(e,t,n){try{var a,s,l=i.parseSigHex(t);a=l.r,s=l.s;var u=o.decodeFromHex(this.ecparams.curve,n),c=new r(e.substring(0,this.ecparams.keylen/4),16);return this.verifyRaw(c,a,s,u)}catch(e){return!1}},this.verify=function(e,t,i){var a,s,l;if(Bitcoin.Util.isArray(t)){var u=this.parseSig(t);a=u.r,s=u.s}else{if("object"!==(void 0===t?"undefined":n(t))||!t.r||!t.s)throw"Invalid value for signature";a=t.r,s=t.s}if(i instanceof ae)l=i;else{if(!Bitcoin.Util.isArray(i))throw"Invalid format for pubkey value, must be byte array or ECPointFp";l=o.decodeFrom(this.ecparams.curve,i)}var c=r.fromByteArrayUnsigned(e);return this.verifyRaw(c,a,s,l)},this.verifyRaw=function(e,t,n,o){var i=this.ecparams.n,a=this.ecparams.G;if(t.compareTo(r.ONE)<0||t.compareTo(i)>=0)return!1;if(n.compareTo(r.ONE)<0||n.compareTo(i)>=0)return!1;var s=n.modInverse(i),l=e.multiply(s).mod(i),u=t.multiply(s).mod(i);return a.multiply(l).add(o.multiply(u)).getX().toBigInteger().mod(i).equals(t)},this.serializeSig=function(e,t){var n=e.toByteArraySigned(),r=t.toByteArraySigned(),o=[];return o.push(2),o.push(n.length),(o=o.concat(n)).push(2),o.push(r.length),(o=o.concat(r)).unshift(o.length),o.unshift(48),o},this.parseSig=function(e){var t;if(48!=e[0])throw new Error("Signature not a valid DERSequence");if(2!=e[t=2])throw new Error("First element in signature must be a DERInteger");var n=e.slice(t+2,t+2+e[t+1]);if(2!=e[t+=2+e[t+1]])throw new Error("Second element in signature must be a DERInteger");var o=e.slice(t+2,t+2+e[t+1]);return t+=2+e[t+1],{r:r.fromByteArrayUnsigned(n),s:r.fromByteArrayUnsigned(o)}},this.parseSigCompact=function(e){if(65!==e.length)throw"Signature has the wrong length";var t=e[0]-27;if(t<0||t>7)throw"Invalid signature type";var n=this.ecparams.n;return{r:r.fromByteArrayUnsigned(e.slice(1,33)).mod(n),s:r.fromByteArrayUnsigned(e.slice(33,65)).mod(n),i:t}},this.readPKCS5PrvKeyHex=function(e){if(!1===c(e))throw new Error("not ASN.1 hex string");var t,n,r;try{t=u(e,0,["[0]",0],"06"),n=u(e,0,[1],"04");try{r=u(e,0,["[1]",0],"03")}catch(e){}}catch(e){throw new Error("malformed PKCS#1/5 plain ECC private key")}if(this.curveName=s(t),void 0===this.curveName)throw"unsupported curve name";this.setNamedCurve(this.curveName),this.setPublicKeyHex(r),this.setPrivateKeyHex(n),this.isPublic=!1},this.readPKCS8PrvKeyHex=function(e){if(!1===c(e))throw new t("not ASN.1 hex string");var n,r,o;try{u(e,0,[1,0],"06"),n=u(e,0,[1,1],"06"),r=u(e,0,[2,0,1],"04");try{o=u(e,0,[2,0,"[1]",0],"03")}catch(e){}}catch(e){throw new t("malformed PKCS#8 plain ECC private key")}if(this.curveName=s(n),void 0===this.curveName)throw new t("unsupported curve name");this.setNamedCurve(this.curveName),this.setPublicKeyHex(o),this.setPrivateKeyHex(r),this.isPublic=!1},this.readPKCS8PubKeyHex=function(e){if(!1===c(e))throw new t("not ASN.1 hex string");var n,r;try{u(e,0,[0,0],"06"),n=u(e,0,[0,1],"06"),r=u(e,0,[1],"03")}catch(e){throw new t("malformed PKCS#8 ECC public key")}if(this.curveName=s(n),null===this.curveName)throw new t("unsupported curve name");this.setNamedCurve(this.curveName),this.setPublicKeyHex(r)},this.readCertPubKeyHex=function(e,n){if(!1===c(e))throw new t("not ASN.1 hex string");var r,o;try{r=u(e,0,[0,5,0,1],"06"),o=u(e,0,[0,5,1],"03")}catch(e){throw new t("malformed X.509 certificate ECC public key")}if(this.curveName=s(r),null===this.curveName)throw new t("unsupported curve name");this.setNamedCurve(this.curveName),this.setPublicKeyHex(o)},void 0!==e&&void 0!==e.curve&&(this.curveName=e.curve),void 0===this.curveName&&(this.curveName="secp256r1"),this.setNamedCurve(this.curveName),void 0!==e&&(void 0!==e.prv&&this.setPrivateKeyHex(e.prv),void 0!==e.pub&&this.setPublicKeyHex(e.pub))},ue.crypto.ECDSA.parseSigHex=function(e){var t=ue.crypto.ECDSA.parseSigHexInHexRS(e);return{r:new x(t.r,16),s:new x(t.s,16)}},ue.crypto.ECDSA.parseSigHexInHexRS=function(e){var t=fe,n=t.getChildIdx,r=t.getV;if(t.checkStrictDER(e,0),"30"!=e.substr(0,2))throw new Error("signature is not a ASN.1 sequence");var o=n(e,0);if(2!=o.length)throw new Error("signature shall have two elements");var i=o[0],a=o[1];if("02"!=e.substr(i,2))throw new Error("1st item not ASN.1 integer");if("02"!=e.substr(a,2))throw new Error("2nd item not ASN.1 integer");return{r:r(e,i),s:r(e,a)}},ue.crypto.ECDSA.asn1SigToConcatSig=function(e){var t=ue.crypto.ECDSA.parseSigHexInHexRS(e),n=t.r,r=t.s;if("00"==n.substr(0,2)&&n.length%32==2&&(n=n.substr(2)),"00"==r.substr(0,2)&&r.length%32==2&&(r=r.substr(2)),n.length%32==30&&(n="00"+n),r.length%32==30&&(r="00"+r),n.length%32!=0)throw"unknown ECDSA sig r length error";if(r.length%32!=0)throw"unknown ECDSA sig s length error";return n+r},ue.crypto.ECDSA.concatSigToASN1Sig=function(e){if(e.length/2*8%128!=0)throw"unknown ECDSA concatinated r-s sig  length error";var t=e.substr(0,e.length/2),n=e.substr(e.length/2);return ue.crypto.ECDSA.hexRSSigToASN1Sig(t,n)},ue.crypto.ECDSA.hexRSSigToASN1Sig=function(e,t){var n=new x(e,16),r=new x(t,16);return ue.crypto.ECDSA.biRSSigToASN1Sig(n,r)},ue.crypto.ECDSA.biRSSigToASN1Sig=function(e,t){var n=ue.asn1,r=new n.DERInteger({bigint:e}),o=new n.DERInteger({bigint:t});return new n.DERSequence({array:[r,o]}).getEncodedHex()},ue.crypto.ECDSA.getName=function(e){return"2b8104001f"===e?"secp192k1":"2a8648ce3d030107"===e?"secp256r1":"2b8104000a"===e?"secp256k1":"2b81040021"===e?"secp224r1":"2b81040022"===e?"secp384r1":-1!=="|secp256r1|NIST P-256|P-256|prime256v1|".indexOf(e)?"secp256r1":-1!=="|secp256k1|".indexOf(e)?"secp256k1":-1!=="|secp224r1|NIST P-224|P-224|".indexOf(e)?"secp224r1":-1!=="|secp384r1|NIST P-384|P-384|".indexOf(e)?"secp384r1":null},void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.crypto&&ue.crypto||(ue.crypto={}),ue.crypto.ECParameterDB=new function(){var e={},t={};function n(e){return new x(e,16)}this.getByName=function(n){var r=n;if(void 0!==t[r]&&(r=t[n]),void 0!==e[r])return e[r];throw"unregistered EC curve name: "+r},this.regist=function(r,o,i,a,s,l,u,c,d,f,p,h){e[r]={};var m=n(i),g=n(a),v=n(s),y=n(l),b=n(u),w=new se(m,g,v),E=w.decodePointHex("04"+c+d);e[r].name=r,e[r].keylen=o,e[r].curve=w,e[r].G=E,e[r].n=y,e[r].h=b,e[r].oid=p,e[r].info=h;for(var S=0;S<f.length;S++)t[f[S]]=r}},ue.crypto.ECParameterDB.regist("secp128r1",128,"FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF","FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC","E87579C11079F43DD824993C2CEE5ED3","FFFFFFFE0000000075A30D1B9038A115","1","161FF7528B899B2D0C28607CA52C5B86","CF5AC8395BAFEB13C02DA292DDED7A83",[],"","secp128r1 : SECG curve over a 128 bit prime field"),ue.crypto.ECParameterDB.regist("secp160k1",160,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73","0","7","0100000000000000000001B8FA16DFAB9ACA16B6B3","1","3B4C382CE37AA192A4019E763036F4F5DD4D7EBB","938CF935318FDCED6BC28286531733C3F03C4FEE",[],"","secp160k1 : SECG curve over a 160 bit prime field"),ue.crypto.ECParameterDB.regist("secp160r1",160,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC","1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45","0100000000000000000001F4C8F927AED3CA752257","1","4A96B5688EF573284664698968C38BB913CBFC82","23A628553168947D59DCC912042351377AC5FB32",[],"","secp160r1 : SECG curve over a 160 bit prime field"),ue.crypto.ECParameterDB.regist("secp192k1",192,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37","0","3","FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D","1","DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D","9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D",[]),ue.crypto.ECParameterDB.regist("secp192r1",192,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC","64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1","FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831","1","188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012","07192B95FFC8DA78631011ED6B24CDD573F977A11E794811",[]),ue.crypto.ECParameterDB.regist("secp224r1",224,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE","B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4","FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D","1","B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21","BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34",[]),ue.crypto.ECParameterDB.regist("secp256k1",256,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F","0","7","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141","1","79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798","483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",[]),ue.crypto.ECParameterDB.regist("secp256r1",256,"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF","FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC","5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B","FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551","1","6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296","4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",["NIST P-256","P-256","prime256v1"]),ue.crypto.ECParameterDB.regist("secp384r1",384,"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC","B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973","1","AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7","3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",["NIST P-384","P-384"]),ue.crypto.ECParameterDB.regist("secp521r1",521,"1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF","1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC","051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00","1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409","1","C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66","011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",["NIST P-521","P-521"]);var ze=function(){var e=function(e,n,r){return t(v.AES,e,n,r)},t=function(e,t,n,r){var o=v.enc.Hex.parse(t),i=v.enc.Hex.parse(n),a=v.enc.Hex.parse(r),s={};s.key=i,s.iv=a,s.ciphertext=o;var l=e.decrypt(s,i,{iv:a});return v.enc.Hex.stringify(l)},n=function(e,t,n){return r(v.AES,e,t,n)},r=function(e,t,n,r){var o=v.enc.Hex.parse(t),i=v.enc.Hex.parse(n),a=v.enc.Hex.parse(r),s=e.encrypt(o,i,{iv:a}),l=v.enc.Hex.parse(s.toString());return v.enc.Base64.stringify(l)},o={"AES-256-CBC":{proc:e,eproc:n,keylen:32,ivlen:16},"AES-192-CBC":{proc:e,eproc:n,keylen:24,ivlen:16},"AES-128-CBC":{proc:e,eproc:n,keylen:16,ivlen:16},"DES-EDE3-CBC":{proc:function(e,n,r){return t(v.TripleDES,e,n,r)},eproc:function(e,t,n){return r(v.TripleDES,e,t,n)},keylen:24,ivlen:8},"DES-CBC":{proc:function(e,n,r){return t(v.DES,e,n,r)},eproc:function(e,t,n){return r(v.DES,e,t,n)},keylen:8,ivlen:8}},i=function(e){var t={},n=e.match(new RegExp("DEK-Info: ([^,]+),([0-9A-Fa-f]+)","m"));n&&(t.cipher=n[1],t.ivsalt=n[2]);var r=e.match(new RegExp("-----BEGIN ([A-Z]+) PRIVATE KEY-----"));r&&(t.type=r[1]);var o=-1,i=0;-1!=e.indexOf("\r\n\r\n")&&(o=e.indexOf("\r\n\r\n"),i=2),-1!=e.indexOf("\n\n")&&(o=e.indexOf("\n\n"),i=1);var a=e.indexOf("-----END");if(-1!=o&&-1!=a){var s=e.substring(o+2*i,a-i);s=s.replace(/\s+/g,""),t.data=s}return t},a=function(e,t,n){for(var r=n.substring(0,16),i=v.enc.Hex.parse(r),a=v.enc.Utf8.parse(t),s=o[e].keylen+o[e].ivlen,l="",u=null;;){var c=v.algo.MD5.create();if(null!=u&&c.update(u),c.update(a),c.update(i),u=c.finalize(),(l+=v.enc.Hex.stringify(u)).length>=2*s)break}var d={};return d.keyhex=l.substr(0,2*o[e].keylen),d.ivhex=l.substr(2*o[e].keylen,2*o[e].ivlen),d},s=function(e,t,n,r){var i=v.enc.Base64.parse(e),a=v.enc.Hex.stringify(i);return(0,o[t].proc)(a,n,r)};return{version:"1.0.0",parsePKCS5PEM:function(e){return i(e)},getKeyAndUnusedIvByPasscodeAndIvsalt:function(e,t,n){return a(e,t,n)},decryptKeyB64:function(e,t,n,r){return s(e,t,n,r)},getDecryptedKeyHex:function(e,t){var n=i(e),r=(n.type,n.cipher),o=n.ivsalt,l=n.data,u=a(r,t,o).keyhex;return s(l,r,u,o)},getEncryptedPKCS5PEMFromPrvKeyHex:function(e,t,n,r,i){var s="";if(void 0!==r&&null!=r||(r="AES-256-CBC"),void 0===o[r])throw"KEYUTIL unsupported algorithm: "+r;void 0!==i&&null!=i||(i=function(e){var t=v.lib.WordArray.random(e);return v.enc.Hex.stringify(t)}(o[r].ivlen).toUpperCase());var l=function(e,t,n,r){return(0,o[t].eproc)(e,n,r)}(t,r,a(r,n,i).keyhex,i);return s="-----BEGIN "+e+" PRIVATE KEY-----\r\n",s+="Proc-Type: 4,ENCRYPTED\r\n",s+="DEK-Info: "+r+","+i+"\r\n",s+="\r\n",(s+=l.replace(/(.{64})/g,"$1\r\n"))+"\r\n-----END "+e+" PRIVATE KEY-----\r\n"},parseHexOfEncryptedPKCS8:function(e){var t=fe,n=t.getChildIdx,r=t.getV,o={},i=n(e,0);if(2!=i.length)throw"malformed format: SEQUENCE(0).items != 2: "+i.length;o.ciphertext=r(e,i[1]);var a=n(e,i[0]);if(2!=a.length)throw"malformed format: SEQUENCE(0.0).items != 2: "+a.length;if("2a864886f70d01050d"!=r(e,a[0]))throw"this only supports pkcs5PBES2";var s=n(e,a[1]);if(2!=a.length)throw"malformed format: SEQUENCE(0.0.1).items != 2: "+s.length;var l=n(e,s[1]);if(2!=l.length)throw"malformed format: SEQUENCE(0.0.1.1).items != 2: "+l.length;if("2a864886f70d0307"!=r(e,l[0]))throw"this only supports TripleDES";o.encryptionSchemeAlg="TripleDES",o.encryptionSchemeIV=r(e,l[1]);var u=n(e,s[0]);if(2!=u.length)throw"malformed format: SEQUENCE(0.0.1.0).items != 2: "+u.length;if("2a864886f70d01050c"!=r(e,u[0]))throw"this only supports pkcs5PBKDF2";var c=n(e,u[1]);if(c.length<2)throw"malformed format: SEQUENCE(0.0.1.0.1).items < 2: "+c.length;o.pbkdf2Salt=r(e,c[0]);var d=r(e,c[1]);try{o.pbkdf2Iter=parseInt(d,16)}catch(e){throw"malformed format pbkdf2Iter: "+d}return o},getPBKDF2KeyHexFromParam:function(e,t){var n=v.enc.Hex.parse(e.pbkdf2Salt),r=e.pbkdf2Iter,o=v.PBKDF2(t,n,{keySize:6,iterations:r});return v.enc.Hex.stringify(o)},_getPlainPKCS8HexFromEncryptedPKCS8PEM:function(e,t){var n=Pe(e,"ENCRYPTED PRIVATE KEY"),r=this.parseHexOfEncryptedPKCS8(n),o=ze.getPBKDF2KeyHexFromParam(r,t),i={};i.ciphertext=v.enc.Hex.parse(r.ciphertext);var a=v.enc.Hex.parse(o),s=v.enc.Hex.parse(r.encryptionSchemeIV),l=v.TripleDES.decrypt(i,a,{iv:s});return v.enc.Hex.stringify(l)},getKeyFromEncryptedPKCS8PEM:function(e,t){var n=this._getPlainPKCS8HexFromEncryptedPKCS8PEM(e,t);return this.getKeyFromPlainPrivatePKCS8Hex(n)},parsePlainPrivatePKCS8Hex:function(e){var t=fe,n=t.getChildIdx,r=t.getV,o={algparam:null};if("30"!=e.substr(0,2))throw"malformed plain PKCS8 private key(code:001)";var i=n(e,0);if(3!=i.length)throw"malformed plain PKCS8 private key(code:002)";if("30"!=e.substr(i[1],2))throw"malformed PKCS8 private key(code:003)";var a=n(e,i[1]);if(2!=a.length)throw"malformed PKCS8 private key(code:004)";if("06"!=e.substr(a[0],2))throw"malformed PKCS8 private key(code:005)";if(o.algoid=r(e,a[0]),"06"==e.substr(a[1],2)&&(o.algparam=r(e,a[1])),"04"!=e.substr(i[2],2))throw"malformed PKCS8 private key(code:006)";return o.keyidx=t.getVidx(e,i[2]),o},getKeyFromPlainPrivatePKCS8PEM:function(e){var t=Pe(e,"PRIVATE KEY");return this.getKeyFromPlainPrivatePKCS8Hex(t)},getKeyFromPlainPrivatePKCS8Hex:function(e){var t,n=this.parsePlainPrivatePKCS8Hex(e);if("2a864886f70d010101"==n.algoid)t=new oe;else if("2a8648ce380401"==n.algoid)t=new ue.crypto.DSA;else{if("2a8648ce3d0201"!=n.algoid)throw"unsupported private key algorithm";t=new ue.crypto.ECDSA}return t.readPKCS8PrvKeyHex(e),t},_getKeyFromPublicPKCS8Hex:function(e){var t,n=fe.getVbyList(e,0,[0,0],"06");if("2a864886f70d010101"===n)t=new oe;else if("2a8648ce380401"===n)t=new ue.crypto.DSA;else{if("2a8648ce3d0201"!==n)throw"unsupported PKCS#8 public key hex";t=new ue.crypto.ECDSA}return t.readPKCS8PubKeyHex(e),t},parsePublicRawRSAKeyHex:function(e){var t=fe,n=t.getChildIdx,r=t.getV,o={};if("30"!=e.substr(0,2))throw"malformed RSA key(code:001)";var i=n(e,0);if(2!=i.length)throw"malformed RSA key(code:002)";if("02"!=e.substr(i[0],2))throw"malformed RSA key(code:003)";if(o.n=r(e,i[0]),"02"!=e.substr(i[1],2))throw"malformed RSA key(code:004)";return o.e=r(e,i[1]),o},parsePublicPKCS8Hex:function(e){var t=fe,n=t.getChildIdx,r=t.getV,o={algparam:null},i=n(e,0);if(2!=i.length)throw"outer DERSequence shall have 2 elements: "+i.length;var a=i[0];if("30"!=e.substr(a,2))throw"malformed PKCS8 public key(code:001)";var s=n(e,a);if(2!=s.length)throw"malformed PKCS8 public key(code:002)";if("06"!=e.substr(s[0],2))throw"malformed PKCS8 public key(code:003)";if(o.algoid=r(e,s[0]),"06"==e.substr(s[1],2)?o.algparam=r(e,s[1]):"30"==e.substr(s[1],2)&&(o.algparam={},o.algparam.p=t.getVbyList(e,s[1],[0],"02"),o.algparam.q=t.getVbyList(e,s[1],[1],"02"),o.algparam.g=t.getVbyList(e,s[1],[2],"02")),"03"!=e.substr(i[1],2))throw"malformed PKCS8 public key(code:004)";return o.key=r(e,i[1]).substr(2),o}}}();ze.getKey=function(e,t,n){var r,o=(v=fe).getChildIdx,i=(v.getV,v.getVbyList),a=ue.crypto,s=a.ECDSA,l=a.DSA,u=oe,c=Pe,d=ze;if(void 0!==u&&e instanceof u)return e;if(void 0!==s&&e instanceof s)return e;if(void 0!==l&&e instanceof l)return e;if(void 0!==e.curve&&void 0!==e.xy&&void 0===e.d)return new s({pub:e.xy,curve:e.curve});if(void 0!==e.curve&&void 0!==e.d)return new s({prv:e.d,curve:e.curve});if(void 0===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0===e.d)return(P=new u).setPublic(e.n,e.e),P;if(void 0===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0!==e.d&&void 0!==e.p&&void 0!==e.q&&void 0!==e.dp&&void 0!==e.dq&&void 0!==e.co&&void 0===e.qi)return(P=new u).setPrivateEx(e.n,e.e,e.d,e.p,e.q,e.dp,e.dq,e.co),P;if(void 0===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0!==e.d&&void 0===e.p)return(P=new u).setPrivate(e.n,e.e,e.d),P;if(void 0!==e.p&&void 0!==e.q&&void 0!==e.g&&void 0!==e.y&&void 0===e.x)return(P=new l).setPublic(e.p,e.q,e.g,e.y),P;if(void 0!==e.p&&void 0!==e.q&&void 0!==e.g&&void 0!==e.y&&void 0!==e.x)return(P=new l).setPrivate(e.p,e.q,e.g,e.y,e.x),P;if("RSA"===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0===e.d)return(P=new u).setPublic(we(e.n),we(e.e)),P;if("RSA"===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0!==e.d&&void 0!==e.p&&void 0!==e.q&&void 0!==e.dp&&void 0!==e.dq&&void 0!==e.qi)return(P=new u).setPrivateEx(we(e.n),we(e.e),we(e.d),we(e.p),we(e.q),we(e.dp),we(e.dq),we(e.qi)),P;if("RSA"===e.kty&&void 0!==e.n&&void 0!==e.e&&void 0!==e.d)return(P=new u).setPrivate(we(e.n),we(e.e),we(e.d)),P;if("EC"===e.kty&&void 0!==e.crv&&void 0!==e.x&&void 0!==e.y&&void 0===e.d){var f=(O=new s({curve:e.crv})).ecparams.keylen/4,p="04"+("0000000000"+we(e.x)).slice(-f)+("0000000000"+we(e.y)).slice(-f);return O.setPublicKeyHex(p),O}if("EC"===e.kty&&void 0!==e.crv&&void 0!==e.x&&void 0!==e.y&&void 0!==e.d){f=(O=new s({curve:e.crv})).ecparams.keylen/4,p="04"+("0000000000"+we(e.x)).slice(-f)+("0000000000"+we(e.y)).slice(-f);var h=("0000000000"+we(e.d)).slice(-f);return O.setPublicKeyHex(p),O.setPrivateKeyHex(h),O}if("pkcs5prv"===n){var m,g=e,v=fe;if(9===(m=o(g,0)).length)(P=new u).readPKCS5PrvKeyHex(g);else if(6===m.length)(P=new l).readPKCS5PrvKeyHex(g);else{if(!(m.length>2&&"04"===g.substr(m[1],2)))throw"unsupported PKCS#1/5 hexadecimal key";(P=new s).readPKCS5PrvKeyHex(g)}return P}if("pkcs8prv"===n)return d.getKeyFromPlainPrivatePKCS8Hex(e);if("pkcs8pub"===n)return d._getKeyFromPublicPKCS8Hex(e);if("x509pub"===n)return qe.getPublicKeyFromCertHex(e);if(-1!=e.indexOf("-END CERTIFICATE-",0)||-1!=e.indexOf("-END X509 CERTIFICATE-",0)||-1!=e.indexOf("-END TRUSTED CERTIFICATE-",0))return qe.getPublicKeyFromCertPEM(e);if(-1!=e.indexOf("-END PUBLIC KEY-")){var y=Pe(e,"PUBLIC KEY");return d._getKeyFromPublicPKCS8Hex(y)}if(-1!=e.indexOf("-END RSA PRIVATE KEY-")&&-1==e.indexOf("4,ENCRYPTED")){var b=c(e,"RSA PRIVATE KEY");return d.getKey(b,null,"pkcs5prv")}if(-1!=e.indexOf("-END DSA PRIVATE KEY-")&&-1==e.indexOf("4,ENCRYPTED")){var w=i(r=c(e,"DSA PRIVATE KEY"),0,[1],"02"),E=i(r,0,[2],"02"),S=i(r,0,[3],"02"),A=i(r,0,[4],"02"),k=i(r,0,[5],"02");return(P=new l).setPrivate(new x(w,16),new x(E,16),new x(S,16),new x(A,16),new x(k,16)),P}if(-1!=e.indexOf("-END EC PRIVATE KEY-")&&-1==e.indexOf("4,ENCRYPTED"))return b=c(e,"EC PRIVATE KEY"),d.getKey(b,null,"pkcs5prv");if(-1!=e.indexOf("-END PRIVATE KEY-"))return d.getKeyFromPlainPrivatePKCS8PEM(e);if(-1!=e.indexOf("-END RSA PRIVATE KEY-")&&-1!=e.indexOf("4,ENCRYPTED")){var _=d.getDecryptedKeyHex(e,t),C=new oe;return C.readPKCS5PrvKeyHex(_),C}if(-1!=e.indexOf("-END EC PRIVATE KEY-")&&-1!=e.indexOf("4,ENCRYPTED")){var O,P=i(r=d.getDecryptedKeyHex(e,t),0,[1],"04"),R=i(r,0,[2,0],"06"),T=i(r,0,[3,0],"03").substr(2);if(void 0===ue.crypto.OID.oidhex2name[R])throw"undefined OID(hex) in KJUR.crypto.OID: "+R;return(O=new s({curve:ue.crypto.OID.oidhex2name[R]})).setPublicKeyHex(T),O.setPrivateKeyHex(P),O.isPublic=!1,O}if(-1!=e.indexOf("-END DSA PRIVATE KEY-")&&-1!=e.indexOf("4,ENCRYPTED"))return w=i(r=d.getDecryptedKeyHex(e,t),0,[1],"02"),E=i(r,0,[2],"02"),S=i(r,0,[3],"02"),A=i(r,0,[4],"02"),k=i(r,0,[5],"02"),(P=new l).setPrivate(new x(w,16),new x(E,16),new x(S,16),new x(A,16),new x(k,16)),P;if(-1!=e.indexOf("-END ENCRYPTED PRIVATE KEY-"))return d.getKeyFromEncryptedPKCS8PEM(e,t);throw new Error("not supported argument")},ze.generateKeypair=function(e,t){if("RSA"==e){var n=t;(a=new oe).generate(n,"10001"),a.isPrivate=!0,a.isPublic=!0;var r=new oe,o=a.n.toString(16),i=a.e.toString(16);return r.setPublic(o,i),r.isPrivate=!1,r.isPublic=!0,(s={}).prvKeyObj=a,s.pubKeyObj=r,s}if("EC"==e){var a,s,l=t,u=new ue.crypto.ECDSA({curve:l}).generateKeyPairHex();return(a=new ue.crypto.ECDSA({curve:l})).setPublicKeyHex(u.ecpubhex),a.setPrivateKeyHex(u.ecprvhex),a.isPrivate=!0,a.isPublic=!1,(r=new ue.crypto.ECDSA({curve:l})).setPublicKeyHex(u.ecpubhex),r.isPrivate=!1,r.isPublic=!0,(s={}).prvKeyObj=a,s.pubKeyObj=r,s}throw"unknown algorithm: "+e},ze.getPEM=function(e,t,n,r,o,i){var a=ue,s=a.asn1,l=s.DERObjectIdentifier,u=s.DERInteger,c=s.ASN1Util.newObject,d=s.x509.SubjectPublicKeyInfo,f=a.crypto,p=f.DSA,h=f.ECDSA,m=oe;function g(e){return c({seq:[{int:0},{int:{bigint:e.n}},{int:e.e},{int:{bigint:e.d}},{int:{bigint:e.p}},{int:{bigint:e.q}},{int:{bigint:e.dmp1}},{int:{bigint:e.dmq1}},{int:{bigint:e.coeff}}]})}function y(e){return c({seq:[{int:1},{octstr:{hex:e.prvKeyHex}},{tag:["a0",!0,{oid:{name:e.curveName}}]},{tag:["a1",!0,{bitstr:{hex:"00"+e.pubKeyHex}}]}]})}function b(e){return c({seq:[{int:0},{int:{bigint:e.p}},{int:{bigint:e.q}},{int:{bigint:e.g}},{int:{bigint:e.y}},{int:{bigint:e.x}}]})}if((void 0!==m&&e instanceof m||void 0!==p&&e instanceof p||void 0!==h&&e instanceof h)&&1==e.isPublic&&(void 0===t||"PKCS8PUB"==t))return Oe(x=new d(e).getEncodedHex(),"PUBLIC KEY");if("PKCS1PRV"==t&&void 0!==m&&e instanceof m&&(void 0===n||null==n)&&1==e.isPrivate)return Oe(x=g(e).getEncodedHex(),"RSA PRIVATE KEY");if("PKCS1PRV"==t&&void 0!==h&&e instanceof h&&(void 0===n||null==n)&&1==e.isPrivate){var w=new l({name:e.curveName}).getEncodedHex(),E=y(e).getEncodedHex(),S="";return(S+=Oe(w,"EC PARAMETERS"))+Oe(E,"EC PRIVATE KEY")}if("PKCS1PRV"==t&&void 0!==p&&e instanceof p&&(void 0===n||null==n)&&1==e.isPrivate)return Oe(x=b(e).getEncodedHex(),"DSA PRIVATE KEY");if("PKCS5PRV"==t&&void 0!==m&&e instanceof m&&void 0!==n&&null!=n&&1==e.isPrivate){var x=g(e).getEncodedHex();return void 0===r&&(r="DES-EDE3-CBC"),this.getEncryptedPKCS5PEMFromPrvKeyHex("RSA",x,n,r,i)}if("PKCS5PRV"==t&&void 0!==h&&e instanceof h&&void 0!==n&&null!=n&&1==e.isPrivate)return x=y(e).getEncodedHex(),void 0===r&&(r="DES-EDE3-CBC"),this.getEncryptedPKCS5PEMFromPrvKeyHex("EC",x,n,r,i);if("PKCS5PRV"==t&&void 0!==p&&e instanceof p&&void 0!==n&&null!=n&&1==e.isPrivate)return x=b(e).getEncodedHex(),void 0===r&&(r="DES-EDE3-CBC"),this.getEncryptedPKCS5PEMFromPrvKeyHex("DSA",x,n,r,i);var A=function(e,t){var n=k(e,t);return new c({seq:[{seq:[{oid:{name:"pkcs5PBES2"}},{seq:[{seq:[{oid:{name:"pkcs5PBKDF2"}},{seq:[{octstr:{hex:n.pbkdf2Salt}},{int:n.pbkdf2Iter}]}]},{seq:[{oid:{name:"des-EDE3-CBC"}},{octstr:{hex:n.encryptionSchemeIV}}]}]}]},{octstr:{hex:n.ciphertext}}]}).getEncodedHex()},k=function(e,t){var n=v.lib.WordArray.random(8),r=v.lib.WordArray.random(8),o=v.PBKDF2(t,n,{keySize:6,iterations:100}),i=v.enc.Hex.parse(e),a=v.TripleDES.encrypt(i,o,{iv:r})+"",s={};return s.ciphertext=a,s.pbkdf2Salt=v.enc.Hex.stringify(n),s.pbkdf2Iter=100,s.encryptionSchemeAlg="DES-EDE3-CBC",s.encryptionSchemeIV=v.enc.Hex.stringify(r),s};if("PKCS8PRV"==t&&null!=m&&e instanceof m&&1==e.isPrivate){var _=g(e).getEncodedHex();return x=c({seq:[{int:0},{seq:[{oid:{name:"rsaEncryption"}},{null:!0}]},{octstr:{hex:_}}]}).getEncodedHex(),void 0===n||null==n?Oe(x,"PRIVATE KEY"):Oe(E=A(x,n),"ENCRYPTED PRIVATE KEY")}if("PKCS8PRV"==t&&void 0!==h&&e instanceof h&&1==e.isPrivate)return _=new c({seq:[{int:1},{octstr:{hex:e.prvKeyHex}},{tag:["a1",!0,{bitstr:{hex:"00"+e.pubKeyHex}}]}]}).getEncodedHex(),x=c({seq:[{int:0},{seq:[{oid:{name:"ecPublicKey"}},{oid:{name:e.curveName}}]},{octstr:{hex:_}}]}).getEncodedHex(),void 0===n||null==n?Oe(x,"PRIVATE KEY"):Oe(E=A(x,n),"ENCRYPTED PRIVATE KEY");if("PKCS8PRV"==t&&void 0!==p&&e instanceof p&&1==e.isPrivate)return _=new u({bigint:e.x}).getEncodedHex(),x=c({seq:[{int:0},{seq:[{oid:{name:"dsa"}},{seq:[{int:{bigint:e.p}},{int:{bigint:e.q}},{int:{bigint:e.g}}]}]},{octstr:{hex:_}}]}).getEncodedHex(),void 0===n||null==n?Oe(x,"PRIVATE KEY"):Oe(E=A(x,n),"ENCRYPTED PRIVATE KEY");throw new Error("unsupported object nor format")},ze.getKeyFromCSRPEM=function(e){var t=Pe(e,"CERTIFICATE REQUEST");return ze.getKeyFromCSRHex(t)},ze.getKeyFromCSRHex=function(e){var t=ze.parseCSRHex(e);return ze.getKey(t.p8pubkeyhex,null,"pkcs8pub")},ze.parseCSRHex=function(e){var t=fe,n=t.getChildIdx,r=t.getTLV,o={},i=e;if("30"!=i.substr(0,2))throw"malformed CSR(code:001)";var a=n(i,0);if(a.length<1)throw"malformed CSR(code:002)";if("30"!=i.substr(a[0],2))throw"malformed CSR(code:003)";var s=n(i,a[0]);if(s.length<3)throw"malformed CSR(code:004)";return o.p8pubkeyhex=r(i,s[2]),o},ze.getKeyID=function(e){var t=ze,n=fe;"string"==typeof e&&-1!=e.indexOf("BEGIN ")&&(e=t.getKey(e));var r=Pe(t.getPEM(e)),o=n.getIdxbyList(r,0,[1]),i=n.getV(r,o).substring(2);return ue.crypto.Util.hashHex(i,"sha1")},ze.getJWKFromKey=function(e){var t={};if(e instanceof oe&&e.isPrivate)return t.kty="RSA",t.n=be(e.n.toString(16)),t.e=be(e.e.toString(16)),t.d=be(e.d.toString(16)),t.p=be(e.p.toString(16)),t.q=be(e.q.toString(16)),t.dp=be(e.dmp1.toString(16)),t.dq=be(e.dmq1.toString(16)),t.qi=be(e.coeff.toString(16)),t;if(e instanceof oe&&e.isPublic)return t.kty="RSA",t.n=be(e.n.toString(16)),t.e=be(e.e.toString(16)),t;if(e instanceof ue.crypto.ECDSA&&e.isPrivate){if("P-256"!==(r=e.getShortNISTPCurveName())&&"P-384"!==r)throw"unsupported curve name for JWT: "+r;var n=e.getPublicKeyXYHex();return t.kty="EC",t.crv=r,t.x=be(n.x),t.y=be(n.y),t.d=be(e.prvKeyHex),t}if(e instanceof ue.crypto.ECDSA&&e.isPublic){var r;if("P-256"!==(r=e.getShortNISTPCurveName())&&"P-384"!==r)throw"unsupported curve name for JWT: "+r;return n=e.getPublicKeyXYHex(),t.kty="EC",t.crv=r,t.x=be(n.x),t.y=be(n.y),t}throw"not supported key object"},oe.getPosArrayOfChildrenFromHex=function(e){return fe.getChildIdx(e,0)},oe.getHexValueArrayOfChildrenFromHex=function(e){var t,n=fe.getV,r=n(e,(t=oe.getPosArrayOfChildrenFromHex(e))[0]),o=n(e,t[1]),i=n(e,t[2]),a=n(e,t[3]),s=n(e,t[4]),l=n(e,t[5]),u=n(e,t[6]),c=n(e,t[7]),d=n(e,t[8]);return(t=new Array).push(r,o,i,a,s,l,u,c,d),t},oe.prototype.readPrivateKeyFromPEMString=function(e){var t=Pe(e),n=oe.getHexValueArrayOfChildrenFromHex(t);this.setPrivateEx(n[1],n[2],n[3],n[4],n[5],n[6],n[7],n[8])},oe.prototype.readPKCS5PrvKeyHex=function(e){var t=oe.getHexValueArrayOfChildrenFromHex(e);this.setPrivateEx(t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8])},oe.prototype.readPKCS8PrvKeyHex=function(e){var t,n,r,o,i,a,s,l,u=fe,c=u.getVbyListEx;if(!1===u.isASN1HEX(e))throw new Error("not ASN.1 hex string");try{t=c(e,0,[2,0,1],"02"),n=c(e,0,[2,0,2],"02"),r=c(e,0,[2,0,3],"02"),o=c(e,0,[2,0,4],"02"),i=c(e,0,[2,0,5],"02"),a=c(e,0,[2,0,6],"02"),s=c(e,0,[2,0,7],"02"),l=c(e,0,[2,0,8],"02")}catch(e){throw new Error("malformed PKCS#8 plain RSA private key")}this.setPrivateEx(t,n,r,o,i,a,s,l)},oe.prototype.readPKCS5PubKeyHex=function(e){var t=fe,n=t.getV;if(!1===t.isASN1HEX(e))throw new Error("keyHex is not ASN.1 hex string");var r=t.getChildIdx(e,0);if(2!==r.length||"02"!==e.substr(r[0],2)||"02"!==e.substr(r[1],2))throw new Error("wrong hex for PKCS#5 public key");var o=n(e,r[0]),i=n(e,r[1]);this.setPublic(o,i)},oe.prototype.readPKCS8PubKeyHex=function(e){var t=fe;if(!1===t.isASN1HEX(e))throw new Error("not ASN.1 hex string");if("06092a864886f70d010101"!==t.getTLVbyListEx(e,0,[0,0]))throw new Error("not PKCS8 RSA public key");var n=t.getTLVbyListEx(e,0,[1,0]);this.readPKCS5PubKeyHex(n)},oe.prototype.readCertPubKeyHex=function(e,t){var n,r;(n=new qe).readCertHex(e),r=n.getPublicKeyHex(),this.readPKCS8PubKeyHex(r)};var $e=new RegExp("[^0-9a-f]","gi");function He(e,t){for(var n="",r=t/4-e.length,o=0;o<r;o++)n+="0";return n+e}function We(e,t,n){for(var r="",o=0;r.length<t;)r+=xe(n(Ae(e+String.fromCharCode.apply(String,[(4278190080&o)>>24,(16711680&o)>>16,(65280&o)>>8,255&o])))),o+=1;return r}function Ve(e){for(var t in ue.crypto.Util.DIGESTINFOHEAD){var n=ue.crypto.Util.DIGESTINFOHEAD[t],r=n.length;if(e.substring(0,r)==n)return[t,e.substring(r)]}return[]}function qe(e){var t,n=fe,r=n.getChildIdx,o=n.getV,i=n.getTLV,a=n.getVbyList,s=n.getVbyListEx,l=n.getTLVbyList,u=n.getTLVbyListEx,c=n.getIdxbyList,d=n.getIdxbyListEx,f=n.getVidx,p=n.oidname,h=n.hextooidstr,m=qe,g=Pe;try{t=ue.asn1.x509.AlgorithmIdentifier.PSSNAME2ASN1TLV}catch(e){}this.HEX2STAG={"0c":"utf8",13:"prn",16:"ia5","1a":"vis","1e":"bmp"},this.hex=null,this.version=0,this.foffset=0,this.aExtInfo=null,this.getVersion=function(){return null===this.hex||0!==this.version?this.version:"a003020102"!==l(this.hex,0,[0,0])?(this.version=1,this.foffset=-1,1):(this.version=3,3)},this.getSerialNumberHex=function(){return s(this.hex,0,[0,0],"02")},this.getSignatureAlgorithmField=function(){var e=u(this.hex,0,[0,1]);return this.getAlgorithmIdentifierName(e)},this.getAlgorithmIdentifierName=function(e){for(var n in t)if(e===t[n])return n;return p(s(e,0,[0],"06"))},this.getIssuer=function(){return this.getX500Name(this.getIssuerHex())},this.getIssuerHex=function(){return l(this.hex,0,[0,3+this.foffset],"30")},this.getIssuerString=function(){return m.hex2dn(this.getIssuerHex())},this.getSubject=function(){return this.getX500Name(this.getSubjectHex())},this.getSubjectHex=function(){return l(this.hex,0,[0,5+this.foffset],"30")},this.getSubjectString=function(){return m.hex2dn(this.getSubjectHex())},this.getNotBefore=function(){var e=a(this.hex,0,[0,4+this.foffset,0]);return e=e.replace(/(..)/g,"%$1"),decodeURIComponent(e)},this.getNotAfter=function(){var e=a(this.hex,0,[0,4+this.foffset,1]);return e=e.replace(/(..)/g,"%$1"),decodeURIComponent(e)},this.getPublicKeyHex=function(){return n.getTLVbyList(this.hex,0,[0,6+this.foffset],"30")},this.getPublicKeyIdx=function(){return c(this.hex,0,[0,6+this.foffset],"30")},this.getPublicKeyContentIdx=function(){var e=this.getPublicKeyIdx();return c(this.hex,e,[1,0],"30")},this.getPublicKey=function(){return ze.getKey(this.getPublicKeyHex(),null,"pkcs8pub")},this.getSignatureAlgorithmName=function(){var e=l(this.hex,0,[1],"30");return this.getAlgorithmIdentifierName(e)},this.getSignatureValueHex=function(){return a(this.hex,0,[2],"03",!0)},this.verifySignature=function(e){var t=this.getSignatureAlgorithmField(),n=this.getSignatureValueHex(),r=l(this.hex,0,[0],"30"),o=new ue.crypto.Signature({alg:t});return o.init(e),o.updateHex(r),o.verify(n)},this.parseExt=function(e){var t,i,s;if(void 0===e){if(s=this.hex,3!==this.version)return-1;t=c(s,0,[0,7,0],"30"),i=r(s,t)}else{s=Pe(e);var l=c(s,0,[0,3,0,0],"06");if("2a864886f70d01090e"!=o(s,l))return void(this.aExtInfo=new Array);t=c(s,0,[0,3,0,1,0],"30"),i=r(s,t),this.hex=s}this.aExtInfo=new Array;for(var u=0;u<i.length;u++){var d={critical:!1},p=0;3===r(s,i[u]).length&&(d.critical=!0,p=1),d.oid=n.hextooidstr(a(s,i[u],[0],"06"));var h=c(s,i[u],[1+p]);d.vidx=f(s,h),this.aExtInfo.push(d)}},this.getExtInfo=function(e){var t=this.aExtInfo,n=e;if(e.match(/^[0-9.]+$/)||(n=ue.asn1.x509.OID.name2oid(e)),""!==n)for(var r=0;r<t.length;r++)if(t[r].oid===n)return t[r]},this.getExtBasicConstraints=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("basicConstraints");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var r={extname:"basicConstraints"};if(t&&(r.critical=!0),"3000"===e)return r;if("30030101ff"===e)return r.cA=!0,r;if("30060101ff02"===e.substr(0,12)){var a=o(e,10),s=parseInt(a,16);return r.cA=!0,r.pathLen=s,r}throw new Error("hExtV parse error: "+e)},this.getExtKeyUsage=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("keyUsage");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var r={extname:"keyUsage"};return t&&(r.critical=!0),r.names=this.getExtKeyUsageString(e).split(","),r},this.getExtKeyUsageBin=function(e){if(void 0===e){var t=this.getExtInfo("keyUsage");if(void 0===t)return"";e=i(this.hex,t.vidx)}if(8!=e.length&&10!=e.length)throw new Error("malformed key usage value: "+e);var n="000000000000000"+parseInt(e.substr(6),16).toString(2);return 8==e.length&&(n=n.slice(-8)),10==e.length&&(n=n.slice(-16)),""==(n=n.replace(/0+$/,""))&&(n="0"),n},this.getExtKeyUsageString=function(e){for(var t=this.getExtKeyUsageBin(e),n=new Array,r=0;r<t.length;r++)"1"==t.substr(r,1)&&n.push(qe.KEYUSAGE_NAME[r]);return n.join(",")},this.getExtSubjectKeyIdentifier=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("subjectKeyIdentifier");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var r={extname:"subjectKeyIdentifier"};t&&(r.critical=!0);var a=o(e,0);return r.kid={hex:a},r},this.getExtAuthorityKeyIdentifier=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("authorityKeyIdentifier");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var a={extname:"authorityKeyIdentifier"};t&&(a.critical=!0);for(var s=r(e,0),l=0;l<s.length;l++){var u=e.substr(s[l],2);if("80"===u&&(a.kid={hex:o(e,s[l])}),"a1"===u){var c=i(e,s[l]),d=this.getGeneralNames(c);a.issuer=d[0].dn}"82"===u&&(a.sn={hex:o(e,s[l])})}return a},this.getExtExtKeyUsage=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("extKeyUsage");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var a={extname:"extKeyUsage",array:[]};t&&(a.critical=!0);for(var s=r(e,0),l=0;l<s.length;l++)a.array.push(p(o(e,s[l])));return a},this.getExtExtKeyUsageName=function(){var e=this.getExtInfo("extKeyUsage");if(void 0===e)return e;var t=new Array,n=i(this.hex,e.vidx);if(""===n)return t;for(var a=r(n,0),s=0;s<a.length;s++)t.push(p(o(n,a[s])));return t},this.getExtSubjectAltName=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("subjectAltName");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var r={extname:"subjectAltName",array:[]};return t&&(r.critical=!0),r.array=this.getGeneralNames(e),r},this.getExtIssuerAltName=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("issuerAltName");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var r={extname:"issuerAltName",array:[]};return t&&(r.critical=!0),r.array=this.getGeneralNames(e),r},this.getGeneralNames=function(e){for(var t=r(e,0),n=[],o=0;o<t.length;o++){var a=this.getGeneralName(i(e,t[o]));void 0!==a&&n.push(a)}return n},this.getGeneralName=function(e){var t=e.substr(0,2),n=o(e,0),r=xe(n);return"81"==t?{rfc822:r}:"82"==t?{dns:r}:"86"==t?{uri:r}:"87"==t?{ip:Le(n)}:"a4"==t?{dn:this.getX500Name(n)}:void 0},this.getExtSubjectAltName2=function(){var e,t,n,a=this.getExtInfo("subjectAltName");if(void 0===a)return a;for(var s=new Array,l=i(this.hex,a.vidx),u=r(l,0),c=0;c<u.length;c++)n=l.substr(u[c],2),e=o(l,u[c]),"81"===n&&(t=Se(e),s.push(["MAIL",t])),"82"===n&&(t=Se(e),s.push(["DNS",t])),"84"===n&&(t=qe.hex2dn(e,0),s.push(["DN",t])),"86"===n&&(t=Se(e),s.push(["URI",t])),"87"===n&&(t=Le(e),s.push(["IP",t]));return s},this.getExtCRLDistributionPoints=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("cRLDistributionPoints");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var o={extname:"cRLDistributionPoints",array:[]};t&&(o.critical=!0);for(var a=r(e,0),s=0;s<a.length;s++){var l=i(e,a[s]);o.array.push(this.getDistributionPoint(l))}return o},this.getDistributionPoint=function(e){for(var t={},n=r(e,0),o=0;o<n.length;o++){var a=e.substr(n[o],2),s=i(e,n[o]);"a0"==a&&(t.dpname=this.getDistributionPointName(s))}return t},this.getDistributionPointName=function(e){for(var t={},n=r(e,0),o=0;o<n.length;o++){var a=e.substr(n[o],2),s=i(e,n[o]);"a0"==a&&(t.full=this.getGeneralNames(s))}return t},this.getExtCRLDistributionPointsURI=function(){var e=this.getExtInfo("cRLDistributionPoints");if(void 0===e)return e;for(var t=new Array,n=r(this.hex,e.vidx),o=0;o<n.length;o++)try{var i=Se(a(this.hex,n[o],[0,0,0],"86"));t.push(i)}catch(e){}return t},this.getExtAIAInfo=function(){var e=this.getExtInfo("authorityInfoAccess");if(void 0===e)return e;for(var t={ocsp:[],caissuer:[]},n=r(this.hex,e.vidx),o=0;o<n.length;o++){var i=a(this.hex,n[o],[0],"06"),s=a(this.hex,n[o],[1],"86");"2b06010505073001"===i&&t.ocsp.push(Se(s)),"2b06010505073002"===i&&t.caissuer.push(Se(s))}return t},this.getExtAuthorityInfoAccess=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("authorityInfoAccess");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var o={extname:"authorityInfoAccess",array:[]};t&&(o.critical=!0);for(var l=r(e,0),u=0;u<l.length;u++){var c=s(e,l[u],[0],"06"),d=Se(a(e,l[u],[1],"86"));if("2b06010505073001"==c)o.array.push({ocsp:d});else{if("2b06010505073002"!=c)throw new Error("unknown method: "+c);o.array.push({caissuer:d})}}return o},this.getExtCertificatePolicies=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("certificatePolicies");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var o={extname:"certificatePolicies",array:[]};t&&(o.critical=!0);for(var a=r(e,0),s=0;s<a.length;s++){var l=i(e,a[s]),u=this.getPolicyInformation(l);o.array.push(u)}return o},this.getPolicyInformation=function(e){var t={},n=a(e,0,[0],"06");t.policyoid=p(n);var o=d(e,0,[1],"30");if(-1!=o){t.array=[];for(var s=r(e,o),l=0;l<s.length;l++){var u=i(e,s[l]),c=this.getPolicyQualifierInfo(u);t.array.push(c)}}return t},this.getPolicyQualifierInfo=function(e){var t={},n=a(e,0,[0],"06");if("2b06010505070201"===n){var r=s(e,0,[1],"16");t.cps=xe(r)}else if("2b06010505070202"===n){var o=l(e,0,[1],"30");t.unotice=this.getUserNotice(o)}return t},this.getUserNotice=function(e){for(var t={},n=r(e,0),o=0;o<n.length;o++){var a=i(e,n[o]);"30"!=a.substr(0,2)&&(t.exptext=this.getDisplayText(a))}return t},this.getDisplayText=function(e){var t={};return t.type={"0c":"utf8",16:"ia5","1a":"vis","1e":"bmp"}[e.substr(0,2)],t.str=xe(o(e,0)),t},this.getExtCRLNumber=function(e,t){var n={extname:"cRLNumber"};if(t&&(n.critical=!0),"02"==e.substr(0,2))return n.num={hex:o(e,0)},n;throw new Error("hExtV parse error: "+e)},this.getExtCRLReason=function(e,t){var n={extname:"cRLReason"};if(t&&(n.critical=!0),"0a"==e.substr(0,2))return n.code=parseInt(o(e,0),16),n;throw new Error("hExtV parse error: "+e)},this.getExtOcspNonce=function(e,t){var n={extname:"ocspNonce"};t&&(n.critical=!0);var r=o(e,0);return n.hex=r,n},this.getExtOcspNoCheck=function(e,t){var n={extname:"ocspNoCheck"};return t&&(n.critical=!0),n},this.getExtAdobeTimeStamp=function(e,t){if(void 0===e&&void 0===t){var n=this.getExtInfo("adobeTimeStamp");if(void 0===n)return;e=i(this.hex,n.vidx),t=n.critical}var o={extname:"adobeTimeStamp"};t&&(o.critical=!0);var a=r(e,0);if(a.length>1){var s=i(e,a[1]),l=this.getGeneralName(s);null!=l.uri&&(o.uri=l.uri)}if(a.length>2){var u=i(e,a[2]);"0101ff"==u&&(o.reqauth=!0),"010100"==u&&(o.reqauth=!1)}return o},this.getX500NameRule=function(e){for(var t=null,n=[],r=0;r<e.length;r++)for(var o=e[r],i=0;i<o.length;i++)n.push(o[i]);for(r=0;r<n.length;r++){var a=n[r],s=a.ds,l=a.value,u=a.type;if("prn"!=s&&"utf8"!=s&&"ia5"!=s)return"mixed";if("ia5"==s){if("CN"!=u)return"mixed";if(ue.lang.String.isMail(l))continue;return"mixed"}if("C"==u){if("prn"==s)continue;return"mixed"}if(null==t)t=s;else if(t!==s)return"mixed"}return null==t?"prn":t},this.getX500Name=function(e){var t=this.getX500NameArray(e);return{array:t,str:this.dnarraytostr(t)}},this.getX500NameArray=function(e){for(var t=[],n=r(e,0),o=0;o<n.length;o++)t.push(this.getRDN(i(e,n[o])));return t},this.getRDN=function(e){for(var t=[],n=r(e,0),o=0;o<n.length;o++)t.push(this.getAttrTypeAndValue(i(e,n[o])));return t},this.getAttrTypeAndValue=function(e){var t={type:null,value:null,ds:null},n=r(e,0),o=a(e,n[0],[],"06"),i=a(e,n[1],[]),s=ue.asn1.ASN1Util.oidHexToInt(o);return t.type=ue.asn1.x509.OID.oid2atype(s),t.value=xe(i),t.ds=this.HEX2STAG[e.substr(n[1],2)],t},this.readCertPEM=function(e){this.readCertHex(g(e))},this.readCertHex=function(e){this.hex=e,this.getVersion();try{c(this.hex,0,[0,7],"a3"),this.parseExt()}catch(e){}},this.getParam=function(){var e={};return e.version=this.getVersion(),e.serial={hex:this.getSerialNumberHex()},e.sigalg=this.getSignatureAlgorithmField(),e.issuer=this.getIssuer(),e.notbefore=this.getNotBefore(),e.notafter=this.getNotAfter(),e.subject=this.getSubject(),e.sbjpubkey=Oe(this.getPublicKeyHex(),"PUBLIC KEY"),this.aExtInfo.length>0&&(e.ext=this.getExtParamArray()),e.sighex=this.getSignatureValueHex(),e},this.getExtParamArray=function(e){null==e&&-1!=d(this.hex,0,[0,"[3]"])&&(e=u(this.hex,0,[0,"[3]",0],"30"));for(var t=[],n=r(e,0),o=0;o<n.length;o++){var a=i(e,n[o]),s=this.getExtParam(a);null!=s&&t.push(s)}return t},this.getExtParam=function(e){var t=r(e,0).length;if(2!=t&&3!=t)throw new Error("wrong number elements in Extension: "+t+" "+e);var n=h(a(e,0,[0],"06")),o=!1;3==t&&"0101ff"==l(e,0,[1])&&(o=!0);var i=l(e,0,[t-1,0]),s=void 0;if("2.5.29.14"==n?s=this.getExtSubjectKeyIdentifier(i,o):"2.5.29.15"==n?s=this.getExtKeyUsage(i,o):"2.5.29.17"==n?s=this.getExtSubjectAltName(i,o):"2.5.29.18"==n?s=this.getExtIssuerAltName(i,o):"2.5.29.19"==n?s=this.getExtBasicConstraints(i,o):"2.5.29.31"==n?s=this.getExtCRLDistributionPoints(i,o):"2.5.29.32"==n?s=this.getExtCertificatePolicies(i,o):"2.5.29.35"==n?s=this.getExtAuthorityKeyIdentifier(i,o):"2.5.29.37"==n?s=this.getExtExtKeyUsage(i,o):"1.3.6.1.5.5.7.1.1"==n?s=this.getExtAuthorityInfoAccess(i,o):"2.5.29.20"==n?s=this.getExtCRLNumber(i,o):"2.5.29.21"==n?s=this.getExtCRLReason(i,o):"1.3.6.1.5.5.7.48.1.2"==n?s=this.getExtOcspNonce(i,o):"1.3.6.1.5.5.7.48.1.5"==n?s=this.getExtOcspNoCheck(i,o):"1.2.840.113583.1.1.9.1"==n&&(s=this.getExtAdobeTimeStamp(i,o)),null!=s)return s;var u={extname:n,extn:i};return o&&(u.critical=!0),u},this.findExt=function(e,t){for(var n=0;n<e.length;n++)if(e[n].extname==t)return e[n];return null},this.updateExtCDPFullURI=function(e,t){var n=this.findExt(e,"cRLDistributionPoints");if(null!=n&&null!=n.array)for(var r=n.array,o=0;o<r.length;o++)if(null!=r[o].dpname&&null!=r[o].dpname.full)for(var i=r[o].dpname.full,a=0;a<i.length;a++){var s=i[o];null!=s.uri&&(s.uri=t)}},this.updateExtAIAOCSP=function(e,t){var n=this.findExt(e,"authorityInfoAccess");if(null!=n&&null!=n.array)for(var r=n.array,o=0;o<r.length;o++)null!=r[o].ocsp&&(r[o].ocsp=t)},this.updateExtAIACAIssuer=function(e,t){var n=this.findExt(e,"authorityInfoAccess");if(null!=n&&null!=n.array)for(var r=n.array,o=0;o<r.length;o++)null!=r[o].caissuer&&(r[o].caissuer=t)},this.dnarraytostr=function(e){return"/"+e.map((function(e){return function(e){return e.map((function(e){return function(e){return e.type+"="+e.value}(e)})).join("+")}(e)})).join("/")},this.getInfo=function(){var e,t,n,r=function(e){return JSON.stringify(e.array).replace(/[\[\]\{\}\"]/g,"")},o=function(e){for(var t="",n=e.array,r=0;r<n.length;r++){var o=n[r];if(t+="    policy oid: "+o.policyoid+"\n",void 0!==o.array)for(var i=0;i<o.array.length;i++){var a=o.array[i];void 0!==a.cps&&(t+="    cps: "+a.cps+"\n")}}return t},i=function(e){for(var t="",n=e.array,r=0;r<n.length;r++){var o=n[r];try{void 0!==o.dpname.full[0].uri&&(t+="    "+o.dpname.full[0].uri+"\n")}catch(e){}try{void 0!==o.dname.full[0].dn.hex&&(t+="    "+qe.hex2dn(o.dpname.full[0].dn.hex)+"\n")}catch(e){}}return t},a=function(e){for(var t="",n=e.array,r=0;r<n.length;r++){var o=n[r];void 0!==o.caissuer&&(t+="    caissuer: "+o.caissuer+"\n"),void 0!==o.ocsp&&(t+="    ocsp: "+o.ocsp+"\n")}return t};if(e="Basic Fields\n",e+="  serial number: "+this.getSerialNumberHex()+"\n",e+="  signature algorithm: "+this.getSignatureAlgorithmField()+"\n",e+="  issuer: "+this.getIssuerString()+"\n",e+="  notBefore: "+this.getNotBefore()+"\n",e+="  notAfter: "+this.getNotAfter()+"\n",e+="  subject: "+this.getSubjectString()+"\n",e+="  subject public key info: \n",e+="    key algorithm: "+(t=this.getPublicKey()).type+"\n","RSA"===t.type&&(e+="    n="+Be(t.n.toString(16)).substr(0,16)+"...\n",e+="    e="+Be(t.e.toString(16))+"\n"),null!=(n=this.aExtInfo)){e+="X509v3 Extensions:\n";for(var s=0;s<n.length;s++){var l=n[s],u=ue.asn1.x509.OID.oid2name(l.oid);""===u&&(u=l.oid);var c="";if(!0===l.critical&&(c="CRITICAL"),e+="  "+u+" "+c+":\n","basicConstraints"===u){var d=this.getExtBasicConstraints();void 0===d.cA?e+="    {}\n":(e+="    cA=true",void 0!==d.pathLen&&(e+=", pathLen="+d.pathLen),e+="\n")}else if("keyUsage"===u)e+="    "+this.getExtKeyUsageString()+"\n";else if("subjectKeyIdentifier"===u)e+="    "+this.getExtSubjectKeyIdentifier().kid.hex+"\n";else if("authorityKeyIdentifier"===u){var f=this.getExtAuthorityKeyIdentifier();void 0!==f.kid&&(e+="    kid="+f.kid.hex+"\n")}else"extKeyUsage"===u?e+="    "+this.getExtExtKeyUsage().array.join(", ")+"\n":"subjectAltName"===u?e+="    "+r(this.getExtSubjectAltName())+"\n":"cRLDistributionPoints"===u?e+=i(this.getExtCRLDistributionPoints()):"authorityInfoAccess"===u?e+=a(this.getExtAuthorityInfoAccess()):"certificatePolicies"===u&&(e+=o(this.getExtCertificatePolicies()))}}return(e+="signature algorithm: "+this.getSignatureAlgorithmName()+"\n")+"signature: "+this.getSignatureValueHex().substr(0,16)+"...\n"},"string"==typeof e&&(-1!=e.indexOf("-----BEGIN")?this.readCertPEM(e):ue.lang.String.isHex(e)&&this.readCertHex(e))}oe.prototype.sign=function(e,t){var n=function(e){return ue.crypto.Util.hashString(e,t)}(e);return this.signWithMessageHash(n,t)},oe.prototype.signWithMessageHash=function(e,t){var n=ne(ue.crypto.Util.getPaddedDigestInfoHex(e,t,this.n.bitLength()),16);return He(this.doPrivate(n).toString(16),this.n.bitLength())},oe.prototype.signPSS=function(e,t,n){var r=function(e){return ue.crypto.Util.hashHex(e,t)}(Ae(e));return void 0===n&&(n=-1),this.signWithMessageHashPSS(r,t,n)},oe.prototype.signWithMessageHashPSS=function(e,t,n){var r,o=xe(e),i=o.length,a=this.n.bitLength()-1,s=Math.ceil(a/8),l=function(e){return ue.crypto.Util.hashHex(e,t)};if(-1===n||void 0===n)n=i;else if(-2===n)n=s-i-2;else if(n<-2)throw new Error("invalid salt length");if(s<i+n+2)throw new Error("data too long");var u="";n>0&&(u=new Array(n),(new te).nextBytes(u),u=String.fromCharCode.apply(String,u));var c=xe(l(Ae("\0\0\0\0\0\0\0\0"+o+u))),d=[];for(r=0;r<s-n-i-2;r+=1)d[r]=0;var f=String.fromCharCode.apply(String,d)+""+u,p=We(c,f.length,l),h=[];for(r=0;r<f.length;r+=1)h[r]=f.charCodeAt(r)^p.charCodeAt(r);var m=65280>>8*s-a&255;for(h[0]&=~m,r=0;r<i;r++)h.push(c.charCodeAt(r));return h.push(188),He(this.doPrivate(new x(h)).toString(16),this.n.bitLength())},oe.prototype.verify=function(e,t){var n=ne(t=(t=t.replace($e,"")).replace(/[ \n]+/g,""),16);if(n.bitLength()>this.n.bitLength())return 0;var r=Ve(this.doPublic(n).toString(16).replace(/^1f+00/,""));if(0==r.length)return!1;var o=r[0];return r[1]==function(e){return ue.crypto.Util.hashString(e,o)}(e)},oe.prototype.verifyWithMessageHash=function(e,t){if(t.length!=Math.ceil(this.n.bitLength()/4))return!1;var n=ne(t,16);if(n.bitLength()>this.n.bitLength())return 0;var r=Ve(this.doPublic(n).toString(16).replace(/^1f+00/,""));return 0!=r.length&&(r[0],r[1]==e)},oe.prototype.verifyPSS=function(e,t,n,r){var o=function(e){return ue.crypto.Util.hashHex(e,n)}(Ae(e));return void 0===r&&(r=-1),this.verifyWithMessageHashPSS(o,t,n,r)},oe.prototype.verifyWithMessageHashPSS=function(e,t,n,r){if(t.length!=Math.ceil(this.n.bitLength()/4))return!1;var o,i=new x(t,16),a=function(e){return ue.crypto.Util.hashHex(e,n)},s=xe(e),l=s.length,u=this.n.bitLength()-1,c=Math.ceil(u/8);if(-1===r||void 0===r)r=l;else if(-2===r)r=c-l-2;else if(r<-2)throw new Error("invalid salt length");if(c<l+r+2)throw new Error("data too long");var d=this.doPublic(i).toByteArray();for(o=0;o<d.length;o+=1)d[o]&=255;for(;d.length<c;)d.unshift(0);if(188!==d[c-1])throw new Error("encoded message does not end in 0xbc");var f=(d=String.fromCharCode.apply(String,d)).substr(0,c-l-1),p=d.substr(f.length,l),h=65280>>8*c-u&255;if(f.charCodeAt(0)&h)throw new Error("bits beyond keysize not zero");var m=We(p,f.length,a),g=[];for(o=0;o<f.length;o+=1)g[o]=f.charCodeAt(o)^m.charCodeAt(o);g[0]&=~h;var v=c-l-r-2;for(o=0;o<v;o+=1)if(0!==g[o])throw new Error("leftmost octets not zero");if(1!==g[v])throw new Error("0x01 marker not found");return p===xe(a(Ae("\0\0\0\0\0\0\0\0"+s+String.fromCharCode.apply(String,g.slice(-r)))))},oe.SALT_LEN_HLEN=-1,oe.SALT_LEN_MAX=-2,oe.SALT_LEN_RECOVER=-2,qe.hex2dn=function(e,t){if(void 0===t&&(t=0),"30"!==e.substr(t,2))throw new Error("malformed DN");for(var n=new Array,r=fe.getChildIdx(e,t),o=0;o<r.length;o++)n.push(qe.hex2rdn(e,r[o]));return"/"+(n=n.map((function(e){return e.replace("/","\\/")}))).join("/")},qe.hex2rdn=function(e,t){if(void 0===t&&(t=0),"31"!==e.substr(t,2))throw new Error("malformed RDN");for(var n=new Array,r=fe.getChildIdx(e,t),o=0;o<r.length;o++)n.push(qe.hex2attrTypeValue(e,r[o]));return(n=n.map((function(e){return e.replace("+","\\+")}))).join("+")},qe.hex2attrTypeValue=function(e,t){var n=fe,r=n.getV;if(void 0===t&&(t=0),"30"!==e.substr(t,2))throw new Error("malformed attribute type and value");var o=n.getChildIdx(e,t);2!==o.length||e.substr(o[0],2);var i=r(e,o[0]),a=ue.asn1.ASN1Util.oidHexToInt(i);return ue.asn1.x509.OID.oid2atype(a)+"="+xe(r(e,o[1]))},qe.getPublicKeyFromCertHex=function(e){var t=new qe;return t.readCertHex(e),t.getPublicKey()},qe.getPublicKeyFromCertPEM=function(e){var t=new qe;return t.readCertPEM(e),t.getPublicKey()},qe.getPublicKeyInfoPropOfCertPEM=function(e){var t,n,r=fe.getVbyList,o={algparam:null};return(t=new qe).readCertPEM(e),n=t.getPublicKeyHex(),o.keyhex=r(n,0,[1],"03").substr(2),o.algoid=r(n,0,[0,0],"06"),"2a8648ce3d0201"===o.algoid&&(o.algparam=r(n,0,[0,1],"06")),o},qe.KEYUSAGE_NAME=["digitalSignature","nonRepudiation","keyEncipherment","dataEncipherment","keyAgreement","keyCertSign","cRLSign","encipherOnly","decipherOnly"],void 0!==ue&&ue||(t.KJUR=ue={}),void 0!==ue.jws&&ue.jws||(ue.jws={}),ue.jws.JWS=function(){var e=ue.jws.JWS.isSafeJSONString;this.parseJWS=function(t,n){if(void 0===this.parsedJWS||!n&&void 0===this.parsedJWS.sigvalH){var r=t.match(/^([^.]+)\.([^.]+)\.([^.]+)$/);if(null==r)throw"JWS signature is not a form of 'Head.Payload.SigValue'.";var o=r[1],i=r[2],a=r[3],s=o+"."+i;if(this.parsedJWS={},this.parsedJWS.headB64U=o,this.parsedJWS.payloadB64U=i,this.parsedJWS.sigvalB64U=a,this.parsedJWS.si=s,!n){var l=we(a),u=ne(l,16);this.parsedJWS.sigvalH=l,this.parsedJWS.sigvalBI=u}var c=de(o),d=de(i);if(this.parsedJWS.headS=c,this.parsedJWS.payloadS=d,!e(c,this.parsedJWS,"headP"))throw"malformed JSON string for JWS Head: "+c}}},ue.jws.JWS.sign=function(e,t,r,o,i){var a,s,l,u=ue,c=u.jws.JWS,d=c.readSafeJSONString,f=c.isSafeJSONString,p=u.crypto,h=(p.ECDSA,p.Mac),m=p.Signature,g=JSON;if("string"!=typeof t&&"object"!=(void 0===t?"undefined":n(t)))throw"spHeader must be JSON string or object: "+t;if("object"==(void 0===t?"undefined":n(t))&&(s=t,a=g.stringify(s)),"string"==typeof t){if(!f(a=t))throw"JWS Head is not safe JSON string: "+a;s=d(a)}if(l=r,"object"==(void 0===r?"undefined":n(r))&&(l=g.stringify(r)),""!=e&&null!=e||void 0===s.alg||(e=s.alg),""!=e&&null!=e&&void 0===s.alg&&(s.alg=e,a=g.stringify(s)),e!==s.alg)throw"alg and sHeader.alg doesn't match: "+e+"!="+s.alg;var v=null;if(void 0===c.jwsalg2sigalg[e])throw"unsupported alg name: "+e;v=c.jwsalg2sigalg[e];var y=ce(a)+"."+ce(l),b="";if("Hmac"==v.substr(0,4)){if(void 0===o)throw"mac key shall be specified for HS* alg";var w=new h({alg:v,prov:"cryptojs",pass:o});w.updateString(y),b=w.doFinal()}else if(-1!=v.indexOf("withECDSA")){(S=new m({alg:v})).init(o,i),S.updateString(y);var E=S.sign();b=ue.crypto.ECDSA.asn1SigToConcatSig(E)}else{var S;"none"!=v&&((S=new m({alg:v})).init(o,i),S.updateString(y),b=S.sign())}return y+"."+be(b)},ue.jws.JWS.verify=function(e,t,r){var o,i=ue,a=i.jws.JWS,s=a.readSafeJSONString,l=i.crypto,u=l.ECDSA,c=l.Mac,d=l.Signature;void 0!==n(oe)&&(o=oe);var f=e.split(".");if(3!==f.length)return!1;var p,h=f[0]+"."+f[1],m=we(f[2]),g=s(de(f[0])),v=null;if(void 0===g.alg)throw"algorithm not specified in header";if(p=(v=g.alg).substr(0,2),null!=r&&"[object Array]"===Object.prototype.toString.call(r)&&r.length>0&&-1==(":"+r.join(":")+":").indexOf(":"+v+":"))throw"algorithm '"+v+"' not accepted in the list";if("none"!=v&&null===t)throw"key shall be specified to verify.";if("string"==typeof t&&-1!=t.indexOf("-----BEGIN ")&&(t=ze.getKey(t)),!("RS"!=p&&"PS"!=p||t instanceof o))throw"key shall be a RSAKey obj for RS* and PS* algs";if("ES"==p&&!(t instanceof u))throw"key shall be a ECDSA obj for ES* algs";var y=null;if(void 0===a.jwsalg2sigalg[g.alg])throw"unsupported alg name: "+v;if("none"==(y=a.jwsalg2sigalg[v]))throw"not supported";if("Hmac"==y.substr(0,4)){if(void 0===t)throw"hexadecimal key shall be specified for HMAC";var b=new c({alg:y,pass:t});return b.updateString(h),m==b.doFinal()}if(-1!=y.indexOf("withECDSA")){var w,E=null;try{E=u.concatSigToASN1Sig(m)}catch(e){return!1}return(w=new d({alg:y})).init(t),w.updateString(h),w.verify(E)}return(w=new d({alg:y})).init(t),w.updateString(h),w.verify(m)},ue.jws.JWS.parse=function(e){var t,n,r,o=e.split("."),i={};if(2!=o.length&&3!=o.length)throw"malformed sJWS: wrong number of '.' splitted elements";return t=o[0],n=o[1],3==o.length&&(r=o[2]),i.headerObj=ue.jws.JWS.readSafeJSONString(de(t)),i.payloadObj=ue.jws.JWS.readSafeJSONString(de(n)),i.headerPP=JSON.stringify(i.headerObj,null,"  "),null==i.payloadObj?i.payloadPP=de(n):i.payloadPP=JSON.stringify(i.payloadObj,null,"  "),void 0!==r&&(i.sigHex=we(r)),i},ue.jws.JWS.verifyJWT=function(e,t,r){var o=ue.jws,i=o.JWS,a=i.readSafeJSONString,s=i.inArray,l=i.includedArray,u=e.split("."),c=u[0],d=u[1],f=(we(u[2]),a(de(c))),p=a(de(d));if(void 0===f.alg)return!1;if(void 0===r.alg)throw"acceptField.alg shall be specified";if(!s(f.alg,r.alg))return!1;if(void 0!==p.iss&&"object"===n(r.iss)&&!s(p.iss,r.iss))return!1;if(void 0!==p.sub&&"object"===n(r.sub)&&!s(p.sub,r.sub))return!1;if(void 0!==p.aud&&"object"===n(r.aud))if("string"==typeof p.aud){if(!s(p.aud,r.aud))return!1}else if("object"==n(p.aud)&&!l(p.aud,r.aud))return!1;var h=o.IntDate.getNow();return void 0!==r.verifyAt&&"number"==typeof r.verifyAt&&(h=r.verifyAt),void 0!==r.gracePeriod&&"number"==typeof r.gracePeriod||(r.gracePeriod=0),!(void 0!==p.exp&&"number"==typeof p.exp&&p.exp+r.gracePeriod<h||void 0!==p.nbf&&"number"==typeof p.nbf&&h<p.nbf-r.gracePeriod||void 0!==p.iat&&"number"==typeof p.iat&&h<p.iat-r.gracePeriod||void 0!==p.jti&&void 0!==r.jti&&p.jti!==r.jti||!i.verify(e,t,r.alg))},ue.jws.JWS.includedArray=function(e,t){var r=ue.jws.JWS.inArray;if(null===e)return!1;if("object"!==(void 0===e?"undefined":n(e)))return!1;if("number"!=typeof e.length)return!1;for(var o=0;o<e.length;o++)if(!r(e[o],t))return!1;return!0},ue.jws.JWS.inArray=function(e,t){if(null===t)return!1;if("object"!==(void 0===t?"undefined":n(t)))return!1;if("number"!=typeof t.length)return!1;for(var r=0;r<t.length;r++)if(t[r]==e)return!0;return!1},ue.jws.JWS.jwsalg2sigalg={HS256:"HmacSHA256",HS384:"HmacSHA384",HS512:"HmacSHA512",RS256:"SHA256withRSA",RS384:"SHA384withRSA",RS512:"SHA512withRSA",ES256:"SHA256withECDSA",ES384:"SHA384withECDSA",PS256:"SHA256withRSAandMGF1",PS384:"SHA384withRSAandMGF1",PS512:"SHA512withRSAandMGF1",none:"none"},ue.jws.JWS.isSafeJSONString=function(e,t,r){var o=null;try{return"object"!=(void 0===(o=le(e))?"undefined":n(o))||o.constructor===Array?0:(t&&(t[r]=o),1)}catch(e){return 0}},ue.jws.JWS.readSafeJSONString=function(e){var t=null;try{return"object"!=(void 0===(t=le(e))?"undefined":n(t))||t.constructor===Array?null:t}catch(e){return null}},ue.jws.JWS.getEncodedSignatureValueFromJWS=function(e){var t=e.match(/^[^.]+\.[^.]+\.([^.]+)$/);if(null==t)throw"JWS signature is not a form of 'Head.Payload.SigValue'.";return t[1]},ue.jws.JWS.getJWKthumbprint=function(e){if("RSA"!==e.kty&&"EC"!==e.kty&&"oct"!==e.kty)throw"unsupported algorithm for JWK Thumprint";var t="{";if("RSA"===e.kty){if("string"!=typeof e.n||"string"!=typeof e.e)throw"wrong n and e value for RSA key";t+='"e":"'+e.e+'",',t+='"kty":"'+e.kty+'",',t+='"n":"'+e.n+'"}'}else if("EC"===e.kty){if("string"!=typeof e.crv||"string"!=typeof e.x||"string"!=typeof e.y)throw"wrong crv, x and y value for EC key";t+='"crv":"'+e.crv+'",',t+='"kty":"'+e.kty+'",',t+='"x":"'+e.x+'",',t+='"y":"'+e.y+'"}'}else if("oct"===e.kty){if("string"!=typeof e.k)throw"wrong k value for oct(symmetric) key";t+='"kty":"'+e.kty+'",',t+='"k":"'+e.k+'"}'}var n=Ae(t);return be(ue.crypto.Util.hashHex(n,"sha256"))},ue.jws.IntDate={},ue.jws.IntDate.get=function(e){var t=ue.jws.IntDate,n=t.getNow,r=t.getZulu;if("now"==e)return n();if("now + 1hour"==e)return n()+3600;if("now + 1day"==e)return n()+86400;if("now + 1month"==e)return n()+2592e3;if("now + 1year"==e)return n()+31536e3;if(e.match(/Z$/))return r(e);if(e.match(/^[0-9]+$/))return parseInt(e);throw"unsupported format: "+e},ue.jws.IntDate.getZulu=function(e){return Te(e)},ue.jws.IntDate.getNow=function(){return~~(new Date/1e3)},ue.jws.IntDate.intDate2UTCString=function(e){return new Date(1e3*e).toUTCString()},ue.jws.IntDate.intDate2Zulu=function(e){var t=new Date(1e3*e);return("0000"+t.getUTCFullYear()).slice(-4)+("00"+(t.getUTCMonth()+1)).slice(-2)+("00"+t.getUTCDate()).slice(-2)+("00"+t.getUTCHours()).slice(-2)+("00"+t.getUTCMinutes()).slice(-2)+("00"+t.getUTCSeconds()).slice(-2)+"Z"},t.SecureRandom=te,t.rng_seed_time=J,t.BigInteger=x,t.RSAKey=oe;var Ke=ue.crypto.EDSA;t.EDSA=Ke;var Ge=ue.crypto.DSA;t.DSA=Ge;var Je=ue.crypto.Signature;t.Signature=Je;var Ye=ue.crypto.MessageDigest;t.MessageDigest=Ye;var Xe=ue.crypto.Mac;t.Mac=Xe;var Qe=ue.crypto.Cipher;t.Cipher=Qe,t.KEYUTIL=ze,t.ASN1HEX=fe,t.X509=qe,t.CryptoJS=v,t.b64tohex=E,t.b64toBA=S,t.stoBA=pe,t.BAtos=he,t.BAtohex=me,t.stohex=ge,t.stob64=function(e){return w(ge(e))},t.stob64u=function(e){return ve(w(ge(e)))},t.b64utos=function(e){return he(S(ye(e)))},t.b64tob64u=ve,t.b64utob64=ye,t.hex2b64=w,t.hextob64u=be,t.b64utohex=we,t.utf8tob64u=ce,t.b64utoutf8=de,t.utf8tob64=function(e){return w(Fe(Me(e)))},t.b64toutf8=function(e){return decodeURIComponent(je(E(e)))},t.utf8tohex=Ee,t.hextoutf8=Se,t.hextorstr=xe,t.rstrtohex=Ae,t.hextob64=ke,t.hextob64nl=_e,t.b64nltohex=Ce,t.hextopem=Oe,t.pemtohex=Pe,t.hextoArrayBuffer=function(e){if(e.length%2!=0)throw"input is not even length";if(null==e.match(/^[0-9A-Fa-f]+$/))throw"input is not hexadecimal";for(var t=new ArrayBuffer(e.length/2),n=new DataView(t),r=0;r<e.length/2;r++)n.setUint8(r,parseInt(e.substr(2*r,2),16));return t},t.ArrayBuffertohex=function(e){for(var t="",n=new DataView(e),r=0;r<e.byteLength;r++)t+=("00"+n.getUint8(r).toString(16)).slice(-2);return t},t.zulutomsec=Re,t.zulutosec=Te,t.zulutodate=function(e){return new Date(Re(e))},t.datetozulu=function(e,t,n){var r,o=e.getUTCFullYear();if(t){if(o<1950||2049<o)throw"not proper year for UTCTime: "+o;r=(""+o).slice(-2)}else r=("000"+o).slice(-4);if(r+=("0"+(e.getUTCMonth()+1)).slice(-2),r+=("0"+e.getUTCDate()).slice(-2),r+=("0"+e.getUTCHours()).slice(-2),r+=("0"+e.getUTCMinutes()).slice(-2),r+=("0"+e.getUTCSeconds()).slice(-2),n){var i=e.getUTCMilliseconds();0!==i&&(r+="."+(i=(i=("00"+i).slice(-3)).replace(/0+$/g,"")))}return r+"Z"},t.uricmptohex=Fe,t.hextouricmp=je,t.ipv6tohex=Ne,t.hextoipv6=Ie,t.hextoip=Le,t.iptohex=function(e){var t="malformed IP address";if(!(e=e.toLowerCase(e)).match(/^[0-9.]+$/)){if(e.match(/^[0-9a-f:]+$/)&&-1!==e.indexOf(":"))return Ne(e);throw t}var n=e.split(".");if(4!==n.length)throw t;var r="";try{for(var o=0;o<4;o++)r+=("0"+parseInt(n[o]).toString(16)).slice(-2);return r}catch(e){throw t}},t.encodeURIComponentAll=Me,t.newline_toUnix=function(e){return e.replace(/\r\n/gm,"\n")},t.newline_toDos=function(e){return(e=e.replace(/\r\n/gm,"\n")).replace(/\n/gm,"\r\n")},t.hextoposhex=Be,t.intarystrtohex=function(e){e=(e=(e=e.replace(/^\s*\[\s*/,"")).replace(/\s*\]\s*$/,"")).replace(/\s*/g,"");try{return e.split(/,/).map((function(e,t,n){var r=parseInt(e);if(r<0||255<r)throw"integer not in range 0-255";return("00"+r.toString(16)).slice(-2)})).join("")}catch(e){throw"malformed integer array string: "+e}},t.strdiffidx=function(e,t){var n=e.length;e.length>t.length&&(n=t.length);for(var r=0;r<n;r++)if(e.charCodeAt(r)!=t.charCodeAt(r))return r;return e.length!=t.length?n:-1},t.KJUR=ue;var Ze=ue.crypto;t.crypto=Ze;var et=ue.asn1;t.asn1=et;var tt=ue.jws;t.jws=tt;var nt=ue.lang;t.lang=nt}).call(this,n(28).Buffer)},function(e,t,n){"use strict";(function(e){var r=n(30),o=n(31),i=n(32);function a(){return l.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()<t)throw new RangeError("Invalid typed array length");return l.TYPED_ARRAY_SUPPORT?(e=new Uint8Array(t)).__proto__=l.prototype:(null===e&&(e=new l(t)),e.length=t),e}function l(e,t,n){if(!(l.TYPED_ARRAY_SUPPORT||this instanceof l))return new l(e,t,n);if("number"==typeof e){if("string"==typeof t)throw new Error("If encoding is specified then the first argument must be a string");return d(this,e)}return u(this,e,t,n)}function u(e,t,n,r){if("number"==typeof t)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer?function(e,t,n,r){if(t.byteLength,n<0||t.byteLength<n)throw new RangeError("'offset' is out of bounds");if(t.byteLength<n+(r||0))throw new RangeError("'length' is out of bounds");return t=void 0===n&&void 0===r?new Uint8Array(t):void 0===r?new Uint8Array(t,n):new Uint8Array(t,n,r),l.TYPED_ARRAY_SUPPORT?(e=t).__proto__=l.prototype:e=f(e,t),e}(e,t,n,r):"string"==typeof t?function(e,t,n){if("string"==typeof n&&""!==n||(n="utf8"),!l.isEncoding(n))throw new TypeError('"encoding" must be a valid string encoding');var r=0|h(t,n),o=(e=s(e,r)).write(t,n);return o!==r&&(e=e.slice(0,o)),e}(e,t,n):function(e,t){if(l.isBuffer(t)){var n=0|p(t.length);return 0===(e=s(e,n)).length||t.copy(e,0,0,n),e}if(t){if("undefined"!=typeof ArrayBuffer&&t.buffer instanceof ArrayBuffer||"length"in t)return"number"!=typeof t.length||function(e){return e!=e}(t.length)?s(e,0):f(e,t);if("Buffer"===t.type&&i(t.data))return f(e,t.data)}throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}(e,t)}function c(e){if("number"!=typeof e)throw new TypeError('"size" argument must be a number');if(e<0)throw new RangeError('"size" argument must not be negative')}function d(e,t){if(c(t),e=s(e,t<0?0:0|p(t)),!l.TYPED_ARRAY_SUPPORT)for(var n=0;n<t;++n)e[n]=0;return e}function f(e,t){var n=t.length<0?0:0|p(t.length);e=s(e,n);for(var r=0;r<n;r+=1)e[r]=255&t[r];return e}function p(e){if(e>=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function h(e,t){if(l.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return z(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return $(e).length;default:if(r)return z(e).length;t=(""+t).toLowerCase(),r=!0}}function m(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return R(this,t,n);case"utf8":case"utf-8":return _(this,t,n);case"ascii":return O(this,t,n);case"latin1":case"binary":return P(this,t,n);case"base64":return k(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function g(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function v(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=l.from(t,r)),l.isBuffer(t))return 0===t.length?-1:y(e,t,n,r,o);if("number"==typeof t)return t&=255,l.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):y(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function y(e,t,n,r,o){var i,a=1,s=e.length,l=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,l/=2,n/=2}function u(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var c=-1;for(i=n;i<s;i++)if(u(e,i)===u(t,-1===c?0:i-c)){if(-1===c&&(c=i),i-c+1===l)return c*a}else-1!==c&&(i-=i-c),c=-1}else for(n+l>s&&(n=s-l),i=n;i>=0;i--){for(var d=!0,f=0;f<l;f++)if(u(e,i+f)!==u(t,f)){d=!1;break}if(d)return i}return-1}function b(e,t,n,r){n=Number(n)||0;var o=e.length-n;r?(r=Number(r))>o&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a<r;++a){var s=parseInt(t.substr(2*a,2),16);if(isNaN(s))return a;e[n+a]=s}return a}function w(e,t,n,r){return H(z(t,e.length-n),e,n,r)}function E(e,t,n,r){return H(function(e){for(var t=[],n=0;n<e.length;++n)t.push(255&e.charCodeAt(n));return t}(t),e,n,r)}function S(e,t,n,r){return E(e,t,n,r)}function x(e,t,n,r){return H($(t),e,n,r)}function A(e,t,n,r){return H(function(e,t){for(var n,r,o,i=[],a=0;a<e.length&&!((t-=2)<0);++a)r=(n=e.charCodeAt(a))>>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function k(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function _(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o<n;){var i,a,s,l,u=e[o],c=null,d=u>239?4:u>223?3:u>191?2:1;if(o+d<=n)switch(d){case 1:u<128&&(c=u);break;case 2:128==(192&(i=e[o+1]))&&(l=(31&u)<<6|63&i)>127&&(c=l);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(l=(15&u)<<12|(63&i)<<6|63&a)>2047&&(l<55296||l>57343)&&(c=l);break;case 4:i=e[o+1],a=e[o+2],s=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(l=(15&u)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&l<1114112&&(c=l)}null===c?(c=65533,d=1):c>65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c),o+=d}return function(e){var t=e.length;if(t<=C)return String.fromCharCode.apply(String,e);for(var n="",r=0;r<t;)n+=String.fromCharCode.apply(String,e.slice(r,r+=C));return n}(r)}t.Buffer=l,t.SlowBuffer=function(e){return+e!=e&&(e=0),l.alloc(+e)},t.INSPECT_MAX_BYTES=50,l.TYPED_ARRAY_SUPPORT=void 0!==e.TYPED_ARRAY_SUPPORT?e.TYPED_ARRAY_SUPPORT:function(){try{var e=new Uint8Array(1);return e.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===e.foo()&&"function"==typeof e.subarray&&0===e.subarray(1,1).byteLength}catch(e){return!1}}(),t.kMaxLength=a(),l.poolSize=8192,l._augment=function(e){return e.__proto__=l.prototype,e},l.from=function(e,t,n){return u(null,e,t,n)},l.TYPED_ARRAY_SUPPORT&&(l.prototype.__proto__=Uint8Array.prototype,l.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&l[Symbol.species]===l&&Object.defineProperty(l,Symbol.species,{value:null,configurable:!0})),l.alloc=function(e,t,n){return function(e,t,n,r){return c(t),t<=0?s(e,t):void 0!==n?"string"==typeof r?s(e,t).fill(n,r):s(e,t).fill(n):s(e,t)}(null,e,t,n)},l.allocUnsafe=function(e){return d(null,e)},l.allocUnsafeSlow=function(e){return d(null,e)},l.isBuffer=function(e){return!(null==e||!e._isBuffer)},l.compare=function(e,t){if(!l.isBuffer(e)||!l.isBuffer(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var n=e.length,r=t.length,o=0,i=Math.min(n,r);o<i;++o)if(e[o]!==t[o]){n=e[o],r=t[o];break}return n<r?-1:r<n?1:0},l.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},l.concat=function(e,t){if(!i(e))throw new TypeError('"list" argument must be an Array of Buffers');if(0===e.length)return l.alloc(0);var n;if(void 0===t)for(t=0,n=0;n<e.length;++n)t+=e[n].length;var r=l.allocUnsafe(t),o=0;for(n=0;n<e.length;++n){var a=e[n];if(!l.isBuffer(a))throw new TypeError('"list" argument must be an Array of Buffers');a.copy(r,o),o+=a.length}return r},l.byteLength=h,l.prototype._isBuffer=!0,l.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;t<e;t+=2)g(this,t,t+1);return this},l.prototype.swap32=function(){var e=this.length;if(e%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var t=0;t<e;t+=4)g(this,t,t+3),g(this,t+1,t+2);return this},l.prototype.swap64=function(){var e=this.length;if(e%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var t=0;t<e;t+=8)g(this,t,t+7),g(this,t+1,t+6),g(this,t+2,t+5),g(this,t+3,t+4);return this},l.prototype.toString=function(){var e=0|this.length;return 0===e?"":0===arguments.length?_(this,0,e):m.apply(this,arguments)},l.prototype.equals=function(e){if(!l.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===l.compare(this,e)},l.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),"<Buffer "+e+">"},l.prototype.compare=function(e,t,n,r,o){if(!l.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),s=Math.min(i,a),u=this.slice(r,o),c=e.slice(t,n),d=0;d<s;++d)if(u[d]!==c[d]){i=u[d],a=c[d];break}return i<a?-1:a<i?1:0},l.prototype.includes=function(e,t,n){return-1!==this.indexOf(e,t,n)},l.prototype.indexOf=function(e,t,n){return v(this,e,t,n,!0)},l.prototype.lastIndexOf=function(e,t,n){return v(this,e,t,n,!1)},l.prototype.write=function(e,t,n,r){if(void 0===t)r="utf8",n=this.length,t=0;else if(void 0===n&&"string"==typeof t)r=t,n=this.length,t=0;else{if(!isFinite(t))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");t|=0,isFinite(n)?(n|=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}var o=this.length-t;if((void 0===n||n>o)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return E(this,e,t,n);case"latin1":case"binary":return S(this,e,t,n);case"base64":return x(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return A(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var C=4096;function O(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;o<n;++o)r+=String.fromCharCode(127&e[o]);return r}function P(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;o<n;++o)r+=String.fromCharCode(e[o]);return r}function R(e,t,n){var r=e.length;(!t||t<0)&&(t=0),(!n||n<0||n>r)&&(n=r);for(var o="",i=t;i<n;++i)o+=U(e[i]);return o}function T(e,t,n){for(var r=e.slice(t,n),o="",i=0;i<r.length;i+=2)o+=String.fromCharCode(r[i]+256*r[i+1]);return o}function F(e,t,n){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function j(e,t,n,r,o,i){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||t<i)throw new RangeError('"value" argument is out of bounds');if(n+r>e.length)throw new RangeError("Index out of range")}function N(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o<i;++o)e[n+o]=(t&255<<8*(r?o:1-o))>>>8*(r?o:1-o)}function I(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o<i;++o)e[n+o]=t>>>8*(r?o:3-o)&255}function L(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function M(e,t,n,r,i){return i||L(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function D(e,t,n,r,i){return i||L(e,0,n,8),o.write(e,t,n,r,52,8),n+8}l.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t<e&&(t=e),l.TYPED_ARRAY_SUPPORT)(n=this.subarray(e,t)).__proto__=l.prototype;else{var o=t-e;n=new l(o,void 0);for(var i=0;i<o;++i)n[i]=this[i+e]}return n},l.prototype.readUIntLE=function(e,t,n){e|=0,t|=0,n||F(e,t,this.length);for(var r=this[e],o=1,i=0;++i<t&&(o*=256);)r+=this[e+i]*o;return r},l.prototype.readUIntBE=function(e,t,n){e|=0,t|=0,n||F(e,t,this.length);for(var r=this[e+--t],o=1;t>0&&(o*=256);)r+=this[e+--t]*o;return r},l.prototype.readUInt8=function(e,t){return t||F(e,1,this.length),this[e]},l.prototype.readUInt16LE=function(e,t){return t||F(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUInt16BE=function(e,t){return t||F(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUInt32LE=function(e,t){return t||F(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUInt32BE=function(e,t){return t||F(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||F(e,t,this.length);for(var r=this[e],o=1,i=0;++i<t&&(o*=256);)r+=this[e+i]*o;return r>=(o*=128)&&(r-=Math.pow(2,8*t)),r},l.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||F(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},l.prototype.readInt8=function(e,t){return t||F(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){t||F(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(e,t){t||F(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(e,t){return t||F(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return t||F(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readFloatLE=function(e,t){return t||F(e,4,this.length),o.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return t||F(e,4,this.length),o.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return t||F(e,8,this.length),o.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return t||F(e,8,this.length),o.read(this,e,!1,52,8)},l.prototype.writeUIntLE=function(e,t,n,r){e=+e,t|=0,n|=0,r||j(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i<n&&(o*=256);)this[t+i]=e/o&255;return t+n},l.prototype.writeUIntBE=function(e,t,n,r){e=+e,t|=0,n|=0,r||j(this,e,t,n,Math.pow(2,8*n)-1,0);var o=n-1,i=1;for(this[t+o]=255&e;--o>=0&&(i*=256);)this[t+o]=e/i&255;return t+n},l.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,1,255,0),l.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},l.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,65535,0),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},l.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,65535,0),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},l.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,4294967295,0),l.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):I(this,e,t,!0),t+4},l.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,4294967295,0),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):I(this,e,t,!1),t+4},l.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);j(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i<n&&(a*=256);)e<0&&0===s&&0!==this[t+i-1]&&(s=1),this[t+i]=(e/a|0)-s&255;return t+n},l.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);j(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a|0)-s&255;return t+n},l.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,1,127,-128),l.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,32767,-32768),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):N(this,e,t,!0),t+2},l.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,2,32767,-32768),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):N(this,e,t,!1),t+2},l.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,2147483647,-2147483648),l.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):I(this,e,t,!0),t+4},l.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||j(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),l.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):I(this,e,t,!1),t+4},l.prototype.writeFloatLE=function(e,t,n){return M(this,e,t,!0,n)},l.prototype.writeFloatBE=function(e,t,n){return M(this,e,t,!1,n)},l.prototype.writeDoubleLE=function(e,t,n){return D(this,e,t,!0,n)},l.prototype.writeDoubleBE=function(e,t,n){return D(this,e,t,!1,n)},l.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r<n&&(r=n),r===n)return 0;if(0===e.length||0===this.length)return 0;if(t<0)throw new RangeError("targetStart out of bounds");if(n<0||n>=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t<r-n&&(r=e.length-t+n);var o,i=r-n;if(this===e&&n<t&&t<r)for(o=i-1;o>=0;--o)e[o+t]=this[o+n];else if(i<1e3||!l.TYPED_ARRAY_SUPPORT)for(o=0;o<i;++o)e[o+t]=this[o+n];else Uint8Array.prototype.set.call(e,this.subarray(n,n+i),t);return i},l.prototype.fill=function(e,t,n,r){if("string"==typeof e){if("string"==typeof t?(r=t,t=0,n=this.length):"string"==typeof n&&(r=n,n=this.length),1===e.length){var o=e.charCodeAt(0);o<256&&(e=o)}if(void 0!==r&&"string"!=typeof r)throw new TypeError("encoding must be a string");if("string"==typeof r&&!l.isEncoding(r))throw new TypeError("Unknown encoding: "+r)}else"number"==typeof e&&(e&=255);if(t<0||this.length<t||this.length<n)throw new RangeError("Out of range index");if(n<=t)return this;var i;if(t>>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i<n;++i)this[i]=e;else{var a=l.isBuffer(e)?e:z(new l(e,r).toString()),s=a.length;for(i=0;i<n-t;++i)this[i+t]=a[i%s]}return this};var B=/[^+\/0-9A-Za-z-_]/g;function U(e){return e<16?"0"+e.toString(16):e.toString(16)}function z(e,t){var n;t=t||1/0;for(var r=e.length,o=null,i=[],a=0;a<r;++a){if((n=e.charCodeAt(a))>55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function $(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(B,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function H(e,t,n,r){for(var o=0;o<r&&!(o+n>=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(29))},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";t.byteLength=function(e){var t=l(e),n=t[0],r=t[1];return 3*(n+r)/4-r},t.toByteArray=function(e){var t,n,r=l(e),a=r[0],s=r[1],u=new i(function(e,t,n){return 3*(t+n)/4-n}(0,a,s)),c=0,d=s>0?a-4:a;for(n=0;n<d;n+=4)t=o[e.charCodeAt(n)]<<18|o[e.charCodeAt(n+1)]<<12|o[e.charCodeAt(n+2)]<<6|o[e.charCodeAt(n+3)],u[c++]=t>>16&255,u[c++]=t>>8&255,u[c++]=255&t;return 2===s&&(t=o[e.charCodeAt(n)]<<2|o[e.charCodeAt(n+1)]>>4,u[c++]=255&t),1===s&&(t=o[e.charCodeAt(n)]<<10|o[e.charCodeAt(n+1)]<<4|o[e.charCodeAt(n+2)]>>2,u[c++]=t>>8&255,u[c++]=255&t),u},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=16383,s=0,l=n-o;s<l;s+=a)i.push(u(e,s,s+a>l?l:s+a));return 1===o?(t=e[n-1],i.push(r[t>>2]+r[t<<4&63]+"==")):2===o&&(t=(e[n-2]<<8)+e[n-1],i.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"=")),i.join("")};for(var r=[],o=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0;s<64;++s)r[s]=a[s],o[a.charCodeAt(s)]=s;function l(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function u(e,t,n){for(var o,i,a=[],s=t;s<n;s+=3)o=(e[s]<<16&16711680)+(e[s+1]<<8&65280)+(255&e[s+2]),a.push(r[(i=o)>>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return a.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,n,r,o){var i,a,s=8*o-r-1,l=(1<<s)-1,u=l>>1,c=-7,d=n?o-1:0,f=n?-1:1,p=e[t+d];for(d+=f,i=p&(1<<-c)-1,p>>=-c,c+=s;c>0;i=256*i+e[t+d],d+=f,c-=8);for(a=i&(1<<-c)-1,i>>=-c,c+=r;c>0;a=256*a+e[t+d],d+=f,c-=8);if(0===i)i=1-u;else{if(i===l)return a?NaN:1/0*(p?-1:1);a+=Math.pow(2,r),i-=u}return(p?-1:1)*a*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var a,s,l,u=8*i-o-1,c=(1<<u)-1,d=c>>1,f=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:i-1,h=r?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=c):(a=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-a))<1&&(a--,l*=2),(t+=a+d>=1?f/l:f*Math.pow(2,1-d))*l>=2&&(a++,l/=2),a+d>=c?(s=0,a=c):a+d>=1?(s=(t*l-1)*Math.pow(2,o),a+=d):(s=t*Math.pow(2,d-1)*Math.pow(2,o),a=0));o>=8;e[n+p]=255&s,p+=h,s/=256,o-=8);for(a=a<<o|s,u+=o;u>0;e[n+p]=255&a,p+=h,a/=256,u-=8);e[n+p-h]|=128*m}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=e.jws,n=e.KeyUtil,o=e.X509,i=e.crypto,a=e.hextob64u,s=e.b64tohex,l=e.AllowedSigningAlgs;return function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.parseJwt=function e(n){r.Log.debug("JoseUtil.parseJwt");try{var o=t.JWS.parse(n);return{header:o.headerObj,payload:o.payloadObj}}catch(e){r.Log.error(e)}},e.validateJwt=function(t,i,a,l,u,c,d){r.Log.debug("JoseUtil.validateJwt");try{if("RSA"===i.kty)if(i.e&&i.n)i=n.getKey(i);else{if(!i.x5c||!i.x5c.length)return r.Log.error("JoseUtil.validateJwt: RSA key missing key material",i),Promise.reject(new Error("RSA key missing key material"));var f=s(i.x5c[0]);i=o.getPublicKeyFromCertHex(f)}else{if("EC"!==i.kty)return r.Log.error("JoseUtil.validateJwt: Unsupported key type",i&&i.kty),Promise.reject(new Error(i.kty));if(!(i.crv&&i.x&&i.y))return r.Log.error("JoseUtil.validateJwt: EC key missing key material",i),Promise.reject(new Error("EC key missing key material"));i=n.getKey(i)}return e._validateJwt(t,i,a,l,u,c,d)}catch(e){return r.Log.error(e&&e.message||e),Promise.reject("JWT validation failed")}},e.validateJwtAttributes=function(t,n,o,i,a,s){i||(i=0),a||(a=parseInt(Date.now()/1e3));var l=e.parseJwt(t).payload;if(!l.iss)return r.Log.error("JoseUtil._validateJwt: issuer was not provided"),Promise.reject(new Error("issuer was not provided"));if(l.iss!==n)return r.Log.error("JoseUtil._validateJwt: Invalid issuer in token",l.iss),Promise.reject(new Error("Invalid issuer in token: "+l.iss));if(!l.aud)return r.Log.error("JoseUtil._validateJwt: aud was not provided"),Promise.reject(new Error("aud was not provided"));if(!(l.aud===o||Array.isArray(l.aud)&&l.aud.indexOf(o)>=0))return r.Log.error("JoseUtil._validateJwt: Invalid audience in token",l.aud),Promise.reject(new Error("Invalid audience in token: "+l.aud));if(l.azp&&l.azp!==o)return r.Log.error("JoseUtil._validateJwt: Invalid azp in token",l.azp),Promise.reject(new Error("Invalid azp in token: "+l.azp));if(!s){var u=a+i,c=a-i;if(!l.iat)return r.Log.error("JoseUtil._validateJwt: iat was not provided"),Promise.reject(new Error("iat was not provided"));if(u<l.iat)return r.Log.error("JoseUtil._validateJwt: iat is in the future",l.iat),Promise.reject(new Error("iat is in the future: "+l.iat));if(l.nbf&&u<l.nbf)return r.Log.error("JoseUtil._validateJwt: nbf is in the future",l.nbf),Promise.reject(new Error("nbf is in the future: "+l.nbf));if(!l.exp)return r.Log.error("JoseUtil._validateJwt: exp was not provided"),Promise.reject(new Error("exp was not provided"));if(l.exp<c)return r.Log.error("JoseUtil._validateJwt: exp is in the past",l.exp),Promise.reject(new Error("exp is in the past:"+l.exp))}return Promise.resolve(l)},e._validateJwt=function(n,o,i,a,s,u,c){return e.validateJwtAttributes(n,i,a,s,u,c).then((function(e){try{return t.JWS.verify(n,o,l)?e:(r.Log.error("JoseUtil._validateJwt: signature validation failed"),Promise.reject(new Error("signature validation failed")))}catch(e){return r.Log.error(e&&e.message||e),Promise.reject(new Error("signature validation failed"))}}))},e.hashString=function e(t,n){try{return i.Util.hashString(t,n)}catch(e){r.Log.error(e)}},e.hexToBase64Url=function e(t){try{return a(t)}catch(e){r.Log.error(e)}},e}()};var r=n(0);e.exports=t.default},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SigninResponse=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(3);t.SigninResponse=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"#";!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e);var r=o.UrlUtility.parseUrlFragment(t,n);this.error=r.error,this.error_description=r.error_description,this.error_uri=r.error_uri,this.code=r.code,this.state=r.state,this.id_token=r.id_token,this.session_state=r.session_state,this.access_token=r.access_token,this.token_type=r.token_type,this.scope=r.scope,this.profile=void 0,this.expires_in=r.expires_in}return r(e,[{key:"expires_in",get:function(){if(this.expires_at){var e=parseInt(Date.now()/1e3);return this.expires_at-e}},set:function(e){var t=parseInt(e);if("number"==typeof t&&t>0){var n=parseInt(Date.now()/1e3);this.expires_at=n+t}}},{key:"expired",get:function(){var e=this.expires_in;if(void 0!==e)return e<=0}},{key:"scopes",get:function(){return(this.scope||"").split(" ")}},{key:"isOpenIdConnect",get:function(){return this.scopes.indexOf("openid")>=0||!!this.id_token}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SignoutRequest=void 0;var r=n(0),o=n(3),i=n(9);t.SignoutRequest=function e(t){var n=t.url,a=t.id_token_hint,s=t.post_logout_redirect_uri,l=t.data,u=t.extraQueryParams,c=t.request_type;if(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),!n)throw r.Log.error("SignoutRequest.ctor: No url passed"),new Error("url");for(var d in a&&(n=o.UrlUtility.addQueryParam(n,"id_token_hint",a)),s&&(n=o.UrlUtility.addQueryParam(n,"post_logout_redirect_uri",s),l&&(this.state=new i.State({data:l,request_type:c}),n=o.UrlUtility.addQueryParam(n,"state",this.state.id))),u)n=o.UrlUtility.addQueryParam(n,d,u[d]);this.url=n}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SignoutResponse=void 0;var r=n(3);t.SignoutResponse=function e(t){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e);var n=r.UrlUtility.parseUrlFragment(t,"?");this.error=n.error,this.error_description=n.error_description,this.error_uri=n.error_uri,this.state=n.state}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InMemoryWebStorage=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0);t.InMemoryWebStorage=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._data={}}return e.prototype.getItem=function(e){return o.Log.debug("InMemoryWebStorage.getItem",e),this._data[e]},e.prototype.setItem=function(e,t){o.Log.debug("InMemoryWebStorage.setItem",e),this._data[e]=t},e.prototype.removeItem=function(e){o.Log.debug("InMemoryWebStorage.removeItem",e),delete this._data[e]},e.prototype.key=function(e){return Object.getOwnPropertyNames(this._data)[e]},r(e,[{key:"length",get:function(){return Object.getOwnPropertyNames(this._data).length}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.UserManager=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(10),a=n(39),s=n(15),l=n(45),u=n(47),c=n(18),d=n(8),f=n(20),p=n(11),h=n(4);t.UserManager=function(e){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:u.SilentRenewService,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:c.SessionMonitor,s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:f.TokenRevocationClient,d=arguments.length>4&&void 0!==arguments[4]?arguments[4]:p.TokenClient,m=arguments.length>5&&void 0!==arguments[5]?arguments[5]:h.JoseUtil;(function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this,t),n instanceof a.UserManagerSettings||(n=new a.UserManagerSettings(n));var g=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,n));return g._events=new l.UserManagerEvents(n),g._silentRenewService=new r(g),g.settings.automaticSilentRenew&&(o.Log.debug("UserManager.ctor: automaticSilentRenew is configured, setting up silent renew"),g.startSilentRenew()),g.settings.monitorSession&&(o.Log.debug("UserManager.ctor: monitorSession is configured, setting up session monitor"),g._sessionMonitor=new i(g)),g._tokenRevocationClient=new s(g._settings),g._tokenClient=new d(g._settings),g._joseUtil=m,g}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.getUser=function(){var e=this;return this._loadUser().then((function(t){return t?(o.Log.info("UserManager.getUser: user loaded"),e._events.load(t,!1),t):(o.Log.info("UserManager.getUser: user not found in storage"),null)}))},t.prototype.removeUser=function(){var e=this;return this.storeUser(null).then((function(){o.Log.info("UserManager.removeUser: user removed from storage"),e._events.unload()}))},t.prototype.signinRedirect=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(e=Object.assign({},e)).request_type="si:r";var t={useReplaceToNavigate:e.useReplaceToNavigate};return this._signinStart(e,this._redirectNavigator,t).then((function(){o.Log.info("UserManager.signinRedirect: successful")}))},t.prototype.signinRedirectCallback=function(e){return this._signinEnd(e||this._redirectNavigator.url).then((function(e){return e.profile&&e.profile.sub?o.Log.info("UserManager.signinRedirectCallback: successful, signed in sub: ",e.profile.sub):o.Log.info("UserManager.signinRedirectCallback: no sub"),e}))},t.prototype.signinPopup=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(e=Object.assign({},e)).request_type="si:p";var t=e.redirect_uri||this.settings.popup_redirect_uri||this.settings.redirect_uri;return t?(e.redirect_uri=t,e.display="popup",this._signin(e,this._popupNavigator,{startUrl:t,popupWindowFeatures:e.popupWindowFeatures||this.settings.popupWindowFeatures,popupWindowTarget:e.popupWindowTarget||this.settings.popupWindowTarget}).then((function(e){return e&&(e.profile&&e.profile.sub?o.Log.info("UserManager.signinPopup: signinPopup successful, signed in sub: ",e.profile.sub):o.Log.info("UserManager.signinPopup: no sub")),e}))):(o.Log.error("UserManager.signinPopup: No popup_redirect_uri or redirect_uri configured"),Promise.reject(new Error("No popup_redirect_uri or redirect_uri configured")))},t.prototype.signinPopupCallback=function(e){return this._signinCallback(e,this._popupNavigator).then((function(e){return e&&(e.profile&&e.profile.sub?o.Log.info("UserManager.signinPopupCallback: successful, signed in sub: ",e.profile.sub):o.Log.info("UserManager.signinPopupCallback: no sub")),e})).catch((function(e){o.Log.error(e.message)}))},t.prototype.signinSilent=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return t=Object.assign({},t),this._loadUser().then((function(n){return n&&n.refresh_token?(t.refresh_token=n.refresh_token,e._useRefreshToken(t)):(t.request_type="si:s",t.id_token_hint=t.id_token_hint||e.settings.includeIdTokenInSilentRenew&&n&&n.id_token,n&&e._settings.validateSubOnSilentRenew&&(o.Log.debug("UserManager.signinSilent, subject prior to silent renew: ",n.profile.sub),t.current_sub=n.profile.sub),e._signinSilentIframe(t))}))},t.prototype._useRefreshToken=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return this._tokenClient.exchangeRefreshToken(t).then((function(t){return t?t.access_token?e._loadUser().then((function(n){if(n){var r=Promise.resolve();return t.id_token&&(r=e._validateIdTokenFromTokenRefreshToken(n.profile,t.id_token)),r.then((function(){return o.Log.debug("UserManager._useRefreshToken: refresh token response success"),n.id_token=t.id_token||n.id_token,n.access_token=t.access_token,n.refresh_token=t.refresh_token||n.refresh_token,n.expires_in=t.expires_in,e.storeUser(n).then((function(){return e._events.load(n),n}))}))}return null})):(o.Log.error("UserManager._useRefreshToken: No access token returned from token endpoint"),Promise.reject("No access token returned from token endpoint")):(o.Log.error("UserManager._useRefreshToken: No response returned from token endpoint"),Promise.reject("No response returned from token endpoint"))}))},t.prototype._validateIdTokenFromTokenRefreshToken=function(e,t){var n=this;return this._metadataService.getIssuer().then((function(r){return n.settings.getEpochTime().then((function(i){return n._joseUtil.validateJwtAttributes(t,r,n._settings.client_id,n._settings.clockSkew,i).then((function(t){return t?t.sub!==e.sub?(o.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: sub in id_token does not match current sub"),Promise.reject(new Error("sub in id_token does not match current sub"))):t.auth_time&&t.auth_time!==e.auth_time?(o.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: auth_time in id_token does not match original auth_time"),Promise.reject(new Error("auth_time in id_token does not match original auth_time"))):t.azp&&t.azp!==e.azp?(o.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: azp in id_token does not match original azp"),Promise.reject(new Error("azp in id_token does not match original azp"))):!t.azp&&e.azp?(o.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: azp not in id_token, but present in original id_token"),Promise.reject(new Error("azp not in id_token, but present in original id_token"))):void 0:(o.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: Failed to validate id_token"),Promise.reject(new Error("Failed to validate id_token")))}))}))}))},t.prototype._signinSilentIframe=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.redirect_uri||this.settings.silent_redirect_uri||this.settings.redirect_uri;return t?(e.redirect_uri=t,e.prompt=e.prompt||"none",this._signin(e,this._iframeNavigator,{startUrl:t,silentRequestTimeout:e.silentRequestTimeout||this.settings.silentRequestTimeout}).then((function(e){return e&&(e.profile&&e.profile.sub?o.Log.info("UserManager.signinSilent: successful, signed in sub: ",e.profile.sub):o.Log.info("UserManager.signinSilent: no sub")),e}))):(o.Log.error("UserManager.signinSilent: No silent_redirect_uri configured"),Promise.reject(new Error("No silent_redirect_uri configured")))},t.prototype.signinSilentCallback=function(e){return this._signinCallback(e,this._iframeNavigator).then((function(e){return e&&(e.profile&&e.profile.sub?o.Log.info("UserManager.signinSilentCallback: successful, signed in sub: ",e.profile.sub):o.Log.info("UserManager.signinSilentCallback: no sub")),e}))},t.prototype.signinCallback=function(e){var t=this;return this.readSigninResponseState(e).then((function(n){var r=n.state;return n.response,"si:r"===r.request_type?t.signinRedirectCallback(e):"si:p"===r.request_type?t.signinPopupCallback(e):"si:s"===r.request_type?t.signinSilentCallback(e):Promise.reject(new Error("invalid response_type in state"))}))},t.prototype.signoutCallback=function(e,t){var n=this;return this.readSignoutResponseState(e).then((function(r){var o=r.state,i=r.response;return o?"so:r"===o.request_type?n.signoutRedirectCallback(e):"so:p"===o.request_type?n.signoutPopupCallback(e,t):Promise.reject(new Error("invalid response_type in state")):i}))},t.prototype.querySessionStatus=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(t=Object.assign({},t)).request_type="si:s";var n=t.redirect_uri||this.settings.silent_redirect_uri||this.settings.redirect_uri;return n?(t.redirect_uri=n,t.prompt="none",t.response_type=t.response_type||this.settings.query_status_response_type,t.scope=t.scope||"openid",t.skipUserInfo=!0,this._signinStart(t,this._iframeNavigator,{startUrl:n,silentRequestTimeout:t.silentRequestTimeout||this.settings.silentRequestTimeout}).then((function(t){return e.processSigninResponse(t.url).then((function(e){if(o.Log.debug("UserManager.querySessionStatus: got signin response"),e.session_state&&e.profile.sub)return o.Log.info("UserManager.querySessionStatus: querySessionStatus success for sub: ",e.profile.sub),{session_state:e.session_state,sub:e.profile.sub,sid:e.profile.sid};o.Log.info("querySessionStatus successful, user not authenticated")})).catch((function(t){if(t.session_state&&e.settings.monitorAnonymousSession&&("login_required"==t.message||"consent_required"==t.message||"interaction_required"==t.message||"account_selection_required"==t.message))return o.Log.info("UserManager.querySessionStatus: querySessionStatus success for anonymous user"),{session_state:t.session_state};throw t}))}))):(o.Log.error("UserManager.querySessionStatus: No silent_redirect_uri configured"),Promise.reject(new Error("No silent_redirect_uri configured")))},t.prototype._signin=function(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this._signinStart(e,t,r).then((function(t){return n._signinEnd(t.url,e)}))},t.prototype._signinStart=function(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.prepare(r).then((function(t){return o.Log.debug("UserManager._signinStart: got navigator window handle"),n.createSigninRequest(e).then((function(e){return o.Log.debug("UserManager._signinStart: got signin request"),r.url=e.url,r.id=e.state.id,t.navigate(r)})).catch((function(e){throw t.close&&(o.Log.debug("UserManager._signinStart: Error after preparing navigator, closing navigator window"),t.close()),e}))}))},t.prototype._signinEnd=function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return this.processSigninResponse(e).then((function(e){o.Log.debug("UserManager._signinEnd: got signin response");var r=new s.User(e);if(n.current_sub){if(n.current_sub!==r.profile.sub)return o.Log.debug("UserManager._signinEnd: current user does not match user returned from signin. sub from signin: ",r.profile.sub),Promise.reject(new Error("login_required"));o.Log.debug("UserManager._signinEnd: current user matches user returned from signin")}return t.storeUser(r).then((function(){return o.Log.debug("UserManager._signinEnd: user stored"),t._events.load(r),r}))}))},t.prototype._signinCallback=function(e,t){o.Log.debug("UserManager._signinCallback");var n="query"===this._settings.response_mode||!this._settings.response_mode&&d.SigninRequest.isCode(this._settings.response_type)?"?":"#";return t.callback(e,void 0,n)},t.prototype.signoutRedirect=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(e=Object.assign({},e)).request_type="so:r";var t=e.post_logout_redirect_uri||this.settings.post_logout_redirect_uri;t&&(e.post_logout_redirect_uri=t);var n={useReplaceToNavigate:e.useReplaceToNavigate};return this._signoutStart(e,this._redirectNavigator,n).then((function(){o.Log.info("UserManager.signoutRedirect: successful")}))},t.prototype.signoutRedirectCallback=function(e){return this._signoutEnd(e||this._redirectNavigator.url).then((function(e){return o.Log.info("UserManager.signoutRedirectCallback: successful"),e}))},t.prototype.signoutPopup=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(e=Object.assign({},e)).request_type="so:p";var t=e.post_logout_redirect_uri||this.settings.popup_post_logout_redirect_uri||this.settings.post_logout_redirect_uri;return e.post_logout_redirect_uri=t,e.display="popup",e.post_logout_redirect_uri&&(e.state=e.state||{}),this._signout(e,this._popupNavigator,{startUrl:t,popupWindowFeatures:e.popupWindowFeatures||this.settings.popupWindowFeatures,popupWindowTarget:e.popupWindowTarget||this.settings.popupWindowTarget}).then((function(){o.Log.info("UserManager.signoutPopup: successful")}))},t.prototype.signoutPopupCallback=function(e,t){return void 0===t&&"boolean"==typeof e&&(t=e,e=null),this._popupNavigator.callback(e,t,"?").then((function(){o.Log.info("UserManager.signoutPopupCallback: successful")}))},t.prototype._signout=function(e,t){var n=this,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this._signoutStart(e,t,r).then((function(e){return n._signoutEnd(e.url)}))},t.prototype._signoutStart=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=this,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return arguments[1].prepare(n).then((function(r){return o.Log.debug("UserManager._signoutStart: got navigator window handle"),t._loadUser().then((function(i){return o.Log.debug("UserManager._signoutStart: loaded current user from storage"),(t._settings.revokeAccessTokenOnSignout?t._revokeInternal(i):Promise.resolve()).then((function(){var a=e.id_token_hint||i&&i.id_token;return a&&(o.Log.debug("UserManager._signoutStart: Setting id_token into signout request"),e.id_token_hint=a),t.removeUser().then((function(){return o.Log.debug("UserManager._signoutStart: user removed, creating signout request"),t.createSignoutRequest(e).then((function(e){return o.Log.debug("UserManager._signoutStart: got signout request"),n.url=e.url,e.state&&(n.id=e.state.id),r.navigate(n)}))}))}))})).catch((function(e){throw r.close&&(o.Log.debug("UserManager._signoutStart: Error after preparing navigator, closing navigator window"),r.close()),e}))}))},t.prototype._signoutEnd=function(e){return this.processSignoutResponse(e).then((function(e){return o.Log.debug("UserManager._signoutEnd: got signout response"),e}))},t.prototype.revokeAccessToken=function(){var e=this;return this._loadUser().then((function(t){return e._revokeInternal(t,!0).then((function(n){if(n)return o.Log.debug("UserManager.revokeAccessToken: removing token properties from user and re-storing"),t.access_token=null,t.refresh_token=null,t.expires_at=null,t.token_type=null,e.storeUser(t).then((function(){o.Log.debug("UserManager.revokeAccessToken: user stored"),e._events.load(t)}))}))})).then((function(){o.Log.info("UserManager.revokeAccessToken: access token revoked successfully")}))},t.prototype._revokeInternal=function(e,t){var n=this;if(e){var r=e.access_token,i=e.refresh_token;return this._revokeAccessTokenInternal(r,t).then((function(e){return n._revokeRefreshTokenInternal(i,t).then((function(t){return e||t||o.Log.debug("UserManager.revokeAccessToken: no need to revoke due to no token(s), or JWT format"),e||t}))}))}return Promise.resolve(!1)},t.prototype._revokeAccessTokenInternal=function(e,t){return!e||e.indexOf(".")>=0?Promise.resolve(!1):this._tokenRevocationClient.revoke(e,t).then((function(){return!0}))},t.prototype._revokeRefreshTokenInternal=function(e,t){return e?this._tokenRevocationClient.revoke(e,t,"refresh_token").then((function(){return!0})):Promise.resolve(!1)},t.prototype.startSilentRenew=function(){this._silentRenewService.start()},t.prototype.stopSilentRenew=function(){this._silentRenewService.stop()},t.prototype._loadUser=function(){return this._userStore.get(this._userStoreKey).then((function(e){return e?(o.Log.debug("UserManager._loadUser: user storageString loaded"),s.User.fromStorageString(e)):(o.Log.debug("UserManager._loadUser: no user storageString"),null)}))},t.prototype.storeUser=function(e){if(e){o.Log.debug("UserManager.storeUser: storing user");var t=e.toStorageString();return this._userStore.set(this._userStoreKey,t)}return o.Log.debug("storeUser.storeUser: removing user"),this._userStore.remove(this._userStoreKey)},r(t,[{key:"_redirectNavigator",get:function(){return this.settings.redirectNavigator}},{key:"_popupNavigator",get:function(){return this.settings.popupNavigator}},{key:"_iframeNavigator",get:function(){return this.settings.iframeNavigator}},{key:"_userStore",get:function(){return this.settings.userStore}},{key:"events",get:function(){return this._events}},{key:"_userStoreKey",get:function(){return"user:"+this.settings.authority+":"+this.settings.client_id}}]),t}(i.OidcClient)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.UserManagerSettings=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=(n(0),n(5)),i=n(40),a=n(41),s=n(43),l=n(6),u=n(1),c=n(8);t.UserManagerSettings=function(e){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=n.popup_redirect_uri,o=n.popup_post_logout_redirect_uri,d=n.popupWindowFeatures,f=n.popupWindowTarget,p=n.silent_redirect_uri,h=n.silentRequestTimeout,m=n.automaticSilentRenew,g=void 0!==m&&m,v=n.validateSubOnSilentRenew,y=void 0!==v&&v,b=n.includeIdTokenInSilentRenew,w=void 0===b||b,E=n.monitorSession,S=void 0===E||E,x=n.monitorAnonymousSession,A=void 0!==x&&x,k=n.checkSessionInterval,_=void 0===k?2e3:k,C=n.stopCheckSessionOnError,O=void 0===C||C,P=n.query_status_response_type,R=n.revokeAccessTokenOnSignout,T=void 0!==R&&R,F=n.accessTokenExpiringNotificationTime,j=void 0===F?60:F,N=n.redirectNavigator,I=void 0===N?new i.RedirectNavigator:N,L=n.popupNavigator,M=void 0===L?new a.PopupNavigator:L,D=n.iframeNavigator,B=void 0===D?new s.IFrameNavigator:D,U=n.userStore,z=void 0===U?new l.WebStorageStateStore({store:u.Global.sessionStorage}):U;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var $=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,arguments[0]));return $._popup_redirect_uri=r,$._popup_post_logout_redirect_uri=o,$._popupWindowFeatures=d,$._popupWindowTarget=f,$._silent_redirect_uri=p,$._silentRequestTimeout=h,$._automaticSilentRenew=g,$._validateSubOnSilentRenew=y,$._includeIdTokenInSilentRenew=w,$._accessTokenExpiringNotificationTime=j,$._monitorSession=S,$._monitorAnonymousSession=A,$._checkSessionInterval=_,$._stopCheckSessionOnError=O,P?$._query_status_response_type=P:arguments[0]&&arguments[0].response_type?$._query_status_response_type=c.SigninRequest.isOidc(arguments[0].response_type)?"id_token":"code":$._query_status_response_type="id_token",$._revokeAccessTokenOnSignout=T,$._redirectNavigator=I,$._popupNavigator=M,$._iframeNavigator=B,$._userStore=z,$}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),r(t,[{key:"popup_redirect_uri",get:function(){return this._popup_redirect_uri}},{key:"popup_post_logout_redirect_uri",get:function(){return this._popup_post_logout_redirect_uri}},{key:"popupWindowFeatures",get:function(){return this._popupWindowFeatures}},{key:"popupWindowTarget",get:function(){return this._popupWindowTarget}},{key:"silent_redirect_uri",get:function(){return this._silent_redirect_uri}},{key:"silentRequestTimeout",get:function(){return this._silentRequestTimeout}},{key:"automaticSilentRenew",get:function(){return this._automaticSilentRenew}},{key:"validateSubOnSilentRenew",get:function(){return this._validateSubOnSilentRenew}},{key:"includeIdTokenInSilentRenew",get:function(){return this._includeIdTokenInSilentRenew}},{key:"accessTokenExpiringNotificationTime",get:function(){return this._accessTokenExpiringNotificationTime}},{key:"monitorSession",get:function(){return this._monitorSession}},{key:"monitorAnonymousSession",get:function(){return this._monitorAnonymousSession}},{key:"checkSessionInterval",get:function(){return this._checkSessionInterval}},{key:"stopCheckSessionOnError",get:function(){return this._stopCheckSessionOnError}},{key:"query_status_response_type",get:function(){return this._query_status_response_type}},{key:"revokeAccessTokenOnSignout",get:function(){return this._revokeAccessTokenOnSignout}},{key:"redirectNavigator",get:function(){return this._redirectNavigator}},{key:"popupNavigator",get:function(){return this._popupNavigator}},{key:"iframeNavigator",get:function(){return this._iframeNavigator}},{key:"userStore",get:function(){return this._userStore}}]),t}(o.OidcClientSettings)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RedirectNavigator=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0);t.RedirectNavigator=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.prepare=function(){return Promise.resolve(this)},e.prototype.navigate=function(e){return e&&e.url?(e.useReplaceToNavigate?window.location.replace(e.url):window.location=e.url,Promise.resolve()):(o.Log.error("RedirectNavigator.navigate: No url provided"),Promise.reject(new Error("No url provided")))},r(e,[{key:"url",get:function(){return window.location.href}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PopupNavigator=void 0;var r=n(0),o=n(42);t.PopupNavigator=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.prepare=function(e){var t=new o.PopupWindow(e);return Promise.resolve(t)},e.prototype.callback=function e(t,n,i){r.Log.debug("PopupNavigator.callback");try{return o.PopupWindow.notifyOpener(t,n,i),Promise.resolve()}catch(e){return Promise.reject(e)}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PopupWindow=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(3);t.PopupWindow=function(){function e(t){var n=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._promise=new Promise((function(e,t){n._resolve=e,n._reject=t}));var r=t.popupWindowTarget||"_blank",i=t.popupWindowFeatures||"location=no,toolbar=no,width=500,height=500,left=100,top=100;";this._popup=window.open("",r,i),this._popup&&(o.Log.debug("PopupWindow.ctor: popup successfully created"),this._checkForPopupClosedTimer=window.setInterval(this._checkForPopupClosed.bind(this),500))}return e.prototype.navigate=function(e){return this._popup?e&&e.url?(o.Log.debug("PopupWindow.navigate: Setting URL in popup"),this._id=e.id,this._id&&(window["popupCallback_"+e.id]=this._callback.bind(this)),this._popup.focus(),this._popup.window.location=e.url):(this._error("PopupWindow.navigate: no url provided"),this._error("No url provided")):this._error("PopupWindow.navigate: Error opening popup window"),this.promise},e.prototype._success=function(e){o.Log.debug("PopupWindow.callback: Successful response from popup window"),this._cleanup(),this._resolve(e)},e.prototype._error=function(e){o.Log.error("PopupWindow.error: ",e),this._cleanup(),this._reject(new Error(e))},e.prototype.close=function(){this._cleanup(!1)},e.prototype._cleanup=function(e){o.Log.debug("PopupWindow.cleanup"),window.clearInterval(this._checkForPopupClosedTimer),this._checkForPopupClosedTimer=null,delete window["popupCallback_"+this._id],this._popup&&!e&&this._popup.close(),this._popup=null},e.prototype._checkForPopupClosed=function(){this._popup&&!this._popup.closed||this._error("Popup window closed")},e.prototype._callback=function(e,t){this._cleanup(t),e?(o.Log.debug("PopupWindow.callback success"),this._success({url:e})):(o.Log.debug("PopupWindow.callback: Invalid response from popup"),this._error("Invalid response from popup"))},e.notifyOpener=function(e,t,n){if(window.opener){if(e=e||window.location.href){var r=i.UrlUtility.parseUrlFragment(e,n);if(r.state){var a="popupCallback_"+r.state,s=window.opener[a];s?(o.Log.debug("PopupWindow.notifyOpener: passing url message to opener"),s(e,t)):o.Log.warn("PopupWindow.notifyOpener: no matching callback found on opener")}else o.Log.warn("PopupWindow.notifyOpener: no state found in response url")}}else o.Log.warn("PopupWindow.notifyOpener: no window.opener. Can't complete notification.")},r(e,[{key:"promise",get:function(){return this._promise}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IFrameNavigator=void 0;var r=n(0),o=n(44);t.IFrameNavigator=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.prepare=function(e){var t=new o.IFrameWindow(e);return Promise.resolve(t)},e.prototype.callback=function e(t){r.Log.debug("IFrameNavigator.callback");try{return o.IFrameWindow.notifyParent(t),Promise.resolve()}catch(e){return Promise.reject(e)}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IFrameWindow=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0);t.IFrameWindow=function(){function e(t){var n=this;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._promise=new Promise((function(e,t){n._resolve=e,n._reject=t})),this._boundMessageEvent=this._message.bind(this),window.addEventListener("message",this._boundMessageEvent,!1),this._frame=window.document.createElement("iframe"),this._frame.style.visibility="hidden",this._frame.style.position="absolute",this._frame.width=0,this._frame.height=0,window.document.body.appendChild(this._frame)}return e.prototype.navigate=function(e){if(e&&e.url){var t=e.silentRequestTimeout||1e4;o.Log.debug("IFrameWindow.navigate: Using timeout of:",t),this._timer=window.setTimeout(this._timeout.bind(this),t),this._frame.src=e.url}else this._error("No url provided");return this.promise},e.prototype._success=function(e){this._cleanup(),o.Log.debug("IFrameWindow: Successful response from frame window"),this._resolve(e)},e.prototype._error=function(e){this._cleanup(),o.Log.error(e),this._reject(new Error(e))},e.prototype.close=function(){this._cleanup()},e.prototype._cleanup=function(){this._frame&&(o.Log.debug("IFrameWindow: cleanup"),window.removeEventListener("message",this._boundMessageEvent,!1),window.clearTimeout(this._timer),window.document.body.removeChild(this._frame),this._timer=null,this._frame=null,this._boundMessageEvent=null)},e.prototype._timeout=function(){o.Log.debug("IFrameWindow.timeout"),this._error("Frame window timed out")},e.prototype._message=function(e){if(o.Log.debug("IFrameWindow.message"),this._timer&&e.origin===this._origin&&e.source===this._frame.contentWindow&&"string"==typeof e.data&&(e.data.startsWith("http://")||e.data.startsWith("https://"))){var t=e.data;t?this._success({url:t}):this._error("Invalid response from frame")}},e.notifyParent=function(e){o.Log.debug("IFrameWindow.notifyParent"),(e=e||window.location.href)&&(o.Log.debug("IFrameWindow.notifyParent: posting url message to parent"),window.parent.postMessage(e,location.protocol+"//"+location.host))},r(e,[{key:"promise",get:function(){return this._promise}},{key:"_origin",get:function(){return location.protocol+"//"+location.host}}]),e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.UserManagerEvents=void 0;var r=n(0),o=n(16),i=n(17);t.UserManagerEvents=function(e){function t(n){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var r=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,n));return r._userLoaded=new i.Event("User loaded"),r._userUnloaded=new i.Event("User unloaded"),r._silentRenewError=new i.Event("Silent renew error"),r._userSignedIn=new i.Event("User signed in"),r._userSignedOut=new i.Event("User signed out"),r._userSessionChanged=new i.Event("User session changed"),r}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.load=function(t){var n=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];r.Log.debug("UserManagerEvents.load"),e.prototype.load.call(this,t),n&&this._userLoaded.raise(t)},t.prototype.unload=function(){r.Log.debug("UserManagerEvents.unload"),e.prototype.unload.call(this),this._userUnloaded.raise()},t.prototype.addUserLoaded=function(e){this._userLoaded.addHandler(e)},t.prototype.removeUserLoaded=function(e){this._userLoaded.removeHandler(e)},t.prototype.addUserUnloaded=function(e){this._userUnloaded.addHandler(e)},t.prototype.removeUserUnloaded=function(e){this._userUnloaded.removeHandler(e)},t.prototype.addSilentRenewError=function(e){this._silentRenewError.addHandler(e)},t.prototype.removeSilentRenewError=function(e){this._silentRenewError.removeHandler(e)},t.prototype._raiseSilentRenewError=function(e){r.Log.debug("UserManagerEvents._raiseSilentRenewError",e.message),this._silentRenewError.raise(e)},t.prototype.addUserSignedIn=function(e){this._userSignedIn.addHandler(e)},t.prototype.removeUserSignedIn=function(e){this._userSignedIn.removeHandler(e)},t.prototype._raiseUserSignedIn=function(){r.Log.debug("UserManagerEvents._raiseUserSignedIn"),this._userSignedIn.raise()},t.prototype.addUserSignedOut=function(e){this._userSignedOut.addHandler(e)},t.prototype.removeUserSignedOut=function(e){this._userSignedOut.removeHandler(e)},t.prototype._raiseUserSignedOut=function(){r.Log.debug("UserManagerEvents._raiseUserSignedOut"),this._userSignedOut.raise()},t.prototype.addUserSessionChanged=function(e){this._userSessionChanged.addHandler(e)},t.prototype.removeUserSessionChanged=function(e){this._userSessionChanged.removeHandler(e)},t.prototype._raiseUserSessionChanged=function(){r.Log.debug("UserManagerEvents._raiseUserSessionChanged"),this._userSessionChanged.raise()},t}(o.AccessTokenEvents)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Timer=void 0;var r=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),o=n(0),i=n(1),a=n(17);t.Timer=function(e){function t(n){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:i.Global.timer,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t);var a=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,e.call(this,n));return a._timer=r,a._nowFunc=o||function(){return Date.now()/1e3},a}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(t,e),t.prototype.init=function(e){e<=0&&(e=1),e=parseInt(e);var t=this.now+e;if(this.expiration===t&&this._timerHandle)o.Log.debug("Timer.init timer "+this._name+" skipping initialization since already initialized for expiration:",this.expiration);else{this.cancel(),o.Log.debug("Timer.init timer "+this._name+" for duration:",e),this._expiration=t;var n=5;e<n&&(n=e),this._timerHandle=this._timer.setInterval(this._callback.bind(this),1e3*n)}},t.prototype.cancel=function(){this._timerHandle&&(o.Log.debug("Timer.cancel: ",this._name),this._timer.clearInterval(this._timerHandle),this._timerHandle=null)},t.prototype._callback=function(){var t=this._expiration-this.now;o.Log.debug("Timer.callback; "+this._name+" timer expires in:",t),this._expiration<=this.now&&(this.cancel(),e.prototype.raise.call(this))},r(t,[{key:"now",get:function(){return parseInt(this._nowFunc())}},{key:"expiration",get:function(){return this._expiration}}]),t}(a.Event)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.SilentRenewService=void 0;var r=n(0);t.SilentRenewService=function(){function e(t){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this._userManager=t}return e.prototype.start=function(){this._callback||(this._callback=this._tokenExpiring.bind(this),this._userManager.events.addAccessTokenExpiring(this._callback),this._userManager.getUser().then((function(e){})).catch((function(e){r.Log.error("SilentRenewService.start: Error from getUser:",e.message)})))},e.prototype.stop=function(){this._callback&&(this._userManager.events.removeAccessTokenExpiring(this._callback),delete this._callback)},e.prototype._tokenExpiring=function(){var e=this;this._userManager.signinSilent().then((function(e){r.Log.debug("SilentRenewService._tokenExpiring: Silent token renewal successful")}),(function(t){r.Log.error("SilentRenewService._tokenExpiring: Error from signinSilent:",t.message),e._userManager.events._raiseSilentRenewError(t)}))},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CordovaPopupNavigator=void 0;var r=n(21);t.CordovaPopupNavigator=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.prepare=function(e){var t=new r.CordovaPopupWindow(e);return Promise.resolve(t)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CordovaIFrameNavigator=void 0;var r=n(21);t.CordovaIFrameNavigator=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e)}return e.prototype.prepare=function(e){e.popupWindowFeatures="hidden=yes";var t=new r.CordovaPopupWindow(e);return Promise.resolve(t)},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Version="1.11.5"}])},e.exports=t()},6462:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.mapToAutowhateverTheme=t.defaultTheme=void 0,t.defaultTheme={container:"react-autosuggest__container",containerOpen:"react-autosuggest__container--open",input:"react-autosuggest__input",inputOpen:"react-autosuggest__input--open",inputFocused:"react-autosuggest__input--focused",suggestionsContainer:"react-autosuggest__suggestions-container",suggestionsContainerOpen:"react-autosuggest__suggestions-container--open",suggestionsList:"react-autosuggest__suggestions-list",suggestion:"react-autosuggest__suggestion",suggestionFirst:"react-autosuggest__suggestion--first",suggestionHighlighted:"react-autosuggest__suggestion--highlighted",sectionContainer:"react-autosuggest__section-container",sectionContainerFirst:"react-autosuggest__section-container--first",sectionTitle:"react-autosuggest__section-title"},t.mapToAutowhateverTheme=function(e){var t={};for(var n in e)switch(n){case"suggestionsContainer":t.itemsContainer=e[n];break;case"suggestionsContainerOpen":t.itemsContainerOpen=e[n];break;case"suggestion":t.item=e[n];break;case"suggestionFirst":t.itemFirst=e[n];break;case"suggestionHighlighted":t.itemHighlighted=e[n];break;case"suggestionsList":t.itemsList=e[n];break;default:t[n]=e[n]}return t}},6468:(e,t,n)=>{"use strict";n.d(t,{LX:()=>h,MA:()=>p,_W:()=>m,Lc:()=>v,Ms:()=>y});var r=n(9452),o=n(6481),i=n(4620);const a={m:"margin",p:"padding"},s={t:"Top",r:"Right",b:"Bottom",l:"Left",x:["Left","Right"],y:["Top","Bottom"]},l={marginX:"mx",marginY:"my",paddingX:"px",paddingY:"py"},u=function(){const e={};return t=>(void 0===e[t]&&(e[t]=(e=>{if(e.length>2){if(!l[e])return[e];e=l[e]}const[t,n]=e.split(""),r=a[t],o=s[n]||"";return Array.isArray(o)?o.map((e=>r+e)):[r+o]})(t)),e[t])}(),c=["m","mt","mr","mb","ml","mx","my","margin","marginTop","marginRight","marginBottom","marginLeft","marginX","marginY","marginInline","marginInlineStart","marginInlineEnd","marginBlock","marginBlockStart","marginBlockEnd"],d=["p","pt","pr","pb","pl","px","py","padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingX","paddingY","paddingInline","paddingInlineStart","paddingInlineEnd","paddingBlock","paddingBlockStart","paddingBlockEnd"],f=[...c,...d];function p(e,t,n,r){var i;const a=null!=(i=(0,o.Yn)(e,t,!1))?i:n;return"number"==typeof a?e=>"string"==typeof e?e:a*e:Array.isArray(a)?e=>"string"==typeof e?e:a[e]:"function"==typeof a?a:()=>{}}function h(e){return p(e,"spacing",8)}function m(e,t){if("string"==typeof t||null==t)return t;const n=e(Math.abs(t));return t>=0?n:"number"==typeof n?-n:`-${n}`}function g(e,t){const n=h(e.theme);return Object.keys(e).map((o=>function(e,t,n,o){if(-1===t.indexOf(n))return null;const i=function(e,t){return n=>e.reduce(((e,r)=>(e[r]=m(t,n),e)),{})}(u(n),o),a=e[n];return(0,r.NI)(e,a,i)}(e,t,o,n))).reduce(i.A,{})}function v(e){return g(e,c)}function y(e){return g(e,d)}function b(e){return g(e,f)}v.propTypes={},v.filterProps=c,y.propTypes={},y.filterProps=d,b.propTypes={},b.filterProps=f},6481:(e,t,n)=>{"use strict";n.d(t,{Ay:()=>s,BO:()=>a,Yn:()=>i});var r=n(3967),o=n(9452);function i(e,t,n=!0){if(!t||"string"!=typeof t)return null;if(e&&e.vars&&n){const n=`vars.${t}`.split(".").reduce(((e,t)=>e&&e[t]?e[t]:null),e);if(null!=n)return n}return t.split(".").reduce(((e,t)=>e&&null!=e[t]?e[t]:null),e)}function a(e,t,n,r=n){let o;return o="function"==typeof e?e(n):Array.isArray(e)?e[n]||r:i(e,n)||r,t&&(o=t(o,r,e)),o}const s=function(e){const{prop:t,cssProperty:n=e.prop,themeKey:s,transform:l}=e,u=e=>{if(null==e[t])return null;const u=e[t],c=i(e.theme,s)||{};return(0,o.NI)(e,u,(e=>{let o=a(c,l,e);return e===o&&"string"==typeof e&&(o=a(c,l,`${t}${"default"===e?"":(0,r.A)(e)}`,e)),!1===n?o:{[n]:o}}))};return u.propTypes={},u.filterProps=[t],u}},6505:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"m19 15-6 6-1.42-1.42L15.17 16H4V4h2v10h9.17l-3.59-3.58L13 9l6 6z"}),"SubdirectoryArrowRight");t.A=a},6523:(e,t,n)=>{"use strict";var r=n(1601),o=n.n(r),i=n(6314);n.n(i)()(o()).push([e.id,"html {\n\tscroll-behavior: smooth;\n}\n",""])},6540:(e,t,n)=>{"use strict";e.exports=n(5287)},6554:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))},i=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}},a=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t},s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=a(n(6540)),u=s(n(5556)),c=n(195),d=n(2997),f=s(n(6269)),p=n(3093),h=n(8647),m={notAuthenticated:u.default.elementType,notAuthorized:u.default.elementType,authenticating:u.default.elementType,callbackComponentOverride:u.default.elementType,sessionLostComponent:u.default.elementType,configuration:c.configurationPropTypes,isEnabled:u.default.bool,loggerLevel:u.default.number,logger:u.default.shape({info:u.default.func.isRequired,warn:u.default.func.isRequired,error:u.default.func.isRequired,debug:u.default.func.isRequired}),UserStore:u.default.func,customEvents:u.default.shape({onUserLoaded:u.default.func,onUserUnloaded:u.default.func,onSilentRenewError:u.default.func,onUserSignedOut:u.default.func,onUserSessionChanged:u.default.func,onAccessTokenExpiring:u.default.func,onAccessTokenExpired:u.default.func})},g={notAuthenticated:null,notAuthorized:null,authenticating:null,callbackComponentOverride:null,sessionLostComponent:null,isEnabled:!0,loggerLevel:0,logger:console,configuration:c.configurationDefaultProps,customEvents:null};t.withComponentOverrideProps=function(e,t){return function(n){return l.default.createElement(e,r({callbackComponentOverride:t},n))}},t.AuthenticationProviderInt=function(e){var n=e.location,a=e.history,s=e.configuration,u=e.isEnabled,c=e.UserStore,d=e.loggerLevel,f=e.logger,m=e.sessionLostComponent,g=e.authenticating,v=e.notAuthenticated,y=e.notAuthorized,b=e.callbackComponentOverride,w=e.children,E=e.customEvents,S=e.authenticationServiceInt,x=e.CallbackInt,A=e.setLoggerInt,k=e.OidcRoutesInt,_=e.oidcLogInt,C=e.authenticateUserInt,O=e.logoutUserInt,P=e.setUserManagerInt,R=S(s,c),T=h.useAuthenticationContextState(R),F=T.oidcState,j=T.loadUser,N=T.onError,I=T.onLoading,L=T.unloadUser,M=T.onLogout,D={loadUser:j,onError:N,onLoading:I,unloadUser:L,onLogout:M},B=h.useOidcEvents(_,R,D,E),U=B.addOidcEvents,z=B.removeOidcEvents;l.useEffect((function(){I(),A(d,f),U();var e=!0;return R.getUser().then((function(t){e&&j(t)})),function(){z(),e=!1,P(null)}}),[U,j,f,d,I,z,A,P,R]);var $=l.default.useMemo((function(){return b?t.withComponentOverrideProps(x,b):x}),[x,b]),H=l.useCallback((function(){return o(void 0,void 0,void 0,(function(){return i(this,(function(e){switch(e.label){case 0:return I(),_.info("Login requested"),[4,C(R,n,a)()];case 1:return e.sent(),[2]}}))}))}),[C,a,n,_,I,R]),W=l.useCallback((function(){return o(void 0,void 0,void 0,(function(){var e;return i(this,(function(t){switch(t.label){case 0:return t.trys.push([0,2,,3]),M(),[4,O(R)];case 1:return t.sent(),_.info("Logout successfull"),[3,3];case 2:return e=t.sent(),N(e.message),[3,3];case 3:return[2]}}))}))}),[O,_,N,M,R]),V=l.useCallback((function(){for(var e,t=[],n=0;n<arguments.length;n++)t[n]=arguments[n];try{_.info("SILENT SIGNIN Requested"),(e=F.userManager).signinSilent.apply(e,t)}catch(e){N(e.message)}}),[_,F.userManager,N]);return l.default.createElement(p.AuthenticationContext.Provider,{value:r(r({},F),{authenticating:g,isEnabled:u,signinSilent:V,login:H,logout:W,events:F.userManager.events})},l.default.createElement(k,{notAuthenticated:v,notAuthorized:y,callbackComponent:$,sessionLost:m,configuration:s},w))};var v=c.withRouter(f.default(t.AuthenticationProviderInt,{CallbackInt:d.Callback,authenticationServiceInt:c.authenticationService,setLoggerInt:c.setLogger,OidcRoutesInt:c.OidcRoutes,oidcLogInt:c.oidcLog,authenticateUserInt:c.authenticateUser,logoutUserInt:c.logoutUser,setUserManagerInt:c.setUserManager}));v.propTypes=m,v.defaultProps=g,v.displayName="AuthenticationProvider",t.default=v},6688:(e,t,n)=>{var r;function o(){if(void 0!==r)return r;if(n.g.XMLHttpRequest){r=new n.g.XMLHttpRequest;try{r.open("GET",n.g.XDomainRequest?"/":"https://example.com")}catch(e){r=null}}else r=null;return r}function i(e){var t=o();if(!t)return!1;try{return t.responseType=e,t.responseType===e}catch(e){}return!1}function a(e){return"function"==typeof e}t.fetch=a(n.g.fetch)&&a(n.g.ReadableStream),t.writableStream=a(n.g.WritableStream),t.abortController=a(n.g.AbortController),t.arraybuffer=t.fetch||i("arraybuffer"),t.msstream=!t.fetch&&i("ms-stream"),t.mozchunkedarraybuffer=!t.fetch&&i("moz-chunked-arraybuffer"),t.overrideMimeType=t.fetch||!!o()&&a(o().overrideMimeType),r=null},6698:e=>{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},6708:(e,t,n)=>{"use strict";var r,o=n(5606);function i(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t){var n=e.entry;for(e.entry=null;n;){var r=n.callback;t.pendingcb--,r(undefined),n=n.next}t.corkedRequestsFree.next=e}(t,e)}}e.exports=k,k.WritableState=A;var a,s={deprecate:n(4643)},l=n(345),u=n(8287).Buffer,c=(void 0!==n.g?n.g:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{}).Uint8Array||function(){},d=n(5896),f=n(5291).getHighWaterMark,p=n(6048).F,h=p.ERR_INVALID_ARG_TYPE,m=p.ERR_METHOD_NOT_IMPLEMENTED,g=p.ERR_MULTIPLE_CALLBACK,v=p.ERR_STREAM_CANNOT_PIPE,y=p.ERR_STREAM_DESTROYED,b=p.ERR_STREAM_NULL_VALUES,w=p.ERR_STREAM_WRITE_AFTER_END,E=p.ERR_UNKNOWN_ENCODING,S=d.errorOrDestroy;function x(){}function A(e,t,a){r=r||n(5382),e=e||{},"boolean"!=typeof a&&(a=t instanceof r),this.objectMode=!!e.objectMode,a&&(this.objectMode=this.objectMode||!!e.writableObjectMode),this.highWaterMark=f(this,e,"writableHighWaterMark",a),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var s=!1===e.decodeStrings;this.decodeStrings=!s,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,i=n.writecb;if("function"!=typeof i)throw new g;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,i){--t.pendingcb,n?(o.nextTick(i,r),o.nextTick(T,e,t),e._writableState.errorEmitted=!0,S(e,r)):(i(r),e._writableState.errorEmitted=!0,S(e,r),T(e,t))}(e,n,r,t,i);else{var a=P(n)||e.destroyed;a||n.corked||n.bufferProcessing||!n.bufferedRequest||O(e,n),r?o.nextTick(C,e,n,a,i):C(e,n,a,i)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!1!==e.emitClose,this.autoDestroy=!!e.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new i(this)}function k(e){var t=this instanceof(r=r||n(5382));if(!t&&!a.call(k,this))return new k(e);this._writableState=new A(e,this,t),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),l.call(this)}function _(e,t,n,r,o,i,a){t.writelen=r,t.writecb=a,t.writing=!0,t.sync=!0,t.destroyed?t.onwrite(new y("write")):n?e._writev(o,t.onwrite):e._write(o,i,t.onwrite),t.sync=!1}function C(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),T(e,t)}function O(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),a=t.corkedRequestsFree;a.entry=n;for(var s=0,l=!0;n;)o[s]=n,n.isBuf||(l=!1),n=n.next,s+=1;o.allBuffers=l,_(e,t,!0,t.length,o,"",a.finish),t.pendingcb++,t.lastBufferedRequest=null,a.next?(t.corkedRequestsFree=a.next,a.next=null):t.corkedRequestsFree=new i(t),t.bufferedRequestCount=0}else{for(;n;){var u=n.chunk,c=n.encoding,d=n.callback;if(_(e,t,!1,t.objectMode?1:u.length,u,c,d),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function P(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function R(e,t){e._final((function(n){t.pendingcb--,n&&S(e,n),t.prefinished=!0,e.emit("prefinish"),T(e,t)}))}function T(e,t){var n=P(t);if(n&&(function(e,t){t.prefinished||t.finalCalled||("function"!=typeof e._final||t.destroyed?(t.prefinished=!0,e.emit("prefinish")):(t.pendingcb++,t.finalCalled=!0,o.nextTick(R,e,t)))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"),t.autoDestroy))){var r=e._readableState;(!r||r.autoDestroy&&r.endEmitted)&&e.destroy()}return n}n(6698)(k,l),A.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(A.prototype,"buffer",{get:s.deprecate((function(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(a=Function.prototype[Symbol.hasInstance],Object.defineProperty(k,Symbol.hasInstance,{value:function(e){return!!a.call(this,e)||this===k&&e&&e._writableState instanceof A}})):a=function(e){return e instanceof this},k.prototype.pipe=function(){S(this,new v)},k.prototype.write=function(e,t,n){var r,i=this._writableState,a=!1,s=!i.objectMode&&(r=e,u.isBuffer(r)||r instanceof c);return s&&!u.isBuffer(e)&&(e=function(e){return u.from(e)}(e)),"function"==typeof t&&(n=t,t=null),s?t="buffer":t||(t=i.defaultEncoding),"function"!=typeof n&&(n=x),i.ending?function(e,t){var n=new w;S(e,n),o.nextTick(t,n)}(this,n):(s||function(e,t,n,r){var i;return null===n?i=new b:"string"==typeof n||t.objectMode||(i=new h("chunk",["string","Buffer"],n)),!i||(S(e,i),o.nextTick(r,i),!1)}(this,i,e,n))&&(i.pendingcb++,a=function(e,t,n,r,o,i){if(!n){var a=function(e,t,n){return e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=u.from(t,n)),t}(t,r,o);r!==a&&(n=!0,o="buffer",r=a)}var s=t.objectMode?1:r.length;t.length+=s;var l=t.length<t.highWaterMark;if(l||(t.needDrain=!0),t.writing||t.corked){var c=t.lastBufferedRequest;t.lastBufferedRequest={chunk:r,encoding:o,isBuf:n,callback:i,next:null},c?c.next=t.lastBufferedRequest:t.bufferedRequest=t.lastBufferedRequest,t.bufferedRequestCount+=1}else _(e,t,!1,s,r,o,i);return l}(this,i,s,e,t,n)),a},k.prototype.cork=function(){this._writableState.corked++},k.prototype.uncork=function(){var e=this._writableState;e.corked&&(e.corked--,e.writing||e.corked||e.bufferProcessing||!e.bufferedRequest||O(this,e))},k.prototype.setDefaultEncoding=function(e){if("string"==typeof e&&(e=e.toLowerCase()),!(["hex","utf8","utf-8","ascii","binary","base64","ucs2","ucs-2","utf16le","utf-16le","raw"].indexOf((e+"").toLowerCase())>-1))throw new E(e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(k.prototype,"writableBuffer",{enumerable:!1,get:function(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(k.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),k.prototype._write=function(e,t,n){n(new m("_write()"))},k.prototype._writev=null,k.prototype.end=function(e,t,n){var r=this._writableState;return"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||function(e,t,n){t.ending=!0,T(e,t),n&&(t.finished?o.nextTick(n):e.once("finish",n)),t.ended=!0,e.writable=!1}(this,r,n),this},Object.defineProperty(k.prototype,"writableLength",{enumerable:!1,get:function(){return this._writableState.length}}),Object.defineProperty(k.prototype,"destroyed",{enumerable:!1,get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),k.prototype.destroy=d.destroy,k.prototype._undestroy=d.undestroy,k.prototype._destroy=function(e,t){t(e)}},6718:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"}),"Add");t.A=a},6735:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(6540)),a=n(195),s=o(n(6269));t.onRedirectSuccess=function(e,t){return function(n){t.info("Successfull login Callback",n),n.state.url?e.push(n.state.url):t.warn("no location in state")}},t.onRedirectError=function(e,t){return function(n){var r=n.message;t.error("There was an error handling the token callback: "+r),e.push("/authentication/not-authenticated?message="+encodeURIComponent(r))}},t.CallbackContainerCore=function(e){var n=e.history,r=e.getUserManager,o=e.oidcLog,s=e.callbackComponentOverride,l=t.onRedirectSuccess(n,o),u=t.onRedirectError(n,o);return i.useEffect((function(){r().signinRedirectCallback().then(l,u)}),[r,u,l]),s?i.default.createElement(s,null):i.default.createElement(a.Callback,null)};var l=a.withRouter(s.default(t.CallbackContainerCore,{getUserManager:a.getUserManager,oidcLog:a.oidcLog}));t.default=i.default.memo(l)},6743:(e,t,n)=>{"use strict";var r=n(9353);e.exports=Function.prototype.bind||r},6852:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(1523).A},6866:e=>{e.exports={100:"Continue",101:"Switching Protocols",102:"Processing",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",208:"Already Reported",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",303:"See Other",304:"Not Modified",305:"Use Proxy",307:"Temporary Redirect",308:"Permanent Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Payload Too Large",414:"URI Too Long",415:"Unsupported Media Type",416:"Range Not Satisfiable",417:"Expectation Failed",418:"I'm a teapot",421:"Misdirected Request",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",425:"Unordered Collection",426:"Upgrade Required",428:"Precondition Required",429:"Too Many Requests",431:"Request Header Fields Too Large",451:"Unavailable For Legal Reasons",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",508:"Loop Detected",509:"Bandwidth Limit Exceeded",510:"Not Extended",511:"Network Authentication Required"}},6897:(e,t,n)=>{"use strict";var r=n(453),o=n(41),i=n(592)(),a=n(5795),s=r("%TypeError%"),l=r("%Math.floor%");e.exports=function(e,t){if("function"!=typeof e)throw new s("`fn` is not a function");if("number"!=typeof t||t<0||t>4294967295||l(t)!==t)throw new s("`length` must be a positive 32-bit integer");var n=arguments.length>2&&!!arguments[2],r=!0,u=!0;if("length"in e&&a){var c=a(e,"length");c&&!c.configurable&&(r=!1),c&&!c.writable&&(u=!1)}return(r||u||!n)&&(i?o(e,"length",t,!0,!0):o(e,"length",t)),e}},6917:(e,t,n)=>{var r=n(5606),o=n(8287).Buffer,i=n(6688),a=n(6698),s=n(8399),l=t.readyStates={UNSENT:0,OPENED:1,HEADERS_RECEIVED:2,LOADING:3,DONE:4},u=t.IncomingMessage=function(e,t,n,a){var l=this;if(s.Readable.call(l),l._mode=n,l.headers={},l.rawHeaders=[],l.trailers={},l.rawTrailers=[],l.on("end",(function(){r.nextTick((function(){l.emit("close")}))})),"fetch"===n){if(l._fetchResponse=t,l.url=t.url,l.statusCode=t.status,l.statusMessage=t.statusText,t.headers.forEach((function(e,t){l.headers[t.toLowerCase()]=e,l.rawHeaders.push(t,e)})),i.writableStream){var u=new WritableStream({write:function(e){return a(!1),new Promise((function(t,n){l._destroyed?n():l.push(o.from(e))?t():l._resumeFetch=t}))},close:function(){a(!0),l._destroyed||l.push(null)},abort:function(e){a(!0),l._destroyed||l.emit("error",e)}});try{return void t.body.pipeTo(u).catch((function(e){a(!0),l._destroyed||l.emit("error",e)}))}catch(e){}}var c=t.body.getReader();!function e(){c.read().then((function(t){l._destroyed||(a(t.done),t.done?l.push(null):(l.push(o.from(t.value)),e()))})).catch((function(e){a(!0),l._destroyed||l.emit("error",e)}))}()}else if(l._xhr=e,l._pos=0,l.url=e.responseURL,l.statusCode=e.status,l.statusMessage=e.statusText,e.getAllResponseHeaders().split(/\r?\n/).forEach((function(e){var t=e.match(/^([^:]+):\s*(.*)/);if(t){var n=t[1].toLowerCase();"set-cookie"===n?(void 0===l.headers[n]&&(l.headers[n]=[]),l.headers[n].push(t[2])):void 0!==l.headers[n]?l.headers[n]+=", "+t[2]:l.headers[n]=t[2],l.rawHeaders.push(t[1],t[2])}})),l._charset="x-user-defined",!i.overrideMimeType){var d=l.rawHeaders["mime-type"];if(d){var f=d.match(/;\s*charset=([^;])(;|$)/);f&&(l._charset=f[1].toLowerCase())}l._charset||(l._charset="utf-8")}};a(u,s.Readable),u.prototype._read=function(){var e=this._resumeFetch;e&&(this._resumeFetch=null,e())},u.prototype._onXHRProgress=function(e){var t=this,r=t._xhr,i=null;switch(t._mode){case"text":if((i=r.responseText).length>t._pos){var a=i.substr(t._pos);if("x-user-defined"===t._charset){for(var s=o.alloc(a.length),u=0;u<a.length;u++)s[u]=255&a.charCodeAt(u);t.push(s)}else t.push(a,t._charset);t._pos=i.length}break;case"arraybuffer":if(r.readyState!==l.DONE||!r.response)break;i=r.response,t.push(o.from(new Uint8Array(i)));break;case"moz-chunked-arraybuffer":if(i=r.response,r.readyState!==l.LOADING||!i)break;t.push(o.from(new Uint8Array(i)));break;case"ms-stream":if(i=r.response,r.readyState!==l.LOADING)break;var c=new n.g.MSStreamReader;c.onprogress=function(){c.result.byteLength>t._pos&&(t.push(o.from(new Uint8Array(c.result.slice(t._pos)))),t._pos=c.result.byteLength)},c.onload=function(){e(!0),t.push(null)},c.readAsArrayBuffer(i)}t._xhr.readyState===l.DONE&&"ms-stream"!==t._mode&&(e(!0),t.push(null))}},6925:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},6993:(e,t,n)=>{var r=n(5546);function o(){var t,n,i="function"==typeof Symbol?Symbol:{},a=i.iterator||"@@iterator",s=i.toStringTag||"@@toStringTag";function l(e,o,i,a){var s=o&&o.prototype instanceof c?o:c,l=Object.create(s.prototype);return r(l,"_invoke",function(e,r,o){var i,a,s,l=0,c=o||[],d=!1,f={p:0,n:0,v:t,a:p,f:p.bind(t,4),d:function(e,n){return i=e,a=0,s=t,f.n=n,u}};function p(e,r){for(a=e,s=r,n=0;!d&&l&&!o&&n<c.length;n++){var o,i=c[n],p=f.p,h=i[2];e>3?(o=h===r)&&(s=i[(a=i[4])?5:(a=3,3)],i[4]=i[5]=t):i[0]<=p&&((o=e<2&&p<i[1])?(a=0,f.v=r,f.n=i[1]):p<h&&(o=e<3||i[0]>r||r>h)&&(i[4]=e,i[5]=r,f.n=h,a=0))}if(o||e>1)return u;throw d=!0,r}return function(o,c,h){if(l>1)throw TypeError("Generator is already running");for(d&&1===c&&p(c,h),a=c,s=h;(n=a<2?t:s)||!d;){i||(a?a<3?(a>1&&(f.n=-1),p(a,s)):f.n=s:f.v=s);try{if(l=2,i){if(a||(o="next"),n=i[o]){if(!(n=n.call(i,s)))throw TypeError("iterator result is not an object");if(!n.done)return n;s=n.value,a<2&&(a=0)}else 1===a&&(n=i.return)&&n.call(i),a<2&&(s=TypeError("The iterator does not provide a '"+o+"' method"),a=1);i=t}else if((n=(d=f.n<0)?s:e.call(r,f))!==u)break}catch(e){i=t,a=1,s=e}finally{l=1}}return{value:n,done:d}}}(e,i,a),!0),l}var u={};function c(){}function d(){}function f(){}n=Object.getPrototypeOf;var p=[][a]?n(n([][a]())):(r(n={},a,(function(){return this})),n),h=f.prototype=c.prototype=Object.create(p);function m(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,f):(e.__proto__=f,r(e,s,"GeneratorFunction")),e.prototype=Object.create(h),e}return d.prototype=f,r(h,"constructor",f),r(f,"constructor",d),d.displayName="GeneratorFunction",r(f,s,"GeneratorFunction"),r(h),r(h,s,"Generator"),r(h,a,(function(){return this})),r(h,"toString",(function(){return"[object Generator]"})),(e.exports=o=function(){return{w:l,m}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=o,e.exports.__esModule=!0,e.exports.default=e.exports},7007:e=>{"use strict";var t,n="object"==typeof Reflect?Reflect:null,r=n&&"function"==typeof n.apply?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};t=n&&"function"==typeof n.ownKeys?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var o=Number.isNaN||function(e){return e!=e};function i(){i.init.call(this)}e.exports=i,e.exports.once=function(e,t){return new Promise((function(n,r){function o(n){e.removeListener(t,i),r(n)}function i(){"function"==typeof e.removeListener&&e.removeListener("error",o),n([].slice.call(arguments))}m(e,t,i,{once:!0}),"error"!==t&&function(e,t){"function"==typeof e.on&&m(e,"error",t,{once:!0})}(e,o)}))},i.EventEmitter=i,i.prototype._events=void 0,i.prototype._eventsCount=0,i.prototype._maxListeners=void 0;var a=10;function s(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function l(e){return void 0===e._maxListeners?i.defaultMaxListeners:e._maxListeners}function u(e,t,n,r){var o,i,a,u;if(s(n),void 0===(i=e._events)?(i=e._events=Object.create(null),e._eventsCount=0):(void 0!==i.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),i=e._events),a=i[t]),void 0===a)a=i[t]=n,++e._eventsCount;else if("function"==typeof a?a=i[t]=r?[n,a]:[a,n]:r?a.unshift(n):a.push(n),(o=l(e))>0&&a.length>o&&!a.warned){a.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+a.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=a.length,u=c,console&&console.warn&&console.warn(u)}return e}function c(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function d(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=c.bind(r);return o.listener=n,r.wrapFn=o,o}function f(e,t,n){var r=e._events;if(void 0===r)return[];var o=r[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(o):h(o,o.length)}function p(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function h(e,t){for(var n=new Array(t),r=0;r<t;++r)n[r]=e[r];return n}function m(e,t,n,r){if("function"==typeof e.on)r.once?e.once(t,n):e.on(t,n);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function o(i){r.once&&e.removeEventListener(t,o),n(i)}))}}Object.defineProperty(i,"defaultMaxListeners",{enumerable:!0,get:function(){return a},set:function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");a=e}}),i.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},i.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},i.prototype.getMaxListeners=function(){return l(this)},i.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var o="error"===e,i=this._events;if(void 0!==i)o=o&&void 0===i.error;else if(!o)return!1;if(o){var a;if(t.length>0&&(a=t[0]),a instanceof Error)throw a;var s=new Error("Unhandled error."+(a?" ("+a.message+")":""));throw s.context=a,s}var l=i[e];if(void 0===l)return!1;if("function"==typeof l)r(l,this,t);else{var u=l.length,c=h(l,u);for(n=0;n<u;++n)r(c[n],this,t)}return!0},i.prototype.addListener=function(e,t){return u(this,e,t,!1)},i.prototype.on=i.prototype.addListener,i.prototype.prependListener=function(e,t){return u(this,e,t,!0)},i.prototype.once=function(e,t){return s(t),this.on(e,d(this,e,t)),this},i.prototype.prependOnceListener=function(e,t){return s(t),this.prependListener(e,d(this,e,t)),this},i.prototype.removeListener=function(e,t){var n,r,o,i,a;if(s(t),void 0===(r=this._events))return this;if(void 0===(n=r[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(o=-1,i=n.length-1;i>=0;i--)if(n[i]===t||n[i].listener===t){a=n[i].listener,o=i;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,o),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,a||t)}return this},i.prototype.off=i.prototype.removeListener,i.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var o,i=Object.keys(n);for(r=0;r<i.length;++r)"removeListener"!==(o=i[r])&&this.removeAllListeners(o);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(r=t.length-1;r>=0;r--)this.removeListener(e,t[r]);return this},i.prototype.listeners=function(e){return f(this,e,!0)},i.prototype.rawListeners=function(e){return f(this,e,!1)},i.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},i.prototype.listenerCount=p,i.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},7008:(e,t,n)=>{"use strict";n.d(t,{A:()=>f});var r=n(8168),o=n(8587),i=n(3272);const a=["values","unit","step"];const s={borderRadius:4};var l=n(6468),u=n(3571),c=n(8552);const d=["breakpoints","palette","spacing","shape"],f=function(e={},...t){const{breakpoints:n={},palette:f={},spacing:p,shape:h={}}=e,m=(0,o.A)(e,d),g=function(e){const{values:t={xs:0,sm:600,md:900,lg:1200,xl:1536},unit:n="px",step:i=5}=e,s=(0,o.A)(e,a),l=(e=>{const t=Object.keys(e).map((t=>({key:t,val:e[t]})))||[];return t.sort(((e,t)=>e.val-t.val)),t.reduce(((e,t)=>(0,r.A)({},e,{[t.key]:t.val})),{})})(t),u=Object.keys(l);function c(e){return`@media (min-width:${"number"==typeof t[e]?t[e]:e}${n})`}function d(e){return`@media (max-width:${("number"==typeof t[e]?t[e]:e)-i/100}${n})`}function f(e,r){const o=u.indexOf(r);return`@media (min-width:${"number"==typeof t[e]?t[e]:e}${n}) and (max-width:${(-1!==o&&"number"==typeof t[u[o]]?t[u[o]]:r)-i/100}${n})`}return(0,r.A)({keys:u,values:l,up:c,down:d,between:f,only:function(e){return u.indexOf(e)+1<u.length?f(e,u[u.indexOf(e)+1]):c(e)},not:function(e){const t=u.indexOf(e);return 0===t?c(u[1]):t===u.length-1?d(u[t]):f(e,u[u.indexOf(e)+1]).replace("@media","@media not all and")},unit:n},s)}(n),v=function(e=8){if(e.mui)return e;const t=(0,l.LX)({spacing:e}),n=(...e)=>(0===e.length?[1]:e).map((e=>{const n=t(e);return"number"==typeof n?`${n}px`:n})).join(" ");return n.mui=!0,n}(p);let y=(0,i.A)({breakpoints:g,direction:"ltr",components:{},palette:(0,r.A)({mode:"light"},f),spacing:v,shape:(0,r.A)({},s,h)},m);return y=t.reduce(((e,t)=>(0,i.A)(e,t)),y),y.unstable_sxConfig=(0,r.A)({},c.A,null==m?void 0:m.unstable_sxConfig),y.unstable_sx=function(e){return(0,u.A)({sx:e,theme:this})},y}},7034:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"}),"Delete");t.A=a},7072:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var i=o(n(6540)),a=n(9550);t.withOidcUser=function(e){var t=e.displayName||e.name||"Component",n=function(t){var n=i.useContext(a.AuthenticationContext).oidcUser;return i.default.createElement(e,r({oidcUser:n},t))};return n.displayName="withOidcUser("+t+")",n}},7091:(e,t,n)=>{"use strict";n.d(t,{Ay:()=>c,p0:()=>s});var r=n(8587),o=n(8168);const i=["duration","easing","delay"],a={easeInOut:"cubic-bezier(0.4, 0, 0.2, 1)",easeOut:"cubic-bezier(0.0, 0, 0.2, 1)",easeIn:"cubic-bezier(0.4, 0, 1, 1)",sharp:"cubic-bezier(0.4, 0, 0.6, 1)"},s={shortest:150,shorter:200,short:250,standard:300,complex:375,enteringScreen:225,leavingScreen:195};function l(e){return`${Math.round(e)}ms`}function u(e){if(!e)return 0;const t=e/36;return Math.round(10*(4+15*t**.25+t/5))}function c(e){const t=(0,o.A)({},a,e.easing),n=(0,o.A)({},s,e.duration);return(0,o.A)({getAutoHeightDuration:u,create:(e=["all"],o={})=>{const{duration:a=n.standard,easing:s=t.easeInOut,delay:u=0}=o;return(0,r.A)(o,i),(Array.isArray(e)?e:[e]).map((e=>`${e} ${"string"==typeof a?a:l(a)} ${s} ${"string"==typeof u?u:l(u)}`)).join(",")}},e,{easing:t,duration:n})}},7151:(e,t,n)=>{"use strict";n.d(t,{A:()=>d});var r=n(6540);let o,i=!0,a=!1;const s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function l(e){e.metaKey||e.altKey||e.ctrlKey||(i=!0)}function u(){i=!1}function c(){"hidden"===this.visibilityState&&a&&(i=!0)}function d(){const e=r.useCallback((e=>{var t;null!=e&&((t=e.ownerDocument).addEventListener("keydown",l,!0),t.addEventListener("mousedown",u,!0),t.addEventListener("pointerdown",u,!0),t.addEventListener("touchstart",u,!0),t.addEventListener("visibilitychange",c,!0))}),[]),t=r.useRef(!1);return{isFocusVisibleRef:t,onFocus:function(e){return!!function(e){const{target:t}=e;try{return t.matches(":focus-visible")}catch(e){}return i||function(e){const{type:t,tagName:n}=e;return!("INPUT"!==n||!s[t]||e.readOnly)||"TEXTAREA"===n&&!e.readOnly||!!e.isContentEditable}(t)}(e)&&(t.current=!0,!0)},onBlur:function(){return!!t.current&&(a=!0,window.clearTimeout(o),o=window.setTimeout((()=>{a=!1}),100),t.current=!1,!0)},ref:e}}},7244:(e,t,n)=>{"use strict";var r=n(9092)(),o=n(8075)("Object.prototype.toString"),i=function(e){return!(r&&e&&"object"==typeof e&&Symbol.toStringTag in e)&&"[object Arguments]"===o(e)},a=function(e){return!!i(e)||null!==e&&"object"==typeof e&&"number"==typeof e.length&&e.length>=0&&"[object Array]"!==o(e)&&"[object Function]"===o(e.callee)},s=function(){return i(arguments)}();i.isLegacyArguments=a,e.exports=s?i:a},7308:(e,t,n)=>{"use strict";n.d(t,{J:()=>h});var r={animationIterationCount:1,aspectRatio:1,borderImageOutset:1,borderImageSlice:1,borderImageWidth:1,boxFlex:1,boxFlexGroup:1,boxOrdinalGroup:1,columnCount:1,columns:1,flex:1,flexGrow:1,flexPositive:1,flexShrink:1,flexNegative:1,flexOrder:1,gridRow:1,gridRowEnd:1,gridRowSpan:1,gridRowStart:1,gridColumn:1,gridColumnEnd:1,gridColumnSpan:1,gridColumnStart:1,msGridRow:1,msGridRowSpan:1,msGridColumn:1,msGridColumnSpan:1,fontWeight:1,lineHeight:1,opacity:1,order:1,orphans:1,tabSize:1,widows:1,zIndex:1,zoom:1,WebkitLineClamp:1,fillOpacity:1,floodOpacity:1,stopOpacity:1,strokeDasharray:1,strokeDashoffset:1,strokeMiterlimit:1,strokeOpacity:1,strokeWidth:1},o=n(6289),i=/[A-Z]|^ms/g,a=/_EMO_([^_]+?)_([^]*?)_EMO_/g,s=function(e){return 45===e.charCodeAt(1)},l=function(e){return null!=e&&"boolean"!=typeof e},u=(0,o.A)((function(e){return s(e)?e:e.replace(i,"-$&").toLowerCase()})),c=function(e,t){switch(e){case"animation":case"animationName":if("string"==typeof t)return t.replace(a,(function(e,t,n){return f={name:t,styles:n,next:f},t}))}return 1===r[e]||s(e)||"number"!=typeof t||0===t?t:t+"px"};function d(e,t,n){if(null==n)return"";if(void 0!==n.__emotion_styles)return n;switch(typeof n){case"boolean":return"";case"object":if(1===n.anim)return f={name:n.name,styles:n.styles,next:f},n.name;if(void 0!==n.styles){var r=n.next;if(void 0!==r)for(;void 0!==r;)f={name:r.name,styles:r.styles,next:f},r=r.next;return n.styles+";"}return function(e,t,n){var r="";if(Array.isArray(n))for(var o=0;o<n.length;o++)r+=d(e,t,n[o])+";";else for(var i in n){var a=n[i];if("object"!=typeof a)null!=t&&void 0!==t[a]?r+=i+"{"+t[a]+"}":l(a)&&(r+=u(i)+":"+c(i,a)+";");else if(!Array.isArray(a)||"string"!=typeof a[0]||null!=t&&void 0!==t[a[0]]){var s=d(e,t,a);switch(i){case"animation":case"animationName":r+=u(i)+":"+s+";";break;default:r+=i+"{"+s+"}"}}else for(var f=0;f<a.length;f++)l(a[f])&&(r+=u(i)+":"+c(i,a[f])+";")}return r}(e,t,n);case"function":if(void 0!==e){var o=f,i=n(e);return f=o,d(e,t,i)}}if(null==t)return n;var a=t[n];return void 0!==a?a:n}var f,p=/label:\s*([^\s;\n{]+)\s*(;|$)/g,h=function(e,t,n){if(1===e.length&&"object"==typeof e[0]&&null!==e[0]&&void 0!==e[0].styles)return e[0];var r=!0,o="";f=void 0;var i=e[0];null==i||void 0===i.raw?(r=!1,o+=d(n,t,i)):o+=i[0];for(var a=1;a<e.length;a++)o+=d(n,t,e[a]),r&&(o+=i[a]);p.lastIndex=0;for(var s,l="";null!==(s=p.exec(o));)l+="-"+s[1];var u=function(e){for(var t,n=0,r=0,o=e.length;o>=4;++r,o-=4)t=1540483477*(65535&(t=255&e.charCodeAt(r)|(255&e.charCodeAt(++r))<<8|(255&e.charCodeAt(++r))<<16|(255&e.charCodeAt(++r))<<24))+(59797*(t>>>16)<<16),n=1540483477*(65535&(t^=t>>>24))+(59797*(t>>>16)<<16)^1540483477*(65535&n)+(59797*(n>>>16)<<16);switch(o){case 3:n^=(255&e.charCodeAt(r+2))<<16;case 2:n^=(255&e.charCodeAt(r+1))<<8;case 1:n=1540483477*(65535&(n^=255&e.charCodeAt(r)))+(59797*(n>>>16)<<16)}return(((n=1540483477*(65535&(n^=n>>>13))+(59797*(n>>>16)<<16))^n>>>15)>>>0).toString(36)}(o)+l;return{name:u,styles:o,next:f}}},7340:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(3726);function o(e){const{theme:t,name:n,props:o}=e;return t&&t.components&&t.components[n]&&t.components[n].defaultProps?(0,r.A)(t.components[n].defaultProps,o):o}},7424:(e,t,n)=>{"use strict";t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==c(e)&&"function"!=typeof e)return{default:e};var t=u();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}return n.default=e,t&&t.set(e,n),n}(n(6540)),o=l(n(5556)),i=l(n(9321)),a=l(n(3486)),s=n(6462);function l(e){return e&&e.__esModule?e:{default:e}}function u(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return u=function(){return e},e}function c(e){return c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c(e)}function d(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function f(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?d(Object(n),!0).forEach((function(t){v(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):d(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function p(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function h(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function m(e){return m=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},m(e)}function g(e,t){return g=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},g(e,t)}function v(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var y=function(){return!0},b="suggestions-revealed",w="input-focused",E="input-changed",S="escape-pressed",x=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&g(e,t)}(u,e);var t,n,o,l=(o=u,function(){var e,t=m(o);if(function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}()){var n=m(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return function(e,t){return!t||"object"!==c(t)&&"function"!=typeof t?h(e):t}(this,e)});function u(e){var t,n=e.alwaysRenderSuggestions;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,u),v(h(t=l.call(this)),"onDocumentMouseDown",(function(e){t.justClickedOnSuggestionsContainer=!1;for(var n=e.detail&&e.detail.target||e.target;null!==n&&n!==document;){if(n.getAttribute&&null!==n.getAttribute("data-suggestion-index"))return;if(n===t.suggestionsContainer)return void(t.justClickedOnSuggestionsContainer=!0);n=n.parentNode}})),v(h(t),"storeAutowhateverRef",(function(e){null!==e&&(t.autowhatever=e)})),v(h(t),"onSuggestionMouseEnter",(function(e,n){var r=n.sectionIndex,o=n.itemIndex;t.updateHighlightedSuggestion(r,o),e.target===t.pressedSuggestion&&(t.justSelectedSuggestion=!0),t.justMouseEntered=!0,setTimeout((function(){t.justMouseEntered=!1}))})),v(h(t),"highlightFirstSuggestion",(function(){t.updateHighlightedSuggestion(t.props.multiSection?0:null,0)})),v(h(t),"onDocumentMouseUp",(function(){t.pressedSuggestion&&!t.justSelectedSuggestion&&t.input.focus(),t.pressedSuggestion=null})),v(h(t),"onSuggestionMouseDown",(function(e){t.justSelectedSuggestion||(t.justSelectedSuggestion=!0,t.pressedSuggestion=e.target)})),v(h(t),"onSuggestionsClearRequested",(function(){var e=t.props.onSuggestionsClearRequested;e&&e()})),v(h(t),"onSuggestionSelected",(function(e,n){var r=t.props,o=r.alwaysRenderSuggestions,i=r.onSuggestionSelected,a=r.onSuggestionsFetchRequested;i&&i(e,n);var s=t.props.shouldKeepSuggestionsOnSelect(n.suggestion);o||s?a({value:n.suggestionValue,reason:"suggestion-selected"}):t.onSuggestionsClearRequested(),t.resetHighlightedSuggestion()})),v(h(t),"onSuggestionClick",(function(e){var n=t.props,r=n.alwaysRenderSuggestions,o=n.focusInputOnSuggestionClick,i=t.getSuggestionIndices(t.findSuggestionElement(e.target)),a=i.sectionIndex,s=i.suggestionIndex,l=t.getSuggestion(a,s),u=t.props.getSuggestionValue(l);t.maybeCallOnChange(e,u,"click"),t.onSuggestionSelected(e,{suggestion:l,suggestionValue:u,suggestionIndex:s,sectionIndex:a,method:"click"});var c=t.props.shouldKeepSuggestionsOnSelect(l);r||c||t.closeSuggestions(),!0===o?t.input.focus():t.onBlur(),setTimeout((function(){t.justSelectedSuggestion=!1}))})),v(h(t),"onBlur",(function(){var e=t.props,n=e.inputProps,r=e.shouldRenderSuggestions,o=n.value,i=n.onBlur,a=t.getHighlightedSuggestion(),s=r(o,"input-blurred");t.setState({isFocused:!1,highlightedSectionIndex:null,highlightedSuggestionIndex:null,highlightedSuggestion:null,valueBeforeUpDown:null,isCollapsed:!s}),i&&i(t.blurEvent,{highlightedSuggestion:a})})),v(h(t),"onSuggestionMouseLeave",(function(e){t.resetHighlightedSuggestion(!1),t.justSelectedSuggestion&&e.target===t.pressedSuggestion&&(t.justSelectedSuggestion=!1)})),v(h(t),"onSuggestionTouchStart",(function(){t.justSelectedSuggestion=!0})),v(h(t),"onSuggestionTouchMove",(function(){t.justSelectedSuggestion=!1,t.pressedSuggestion=null,t.input.focus()})),v(h(t),"itemProps",(function(e){return{"data-section-index":e.sectionIndex,"data-suggestion-index":e.itemIndex,onMouseEnter:t.onSuggestionMouseEnter,onMouseLeave:t.onSuggestionMouseLeave,onMouseDown:t.onSuggestionMouseDown,onTouchStart:t.onSuggestionTouchStart,onTouchMove:t.onSuggestionTouchMove,onClick:t.onSuggestionClick}})),v(h(t),"renderSuggestionsContainer",(function(e){var n=e.containerProps,r=e.children;return(0,t.props.renderSuggestionsContainer)({containerProps:n,children:r,query:t.getQuery()})})),t.state={isFocused:!1,isCollapsed:!n,highlightedSectionIndex:null,highlightedSuggestionIndex:null,highlightedSuggestion:null,valueBeforeUpDown:null},t.justPressedUpDown=!1,t.justMouseEntered=!1,t.pressedSuggestion=null,t}return t=u,n=[{key:"componentDidMount",value:function(){document.addEventListener("mousedown",this.onDocumentMouseDown),document.addEventListener("mouseup",this.onDocumentMouseUp),this.input=this.autowhatever.input,this.suggestionsContainer=this.autowhatever.itemsContainer}},{key:"UNSAFE_componentWillReceiveProps",value:function(e){var t=0===this.state.highlightedSuggestionIndex&&this.props.highlightFirstSuggestion&&!e.highlightFirstSuggestion;(0,i.default)(e.suggestions,this.props.suggestions)?e.highlightFirstSuggestion&&e.suggestions.length>0&&!1===this.justPressedUpDown&&!1===this.justMouseEntered?this.highlightFirstSuggestion():t&&this.resetHighlightedSuggestion():this.willRenderSuggestions(e,"suggestions-updated")?(this.state.isCollapsed&&!this.justSelectedSuggestion&&this.revealSuggestions(),t&&this.resetHighlightedSuggestion()):this.resetHighlightedSuggestion()}},{key:"componentDidUpdate",value:function(e,t){var n=this.props,r=n.suggestions,o=n.onSuggestionHighlighted,a=n.highlightFirstSuggestion;if(!(0,i.default)(r,e.suggestions)&&r.length>0&&a)this.highlightFirstSuggestion();else if(o){var s=this.getHighlightedSuggestion();s!=t.highlightedSuggestion&&o({suggestion:s})}}},{key:"componentWillUnmount",value:function(){document.removeEventListener("mousedown",this.onDocumentMouseDown),document.removeEventListener("mouseup",this.onDocumentMouseUp)}},{key:"updateHighlightedSuggestion",value:function(e,t,n){var r=this;this.setState((function(o){var i=o.valueBeforeUpDown;return null===t?i=null:null===i&&void 0!==n&&(i=n),{highlightedSectionIndex:e,highlightedSuggestionIndex:t,highlightedSuggestion:null===t?null:r.getSuggestion(e,t),valueBeforeUpDown:i}}))}},{key:"resetHighlightedSuggestion",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.setState((function(t){var n=t.valueBeforeUpDown;return{highlightedSectionIndex:null,highlightedSuggestionIndex:null,highlightedSuggestion:null,valueBeforeUpDown:e?null:n}}))}},{key:"revealSuggestions",value:function(){this.setState({isCollapsed:!1})}},{key:"closeSuggestions",value:function(){this.setState({highlightedSectionIndex:null,highlightedSuggestionIndex:null,highlightedSuggestion:null,valueBeforeUpDown:null,isCollapsed:!0})}},{key:"getSuggestion",value:function(e,t){var n=this.props,r=n.suggestions,o=n.multiSection,i=n.getSectionSuggestions;return o?i(r[e])[t]:r[t]}},{key:"getHighlightedSuggestion",value:function(){var e=this.state,t=e.highlightedSectionIndex,n=e.highlightedSuggestionIndex;return null===n?null:this.getSuggestion(t,n)}},{key:"getSuggestionValueByIndex",value:function(e,t){return(0,this.props.getSuggestionValue)(this.getSuggestion(e,t))}},{key:"getSuggestionIndices",value:function(e){var t=e.getAttribute("data-section-index"),n=e.getAttribute("data-suggestion-index");return{sectionIndex:"string"==typeof t?parseInt(t,10):null,suggestionIndex:parseInt(n,10)}}},{key:"findSuggestionElement",value:function(e){var t=e;do{if(t.getAttribute&&null!==t.getAttribute("data-suggestion-index"))return t;t=t.parentNode}while(null!==t);throw console.error("Clicked element:",e),new Error("Couldn't find suggestion element")}},{key:"maybeCallOnChange",value:function(e,t,n){var r=this.props.inputProps,o=r.value,i=r.onChange;t!==o&&i(e,{newValue:t,method:n})}},{key:"willRenderSuggestions",value:function(e,t){var n=e.suggestions,r=e.inputProps,o=e.shouldRenderSuggestions,i=r.value;return n.length>0&&o(i,t)}},{key:"getQuery",value:function(){var e=this.props.inputProps.value,t=this.state.valueBeforeUpDown;return(null===t?e:t).trim()}},{key:"render",value:function(){var e=this,t=this.props,n=t.suggestions,o=t.renderInputComponent,i=t.onSuggestionsFetchRequested,l=t.renderSuggestion,u=t.inputProps,c=t.multiSection,d=t.renderSectionTitle,p=t.id,h=t.getSectionSuggestions,m=t.theme,g=t.getSuggestionValue,v=t.alwaysRenderSuggestions,x=t.highlightFirstSuggestion,A=t.containerProps,k=this.state,_=k.isFocused,C=k.isCollapsed,O=k.highlightedSectionIndex,P=k.highlightedSuggestionIndex,R=k.valueBeforeUpDown,T=v?y:this.props.shouldRenderSuggestions,F=u.value,j=u.onFocus,N=u.onKeyDown,I=this.willRenderSuggestions(this.props,"render"),L=v||_&&!C&&I,M=L?n:[],D=f({},u,{onFocus:function(t){if(!e.justSelectedSuggestion&&!e.justClickedOnSuggestionsContainer){var n=T(F,w);e.setState({isFocused:!0,isCollapsed:!n}),j&&j(t),n&&i({value:F,reason:w})}},onBlur:function(t){e.justClickedOnSuggestionsContainer?e.input.focus():(e.blurEvent=t,e.justSelectedSuggestion||(e.onBlur(),e.onSuggestionsClearRequested()))},onChange:function(t){var n=t.target.value,r=T(n,E);e.maybeCallOnChange(t,n,"type"),e.suggestionsContainer&&(e.suggestionsContainer.scrollTop=0),e.setState(f({},x?{}:{highlightedSectionIndex:null,highlightedSuggestionIndex:null,highlightedSuggestion:null},{valueBeforeUpDown:null,isCollapsed:!r})),r?i({value:n,reason:E}):e.onSuggestionsClearRequested()},onKeyDown:function(t,r){var o=t.keyCode;switch(o){case 40:case 38:if(C)T(F,b)&&(i({value:F,reason:b}),e.revealSuggestions(),t.preventDefault());else if(n.length>0){var a,s=r.newHighlightedSectionIndex,l=r.newHighlightedItemIndex;a=null===l?null===R?F:R:e.getSuggestionValueByIndex(s,l),e.updateHighlightedSuggestion(s,l,F),e.maybeCallOnChange(t,a,40===o?"down":"up"),t.preventDefault()}e.justPressedUpDown=!0,setTimeout((function(){e.justPressedUpDown=!1}));break;case 13:if(229===t.keyCode)break;var u=e.getHighlightedSuggestion();if(L&&!v&&e.closeSuggestions(),null!=u){t.preventDefault();var c=g(u);e.maybeCallOnChange(t,c,"enter"),e.onSuggestionSelected(t,{suggestion:u,suggestionValue:c,suggestionIndex:P,sectionIndex:O,method:"enter"}),e.justSelectedSuggestion=!0,setTimeout((function(){e.justSelectedSuggestion=!1}))}break;case 27:L&&t.preventDefault();var d=L&&!v;null===R?d||(e.maybeCallOnChange(t,"","escape"),T("",S)?i({value:"",reason:S}):e.onSuggestionsClearRequested()):e.maybeCallOnChange(t,R,"escape"),d?(e.onSuggestionsClearRequested(),e.closeSuggestions()):e.resetHighlightedSuggestion()}N&&N(t)}}),B={query:this.getQuery()};return r.default.createElement(a.default,{multiSection:c,items:M,renderInputComponent:o,renderItemsContainer:this.renderSuggestionsContainer,renderItem:l,renderItemData:B,renderSectionTitle:d,getSectionItems:h,highlightedSectionIndex:O,highlightedItemIndex:P,containerProps:A,inputProps:D,itemProps:this.itemProps,theme:(0,s.mapToAutowhateverTheme)(m),id:p,ref:this.storeAutowhateverRef})}}],n&&p(t.prototype,n),u}(r.Component);t.default=x,v(x,"propTypes",{suggestions:o.default.array.isRequired,onSuggestionsFetchRequested:function(e,t){if("function"!=typeof e[t])throw new Error("'onSuggestionsFetchRequested' must be implemented. See: https://github.com/moroshko/react-autosuggest#onSuggestionsFetchRequestedProp")},onSuggestionsClearRequested:function(e,t){var n=e[t];if(!1===e.alwaysRenderSuggestions&&"function"!=typeof n)throw new Error("'onSuggestionsClearRequested' must be implemented. See: https://github.com/moroshko/react-autosuggest#onSuggestionsClearRequestedProp")},shouldKeepSuggestionsOnSelect:o.default.func,onSuggestionSelected:o.default.func,onSuggestionHighlighted:o.default.func,renderInputComponent:o.default.func,renderSuggestionsContainer:o.default.func,getSuggestionValue:o.default.func.isRequired,renderSuggestion:o.default.func.isRequired,inputProps:function(e,t){var n=e[t];if(!n)throw new Error("'inputProps' must be passed.");if(!Object.prototype.hasOwnProperty.call(n,"value"))throw new Error("'inputProps' must have 'value'.");if(!Object.prototype.hasOwnProperty.call(n,"onChange"))throw new Error("'inputProps' must have 'onChange'.")},shouldRenderSuggestions:o.default.func,alwaysRenderSuggestions:o.default.bool,multiSection:o.default.bool,renderSectionTitle:function(e,t){var n=e[t];if(!0===e.multiSection&&"function"!=typeof n)throw new Error("'renderSectionTitle' must be implemented. See: https://github.com/moroshko/react-autosuggest#renderSectionTitleProp")},getSectionSuggestions:function(e,t){var n=e[t];if(!0===e.multiSection&&"function"!=typeof n)throw new Error("'getSectionSuggestions' must be implemented. See: https://github.com/moroshko/react-autosuggest#getSectionSuggestionsProp")},focusInputOnSuggestionClick:o.default.bool,highlightFirstSuggestion:o.default.bool,theme:o.default.object,id:o.default.string,containerProps:o.default.object}),v(x,"defaultProps",{renderSuggestionsContainer:function(e){var t=e.containerProps,n=e.children;return r.default.createElement("div",t,n)},shouldRenderSuggestions:function(e){return e.trim().length>0},alwaysRenderSuggestions:!1,multiSection:!1,shouldKeepSuggestionsOnSelect:function(){return!1},focusInputOnSuggestionClick:!0,highlightFirstSuggestion:!1,theme:s.defaultTheme,id:"1",containerProps:{}})},7463:(e,t)=>{"use strict";function n(e,t){var n=e.length;e.push(t);e:for(;0<n;){var r=n-1>>>1,o=e[r];if(!(0<i(o,t)))break e;e[r]=t,e[n]=o,n=r}}function r(e){return 0===e.length?null:e[0]}function o(e){if(0===e.length)return null;var t=e[0],n=e.pop();if(n!==t){e[0]=n;e:for(var r=0,o=e.length,a=o>>>1;r<a;){var s=2*(r+1)-1,l=e[s],u=s+1,c=e[u];if(0>i(l,n))u<o&&0>i(c,l)?(e[r]=c,e[u]=n,r=u):(e[r]=l,e[s]=n,r=s);else{if(!(u<o&&0>i(c,n)))break e;e[r]=c,e[u]=n,r=u}}}return t}function i(e,t){var n=e.sortIndex-t.sortIndex;return 0!==n?n:e.id-t.id}if("object"==typeof performance&&"function"==typeof performance.now){var a=performance;t.unstable_now=function(){return a.now()}}else{var s=Date,l=s.now();t.unstable_now=function(){return s.now()-l}}var u=[],c=[],d=1,f=null,p=3,h=!1,m=!1,g=!1,v="function"==typeof setTimeout?setTimeout:null,y="function"==typeof clearTimeout?clearTimeout:null,b="undefined"!=typeof setImmediate?setImmediate:null;function w(e){for(var t=r(c);null!==t;){if(null===t.callback)o(c);else{if(!(t.startTime<=e))break;o(c),t.sortIndex=t.expirationTime,n(u,t)}t=r(c)}}function E(e){if(g=!1,w(e),!m)if(null!==r(u))m=!0,j(S);else{var t=r(c);null!==t&&N(E,t.startTime-e)}}function S(e,n){m=!1,g&&(g=!1,y(_),_=-1),h=!0;var i=p;try{for(w(n),f=r(u);null!==f&&(!(f.expirationTime>n)||e&&!P());){var a=f.callback;if("function"==typeof a){f.callback=null,p=f.priorityLevel;var s=a(f.expirationTime<=n);n=t.unstable_now(),"function"==typeof s?f.callback=s:f===r(u)&&o(u),w(n)}else o(u);f=r(u)}if(null!==f)var l=!0;else{var d=r(c);null!==d&&N(E,d.startTime-n),l=!1}return l}finally{f=null,p=i,h=!1}}"undefined"!=typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var x,A=!1,k=null,_=-1,C=5,O=-1;function P(){return!(t.unstable_now()-O<C)}function R(){if(null!==k){var e=t.unstable_now();O=e;var n=!0;try{n=k(!0,e)}finally{n?x():(A=!1,k=null)}}else A=!1}if("function"==typeof b)x=function(){b(R)};else if("undefined"!=typeof MessageChannel){var T=new MessageChannel,F=T.port2;T.port1.onmessage=R,x=function(){F.postMessage(null)}}else x=function(){v(R,0)};function j(e){k=e,A||(A=!0,x())}function N(e,n){_=v((function(){e(t.unstable_now())}),n)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(e){e.callback=null},t.unstable_continueExecution=function(){m||h||(m=!0,j(S))},t.unstable_forceFrameRate=function(e){0>e||125<e?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):C=0<e?Math.floor(1e3/e):5},t.unstable_getCurrentPriorityLevel=function(){return p},t.unstable_getFirstCallbackNode=function(){return r(u)},t.unstable_next=function(e){switch(p){case 1:case 2:case 3:var t=3;break;default:t=p}var n=p;p=t;try{return e()}finally{p=n}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=function(){},t.unstable_runWithPriority=function(e,t){switch(e){case 1:case 2:case 3:case 4:case 5:break;default:e=3}var n=p;p=e;try{return t()}finally{p=n}},t.unstable_scheduleCallback=function(e,o,i){var a=t.unstable_now();switch(i="object"==typeof i&&null!==i&&"number"==typeof(i=i.delay)&&0<i?a+i:a,e){case 1:var s=-1;break;case 2:s=250;break;case 5:s=1073741823;break;case 4:s=1e4;break;default:s=5e3}return e={id:d++,callback:o,priorityLevel:e,startTime:i,expirationTime:s=i+s,sortIndex:-1},i>a?(e.sortIndex=i,n(c,e),null===r(u)&&e===r(c)&&(g?(y(_),_=-1):g=!0,N(E,i-a))):(e.sortIndex=s,n(u,e),m||h||(m=!0,j(S))),e},t.unstable_shouldYield=P,t.unstable_wrapCallback=function(e){var t=p;return function(){var n=p;p=t;try{return e.apply(this,arguments)}finally{p=n}}}},7510:e=>{e.exports=function(){for(var e={},n=0;n<arguments.length;n++){var r=arguments[n];for(var o in r)t.call(r,o)&&(e[o]=r[o])}return e};var t=Object.prototype.hasOwnProperty},7526:(e,t)=>{"use strict";t.byteLength=function(e){var t=s(e),n=t[0],r=t[1];return 3*(n+r)/4-r},t.toByteArray=function(e){var t,n,i=s(e),a=i[0],l=i[1],u=new o(function(e,t,n){return 3*(t+n)/4-n}(0,a,l)),c=0,d=l>0?a-4:a;for(n=0;n<d;n+=4)t=r[e.charCodeAt(n)]<<18|r[e.charCodeAt(n+1)]<<12|r[e.charCodeAt(n+2)]<<6|r[e.charCodeAt(n+3)],u[c++]=t>>16&255,u[c++]=t>>8&255,u[c++]=255&t;return 2===l&&(t=r[e.charCodeAt(n)]<<2|r[e.charCodeAt(n+1)]>>4,u[c++]=255&t),1===l&&(t=r[e.charCodeAt(n)]<<10|r[e.charCodeAt(n+1)]<<4|r[e.charCodeAt(n+2)]>>2,u[c++]=t>>8&255,u[c++]=255&t),u},t.fromByteArray=function(e){for(var t,r=e.length,o=r%3,i=[],a=16383,s=0,u=r-o;s<u;s+=a)i.push(l(e,s,s+a>u?u:s+a));return 1===o?(t=e[r-1],i.push(n[t>>2]+n[t<<4&63]+"==")):2===o&&(t=(e[r-2]<<8)+e[r-1],i.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"=")),i.join("")};for(var n=[],r=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0;a<64;++a)n[a]=i[a],r[i.charCodeAt(a)]=a;function s(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function l(e,t,r){for(var o,i,a=[],s=t;s<r;s+=3)o=(e[s]<<16&16711680)+(e[s+1]<<8&65280)+(255&e[s+2]),a.push(n[(i=o)>>18&63]+n[i>>12&63]+n[i>>6&63]+n[63&i]);return a.join("")}r["-".charCodeAt(0)]=62,r["_".charCodeAt(0)]=63},7669:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540));t.default=function(){return o.createElement("div",{className:"oidc-callback"},o.createElement("div",{className:"oidc-callback__container"},o.createElement("h1",{className:"oidc-callback__title"},"Authentification terminÃ©e"),o.createElement("p",{className:"oidc-callback__content"},"Vous allez Ãªtre redirigÃ© sur votre application.")))}},7671:function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}};Object.defineProperty(t,"__esModule",{value:!0});var i=n(4691),a=!1,s=0;t.isRequireAuthentication=function(e,t){return t||!e||e&&!0===e.expired},t.isRequireSignin=function(e,t){return t||!e},t.authenticateUser=function(e,n,l,u){return void 0===u&&(u=null),function(c,d){return void 0===c&&(c=!1),void 0===d&&(d=null),r(void 0,void 0,void 0,(function(){var r,f,p;return o(this,(function(o){switch(o.label){case 0:return(r=u)?[3,2]:[4,e.getUser()];case 1:r=o.sent(),o.label=2;case 2:return a?[2]:(s++,f=d||n.pathname+(n.search||"")+(n.hash||""),t.isRequireSignin(r,c)?(i.oidcLog.info("authenticate user..."),a=!0,[4,e.signinRedirect({data:{url:f}})]):[3,4]);case 3:return o.sent(),a=!1,[3,12];case 4:if(!r||!r.expired)return[3,12];a=!0,o.label=5;case 5:return o.trys.push([5,7,,11]),[4,e.signinSilent()];case 6:return o.sent(),[3,11];case 7:return p=o.sent(),s<=1?[4,e.signinRedirect({data:{url:f}})]:[3,9];case 8:return o.sent(),[3,10];case 9:a=!1,i.oidcLog.warn("session lost "+p.toString()),l.push("/authentication/session-lost?path="+encodeURI(f)),o.label=10;case 10:return[3,11];case 11:a=!1,o.label=12;case 12:return[2]}}))}))}},t.logoutUser=function(e){return r(void 0,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return e&&e.getUser?[4,e.getUser()]:[2];case 1:return t.sent()?(i.oidcLog.info("Logout user..."),[4,e.signoutRedirect()]):[3,3];case 2:t.sent(),t.label=3;case 3:return[2]}}))}))},t.signinSilent=function(e){return function(t){return void 0===t&&(t=void 0),e().signinSilent(t)}}},7720:(e,t,n)=>{"use strict";var r=n(4765),o=Object.prototype.hasOwnProperty,i=Array.isArray,a=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}(),s=function(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r<e.length;++r)void 0!==e[r]&&(n[r]=e[r]);return n};e.exports={arrayToObject:s,assign:function(e,t){return Object.keys(t).reduce((function(e,n){return e[n]=t[n],e}),e)},combine:function(e,t){return[].concat(e,t)},compact:function(e){for(var t=[{obj:{o:e},prop:"o"}],n=[],r=0;r<t.length;++r)for(var o=t[r],a=o.obj[o.prop],s=Object.keys(a),l=0;l<s.length;++l){var u=s[l],c=a[u];"object"==typeof c&&null!==c&&-1===n.indexOf(c)&&(t.push({obj:a,prop:u}),n.push(c))}return function(e){for(;e.length>1;){var t=e.pop(),n=t.obj[t.prop];if(i(n)){for(var r=[],o=0;o<n.length;++o)void 0!==n[o]&&r.push(n[o]);t.obj[t.prop]=r}}}(t),e},decode:function(e,t,n){var r=e.replace(/\+/g," ");if("iso-8859-1"===n)return r.replace(/%[0-9a-f]{2}/gi,unescape);try{return decodeURIComponent(r)}catch(e){return r}},encode:function(e,t,n,o,i){if(0===e.length)return e;var s=e;if("symbol"==typeof e?s=Symbol.prototype.toString.call(e):"string"!=typeof e&&(s=String(e)),"iso-8859-1"===n)return escape(s).replace(/%u[0-9a-f]{4}/gi,(function(e){return"%26%23"+parseInt(e.slice(2),16)+"%3B"}));for(var l="",u=0;u<s.length;++u){var c=s.charCodeAt(u);45===c||46===c||95===c||126===c||c>=48&&c<=57||c>=65&&c<=90||c>=97&&c<=122||i===r.RFC1738&&(40===c||41===c)?l+=s.charAt(u):c<128?l+=a[c]:c<2048?l+=a[192|c>>6]+a[128|63&c]:c<55296||c>=57344?l+=a[224|c>>12]+a[128|c>>6&63]+a[128|63&c]:(u+=1,c=65536+((1023&c)<<10|1023&s.charCodeAt(u)),l+=a[240|c>>18]+a[128|c>>12&63]+a[128|c>>6&63]+a[128|63&c])}return l},isBuffer:function(e){return!(!e||"object"!=typeof e||!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e)))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(i(e)){for(var n=[],r=0;r<e.length;r+=1)n.push(t(e[r]));return n}return t(e)},merge:function e(t,n,r){if(!n)return t;if("object"!=typeof n){if(i(t))t.push(n);else{if(!t||"object"!=typeof t)return[t,n];(r&&(r.plainObjects||r.allowPrototypes)||!o.call(Object.prototype,n))&&(t[n]=!0)}return t}if(!t||"object"!=typeof t)return[t].concat(n);var a=t;return i(t)&&!i(n)&&(a=s(t,r)),i(t)&&i(n)?(n.forEach((function(n,i){if(o.call(t,i)){var a=t[i];a&&"object"==typeof a&&n&&"object"==typeof n?t[i]=e(a,n,r):t.push(n)}else t[i]=n})),t):Object.keys(n).reduce((function(t,i){var a=n[i];return o.call(t,i)?t[i]=e(t[i],a,r):t[i]=a,t}),a)}}},7758:(e,t,n)=>{"use strict";var r,o=n(6048).F,i=o.ERR_MISSING_ARGS,a=o.ERR_STREAM_DESTROYED;function s(e){if(e)throw e}function l(e){e()}function u(e,t){return e.pipe(t)}e.exports=function(){for(var e=arguments.length,t=new Array(e),o=0;o<e;o++)t[o]=arguments[o];var c,d=function(e){return e.length?"function"!=typeof e[e.length-1]?s:e.pop():s}(t);if(Array.isArray(t[0])&&(t=t[0]),t.length<2)throw new i("streams");var f=t.map((function(e,o){var i=o<t.length-1;return function(e,t,o,i){i=function(e){var t=!1;return function(){t||(t=!0,e.apply(void 0,arguments))}}(i);var s=!1;e.on("close",(function(){s=!0})),void 0===r&&(r=n(6238)),r(e,{readable:t,writable:o},(function(e){if(e)return i(e);s=!0,i()}));var l=!1;return function(t){if(!s&&!l)return l=!0,function(e){return e.setHeader&&"function"==typeof e.abort}(e)?e.abort():"function"==typeof e.destroy?e.destroy():void i(t||new a("pipe"))}}(e,i,o>0,(function(e){c||(c=e),e&&f.forEach(l),i||(f.forEach(l),d(c))}))}));return t.reduce(u)}},7874:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M18 13h-.68l-2 2h1.91L19 17H5l1.78-2h2.05l-2-2H6l-3 3v4c0 1.1.89 2 1.99 2H19c1.1 0 2-.89 2-2v-4l-3-3zm-1-5.05-4.95 4.95-3.54-3.54 4.95-4.95L17 7.95zm-4.24-5.66L6.39 8.66c-.39.39-.39 1.02 0 1.41l4.95 4.95c.39.39 1.02.39 1.41 0l6.36-6.36c.39-.39.39-1.02 0-1.41L14.16 2.3c-.38-.4-1.01-.4-1.4-.01z"}),"HowToVote");t.A=a},7888:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"}),"Widgets");t.A=a},7926:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z"}),"Backspace");t.A=a},7932:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==u(e)&&"function"!=typeof e)return{default:e};var t=l();if(t&&t.has(e))return t.get(e);var n={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){var i=r?Object.getOwnPropertyDescriptor(e,o):null;i&&(i.get||i.set)?Object.defineProperty(n,o,i):n[o]=e[o]}return n.default=e,t&&t.set(e,n),n}(n(6540)),o=s(n(5556)),i=s(n(1572)),a=s(n(1438));function s(e){return e&&e.__esModule?e:{default:e}}function l(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return l=function(){return e},e}function u(e){return u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},u(e)}function c(){return c=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},c.apply(this,arguments)}function d(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function f(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function p(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function h(e){return h=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},h(e)}function m(e,t){return m=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},m(e,t)}function g(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var v=function(e){!function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&m(e,t)}(l,e);var t,n,o,s=(o=l,function(){var e,t=h(o);if(function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],(function(){}))),!0}catch(e){return!1}}()){var n=h(this).constructor;e=Reflect.construct(t,arguments,n)}else e=t.apply(this,arguments);return function(e,t){return!t||"object"!==u(t)&&"function"!=typeof t?p(e):t}(this,e)});function l(){var e;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,l);for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];return g(p(e=s.call.apply(s,[this].concat(n))),"storeHighlightedItemReference",(function(t){e.props.onHighlightedItemChange(null===t?null:t.item)})),e}return t=l,n=[{key:"shouldComponentUpdate",value:function(e){return(0,a.default)(e,this.props,["itemProps"])}},{key:"render",value:function(){var e=this,t=this.props,n=t.items,o=t.itemProps,a=t.renderItem,s=t.renderItemData,l=t.sectionIndex,u=t.highlightedItemIndex,f=t.getItemId,p=t.theme,h=t.keyPrefix,m=null===l?h:"".concat(h,"section-").concat(l,"-"),v="function"==typeof o;return r.default.createElement("ul",c({role:"listbox"},p("".concat(m,"items-list"),"itemsList")),n.map((function(t,n){var h=0===n,y=n===u,b="".concat(m,"item-").concat(n),w=v?o({sectionIndex:l,itemIndex:n}):o,E=function(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?d(Object(n),!0).forEach((function(t){g(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):d(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}({id:f(l,n),"aria-selected":y},p(b,"item",h&&"itemFirst",y&&"itemHighlighted"),{},w);return y&&(E.ref=e.storeHighlightedItemReference),r.default.createElement(i.default,c({},E,{sectionIndex:l,isHighlighted:y,itemIndex:n,item:t,renderItem:a,renderItemData:s}))})))}}],n&&f(t.prototype,n),l}(r.Component);t.default=v,g(v,"propTypes",{items:o.default.array.isRequired,itemProps:o.default.oneOfType([o.default.object,o.default.func]),renderItem:o.default.func.isRequired,renderItemData:o.default.object.isRequired,sectionIndex:o.default.number,highlightedItemIndex:o.default.number,onHighlightedItemChange:o.default.func.isRequired,getItemId:o.default.func.isRequired,theme:o.default.func.isRequired,keyPrefix:o.default.string.isRequired}),g(v,"defaultProps",{sectionIndex:null})},8075:(e,t,n)=>{"use strict";var r=n(453),o=n(487),i=o(r("String.prototype.indexOf"));e.exports=function(e,t){var n=r(e,!!t);return"function"==typeof n&&i(e,".prototype.")>-1?o(n):n}},8142:(e,t,n)=>{e=n.nmd(e);var r="__lodash_hash_undefined__",o=9007199254740991,i="[object Arguments]",a="[object Array]",s="[object Boolean]",l="[object Date]",u="[object Error]",c="[object Function]",d="[object Map]",f="[object Number]",p="[object Object]",h="[object Promise]",m="[object RegExp]",g="[object Set]",v="[object String]",y="[object WeakMap]",b="[object ArrayBuffer]",w="[object DataView]",E=/^\[object .+?Constructor\]$/,S=/^(?:0|[1-9]\d*)$/,x={};x["[object Float32Array]"]=x["[object Float64Array]"]=x["[object Int8Array]"]=x["[object Int16Array]"]=x["[object Int32Array]"]=x["[object Uint8Array]"]=x["[object Uint8ClampedArray]"]=x["[object Uint16Array]"]=x["[object Uint32Array]"]=!0,x[i]=x[a]=x[b]=x[s]=x[w]=x[l]=x[u]=x[c]=x[d]=x[f]=x[p]=x[m]=x[g]=x[v]=x[y]=!1;var A="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,k="object"==typeof self&&self&&self.Object===Object&&self,_=A||k||Function("return this")(),C=t&&!t.nodeType&&t,O=C&&e&&!e.nodeType&&e,P=O&&O.exports===C,R=P&&A.process,T=function(){try{return R&&R.binding&&R.binding("util")}catch(e){}}(),F=T&&T.isTypedArray;function j(e,t){for(var n=-1,r=null==e?0:e.length;++n<r;)if(t(e[n],n,e))return!0;return!1}function N(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function I(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}var L,M,D,B=Array.prototype,U=Function.prototype,z=Object.prototype,$=_["__core-js_shared__"],H=U.toString,W=z.hasOwnProperty,V=(L=/[^.]+$/.exec($&&$.keys&&$.keys.IE_PROTO||""))?"Symbol(src)_1."+L:"",q=z.toString,K=RegExp("^"+H.call(W).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),G=P?_.Buffer:void 0,J=_.Symbol,Y=_.Uint8Array,X=z.propertyIsEnumerable,Q=B.splice,Z=J?J.toStringTag:void 0,ee=Object.getOwnPropertySymbols,te=G?G.isBuffer:void 0,ne=(M=Object.keys,D=Object,function(e){return M(D(e))}),re=Oe(_,"DataView"),oe=Oe(_,"Map"),ie=Oe(_,"Promise"),ae=Oe(_,"Set"),se=Oe(_,"WeakMap"),le=Oe(Object,"create"),ue=Fe(re),ce=Fe(oe),de=Fe(ie),fe=Fe(ae),pe=Fe(se),he=J?J.prototype:void 0,me=he?he.valueOf:void 0;function ge(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function ve(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function ye(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}function be(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new ye;++t<n;)this.add(e[t])}function we(e){var t=this.__data__=new ve(e);this.size=t.size}function Ee(e,t){for(var n=e.length;n--;)if(je(e[n][0],t))return n;return-1}function Se(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":Z&&Z in Object(e)?function(e){var t=W.call(e,Z),n=e[Z];try{e[Z]=void 0;var r=!0}catch(e){}var o=q.call(e);return r&&(t?e[Z]=n:delete e[Z]),o}(e):function(e){return q.call(e)}(e)}function xe(e){return Ue(e)&&Se(e)==i}function Ae(e,t,n,r,o){return e===t||(null==e||null==t||!Ue(e)&&!Ue(t)?e!=e&&t!=t:function(e,t,n,r,o,c){var h=Ie(e),y=Ie(t),E=h?a:Re(e),S=y?a:Re(t),x=(E=E==i?p:E)==p,A=(S=S==i?p:S)==p,k=E==S;if(k&&Le(e)){if(!Le(t))return!1;h=!0,x=!1}if(k&&!x)return c||(c=new we),h||ze(e)?ke(e,t,n,r,o,c):function(e,t,n,r,o,i,a){switch(n){case w:if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case b:return!(e.byteLength!=t.byteLength||!i(new Y(e),new Y(t)));case s:case l:case f:return je(+e,+t);case u:return e.name==t.name&&e.message==t.message;case m:case v:return e==t+"";case d:var c=N;case g:var p=1&r;if(c||(c=I),e.size!=t.size&&!p)return!1;var h=a.get(e);if(h)return h==t;r|=2,a.set(e,t);var y=ke(c(e),c(t),r,o,i,a);return a.delete(e),y;case"[object Symbol]":if(me)return me.call(e)==me.call(t)}return!1}(e,t,E,n,r,o,c);if(!(1&n)){var _=x&&W.call(e,"__wrapped__"),C=A&&W.call(t,"__wrapped__");if(_||C){var O=_?e.value():e,P=C?t.value():t;return c||(c=new we),o(O,P,n,r,c)}}return!!k&&(c||(c=new we),function(e,t,n,r,o,i){var a=1&n,s=_e(e),l=s.length;if(l!=_e(t).length&&!a)return!1;for(var u=l;u--;){var c=s[u];if(!(a?c in t:W.call(t,c)))return!1}var d=i.get(e);if(d&&i.get(t))return d==t;var f=!0;i.set(e,t),i.set(t,e);for(var p=a;++u<l;){var h=e[c=s[u]],m=t[c];if(r)var g=a?r(m,h,c,t,e,i):r(h,m,c,e,t,i);if(!(void 0===g?h===m||o(h,m,n,r,i):g)){f=!1;break}p||(p="constructor"==c)}if(f&&!p){var v=e.constructor,y=t.constructor;v==y||!("constructor"in e)||!("constructor"in t)||"function"==typeof v&&v instanceof v&&"function"==typeof y&&y instanceof y||(f=!1)}return i.delete(e),i.delete(t),f}(e,t,n,r,o,c))}(e,t,n,r,Ae,o))}function ke(e,t,n,r,o,i){var a=1&n,s=e.length,l=t.length;if(s!=l&&!(a&&l>s))return!1;var u=i.get(e);if(u&&i.get(t))return u==t;var c=-1,d=!0,f=2&n?new be:void 0;for(i.set(e,t),i.set(t,e);++c<s;){var p=e[c],h=t[c];if(r)var m=a?r(h,p,c,t,e,i):r(p,h,c,e,t,i);if(void 0!==m){if(m)continue;d=!1;break}if(f){if(!j(t,(function(e,t){if(a=t,!f.has(a)&&(p===e||o(p,e,n,r,i)))return f.push(t);var a}))){d=!1;break}}else if(p!==h&&!o(p,h,n,r,i)){d=!1;break}}return i.delete(e),i.delete(t),d}function _e(e){return function(e,t,n){var r=t(e);return Ie(e)?r:function(e,t){for(var n=-1,r=t.length,o=e.length;++n<r;)e[o+n]=t[n];return e}(r,n(e))}(e,$e,Pe)}function Ce(e,t){var n,r,o=e.__data__;return("string"==(r=typeof(n=t))||"number"==r||"symbol"==r||"boolean"==r?"__proto__"!==n:null===n)?o["string"==typeof t?"string":"hash"]:o.map}function Oe(e,t){var n=function(e,t){return null==e?void 0:e[t]}(e,t);return function(e){return!(!Be(e)||function(e){return!!V&&V in e}(e))&&(Me(e)?K:E).test(Fe(e))}(n)?n:void 0}ge.prototype.clear=function(){this.__data__=le?le(null):{},this.size=0},ge.prototype.delete=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t},ge.prototype.get=function(e){var t=this.__data__;if(le){var n=t[e];return n===r?void 0:n}return W.call(t,e)?t[e]:void 0},ge.prototype.has=function(e){var t=this.__data__;return le?void 0!==t[e]:W.call(t,e)},ge.prototype.set=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=le&&void 0===t?r:t,this},ve.prototype.clear=function(){this.__data__=[],this.size=0},ve.prototype.delete=function(e){var t=this.__data__,n=Ee(t,e);return!(n<0||(n==t.length-1?t.pop():Q.call(t,n,1),--this.size,0))},ve.prototype.get=function(e){var t=this.__data__,n=Ee(t,e);return n<0?void 0:t[n][1]},ve.prototype.has=function(e){return Ee(this.__data__,e)>-1},ve.prototype.set=function(e,t){var n=this.__data__,r=Ee(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},ye.prototype.clear=function(){this.size=0,this.__data__={hash:new ge,map:new(oe||ve),string:new ge}},ye.prototype.delete=function(e){var t=Ce(this,e).delete(e);return this.size-=t?1:0,t},ye.prototype.get=function(e){return Ce(this,e).get(e)},ye.prototype.has=function(e){return Ce(this,e).has(e)},ye.prototype.set=function(e,t){var n=Ce(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},be.prototype.add=be.prototype.push=function(e){return this.__data__.set(e,r),this},be.prototype.has=function(e){return this.__data__.has(e)},we.prototype.clear=function(){this.__data__=new ve,this.size=0},we.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},we.prototype.get=function(e){return this.__data__.get(e)},we.prototype.has=function(e){return this.__data__.has(e)},we.prototype.set=function(e,t){var n=this.__data__;if(n instanceof ve){var r=n.__data__;if(!oe||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new ye(r)}return n.set(e,t),this.size=n.size,this};var Pe=ee?function(e){return null==e?[]:(e=Object(e),function(t){for(var n=-1,r=null==t?0:t.length,o=0,i=[];++n<r;){var a=t[n];s=a,X.call(e,s)&&(i[o++]=a)}var s;return i}(ee(e)))}:function(){return[]},Re=Se;function Te(e,t){return!!(t=null==t?o:t)&&("number"==typeof e||S.test(e))&&e>-1&&e%1==0&&e<t}function Fe(e){if(null!=e){try{return H.call(e)}catch(e){}try{return e+""}catch(e){}}return""}function je(e,t){return e===t||e!=e&&t!=t}(re&&Re(new re(new ArrayBuffer(1)))!=w||oe&&Re(new oe)!=d||ie&&Re(ie.resolve())!=h||ae&&Re(new ae)!=g||se&&Re(new se)!=y)&&(Re=function(e){var t=Se(e),n=t==p?e.constructor:void 0,r=n?Fe(n):"";if(r)switch(r){case ue:return w;case ce:return d;case de:return h;case fe:return g;case pe:return y}return t});var Ne=xe(function(){return arguments}())?xe:function(e){return Ue(e)&&W.call(e,"callee")&&!X.call(e,"callee")},Ie=Array.isArray,Le=te||function(){return!1};function Me(e){if(!Be(e))return!1;var t=Se(e);return t==c||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}function De(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=o}function Be(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Ue(e){return null!=e&&"object"==typeof e}var ze=F?function(e){return function(t){return e(t)}}(F):function(e){return Ue(e)&&De(e.length)&&!!x[Se(e)]};function $e(e){return null!=(t=e)&&De(t.length)&&!Me(t)?function(e,t){var n=Ie(e),r=!n&&Ne(e),o=!n&&!r&&Le(e),i=!n&&!r&&!o&&ze(e),a=n||r||o||i,s=a?function(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}(e.length,String):[],l=s.length;for(var u in e)!t&&!W.call(e,u)||a&&("length"==u||o&&("offset"==u||"parent"==u)||i&&("buffer"==u||"byteLength"==u||"byteOffset"==u)||Te(u,l))||s.push(u);return s}(e):function(e){if(n=(t=e)&&t.constructor,t!==("function"==typeof n&&n.prototype||z))return ne(e);var t,n,r=[];for(var o in Object(e))W.call(e,o)&&"constructor"!=o&&r.push(o);return r}(e);var t}e.exports=function(e,t){return Ae(e,t)}},8157:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)([(0,i.jsx)("circle",{cx:"6.18",cy:"17.82",r:"2.18"},"0"),(0,i.jsx)("path",{d:"M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"},"1")],"RssFeed");t.A=a},8168:(e,t,n)=>{"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)({}).hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},r.apply(null,arguments)}n.d(t,{A:()=>r})},8184:(e,t,n)=>{"use strict";var r,o=Object.prototype.toString,i=Function.prototype.toString,a=/^\s*(?:function)?\*/,s=n(9092)(),l=Object.getPrototypeOf;e.exports=function(e){if("function"!=typeof e)return!1;if(a.test(i.call(e)))return!0;if(!s)return"[object GeneratorFunction]"===o.call(e);if(!l)return!1;if(void 0===r){var t=function(){if(!s)return!1;try{return Function("return function*() {}")()}catch(e){}}();r=!!t&&l(t)}return l(e)===r}},8228:(e,t,n)=>{"use strict";n.r(t),n.d(t,{capitalize:()=>o.A,createChainedFunction:()=>i,createSvgIcon:()=>a.A,debounce:()=>s.A,deprecatedPropType:()=>l,isMuiElement:()=>u.A,ownerDocument:()=>c.A,ownerWindow:()=>d.A,requirePropFactory:()=>f,setRef:()=>p,unstable_ClassNameGenerator:()=>E,unstable_useEnhancedEffect:()=>h.A,unstable_useId:()=>m.A,unsupportedProp:()=>g,useControlled:()=>v.A,useEventCallback:()=>y.A,useForkRef:()=>b.A,useIsFocusVisible:()=>w.A});var r=n(9071),o=n(8466);const i=n(1210).A;var a=n(561),s=n(1935);const l=function(e,t){return()=>null};var u=n(6288),c=n(6248),d=n(3749);n(8168);const f=function(e,t){return()=>null},p=n(989).A;var h=n(2778),m=n(1668);const g=function(e,t,n,r,o){return null};var v=n(1159),y=n(3034),b=n(6852),w=n(8851);const E={configure:e=>{r.A.configure(e)}}},8232:(e,t)=>{"use strict";Object.prototype.toString},8277:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M9 16h2V8H9v8zm3-14C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm1-4h2V8h-2v8z"}),"PauseCircleOutline");t.A=a},8287:(e,t,n)=>{"use strict";const r=n(7526),o=n(251),i="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=l,t.SlowBuffer=function(e){return+e!=e&&(e=0),l.alloc(+e)},t.INSPECT_MAX_BYTES=50;const a=2147483647;function s(e){if(e>a)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,l.prototype),t}function l(e,t,n){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return d(e)}return u(e,t,n)}function u(e,t,n){if("string"==typeof e)return function(e,t){if("string"==typeof t&&""!==t||(t="utf8"),!l.isEncoding(t))throw new TypeError("Unknown encoding: "+t);const n=0|m(e,t);let r=s(n);const o=r.write(e,t);return o!==n&&(r=r.slice(0,o)),r}(e,t);if(ArrayBuffer.isView(e))return function(e){if(J(e,Uint8Array)){const t=new Uint8Array(e);return p(t.buffer,t.byteOffset,t.byteLength)}return f(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(J(e,ArrayBuffer)||e&&J(e.buffer,ArrayBuffer))return p(e,t,n);if("undefined"!=typeof SharedArrayBuffer&&(J(e,SharedArrayBuffer)||e&&J(e.buffer,SharedArrayBuffer)))return p(e,t,n);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');const r=e.valueOf&&e.valueOf();if(null!=r&&r!==e)return l.from(r,t,n);const o=function(e){if(l.isBuffer(e)){const t=0|h(e.length),n=s(t);return 0===n.length||e.copy(n,0,0,t),n}return void 0!==e.length?"number"!=typeof e.length||Y(e.length)?s(0):f(e):"Buffer"===e.type&&Array.isArray(e.data)?f(e.data):void 0}(e);if(o)return o;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return l.from(e[Symbol.toPrimitive]("string"),t,n);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function c(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function d(e){return c(e),s(e<0?0:0|h(e))}function f(e){const t=e.length<0?0:0|h(e.length),n=s(t);for(let r=0;r<t;r+=1)n[r]=255&e[r];return n}function p(e,t,n){if(t<0||e.byteLength<t)throw new RangeError('"offset" is outside of buffer bounds');if(e.byteLength<t+(n||0))throw new RangeError('"length" is outside of buffer bounds');let r;return r=void 0===t&&void 0===n?new Uint8Array(e):void 0===n?new Uint8Array(e,t):new Uint8Array(e,t,n),Object.setPrototypeOf(r,l.prototype),r}function h(e){if(e>=a)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a.toString(16)+" bytes");return 0|e}function m(e,t){if(l.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||J(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);const n=e.length,r=arguments.length>2&&!0===arguments[2];if(!r&&0===n)return 0;let o=!1;for(;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return q(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return K(e).length;default:if(o)return r?-1:q(e).length;t=(""+t).toLowerCase(),o=!0}}function g(e,t,n){let r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return R(this,t,n);case"utf8":case"utf-8":return _(this,t,n);case"ascii":return O(this,t,n);case"latin1":case"binary":return P(this,t,n);case"base64":return k(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function v(e,t,n){const r=e[t];e[t]=e[n],e[n]=r}function y(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),Y(n=+n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=l.from(t,r)),l.isBuffer(t))return 0===t.length?-1:b(e,t,n,r,o);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):b(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,o){let i,a=1,s=e.length,l=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,l/=2,n/=2}function u(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){let r=-1;for(i=n;i<s;i++)if(u(e,i)===u(t,-1===r?0:i-r)){if(-1===r&&(r=i),i-r+1===l)return r*a}else-1!==r&&(i-=i-r),r=-1}else for(n+l>s&&(n=s-l),i=n;i>=0;i--){let n=!0;for(let r=0;r<l;r++)if(u(e,i+r)!==u(t,r)){n=!1;break}if(n)return i}return-1}function w(e,t,n,r){n=Number(n)||0;const o=e.length-n;r?(r=Number(r))>o&&(r=o):r=o;const i=t.length;let a;for(r>i/2&&(r=i/2),a=0;a<r;++a){const r=parseInt(t.substr(2*a,2),16);if(Y(r))return a;e[n+a]=r}return a}function E(e,t,n,r){return G(q(t,e.length-n),e,n,r)}function S(e,t,n,r){return G(function(e){const t=[];for(let n=0;n<e.length;++n)t.push(255&e.charCodeAt(n));return t}(t),e,n,r)}function x(e,t,n,r){return G(K(t),e,n,r)}function A(e,t,n,r){return G(function(e,t){let n,r,o;const i=[];for(let a=0;a<e.length&&!((t-=2)<0);++a)n=e.charCodeAt(a),r=n>>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function k(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function _(e,t,n){n=Math.min(e.length,n);const r=[];let o=t;for(;o<n;){const t=e[o];let i=null,a=t>239?4:t>223?3:t>191?2:1;if(o+a<=n){let n,r,s,l;switch(a){case 1:t<128&&(i=t);break;case 2:n=e[o+1],128==(192&n)&&(l=(31&t)<<6|63&n,l>127&&(i=l));break;case 3:n=e[o+1],r=e[o+2],128==(192&n)&&128==(192&r)&&(l=(15&t)<<12|(63&n)<<6|63&r,l>2047&&(l<55296||l>57343)&&(i=l));break;case 4:n=e[o+1],r=e[o+2],s=e[o+3],128==(192&n)&&128==(192&r)&&128==(192&s)&&(l=(15&t)<<18|(63&n)<<12|(63&r)<<6|63&s,l>65535&&l<1114112&&(i=l))}}null===i?(i=65533,a=1):i>65535&&(i-=65536,r.push(i>>>10&1023|55296),i=56320|1023&i),r.push(i),o+=a}return function(e){const t=e.length;if(t<=C)return String.fromCharCode.apply(String,e);let n="",r=0;for(;r<t;)n+=String.fromCharCode.apply(String,e.slice(r,r+=C));return n}(r)}t.kMaxLength=a,l.TYPED_ARRAY_SUPPORT=function(){try{const e=new Uint8Array(1),t={foo:function(){return 42}};return Object.setPrototypeOf(t,Uint8Array.prototype),Object.setPrototypeOf(e,t),42===e.foo()}catch(e){return!1}}(),l.TYPED_ARRAY_SUPPORT||"undefined"==typeof console||"function"!=typeof console.error||console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(l.prototype,"parent",{enumerable:!0,get:function(){if(l.isBuffer(this))return this.buffer}}),Object.defineProperty(l.prototype,"offset",{enumerable:!0,get:function(){if(l.isBuffer(this))return this.byteOffset}}),l.poolSize=8192,l.from=function(e,t,n){return u(e,t,n)},Object.setPrototypeOf(l.prototype,Uint8Array.prototype),Object.setPrototypeOf(l,Uint8Array),l.alloc=function(e,t,n){return function(e,t,n){return c(e),e<=0?s(e):void 0!==t?"string"==typeof n?s(e).fill(t,n):s(e).fill(t):s(e)}(e,t,n)},l.allocUnsafe=function(e){return d(e)},l.allocUnsafeSlow=function(e){return d(e)},l.isBuffer=function(e){return null!=e&&!0===e._isBuffer&&e!==l.prototype},l.compare=function(e,t){if(J(e,Uint8Array)&&(e=l.from(e,e.offset,e.byteLength)),J(t,Uint8Array)&&(t=l.from(t,t.offset,t.byteLength)),!l.isBuffer(e)||!l.isBuffer(t))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(e===t)return 0;let n=e.length,r=t.length;for(let o=0,i=Math.min(n,r);o<i;++o)if(e[o]!==t[o]){n=e[o],r=t[o];break}return n<r?-1:r<n?1:0},l.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},l.concat=function(e,t){if(!Array.isArray(e))throw new TypeError('"list" argument must be an Array of Buffers');if(0===e.length)return l.alloc(0);let n;if(void 0===t)for(t=0,n=0;n<e.length;++n)t+=e[n].length;const r=l.allocUnsafe(t);let o=0;for(n=0;n<e.length;++n){let t=e[n];if(J(t,Uint8Array))o+t.length>r.length?(l.isBuffer(t)||(t=l.from(t)),t.copy(r,o)):Uint8Array.prototype.set.call(r,t,o);else{if(!l.isBuffer(t))throw new TypeError('"list" argument must be an Array of Buffers');t.copy(r,o)}o+=t.length}return r},l.byteLength=m,l.prototype._isBuffer=!0,l.prototype.swap16=function(){const e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let t=0;t<e;t+=2)v(this,t,t+1);return this},l.prototype.swap32=function(){const e=this.length;if(e%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(let t=0;t<e;t+=4)v(this,t,t+3),v(this,t+1,t+2);return this},l.prototype.swap64=function(){const e=this.length;if(e%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(let t=0;t<e;t+=8)v(this,t,t+7),v(this,t+1,t+6),v(this,t+2,t+5),v(this,t+3,t+4);return this},l.prototype.toString=function(){const e=this.length;return 0===e?"":0===arguments.length?_(this,0,e):g.apply(this,arguments)},l.prototype.toLocaleString=l.prototype.toString,l.prototype.equals=function(e){if(!l.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===l.compare(this,e)},l.prototype.inspect=function(){let e="";const n=t.INSPECT_MAX_BYTES;return e=this.toString("hex",0,n).replace(/(.{2})/g,"$1 ").trim(),this.length>n&&(e+=" ... "),"<Buffer "+e+">"},i&&(l.prototype[i]=l.prototype.inspect),l.prototype.compare=function(e,t,n,r,o){if(J(e,Uint8Array)&&(e=l.from(e,e.offset,e.byteLength)),!l.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;let i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0);const s=Math.min(i,a),u=this.slice(r,o),c=e.slice(t,n);for(let e=0;e<s;++e)if(u[e]!==c[e]){i=u[e],a=c[e];break}return i<a?-1:a<i?1:0},l.prototype.includes=function(e,t,n){return-1!==this.indexOf(e,t,n)},l.prototype.indexOf=function(e,t,n){return y(this,e,t,n,!0)},l.prototype.lastIndexOf=function(e,t,n){return y(this,e,t,n,!1)},l.prototype.write=function(e,t,n,r){if(void 0===t)r="utf8",n=this.length,t=0;else if(void 0===n&&"string"==typeof t)r=t,n=this.length,t=0;else{if(!isFinite(t))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");t>>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}const o=this.length-t;if((void 0===n||n>o)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");let i=!1;for(;;)switch(r){case"hex":return w(this,e,t,n);case"utf8":case"utf-8":return E(this,e,t,n);case"ascii":case"latin1":case"binary":return S(this,e,t,n);case"base64":return x(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return A(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const C=4096;function O(e,t,n){let r="";n=Math.min(e.length,n);for(let o=t;o<n;++o)r+=String.fromCharCode(127&e[o]);return r}function P(e,t,n){let r="";n=Math.min(e.length,n);for(let o=t;o<n;++o)r+=String.fromCharCode(e[o]);return r}function R(e,t,n){const r=e.length;(!t||t<0)&&(t=0),(!n||n<0||n>r)&&(n=r);let o="";for(let r=t;r<n;++r)o+=X[e[r]];return o}function T(e,t,n){const r=e.slice(t,n);let o="";for(let e=0;e<r.length-1;e+=2)o+=String.fromCharCode(r[e]+256*r[e+1]);return o}function F(e,t,n){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(e+t>n)throw new RangeError("Trying to access beyond buffer length")}function j(e,t,n,r,o,i){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||t<i)throw new RangeError('"value" argument is out of bounds');if(n+r>e.length)throw new RangeError("Index out of range")}function N(e,t,n,r,o){$(t,r,o,e,n,7);let i=Number(t&BigInt(4294967295));e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i;let a=Number(t>>BigInt(32)&BigInt(4294967295));return e[n++]=a,a>>=8,e[n++]=a,a>>=8,e[n++]=a,a>>=8,e[n++]=a,n}function I(e,t,n,r,o){$(t,r,o,e,n,7);let i=Number(t&BigInt(4294967295));e[n+7]=i,i>>=8,e[n+6]=i,i>>=8,e[n+5]=i,i>>=8,e[n+4]=i;let a=Number(t>>BigInt(32)&BigInt(4294967295));return e[n+3]=a,a>>=8,e[n+2]=a,a>>=8,e[n+1]=a,a>>=8,e[n]=a,n+8}function L(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function M(e,t,n,r,i){return t=+t,n>>>=0,i||L(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function D(e,t,n,r,i){return t=+t,n>>>=0,i||L(e,0,n,8),o.write(e,t,n,r,52,8),n+8}l.prototype.slice=function(e,t){const n=this.length;(e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t<e&&(t=e);const r=this.subarray(e,t);return Object.setPrototypeOf(r,l.prototype),r},l.prototype.readUintLE=l.prototype.readUIntLE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);let r=this[e],o=1,i=0;for(;++i<t&&(o*=256);)r+=this[e+i]*o;return r},l.prototype.readUintBE=l.prototype.readUIntBE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);let r=this[e+--t],o=1;for(;t>0&&(o*=256);)r+=this[e+--t]*o;return r},l.prototype.readUint8=l.prototype.readUInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),this[e]},l.prototype.readUint16LE=l.prototype.readUInt16LE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUint16BE=l.prototype.readUInt16BE=function(e,t){return e>>>=0,t||F(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUint32LE=l.prototype.readUInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUint32BE=l.prototype.readUInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readBigUInt64LE=Q((function(e){H(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||W(e,this.length-8);const r=t+256*this[++e]+65536*this[++e]+this[++e]*2**24,o=this[++e]+256*this[++e]+65536*this[++e]+n*2**24;return BigInt(r)+(BigInt(o)<<BigInt(32))})),l.prototype.readBigUInt64BE=Q((function(e){H(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||W(e,this.length-8);const r=t*2**24+65536*this[++e]+256*this[++e]+this[++e],o=this[++e]*2**24+65536*this[++e]+256*this[++e]+n;return(BigInt(r)<<BigInt(32))+BigInt(o)})),l.prototype.readIntLE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);let r=this[e],o=1,i=0;for(;++i<t&&(o*=256);)r+=this[e+i]*o;return o*=128,r>=o&&(r-=Math.pow(2,8*t)),r},l.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||F(e,t,this.length);let r=t,o=1,i=this[e+--r];for(;r>0&&(o*=256);)i+=this[e+--r]*o;return o*=128,i>=o&&(i-=Math.pow(2,8*t)),i},l.prototype.readInt8=function(e,t){return e>>>=0,t||F(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){e>>>=0,t||F(e,2,this.length);const n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(e,t){e>>>=0,t||F(e,2,this.length);const n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return e>>>=0,t||F(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readBigInt64LE=Q((function(e){H(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||W(e,this.length-8);const r=this[e+4]+256*this[e+5]+65536*this[e+6]+(n<<24);return(BigInt(r)<<BigInt(32))+BigInt(t+256*this[++e]+65536*this[++e]+this[++e]*2**24)})),l.prototype.readBigInt64BE=Q((function(e){H(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||W(e,this.length-8);const r=(t<<24)+65536*this[++e]+256*this[++e]+this[++e];return(BigInt(r)<<BigInt(32))+BigInt(this[++e]*2**24+65536*this[++e]+256*this[++e]+n)})),l.prototype.readFloatLE=function(e,t){return e>>>=0,t||F(e,4,this.length),o.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return e>>>=0,t||F(e,4,this.length),o.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return e>>>=0,t||F(e,8,this.length),o.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return e>>>=0,t||F(e,8,this.length),o.read(this,e,!1,52,8)},l.prototype.writeUintLE=l.prototype.writeUIntLE=function(e,t,n,r){e=+e,t>>>=0,n>>>=0,r||j(this,e,t,n,Math.pow(2,8*n)-1,0);let o=1,i=0;for(this[t]=255&e;++i<n&&(o*=256);)this[t+i]=e/o&255;return t+n},l.prototype.writeUintBE=l.prototype.writeUIntBE=function(e,t,n,r){e=+e,t>>>=0,n>>>=0,r||j(this,e,t,n,Math.pow(2,8*n)-1,0);let o=n-1,i=1;for(this[t+o]=255&e;--o>=0&&(i*=256);)this[t+o]=e/i&255;return t+n},l.prototype.writeUint8=l.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,1,255,0),this[t]=255&e,t+1},l.prototype.writeUint16LE=l.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeUint16BE=l.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeUint32LE=l.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},l.prototype.writeUint32BE=l.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigUInt64LE=Q((function(e,t=0){return N(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeBigUInt64BE=Q((function(e,t=0){return I(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);j(this,e,t,n,r-1,-r)}let o=0,i=1,a=0;for(this[t]=255&e;++o<n&&(i*=256);)e<0&&0===a&&0!==this[t+o-1]&&(a=1),this[t+o]=(e/i|0)-a&255;return t+n},l.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);j(this,e,t,n,r-1,-r)}let o=n-1,i=1,a=0;for(this[t+o]=255&e;--o>=0&&(i*=256);)e<0&&0===a&&0!==this[t+o+1]&&(a=1),this[t+o]=(e/i|0)-a&255;return t+n},l.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},l.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||j(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigInt64LE=Q((function(e,t=0){return N(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeBigInt64BE=Q((function(e,t=0){return I(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeFloatLE=function(e,t,n){return M(this,e,t,!0,n)},l.prototype.writeFloatBE=function(e,t,n){return M(this,e,t,!1,n)},l.prototype.writeDoubleLE=function(e,t,n){return D(this,e,t,!0,n)},l.prototype.writeDoubleBE=function(e,t,n){return D(this,e,t,!1,n)},l.prototype.copy=function(e,t,n,r){if(!l.isBuffer(e))throw new TypeError("argument should be a Buffer");if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r<n&&(r=n),r===n)return 0;if(0===e.length||0===this.length)return 0;if(t<0)throw new RangeError("targetStart out of bounds");if(n<0||n>=this.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t<r-n&&(r=e.length-t+n);const o=r-n;return this===e&&"function"==typeof Uint8Array.prototype.copyWithin?this.copyWithin(t,n,r):Uint8Array.prototype.set.call(e,this.subarray(n,r),t),o},l.prototype.fill=function(e,t,n,r){if("string"==typeof e){if("string"==typeof t?(r=t,t=0,n=this.length):"string"==typeof n&&(r=n,n=this.length),void 0!==r&&"string"!=typeof r)throw new TypeError("encoding must be a string");if("string"==typeof r&&!l.isEncoding(r))throw new TypeError("Unknown encoding: "+r);if(1===e.length){const t=e.charCodeAt(0);("utf8"===r&&t<128||"latin1"===r)&&(e=t)}}else"number"==typeof e?e&=255:"boolean"==typeof e&&(e=Number(e));if(t<0||this.length<t||this.length<n)throw new RangeError("Out of range index");if(n<=t)return this;let o;if(t>>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(o=t;o<n;++o)this[o]=e;else{const i=l.isBuffer(e)?e:l.from(e,r),a=i.length;if(0===a)throw new TypeError('The value "'+e+'" is invalid for argument "value"');for(o=0;o<n-t;++o)this[o+t]=i[o%a]}return this};const B={};function U(e,t,n){B[e]=class extends n{constructor(){super(),Object.defineProperty(this,"message",{value:t.apply(this,arguments),writable:!0,configurable:!0}),this.name=`${this.name} [${e}]`,this.stack,delete this.name}get code(){return e}set code(e){Object.defineProperty(this,"code",{configurable:!0,enumerable:!0,value:e,writable:!0})}toString(){return`${this.name} [${e}]: ${this.message}`}}}function z(e){let t="",n=e.length;const r="-"===e[0]?1:0;for(;n>=r+4;n-=3)t=`_${e.slice(n-3,n)}${t}`;return`${e.slice(0,n)}${t}`}function $(e,t,n,r,o,i){if(e>n||e<t){const r="bigint"==typeof t?"n":"";let o;throw o=i>3?0===t||t===BigInt(0)?`>= 0${r} and < 2${r} ** ${8*(i+1)}${r}`:`>= -(2${r} ** ${8*(i+1)-1}${r}) and < 2 ** ${8*(i+1)-1}${r}`:`>= ${t}${r} and <= ${n}${r}`,new B.ERR_OUT_OF_RANGE("value",o,e)}!function(e,t,n){H(t,"offset"),void 0!==e[t]&&void 0!==e[t+n]||W(t,e.length-(n+1))}(r,o,i)}function H(e,t){if("number"!=typeof e)throw new B.ERR_INVALID_ARG_TYPE(t,"number",e)}function W(e,t,n){if(Math.floor(e)!==e)throw H(e,n),new B.ERR_OUT_OF_RANGE(n||"offset","an integer",e);if(t<0)throw new B.ERR_BUFFER_OUT_OF_BOUNDS;throw new B.ERR_OUT_OF_RANGE(n||"offset",`>= ${n?1:0} and <= ${t}`,e)}U("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?`${e} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),U("ERR_INVALID_ARG_TYPE",(function(e,t){return`The "${e}" argument must be of type number. Received type ${typeof t}`}),TypeError),U("ERR_OUT_OF_RANGE",(function(e,t,n){let r=`The value of "${e}" is out of range.`,o=n;return Number.isInteger(n)&&Math.abs(n)>2**32?o=z(String(n)):"bigint"==typeof n&&(o=String(n),(n>BigInt(2)**BigInt(32)||n<-(BigInt(2)**BigInt(32)))&&(o=z(o)),o+="n"),r+=` It must be ${t}. Received ${o}`,r}),RangeError);const V=/[^+/0-9A-Za-z-_]/g;function q(e,t){let n;t=t||1/0;const r=e.length;let o=null;const i=[];for(let a=0;a<r;++a){if(n=e.charCodeAt(a),n>55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function K(e){return r.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(V,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function G(e,t,n,r){let o;for(o=0;o<r&&!(o+n>=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function J(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function Y(e){return e!=e}const X=function(){const e="0123456789abcdef",t=new Array(256);for(let n=0;n<16;++n){const r=16*n;for(let o=0;o<16;++o)t[r+o]=e[n]+e[o]}return t}();function Q(e){return"undefined"==typeof BigInt?Z:e}function Z(){throw new Error("BigInt not supported")}},8312:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r="$$material"},8399:(e,t,n)=>{(t=e.exports=n(5412)).Stream=t,t.Readable=t,t.Writable=n(6708),t.Duplex=n(5382),t.Transform=n(4610),t.PassThrough=n(3600),t.finished=n(6238),t.pipeline=n(7758)},8413:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});var r=n(1609);function o(e,t,n="Mui"){const o={};return t.forEach((t=>{o[t]=(0,r.A)(e,t,n)})),o}},8466:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(3967).A},8550:e=>{"use strict";e.exports=JSON.parse('{"en":{"translation":{"delete_resource":"Deleting a section does not stop the corresponding resource. You might want to stop the resource before.","add_node_label":"Node labels can be used in node selector expressions."}},"fr":{"translation":{"{{count}} selected":"{{count}} sÃ©lectionnÃ©s","15min average":"Moyenne sur 15min","Access Token":"Jeton d\'accÃ¨s","Action in progress.":"Action en cours.","add_node_label":"Les labels de noeud peuvent Ãªtre utilisÃ©s dans les expressions du sÃ©lecteur de noeuds.","Add Node Label":"Ajouter un label de noeud","Addresses":"Adresses","Agent":"Agent","Arch":"Arch","Arbitrators":"Arbitres","Authenticated via":"AuthentifiÃ© via","Availability":"DisponibilitÃ©","Availability is undefined":"DisponibilitÃ© indÃ©terminÃ©e","Available":"Disponible","Banks":"Banques","Bios":"Bios","Change Node Label":"Changer un label de noeud","Cluster":"Grappe","Cores":"Coeurs","Delete section {{rid}} of {{path}}":"Supprimer la section {{rid}} de {{path}}","delete_resource":"Supprimer une section ne stoppe pas la resource correspondante. Vous pourriez vouloir stopper la ressource au prÃ©alable.","Dies":"Puces","Enclosure":"Enceinte","Env":"Env","Expires at {{date}}":"Expire le {{date}}","Filter":"Filtre","Filters":"Filtres","Frequency":"FrÃ©quence","Frozen. Orchestration is disabled.":"GelÃ©. L\'orchestration est dÃ©sactivÃ©e.","Grants":"Permissions","Hardware":"MatÃ©riel","Heartbeats":"Pulseurs","Initiators":"Initiateurs","Kernel":"Noyau","Key":"Clef","Keys":"Clefs","Kind":"Genre","Kinds":"Genres","Last":"Dernier","Load":"Charge","Load15m":"Charge sur 15min","Log":"Journal","Logout":"Logout","Manufacturer":"Fabricant","Memory":"MÃ©moire","Mem Avail":"MÃ©moire Dispo","MaxAvail":"MaxDispo","MinAvail":"MinDispo","Model":"ModÃ¨le","Name":"Nom","Network":"RÃ©seau","Networks":"RÃ©seaux","Node":"Noeud","Nodes":"Noeuds","Nodes run different versions.":"Les noeuds utilisent des versions diffÃ©rentes.","Nodes run incompatible versions. Orchestration is disabled.":"Les noeuds utilisent des versions incompatibles. L\'orchestration est dÃ©sactivÃ©e.","Namespace":"Espace de noms","Namespaces":"Espaces de noms","Not provisioned. Orchestration is disabled.":"Non-provisionnÃ©. L\'orchestration est dÃ©sactivÃ©e.","Object":"Objet","Objects":"Objets","Openid Provider":"Fournisseur Openid","Overall state is {{state}}.":"L\'Ã©tat gÃ©nÃ©ral est {{state}}.","Path":"Chemin","Placement leader.":"Leader de placement.","Placement is not optimal.":"Le placement est non-optimal.","Pools":"RÃ©servoirs","Processor":"Processeur","Release":"Release","Regular Expression":"Expression RÃ©guliÃ¨re","Search In":"Chercher dans","Search Regular Expression":"Rechercher l\'expression rÃ©guliÃ¨re","Select all":"Tout sÃ©lectionner","Serial":"No de sÃ©rie","Server":"Serveur","Size":"Taille","Slots":"Slots","SP":"SP","State":"Etat","Swap":"Echange","Swap Avail":"Echange dispo","System":"SystÃ¨me","Target state is {{state}}.":"L\'Ã©tat ciblÃ© est {{state}}.","Timezone":"Fuseau horaire","This node feeds the collector.":"Ce noeud alimente le collecteur.","Threads":"Threads","Unavailable":"Indisponible","Usage":"Utilisation","User":"Utilisateur","Users":"Utilisateurs","Used":"UtilisÃ©","Value":"Valeur","Vendor":"Vendeur","Version":"Version","Warning":"Attention"}}}')},8552:(e,t,n)=>{"use strict";n.d(t,{A:()=>N});var r=n(6468),o=n(6481),i=n(4620);const a=function(...e){const t=e.reduce(((e,t)=>(t.filterProps.forEach((n=>{e[n]=t})),e)),{}),n=e=>Object.keys(e).reduce(((n,r)=>t[r]?(0,i.A)(n,t[r](e)):n),{});return n.propTypes={},n.filterProps=e.reduce(((e,t)=>e.concat(t.filterProps)),[]),n};var s=n(9452);function l(e){return"number"!=typeof e?e:`${e}px solid`}function u(e,t){return(0,o.Ay)({prop:e,themeKey:"borders",transform:t})}const c=u("border",l),d=u("borderTop",l),f=u("borderRight",l),p=u("borderBottom",l),h=u("borderLeft",l),m=u("borderColor"),g=u("borderTopColor"),v=u("borderRightColor"),y=u("borderBottomColor"),b=u("borderLeftColor"),w=u("outline",l),E=u("outlineColor"),S=e=>{if(void 0!==e.borderRadius&&null!==e.borderRadius){const t=(0,r.MA)(e.theme,"shape.borderRadius",4,"borderRadius"),n=e=>({borderRadius:(0,r._W)(t,e)});return(0,s.NI)(e,e.borderRadius,n)}return null};S.propTypes={},S.filterProps=["borderRadius"],a(c,d,f,p,h,m,g,v,y,b,S,w,E);const x=e=>{if(void 0!==e.gap&&null!==e.gap){const t=(0,r.MA)(e.theme,"spacing",8,"gap"),n=e=>({gap:(0,r._W)(t,e)});return(0,s.NI)(e,e.gap,n)}return null};x.propTypes={},x.filterProps=["gap"];const A=e=>{if(void 0!==e.columnGap&&null!==e.columnGap){const t=(0,r.MA)(e.theme,"spacing",8,"columnGap"),n=e=>({columnGap:(0,r._W)(t,e)});return(0,s.NI)(e,e.columnGap,n)}return null};A.propTypes={},A.filterProps=["columnGap"];const k=e=>{if(void 0!==e.rowGap&&null!==e.rowGap){const t=(0,r.MA)(e.theme,"spacing",8,"rowGap"),n=e=>({rowGap:(0,r._W)(t,e)});return(0,s.NI)(e,e.rowGap,n)}return null};function _(e,t){return"grey"===t?t:e}function C(e){return e<=1&&0!==e?100*e+"%":e}k.propTypes={},k.filterProps=["rowGap"],a(x,A,k,(0,o.Ay)({prop:"gridColumn"}),(0,o.Ay)({prop:"gridRow"}),(0,o.Ay)({prop:"gridAutoFlow"}),(0,o.Ay)({prop:"gridAutoColumns"}),(0,o.Ay)({prop:"gridAutoRows"}),(0,o.Ay)({prop:"gridTemplateColumns"}),(0,o.Ay)({prop:"gridTemplateRows"}),(0,o.Ay)({prop:"gridTemplateAreas"}),(0,o.Ay)({prop:"gridArea"})),a((0,o.Ay)({prop:"color",themeKey:"palette",transform:_}),(0,o.Ay)({prop:"bgcolor",cssProperty:"backgroundColor",themeKey:"palette",transform:_}),(0,o.Ay)({prop:"backgroundColor",themeKey:"palette",transform:_}));const O=(0,o.Ay)({prop:"width",transform:C}),P=e=>{if(void 0!==e.maxWidth&&null!==e.maxWidth){const t=t=>{var n,r;const o=(null==(n=e.theme)||null==(n=n.breakpoints)||null==(n=n.values)?void 0:n[t])||s.zu[t];return o?"px"!==(null==(r=e.theme)||null==(r=r.breakpoints)?void 0:r.unit)?{maxWidth:`${o}${e.theme.breakpoints.unit}`}:{maxWidth:o}:{maxWidth:C(t)}};return(0,s.NI)(e,e.maxWidth,t)}return null};P.filterProps=["maxWidth"];const R=(0,o.Ay)({prop:"minWidth",transform:C}),T=(0,o.Ay)({prop:"height",transform:C}),F=(0,o.Ay)({prop:"maxHeight",transform:C}),j=(0,o.Ay)({prop:"minHeight",transform:C}),N=((0,o.Ay)({prop:"size",cssProperty:"width",transform:C}),(0,o.Ay)({prop:"size",cssProperty:"height",transform:C}),a(O,P,R,T,F,j,(0,o.Ay)({prop:"boxSizing"})),{border:{themeKey:"borders",transform:l},borderTop:{themeKey:"borders",transform:l},borderRight:{themeKey:"borders",transform:l},borderBottom:{themeKey:"borders",transform:l},borderLeft:{themeKey:"borders",transform:l},borderColor:{themeKey:"palette"},borderTopColor:{themeKey:"palette"},borderRightColor:{themeKey:"palette"},borderBottomColor:{themeKey:"palette"},borderLeftColor:{themeKey:"palette"},outline:{themeKey:"borders",transform:l},outlineColor:{themeKey:"palette"},borderRadius:{themeKey:"shape.borderRadius",style:S},color:{themeKey:"palette",transform:_},bgcolor:{themeKey:"palette",cssProperty:"backgroundColor",transform:_},backgroundColor:{themeKey:"palette",transform:_},p:{style:r.Ms},pt:{style:r.Ms},pr:{style:r.Ms},pb:{style:r.Ms},pl:{style:r.Ms},px:{style:r.Ms},py:{style:r.Ms},padding:{style:r.Ms},paddingTop:{style:r.Ms},paddingRight:{style:r.Ms},paddingBottom:{style:r.Ms},paddingLeft:{style:r.Ms},paddingX:{style:r.Ms},paddingY:{style:r.Ms},paddingInline:{style:r.Ms},paddingInlineStart:{style:r.Ms},paddingInlineEnd:{style:r.Ms},paddingBlock:{style:r.Ms},paddingBlockStart:{style:r.Ms},paddingBlockEnd:{style:r.Ms},m:{style:r.Lc},mt:{style:r.Lc},mr:{style:r.Lc},mb:{style:r.Lc},ml:{style:r.Lc},mx:{style:r.Lc},my:{style:r.Lc},margin:{style:r.Lc},marginTop:{style:r.Lc},marginRight:{style:r.Lc},marginBottom:{style:r.Lc},marginLeft:{style:r.Lc},marginX:{style:r.Lc},marginY:{style:r.Lc},marginInline:{style:r.Lc},marginInlineStart:{style:r.Lc},marginInlineEnd:{style:r.Lc},marginBlock:{style:r.Lc},marginBlockStart:{style:r.Lc},marginBlockEnd:{style:r.Lc},displayPrint:{cssProperty:!1,transform:e=>({"@media print":{display:e}})},display:{},overflow:{},textOverflow:{},visibility:{},whiteSpace:{},flexBasis:{},flexDirection:{},flexWrap:{},justifyContent:{},alignItems:{},alignContent:{},order:{},flex:{},flexGrow:{},flexShrink:{},alignSelf:{},justifyItems:{},justifySelf:{},gap:{style:x},rowGap:{style:k},columnGap:{style:A},gridColumn:{},gridRow:{},gridAutoFlow:{},gridAutoColumns:{},gridAutoRows:{},gridTemplateColumns:{},gridTemplateRows:{},gridTemplateAreas:{},gridArea:{},position:{},zIndex:{themeKey:"zIndex"},top:{},right:{},bottom:{},left:{},boxShadow:{themeKey:"shadows"},width:{transform:C},maxWidth:{style:P},minWidth:{transform:C},height:{transform:C},maxHeight:{transform:C},minHeight:{transform:C},boxSizing:{},fontFamily:{themeKey:"typography"},fontSize:{themeKey:"typography"},fontStyle:{themeKey:"typography"},fontWeight:{themeKey:"typography"},letterSpacing:{},textTransform:{},lineHeight:{},textAlign:{},typography:{cssProperty:!1,themeKey:"typography"}})},8586:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M8 5v14l11-7z"}),"PlayArrow");t.A=a},8587:(e,t,n)=>{"use strict";function r(e,t){if(null==e)return{};var n={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(-1!==t.indexOf(r))continue;n[r]=e[r]}return n}n.d(t,{A:()=>r})},8597:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34a.9959.9959 0 0 0-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"}),"Edit");t.A=a},8636:(e,t,n)=>{"use strict";var r=n(920),o=n(7720),i=n(4765),a=Object.prototype.hasOwnProperty,s={brackets:function(e){return e+"[]"},comma:"comma",indices:function(e,t){return e+"["+t+"]"},repeat:function(e){return e}},l=Array.isArray,u=Array.prototype.push,c=function(e,t){u.apply(e,l(t)?t:[t])},d=Date.prototype.toISOString,f=i.default,p={addQueryPrefix:!1,allowDots:!1,charset:"utf-8",charsetSentinel:!1,delimiter:"&",encode:!0,encoder:o.encode,encodeValuesOnly:!1,format:f,formatter:i.formatters[f],indices:!1,serializeDate:function(e){return d.call(e)},skipNulls:!1,strictNullHandling:!1},h={},m=function e(t,n,i,a,s,u,d,f,m,g,v,y,b,w,E,S){for(var x,A=t,k=S,_=0,C=!1;void 0!==(k=k.get(h))&&!C;){var O=k.get(t);if(_+=1,void 0!==O){if(O===_)throw new RangeError("Cyclic object value");C=!0}void 0===k.get(h)&&(_=0)}if("function"==typeof f?A=f(n,A):A instanceof Date?A=v(A):"comma"===i&&l(A)&&(A=o.maybeMap(A,(function(e){return e instanceof Date?v(e):e}))),null===A){if(s)return d&&!w?d(n,p.encoder,E,"key",y):n;A=""}if("string"==typeof(x=A)||"number"==typeof x||"boolean"==typeof x||"symbol"==typeof x||"bigint"==typeof x||o.isBuffer(A))return d?[b(w?n:d(n,p.encoder,E,"key",y))+"="+b(d(A,p.encoder,E,"value",y))]:[b(n)+"="+b(String(A))];var P,R=[];if(void 0===A)return R;if("comma"===i&&l(A))w&&d&&(A=o.maybeMap(A,d)),P=[{value:A.length>0?A.join(",")||null:void 0}];else if(l(f))P=f;else{var T=Object.keys(A);P=m?T.sort(m):T}for(var F=a&&l(A)&&1===A.length?n+"[]":n,j=0;j<P.length;++j){var N=P[j],I="object"==typeof N&&void 0!==N.value?N.value:A[N];if(!u||null!==I){var L=l(A)?"function"==typeof i?i(F,N):F:F+(g?"."+N:"["+N+"]");S.set(t,_);var M=r();M.set(h,S),c(R,e(I,L,i,a,s,u,"comma"===i&&w&&l(A)?null:d,f,m,g,v,y,b,w,E,M))}}return R};e.exports=function(e,t){var n,o=e,u=function(e){if(!e)return p;if(null!==e.encoder&&void 0!==e.encoder&&"function"!=typeof e.encoder)throw new TypeError("Encoder has to be a function.");var t=e.charset||p.charset;if(void 0!==e.charset&&"utf-8"!==e.charset&&"iso-8859-1"!==e.charset)throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var n=i.default;if(void 0!==e.format){if(!a.call(i.formatters,e.format))throw new TypeError("Unknown format option provided.");n=e.format}var r=i.formatters[n],o=p.filter;return("function"==typeof e.filter||l(e.filter))&&(o=e.filter),{addQueryPrefix:"boolean"==typeof e.addQueryPrefix?e.addQueryPrefix:p.addQueryPrefix,allowDots:void 0===e.allowDots?p.allowDots:!!e.allowDots,charset:t,charsetSentinel:"boolean"==typeof e.charsetSentinel?e.charsetSentinel:p.charsetSentinel,delimiter:void 0===e.delimiter?p.delimiter:e.delimiter,encode:"boolean"==typeof e.encode?e.encode:p.encode,encoder:"function"==typeof e.encoder?e.encoder:p.encoder,encodeValuesOnly:"boolean"==typeof e.encodeValuesOnly?e.encodeValuesOnly:p.encodeValuesOnly,filter:o,format:n,formatter:r,serializeDate:"function"==typeof e.serializeDate?e.serializeDate:p.serializeDate,skipNulls:"boolean"==typeof e.skipNulls?e.skipNulls:p.skipNulls,sort:"function"==typeof e.sort?e.sort:null,strictNullHandling:"boolean"==typeof e.strictNullHandling?e.strictNullHandling:p.strictNullHandling}}(t);"function"==typeof u.filter?o=(0,u.filter)("",o):l(u.filter)&&(n=u.filter);var d,f=[];if("object"!=typeof o||null===o)return"";d=t&&t.arrayFormat in s?t.arrayFormat:t&&"indices"in t?t.indices?"indices":"repeat":"indices";var h=s[d];if(t&&"commaRoundTrip"in t&&"boolean"!=typeof t.commaRoundTrip)throw new TypeError("`commaRoundTrip` must be a boolean, or absent");var g="comma"===h&&t&&t.commaRoundTrip;n||(n=Object.keys(o)),u.sort&&n.sort(u.sort);for(var v=r(),y=0;y<n.length;++y){var b=n[y];u.skipNulls&&null===o[b]||c(f,m(o[b],b,h,g,u.strictNullHandling,u.skipNulls,u.encode?u.encoder:null,u.filter,u.sort,u.allowDots,u.serializeDate,u.format,u.formatter,u.encodeValuesOnly,u.charset,v))}var w=f.join(u.delimiter),E=!0===u.addQueryPrefix?"?":"";return u.charsetSentinel&&("iso-8859-1"===u.charset?E+="utf8=%26%2310003%3B&":E+="utf8=%E2%9C%93&"),w.length>0?E+w:""}},8647:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))((function(o,i){function a(e){try{l(r.next(e))}catch(e){i(e)}}function s(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}l((r=r.apply(e,t||[])).next())}))},i=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!((o=(o=a.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){a.label=i[1];break}if(6===i[0]&&a.label<o[1]){a.label=o[1],o=i;break}if(o&&a.label<o[2]){a.label=o[2],a.ops.push(i);break}o[2]&&a.ops.pop(),a.trys.pop();continue}i=t.call(e,a)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,s])}}};Object.defineProperty(t,"__esModule",{value:!0});var a=n(6540),s=function(e,t){switch(t.type){case"ON_ERROR":return r(r({},e),{error:t.message,isLoading:!1});case"ON_LOADING":return r(r({},e),{isLoading:!0});case"ON_LOAD_USER":return r(r({},e),{oidcUser:t.user,isLoading:!1});case"ON_UNLOAD_USER":return r(r({},e),{oidcUser:null,isLoading:!1});case"ON_LOGOUT":return r(r({},e),{isLoggingOut:!0});default:return e}};t.useAuthenticationContextState=function(e){var t=function(e){return{oidcUser:null,userManager:e,isLoading:!1,error:"",isLoggingOut:!1}}(e),n=a.useReducer(s,t),r=n[0],o=n[1];return{onError:a.useCallback((function(e){return function(e){return function(t){return e({type:"ON_ERROR",message:t})}}(o)(e)}),[]),loadUser:a.useCallback((function(e){return function(e){return function(t){return e({type:"ON_LOAD_USER",user:t})}}(o)(e)}),[]),onLoading:a.useCallback((function(){return function(e){return function(){return e({type:"ON_LOADING"})}}(o)()}),[]),unloadUser:a.useCallback((function(){return function(e){return function(){return e({type:"ON_UNLOAD_USER"})}}(o)()}),[]),onLogout:a.useCallback((function(){return function(e){return function(){return e({type:"ON_LOGOUT"})}}(o)()}),[]),oidcState:r}},t.onErrorEvent=function(e,t){return function(n){e.error("Error : "+n.message),t(n.message)}},t.onUserLoadedEvent=function(e,t){return function(n){e.info("User Loaded"),t(n)}},t.onUserUnloadedEvent=function(e,t){return function(){e.info("User unloaded"),t()}},t.onAccessTokenExpiredEvent=function(e,t,n){return function(){return o(void 0,void 0,void 0,(function(){return i(this,(function(r){switch(r.label){case 0:return e.info("AccessToken Expired"),t(),[4,n.signinSilent()];case 1:return r.sent(),[2]}}))}))}},t.useOidcEvents=function(e,n,r,o){return{addOidcEvents:a.useCallback((function(){n.events.addUserLoaded(t.onUserLoadedEvent(e,r.loadUser)),n.events.addSilentRenewError(t.onErrorEvent(e,r.onError)),n.events.addUserUnloaded(t.onUserUnloadedEvent(e,r.unloadUser)),n.events.addUserSignedOut(t.onUserUnloadedEvent(e,r.unloadUser)),n.events.addAccessTokenExpired(t.onAccessTokenExpiredEvent(e,r.unloadUser,n)),o&&o.onUserSessionChanged&&n.events.addUserSessionChanged(o.onUserSessionChanged),o&&o.onUserLoaded&&n.events.addUserLoaded(o.onUserLoaded),o&&o.onSilentRenewError&&n.events.addSilentRenewError(o.onSilentRenewError),o&&o.onUserUnloaded&&n.events.addUserUnloaded(o.onUserUnloaded),o&&o.onUserSignedOut&&n.events.addUserSignedOut(o.onUserSignedOut),o&&o.onAccessTokenExpired&&n.events.addAccessTokenExpired(o.onAccessTokenExpired),o&&o.onAccessTokenExpiring&&n.events.addAccessTokenExpiring(o.onAccessTokenExpiring)}),[r.loadUser,r.onError,r.unloadUser,e,n,o]),removeOidcEvents:a.useCallback((function(){n.events.removeUserLoaded(t.onUserLoadedEvent(e,r.loadUser)),n.events.removeSilentRenewError(t.onErrorEvent(e,r.onError)),n.events.removeUserUnloaded(t.onUserUnloadedEvent(e,r.unloadUser)),n.events.removeUserSignedOut(t.onUserUnloadedEvent(e,r.unloadUser)),n.events.removeAccessTokenExpired(t.onAccessTokenExpiredEvent(e,r.unloadUser,n)),o&&o.onUserSessionChanged&&n.events.removeUserSessionChanged(o.onUserSessionChanged),o&&o.onUserLoaded&&n.events.removeUserLoaded(o.onUserLoaded),o&&o.onSilentRenewError&&n.events.removeSilentRenewError(o.onSilentRenewError),o&&o.onUserUnloaded&&n.events.removeUserUnloaded(o.onUserUnloaded),o&&o.onUserSignedOut&&n.events.removeUserSignedOut(o.onUserSignedOut),o&&o.onAccessTokenExpired&&n.events.removeAccessTokenExpired(o.onAccessTokenExpired),o&&o.onAccessTokenExpiring&&n.events.removeAccessTokenExpiring(o.onAccessTokenExpiring)}),[r.loadUser,r.onError,r.unloadUser,e,n,o])}}},8656:(e,t,n)=>{"use strict";function r(e){for(var n in e)t.hasOwnProperty(n)||(t[n]=e[n])}Object.defineProperty(t,"__esModule",{value:!0}),r(n(9903)),r(n(7671));var o=n(4691);t.oidcLog=o.oidcLog,t.setLogger=o.setLogger},8707:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"}),"Label");t.A=a},8727:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"}),"NavigateNext");t.A=a},8761:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)},o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(n(6540)),s=n(195),l=i(n(6269)),u=n(9550);t.useOidcSecure=function(e,t,n,r,o,i,s,l){var c=a.useContext(u.AuthenticationContext),d=c.isEnabled,f=c.oidcUser,p=c.authenticating,h=c.isLoggingOut;return a.useEffect((function(){return o.info("Protection : ",d),d&&!h&&(o.info("Protected component mounted"),e(t,n,r)()),function(){o.info("Protected component unmounted")}}),[d,e,t,o,n,r,h]),a.useMemo((function(){return s(f,!1)&&d}),[d,s,f])?p||i:l},t.OidcSecureWithInjectedFunctions=function(e){var n=e.children,r=e.location,o=e.history,i=e.authenticateUserInternal,l=e.getUserManagerInternal,u=e.isRequireAuthenticationInternal,c=e.AuthenticatingInternal,d=l(),f=a.useMemo((function(){return function(){return a.default.createElement(a.default.Fragment,null,n)}}),[n]),p=t.useOidcSecure(i,d,r,o,s.oidcLog,c,u,f);return a.default.createElement(p,null)};var c=s.withRouter(l.default(t.OidcSecureWithInjectedFunctions,{authenticateUserInternal:s.authenticateUser,getUserManagerInternal:s.getUserManager,isRequireAuthenticationInternal:s.isRequireAuthentication,AuthenticatingInternal:s.Authenticating}));t.withOidcSecure=function(e){return function(t){return a.default.createElement(c,null,a.default.createElement(e,r({},t)))}},t.default=c},8777:(e,t,n)=>{"use strict";n.d(t,{A:()=>y});var r=n(8168),o=n(8587),i=n(6540),a=n(4164),s=n(5659),l=n(8466),u=n(3541),c=n(1848),d=n(8413),f=n(1609);function p(e){return(0,f.A)("MuiSvgIcon",e)}(0,d.A)("MuiSvgIcon",["root","colorPrimary","colorSecondary","colorAction","colorError","colorDisabled","fontSizeInherit","fontSizeSmall","fontSizeMedium","fontSizeLarge"]);var h=n(4848);const m=["children","className","color","component","fontSize","htmlColor","inheritViewBox","titleAccess","viewBox"],g=(0,c.Ay)("svg",{name:"MuiSvgIcon",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,"inherit"!==n.color&&t[`color${(0,l.A)(n.color)}`],t[`fontSize${(0,l.A)(n.fontSize)}`]]}})((({theme:e,ownerState:t})=>{var n,r,o,i,a,s,l,u,c,d,f,p,h;return{userSelect:"none",width:"1em",height:"1em",display:"inline-block",fill:t.hasSvgAsChild?void 0:"currentColor",flexShrink:0,transition:null==(n=e.transitions)||null==(r=n.create)?void 0:r.call(n,"fill",{duration:null==(o=e.transitions)||null==(o=o.duration)?void 0:o.shorter}),fontSize:{inherit:"inherit",small:(null==(i=e.typography)||null==(a=i.pxToRem)?void 0:a.call(i,20))||"1.25rem",medium:(null==(s=e.typography)||null==(l=s.pxToRem)?void 0:l.call(s,24))||"1.5rem",large:(null==(u=e.typography)||null==(c=u.pxToRem)?void 0:c.call(u,35))||"2.1875rem"}[t.fontSize],color:null!=(d=null==(f=(e.vars||e).palette)||null==(f=f[t.color])?void 0:f.main)?d:{action:null==(p=(e.vars||e).palette)||null==(p=p.action)?void 0:p.active,disabled:null==(h=(e.vars||e).palette)||null==(h=h.action)?void 0:h.disabled,inherit:void 0}[t.color]}})),v=i.forwardRef((function(e,t){const n=(0,u.A)({props:e,name:"MuiSvgIcon"}),{children:c,className:d,color:f="inherit",component:v="svg",fontSize:y="medium",htmlColor:b,inheritViewBox:w=!1,titleAccess:E,viewBox:S="0 0 24 24"}=n,x=(0,o.A)(n,m),A=i.isValidElement(c)&&"svg"===c.type,k=(0,r.A)({},n,{color:f,component:v,fontSize:y,instanceFontSize:e.fontSize,inheritViewBox:w,viewBox:S,hasSvgAsChild:A}),_={};w||(_.viewBox=S);const C=(e=>{const{color:t,fontSize:n,classes:r}=e,o={root:["root","inherit"!==t&&`color${(0,l.A)(t)}`,`fontSize${(0,l.A)(n)}`]};return(0,s.A)(o,p,r)})(k);return(0,h.jsxs)(g,(0,r.A)({as:v,className:(0,a.A)(C.root,d),focusable:"false",color:b,"aria-hidden":!E||void 0,role:E?"img":void 0,ref:t},_,x,A&&c.props,{ownerState:k,children:[A?c.props.children:c,E?(0,h.jsx)("title",{children:E}):null]}))}));v.muiName="SvgIcon";const y=v},8835:(e,t,n)=>{"use strict";var r=n(1270);function o(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}var i=/^([a-z0-9.+-]+:)/i,a=/:[0-9]*$/,s=/^(\/\/?(?!\/)[^?\s]*)(\?[^\s]*)?$/,l=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r","\n","\t"]),u=["'"].concat(l),c=["%","/","?",";","#"].concat(u),d=["/","?","#"],f=/^[+a-z0-9A-Z_-]{0,63}$/,p=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,h={javascript:!0,"javascript:":!0},m={javascript:!0,"javascript:":!0},g={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},v=n(5373);function y(e,t,n){if(e&&"object"==typeof e&&e instanceof o)return e;var r=new o;return r.parse(e,t,n),r}o.prototype.parse=function(e,t,n){if("string"!=typeof e)throw new TypeError("Parameter 'url' must be a string, not "+typeof e);var o=e.indexOf("?"),a=-1!==o&&o<e.indexOf("#")?"?":"#",l=e.split(a);l[0]=l[0].replace(/\\/g,"/");var y=e=l.join(a);if(y=y.trim(),!n&&1===e.split("#").length){var b=s.exec(y);if(b)return this.path=y,this.href=y,this.pathname=b[1],b[2]?(this.search=b[2],this.query=t?v.parse(this.search.substr(1)):this.search.substr(1)):t&&(this.search="",this.query={}),this}var w=i.exec(y);if(w){var E=(w=w[0]).toLowerCase();this.protocol=E,y=y.substr(w.length)}if(n||w||y.match(/^\/\/[^@/]+@[^@/]+/)){var S="//"===y.substr(0,2);!S||w&&m[w]||(y=y.substr(2),this.slashes=!0)}if(!m[w]&&(S||w&&!g[w])){for(var x,A,k=-1,_=0;_<d.length;_++)-1!==(C=y.indexOf(d[_]))&&(-1===k||C<k)&&(k=C);for(-1!==(A=-1===k?y.lastIndexOf("@"):y.lastIndexOf("@",k))&&(x=y.slice(0,A),y=y.slice(A+1),this.auth=decodeURIComponent(x)),k=-1,_=0;_<c.length;_++){var C;-1!==(C=y.indexOf(c[_]))&&(-1===k||C<k)&&(k=C)}-1===k&&(k=y.length),this.host=y.slice(0,k),y=y.slice(k),this.parseHost(),this.hostname=this.hostname||"";var O="["===this.hostname[0]&&"]"===this.hostname[this.hostname.length-1];if(!O)for(var P=this.hostname.split(/\./),R=(_=0,P.length);_<R;_++){var T=P[_];if(T&&!T.match(f)){for(var F="",j=0,N=T.length;j<N;j++)T.charCodeAt(j)>127?F+="x":F+=T[j];if(!F.match(f)){var I=P.slice(0,_),L=P.slice(_+1),M=T.match(p);M&&(I.push(M[1]),L.unshift(M[2])),L.length&&(y="/"+L.join(".")+y),this.hostname=I.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),O||(this.hostname=r.toASCII(this.hostname));var D=this.port?":"+this.port:"",B=this.hostname||"";this.host=B+D,this.href+=this.host,O&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),"/"!==y[0]&&(y="/"+y))}if(!h[E])for(_=0,R=u.length;_<R;_++){var U=u[_];if(-1!==y.indexOf(U)){var z=encodeURIComponent(U);z===U&&(z=escape(U)),y=y.split(U).join(z)}}var $=y.indexOf("#");-1!==$&&(this.hash=y.substr($),y=y.slice(0,$));var H=y.indexOf("?");if(-1!==H?(this.search=y.substr(H),this.query=y.substr(H+1),t&&(this.query=v.parse(this.query)),y=y.slice(0,H)):t&&(this.search="",this.query={}),y&&(this.pathname=y),g[E]&&this.hostname&&!this.pathname&&(this.pathname="/"),this.pathname||this.search){D=this.pathname||"";var W=this.search||"";this.path=D+W}return this.href=this.format(),this},o.prototype.format=function(){var e=this.auth||"";e&&(e=(e=encodeURIComponent(e)).replace(/%3A/i,":"),e+="@");var t=this.protocol||"",n=this.pathname||"",r=this.hash||"",o=!1,i="";this.host?o=e+this.host:this.hostname&&(o=e+(-1===this.hostname.indexOf(":")?this.hostname:"["+this.hostname+"]"),this.port&&(o+=":"+this.port)),this.query&&"object"==typeof this.query&&Object.keys(this.query).length&&(i=v.stringify(this.query,{arrayFormat:"repeat",addQueryPrefix:!1}));var a=this.search||i&&"?"+i||"";return t&&":"!==t.substr(-1)&&(t+=":"),this.slashes||(!t||g[t])&&!1!==o?(o="//"+(o||""),n&&"/"!==n.charAt(0)&&(n="/"+n)):o||(o=""),r&&"#"!==r.charAt(0)&&(r="#"+r),a&&"?"!==a.charAt(0)&&(a="?"+a),t+o+(n=n.replace(/[?#]/g,(function(e){return encodeURIComponent(e)})))+(a=a.replace("#","%23"))+r},o.prototype.resolve=function(e){return this.resolveObject(y(e,!1,!0)).format()},o.prototype.resolveObject=function(e){if("string"==typeof e){var t=new o;t.parse(e,!1,!0),e=t}for(var n=new o,r=Object.keys(this),i=0;i<r.length;i++){var a=r[i];n[a]=this[a]}if(n.hash=e.hash,""===e.href)return n.href=n.format(),n;if(e.slashes&&!e.protocol){for(var s=Object.keys(e),l=0;l<s.length;l++){var u=s[l];"protocol"!==u&&(n[u]=e[u])}return g[n.protocol]&&n.hostname&&!n.pathname&&(n.pathname="/",n.path=n.pathname),n.href=n.format(),n}if(e.protocol&&e.protocol!==n.protocol){if(!g[e.protocol]){for(var c=Object.keys(e),d=0;d<c.length;d++){var f=c[d];n[f]=e[f]}return n.href=n.format(),n}if(n.protocol=e.protocol,e.host||m[e.protocol])n.pathname=e.pathname;else{for(var p=(e.pathname||"").split("/");p.length&&!(e.host=p.shift()););e.host||(e.host=""),e.hostname||(e.hostname=""),""!==p[0]&&p.unshift(""),p.length<2&&p.unshift(""),n.pathname=p.join("/")}if(n.search=e.search,n.query=e.query,n.host=e.host||"",n.auth=e.auth,n.hostname=e.hostname||e.host,n.port=e.port,n.pathname||n.search){var h=n.pathname||"",v=n.search||"";n.path=h+v}return n.slashes=n.slashes||e.slashes,n.href=n.format(),n}var y=n.pathname&&"/"===n.pathname.charAt(0),b=e.host||e.pathname&&"/"===e.pathname.charAt(0),w=b||y||n.host&&e.pathname,E=w,S=n.pathname&&n.pathname.split("/")||[],x=(p=e.pathname&&e.pathname.split("/")||[],n.protocol&&!g[n.protocol]);if(x&&(n.hostname="",n.port=null,n.host&&(""===S[0]?S[0]=n.host:S.unshift(n.host)),n.host="",e.protocol&&(e.hostname=null,e.port=null,e.host&&(""===p[0]?p[0]=e.host:p.unshift(e.host)),e.host=null),w=w&&(""===p[0]||""===S[0])),b)n.host=e.host||""===e.host?e.host:n.host,n.hostname=e.hostname||""===e.hostname?e.hostname:n.hostname,n.search=e.search,n.query=e.query,S=p;else if(p.length)S||(S=[]),S.pop(),S=S.concat(p),n.search=e.search,n.query=e.query;else if(null!=e.search)return x&&(n.host=S.shift(),n.hostname=n.host,(O=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@"))&&(n.auth=O.shift(),n.hostname=O.shift(),n.host=n.hostname)),n.search=e.search,n.query=e.query,null===n.pathname&&null===n.search||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.href=n.format(),n;if(!S.length)return n.pathname=null,n.search?n.path="/"+n.search:n.path=null,n.href=n.format(),n;for(var A=S.slice(-1)[0],k=(n.host||e.host||S.length>1)&&("."===A||".."===A)||""===A,_=0,C=S.length;C>=0;C--)"."===(A=S[C])?S.splice(C,1):".."===A?(S.splice(C,1),_++):_&&(S.splice(C,1),_--);if(!w&&!E)for(;_--;_)S.unshift("..");!w||""===S[0]||S[0]&&"/"===S[0].charAt(0)||S.unshift(""),k&&"/"!==S.join("/").substr(-1)&&S.push("");var O,P=""===S[0]||S[0]&&"/"===S[0].charAt(0);return x&&(n.hostname=P?"":S.length?S.shift():"",n.host=n.hostname,(O=!!(n.host&&n.host.indexOf("@")>0)&&n.host.split("@"))&&(n.auth=O.shift(),n.hostname=O.shift(),n.host=n.hostname)),(w=w||n.host&&S.length)&&!P&&S.unshift(""),S.length>0?n.pathname=S.join("/"):(n.pathname=null,n.path=null),null===n.pathname&&null===n.search||(n.path=(n.pathname?n.pathname:"")+(n.search?n.search:"")),n.auth=e.auth||n.auth,n.slashes=n.slashes||e.slashes,n.href=n.format(),n},o.prototype.parseHost=function(){var e=this.host,t=a.exec(e);t&&(":"!==(t=t[0])&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)},t.parse=y,t.resolve=function(e,t){return y(e,!1,!0).resolve(t)},t.resolveObject=function(e,t){return e?y(e,!1,!0).resolveObject(t):t},t.format=function(e){return"string"==typeof e&&(e=y(e)),e instanceof o?e.format():o.prototype.format.call(e)},t.Url=o},8851:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r=n(7151).A},8859:(e,t,n)=>{var r="function"==typeof Map&&Map.prototype,o=Object.getOwnPropertyDescriptor&&r?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,i=r&&o&&"function"==typeof o.get?o.get:null,a=r&&Map.prototype.forEach,s="function"==typeof Set&&Set.prototype,l=Object.getOwnPropertyDescriptor&&s?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,u=s&&l&&"function"==typeof l.get?l.get:null,c=s&&Set.prototype.forEach,d="function"==typeof WeakMap&&WeakMap.prototype?WeakMap.prototype.has:null,f="function"==typeof WeakSet&&WeakSet.prototype?WeakSet.prototype.has:null,p="function"==typeof WeakRef&&WeakRef.prototype?WeakRef.prototype.deref:null,h=Boolean.prototype.valueOf,m=Object.prototype.toString,g=Function.prototype.toString,v=String.prototype.match,y=String.prototype.slice,b=String.prototype.replace,w=String.prototype.toUpperCase,E=String.prototype.toLowerCase,S=RegExp.prototype.test,x=Array.prototype.concat,A=Array.prototype.join,k=Array.prototype.slice,_=Math.floor,C="function"==typeof BigInt?BigInt.prototype.valueOf:null,O=Object.getOwnPropertySymbols,P="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?Symbol.prototype.toString:null,R="function"==typeof Symbol&&"object"==typeof Symbol.iterator,T="function"==typeof Symbol&&Symbol.toStringTag&&(Symbol.toStringTag,1)?Symbol.toStringTag:null,F=Object.prototype.propertyIsEnumerable,j=("function"==typeof Reflect?Reflect.getPrototypeOf:Object.getPrototypeOf)||([].__proto__===Array.prototype?function(e){return e.__proto__}:null);function N(e,t){if(e===1/0||e===-1/0||e!=e||e&&e>-1e3&&e<1e3||S.call(/e/,t))return t;var n=/[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;if("number"==typeof e){var r=e<0?-_(-e):_(e);if(r!==e){var o=String(r),i=y.call(t,o.length+1);return b.call(o,n,"$&_")+"."+b.call(b.call(i,/([0-9]{3})/g,"$&_"),/_$/,"")}}return b.call(t,n,"$&_")}var I=n(2634),L=I.custom,M=$(L)?L:null;function D(e,t,n){var r="double"===(n.quoteStyle||t)?'"':"'";return r+e+r}function B(e){return b.call(String(e),/"/g,"&quot;")}function U(e){return!("[object Array]"!==V(e)||T&&"object"==typeof e&&T in e)}function z(e){return!("[object RegExp]"!==V(e)||T&&"object"==typeof e&&T in e)}function $(e){if(R)return e&&"object"==typeof e&&e instanceof Symbol;if("symbol"==typeof e)return!0;if(!e||"object"!=typeof e||!P)return!1;try{return P.call(e),!0}catch(e){}return!1}e.exports=function e(t,r,o,s){var l=r||{};if(W(l,"quoteStyle")&&"single"!==l.quoteStyle&&"double"!==l.quoteStyle)throw new TypeError('option "quoteStyle" must be "single" or "double"');if(W(l,"maxStringLength")&&("number"==typeof l.maxStringLength?l.maxStringLength<0&&l.maxStringLength!==1/0:null!==l.maxStringLength))throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');var m=!W(l,"customInspect")||l.customInspect;if("boolean"!=typeof m&&"symbol"!==m)throw new TypeError("option \"customInspect\", if provided, must be `true`, `false`, or `'symbol'`");if(W(l,"indent")&&null!==l.indent&&"\t"!==l.indent&&!(parseInt(l.indent,10)===l.indent&&l.indent>0))throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');if(W(l,"numericSeparator")&&"boolean"!=typeof l.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var w=l.numericSeparator;if(void 0===t)return"undefined";if(null===t)return"null";if("boolean"==typeof t)return t?"true":"false";if("string"==typeof t)return K(t,l);if("number"==typeof t){if(0===t)return 1/0/t>0?"0":"-0";var S=String(t);return w?N(t,S):S}if("bigint"==typeof t){var _=String(t)+"n";return w?N(t,_):_}var O=void 0===l.depth?5:l.depth;if(void 0===o&&(o=0),o>=O&&O>0&&"object"==typeof t)return U(t)?"[Array]":"[Object]";var L,H=function(e,t){var n;if("\t"===e.indent)n="\t";else{if(!("number"==typeof e.indent&&e.indent>0))return null;n=A.call(Array(e.indent+1)," ")}return{base:n,prev:A.call(Array(t+1),n)}}(l,o);if(void 0===s)s=[];else if(q(s,t)>=0)return"[Circular]";function G(t,n,r){if(n&&(s=k.call(s)).push(n),r){var i={depth:l.depth};return W(l,"quoteStyle")&&(i.quoteStyle=l.quoteStyle),e(t,i,o+1,s)}return e(t,l,o+1,s)}if("function"==typeof t&&!z(t)){var ee=function(e){if(e.name)return e.name;var t=v.call(g.call(e),/^function\s*([\w$]+)/);return t?t[1]:null}(t),te=Z(t,G);return"[Function"+(ee?": "+ee:" (anonymous)")+"]"+(te.length>0?" { "+A.call(te,", ")+" }":"")}if($(t)){var ne=R?b.call(String(t),/^(Symbol\(.*\))_[^)]*$/,"$1"):P.call(t);return"object"!=typeof t||R?ne:J(ne)}if((L=t)&&"object"==typeof L&&("undefined"!=typeof HTMLElement&&L instanceof HTMLElement||"string"==typeof L.nodeName&&"function"==typeof L.getAttribute)){for(var re="<"+E.call(String(t.nodeName)),oe=t.attributes||[],ie=0;ie<oe.length;ie++)re+=" "+oe[ie].name+"="+D(B(oe[ie].value),"double",l);return re+=">",t.childNodes&&t.childNodes.length&&(re+="..."),re+"</"+E.call(String(t.nodeName))+">"}if(U(t)){if(0===t.length)return"[]";var ae=Z(t,G);return H&&!function(e){for(var t=0;t<e.length;t++)if(q(e[t],"\n")>=0)return!1;return!0}(ae)?"["+Q(ae,H)+"]":"[ "+A.call(ae,", ")+" ]"}if(function(e){return!("[object Error]"!==V(e)||T&&"object"==typeof e&&T in e)}(t)){var se=Z(t,G);return"cause"in Error.prototype||!("cause"in t)||F.call(t,"cause")?0===se.length?"["+String(t)+"]":"{ ["+String(t)+"] "+A.call(se,", ")+" }":"{ ["+String(t)+"] "+A.call(x.call("[cause]: "+G(t.cause),se),", ")+" }"}if("object"==typeof t&&m){if(M&&"function"==typeof t[M]&&I)return I(t,{depth:O-o});if("symbol"!==m&&"function"==typeof t.inspect)return t.inspect()}if(function(e){if(!i||!e||"object"!=typeof e)return!1;try{i.call(e);try{u.call(e)}catch(e){return!0}return e instanceof Map}catch(e){}return!1}(t)){var le=[];return a&&a.call(t,(function(e,n){le.push(G(n,t,!0)+" => "+G(e,t))})),X("Map",i.call(t),le,H)}if(function(e){if(!u||!e||"object"!=typeof e)return!1;try{u.call(e);try{i.call(e)}catch(e){return!0}return e instanceof Set}catch(e){}return!1}(t)){var ue=[];return c&&c.call(t,(function(e){ue.push(G(e,t))})),X("Set",u.call(t),ue,H)}if(function(e){if(!d||!e||"object"!=typeof e)return!1;try{d.call(e,d);try{f.call(e,f)}catch(e){return!0}return e instanceof WeakMap}catch(e){}return!1}(t))return Y("WeakMap");if(function(e){if(!f||!e||"object"!=typeof e)return!1;try{f.call(e,f);try{d.call(e,d)}catch(e){return!0}return e instanceof WeakSet}catch(e){}return!1}(t))return Y("WeakSet");if(function(e){if(!p||!e||"object"!=typeof e)return!1;try{return p.call(e),!0}catch(e){}return!1}(t))return Y("WeakRef");if(function(e){return!("[object Number]"!==V(e)||T&&"object"==typeof e&&T in e)}(t))return J(G(Number(t)));if(function(e){if(!e||"object"!=typeof e||!C)return!1;try{return C.call(e),!0}catch(e){}return!1}(t))return J(G(C.call(t)));if(function(e){return!("[object Boolean]"!==V(e)||T&&"object"==typeof e&&T in e)}(t))return J(h.call(t));if(function(e){return!("[object String]"!==V(e)||T&&"object"==typeof e&&T in e)}(t))return J(G(String(t)));if("undefined"!=typeof window&&t===window)return"{ [object Window] }";if(t===n.g)return"{ [object globalThis] }";if(!function(e){return!("[object Date]"!==V(e)||T&&"object"==typeof e&&T in e)}(t)&&!z(t)){var ce=Z(t,G),de=j?j(t)===Object.prototype:t instanceof Object||t.constructor===Object,fe=t instanceof Object?"":"null prototype",pe=!de&&T&&Object(t)===t&&T in t?y.call(V(t),8,-1):fe?"Object":"",he=(de||"function"!=typeof t.constructor?"":t.constructor.name?t.constructor.name+" ":"")+(pe||fe?"["+A.call(x.call([],pe||[],fe||[]),": ")+"] ":"");return 0===ce.length?he+"{}":H?he+"{"+Q(ce,H)+"}":he+"{ "+A.call(ce,", ")+" }"}return String(t)};var H=Object.prototype.hasOwnProperty||function(e){return e in this};function W(e,t){return H.call(e,t)}function V(e){return m.call(e)}function q(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1}function K(e,t){if(e.length>t.maxStringLength){var n=e.length-t.maxStringLength,r="... "+n+" more character"+(n>1?"s":"");return K(y.call(e,0,t.maxStringLength),t)+r}return D(b.call(b.call(e,/(['\\])/g,"\\$1"),/[\x00-\x1f]/g,G),"single",t)}function G(e){var t=e.charCodeAt(0),n={8:"b",9:"t",10:"n",12:"f",13:"r"}[t];return n?"\\"+n:"\\x"+(t<16?"0":"")+w.call(t.toString(16))}function J(e){return"Object("+e+")"}function Y(e){return e+" { ? }"}function X(e,t,n,r){return e+" ("+t+") {"+(r?Q(n,r):A.call(n,", "))+"}"}function Q(e,t){if(0===e.length)return"";var n="\n"+t.prev+t.base;return n+A.call(e,","+n)+"\n"+t.prev}function Z(e,t){var n=U(e),r=[];if(n){r.length=e.length;for(var o=0;o<e.length;o++)r[o]=W(e,o)?t(e[o],e):""}var i,a="function"==typeof O?O(e):[];if(R){i={};for(var s=0;s<a.length;s++)i["$"+a[s]]=a[s]}for(var l in e)W(e,l)&&(n&&String(Number(l))===l&&l<e.length||R&&i["$"+l]instanceof Symbol||(S.call(/[^\w$]/,l)?r.push(t(l,e)+": "+t(e[l],e)):r.push(l+": "+t(e[l],e))));if("function"==typeof O)for(var u=0;u<a.length;u++)F.call(e,a[u])&&r.push("["+t(a[u])+"]: "+t(e[a[u]],e));return r}},8966:(e,t,n)=>{"use strict";n.d(t,{A:()=>re});var r=function(){function e(e){var t=this;this._insertTag=function(e){var n;n=0===t.tags.length?t.insertionPoint?t.insertionPoint.nextSibling:t.prepend?t.container.firstChild:t.before:t.tags[t.tags.length-1].nextSibling,t.container.insertBefore(e,n),t.tags.push(e)},this.isSpeedy=void 0===e.speedy||e.speedy,this.tags=[],this.ctr=0,this.nonce=e.nonce,this.key=e.key,this.container=e.container,this.prepend=e.prepend,this.insertionPoint=e.insertionPoint,this.before=null}var t=e.prototype;return t.hydrate=function(e){e.forEach(this._insertTag)},t.insert=function(e){this.ctr%(this.isSpeedy?65e3:1)==0&&this._insertTag(function(e){var t=document.createElement("style");return t.setAttribute("data-emotion",e.key),void 0!==e.nonce&&t.setAttribute("nonce",e.nonce),t.appendChild(document.createTextNode("")),t.setAttribute("data-s",""),t}(this));var t=this.tags[this.tags.length-1];if(this.isSpeedy){var n=function(e){if(e.sheet)return e.sheet;for(var t=0;t<document.styleSheets.length;t++)if(document.styleSheets[t].ownerNode===e)return document.styleSheets[t]}(t);try{n.insertRule(e,n.cssRules.length)}catch(e){}}else t.appendChild(document.createTextNode(e));this.ctr++},t.flush=function(){this.tags.forEach((function(e){return e.parentNode&&e.parentNode.removeChild(e)})),this.tags=[],this.ctr=0},e}(),o=Math.abs,i=String.fromCharCode,a=Object.assign;function s(e){return e.trim()}function l(e,t,n){return e.replace(t,n)}function u(e,t){return e.indexOf(t)}function c(e,t){return 0|e.charCodeAt(t)}function d(e,t,n){return e.slice(t,n)}function f(e){return e.length}function p(e){return e.length}function h(e,t){return t.push(e),e}var m=1,g=1,v=0,y=0,b=0,w="";function E(e,t,n,r,o,i,a){return{value:e,root:t,parent:n,type:r,props:o,children:i,line:m,column:g,length:a,return:""}}function S(e,t){return a(E("",null,null,"",null,null,0),e,{length:-e.length},t)}function x(){return b=y>0?c(w,--y):0,g--,10===b&&(g=1,m--),b}function A(){return b=y<v?c(w,y++):0,g++,10===b&&(g=1,m++),b}function k(){return c(w,y)}function _(){return y}function C(e,t){return d(w,e,t)}function O(e){switch(e){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function P(e){return m=g=1,v=f(w=e),y=0,[]}function R(e){return w="",e}function T(e){return s(C(y-1,N(91===e?e+2:40===e?e+1:e)))}function F(e){for(;(b=k())&&b<33;)A();return O(e)>2||O(b)>3?"":" "}function j(e,t){for(;--t&&A()&&!(b<48||b>102||b>57&&b<65||b>70&&b<97););return C(e,_()+(t<6&&32==k()&&32==A()))}function N(e){for(;A();)switch(b){case e:return y;case 34:case 39:34!==e&&39!==e&&N(b);break;case 40:41===e&&N(e);break;case 92:A()}return y}function I(e,t){for(;A()&&e+b!==57&&(e+b!==84||47!==k()););return"/*"+C(t,y-1)+"*"+i(47===e?e:A())}function L(e){for(;!O(k());)A();return C(e,y)}var M="-ms-",D="-moz-",B="-webkit-",U="comm",z="rule",$="decl",H="@keyframes";function W(e,t){for(var n="",r=p(e),o=0;o<r;o++)n+=t(e[o],o,e,t)||"";return n}function V(e,t,n,r){switch(e.type){case"@layer":if(e.children.length)break;case"@import":case $:return e.return=e.return||e.value;case U:return"";case H:return e.return=e.value+"{"+W(e.children,r)+"}";case z:e.value=e.props.join(",")}return f(n=W(e.children,r))?e.return=e.value+"{"+n+"}":""}function q(e){return R(K("",null,null,null,[""],e=P(e),0,[0],e))}function K(e,t,n,r,o,a,s,d,p){for(var m=0,g=0,v=s,y=0,b=0,w=0,E=1,S=1,C=1,O=0,P="",R=o,N=a,M=r,D=P;S;)switch(w=O,O=A()){case 40:if(108!=w&&58==c(D,v-1)){-1!=u(D+=l(T(O),"&","&\f"),"&\f")&&(C=-1);break}case 34:case 39:case 91:D+=T(O);break;case 9:case 10:case 13:case 32:D+=F(w);break;case 92:D+=j(_()-1,7);continue;case 47:switch(k()){case 42:case 47:h(J(I(A(),_()),t,n),p);break;default:D+="/"}break;case 123*E:d[m++]=f(D)*C;case 125*E:case 59:case 0:switch(O){case 0:case 125:S=0;case 59+g:-1==C&&(D=l(D,/\f/g,"")),b>0&&f(D)-v&&h(b>32?Y(D+";",r,n,v-1):Y(l(D," ","")+";",r,n,v-2),p);break;case 59:D+=";";default:if(h(M=G(D,t,n,m,g,o,d,P,R=[],N=[],v),a),123===O)if(0===g)K(D,t,M,M,R,a,v,d,N);else switch(99===y&&110===c(D,3)?100:y){case 100:case 108:case 109:case 115:K(e,M,M,r&&h(G(e,M,M,0,0,o,d,P,o,R=[],v),N),o,N,v,d,r?R:N);break;default:K(D,M,M,M,[""],N,0,d,N)}}m=g=b=0,E=C=1,P=D="",v=s;break;case 58:v=1+f(D),b=w;default:if(E<1)if(123==O)--E;else if(125==O&&0==E++&&125==x())continue;switch(D+=i(O),O*E){case 38:C=g>0?1:(D+="\f",-1);break;case 44:d[m++]=(f(D)-1)*C,C=1;break;case 64:45===k()&&(D+=T(A())),y=k(),g=v=f(P=D+=L(_())),O++;break;case 45:45===w&&2==f(D)&&(E=0)}}return a}function G(e,t,n,r,i,a,u,c,f,h,m){for(var g=i-1,v=0===i?a:[""],y=p(v),b=0,w=0,S=0;b<r;++b)for(var x=0,A=d(e,g+1,g=o(w=u[b])),k=e;x<y;++x)(k=s(w>0?v[x]+" "+A:l(A,/&\f/g,v[x])))&&(f[S++]=k);return E(e,t,n,0===i?z:c,f,h,m)}function J(e,t,n){return E(e,t,n,U,i(b),d(e,2,-2),0)}function Y(e,t,n,r){return E(e,t,n,$,d(e,0,r),d(e,r+1,-1),r)}var X=function(e,t,n){for(var r=0,o=0;r=o,o=k(),38===r&&12===o&&(t[n]=1),!O(o);)A();return C(e,y)},Q=new WeakMap,Z=function(e){if("rule"===e.type&&e.parent&&!(e.length<1)){for(var t=e.value,n=e.parent,r=e.column===n.column&&e.line===n.line;"rule"!==n.type;)if(!(n=n.parent))return;if((1!==e.props.length||58===t.charCodeAt(0)||Q.get(n))&&!r){Q.set(e,!0);for(var o=[],a=function(e,t){return R(function(e,t){var n=-1,r=44;do{switch(O(r)){case 0:38===r&&12===k()&&(t[n]=1),e[n]+=X(y-1,t,n);break;case 2:e[n]+=T(r);break;case 4:if(44===r){e[++n]=58===k()?"&\f":"",t[n]=e[n].length;break}default:e[n]+=i(r)}}while(r=A());return e}(P(e),t))}(t,o),s=n.props,l=0,u=0;l<a.length;l++)for(var c=0;c<s.length;c++,u++)e.props[u]=o[l]?a[l].replace(/&\f/g,s[c]):s[c]+" "+a[l]}}},ee=function(e){if("decl"===e.type){var t=e.value;108===t.charCodeAt(0)&&98===t.charCodeAt(2)&&(e.return="",e.value="")}};function te(e,t){switch(function(e,t){return 45^c(e,0)?(((t<<2^c(e,0))<<2^c(e,1))<<2^c(e,2))<<2^c(e,3):0}(e,t)){case 5103:return B+"print-"+e+e;case 5737:case 4201:case 3177:case 3433:case 1641:case 4457:case 2921:case 5572:case 6356:case 5844:case 3191:case 6645:case 3005:case 6391:case 5879:case 5623:case 6135:case 4599:case 4855:case 4215:case 6389:case 5109:case 5365:case 5621:case 3829:return B+e+e;case 5349:case 4246:case 4810:case 6968:case 2756:return B+e+D+e+M+e+e;case 6828:case 4268:return B+e+M+e+e;case 6165:return B+e+M+"flex-"+e+e;case 5187:return B+e+l(e,/(\w+).+(:[^]+)/,B+"box-$1$2"+M+"flex-$1$2")+e;case 5443:return B+e+M+"flex-item-"+l(e,/flex-|-self/,"")+e;case 4675:return B+e+M+"flex-line-pack"+l(e,/align-content|flex-|-self/,"")+e;case 5548:return B+e+M+l(e,"shrink","negative")+e;case 5292:return B+e+M+l(e,"basis","preferred-size")+e;case 6060:return B+"box-"+l(e,"-grow","")+B+e+M+l(e,"grow","positive")+e;case 4554:return B+l(e,/([^-])(transform)/g,"$1"+B+"$2")+e;case 6187:return l(l(l(e,/(zoom-|grab)/,B+"$1"),/(image-set)/,B+"$1"),e,"")+e;case 5495:case 3959:return l(e,/(image-set\([^]*)/,B+"$1$`$1");case 4968:return l(l(e,/(.+:)(flex-)?(.*)/,B+"box-pack:$3"+M+"flex-pack:$3"),/s.+-b[^;]+/,"justify")+B+e+e;case 4095:case 3583:case 4068:case 2532:return l(e,/(.+)-inline(.+)/,B+"$1$2")+e;case 8116:case 7059:case 5753:case 5535:case 5445:case 5701:case 4933:case 4677:case 5533:case 5789:case 5021:case 4765:if(f(e)-1-t>6)switch(c(e,t+1)){case 109:if(45!==c(e,t+4))break;case 102:return l(e,/(.+:)(.+)-([^]+)/,"$1"+B+"$2-$3$1"+D+(108==c(e,t+3)?"$3":"$2-$3"))+e;case 115:return~u(e,"stretch")?te(l(e,"stretch","fill-available"),t)+e:e}break;case 4949:if(115!==c(e,t+1))break;case 6444:switch(c(e,f(e)-3-(~u(e,"!important")&&10))){case 107:return l(e,":",":"+B)+e;case 101:return l(e,/(.+:)([^;!]+)(;|!.+)?/,"$1"+B+(45===c(e,14)?"inline-":"")+"box$3$1"+B+"$2$3$1"+M+"$2box$3")+e}break;case 5936:switch(c(e,t+11)){case 114:return B+e+M+l(e,/[svh]\w+-[tblr]{2}/,"tb")+e;case 108:return B+e+M+l(e,/[svh]\w+-[tblr]{2}/,"tb-rl")+e;case 45:return B+e+M+l(e,/[svh]\w+-[tblr]{2}/,"lr")+e}return B+e+M+e+e}return e}var ne=[function(e,t,n,r){if(e.length>-1&&!e.return)switch(e.type){case $:e.return=te(e.value,e.length);break;case H:return W([S(e,{value:l(e.value,"@","@"+B)})],r);case z:if(e.length)return function(e,t){return e.map(t).join("")}(e.props,(function(t){switch(function(e){return(e=/(::plac\w+|:read-\w+)/.exec(e))?e[0]:e}(t)){case":read-only":case":read-write":return W([S(e,{props:[l(t,/:(read-\w+)/,":-moz-$1")]})],r);case"::placeholder":return W([S(e,{props:[l(t,/:(plac\w+)/,":"+B+"input-$1")]}),S(e,{props:[l(t,/:(plac\w+)/,":-moz-$1")]}),S(e,{props:[l(t,/:(plac\w+)/,M+"input-$1")]})],r)}return""}))}}],re=function(e){var t=e.key;if("css"===t){var n=document.querySelectorAll("style[data-emotion]:not([data-s])");Array.prototype.forEach.call(n,(function(e){-1!==e.getAttribute("data-emotion").indexOf(" ")&&(document.head.appendChild(e),e.setAttribute("data-s",""))}))}var o,i,a=e.stylisPlugins||ne,s={},l=[];o=e.container||document.head,Array.prototype.forEach.call(document.querySelectorAll('style[data-emotion^="'+t+' "]'),(function(e){for(var t=e.getAttribute("data-emotion").split(" "),n=1;n<t.length;n++)s[t[n]]=!0;l.push(e)}));var u,c,d,f,h=[V,(f=function(e){u.insert(e)},function(e){e.root||(e=e.return)&&f(e)})],m=(c=[Z,ee].concat(a,h),d=p(c),function(e,t,n,r){for(var o="",i=0;i<d;i++)o+=c[i](e,t,n,r)||"";return o});i=function(e,t,n,r){u=n,W(q(e?e+"{"+t.styles+"}":t.styles),m),r&&(g.inserted[t.name]=!0)};var g={key:t,sheet:new r({key:t,container:o,nonce:e.nonce,speedy:e.speedy,prepend:e.prepend,insertionPoint:e.insertionPoint}),nonce:e.nonce,inserted:s,registered:{},insert:i};return g.sheet.hydrate(l),g}},8971:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM8 17.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5zM9.5 8c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5S9.5 9.38 9.5 8zm6.5 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"}),"GroupWork");t.A=a},8984:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"}),"Save");t.A=a},9030:function(e,t,n){"use strict";var r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(6540));t.default=function(){return o.createElement("div",{className:"oidc-not-authenticated"},o.createElement("div",{className:"oidc-not-authenticated__container"},o.createElement("h1",{className:"oidc-not-authenticated__title"},"Authentification"),o.createElement("p",{className:"oidc-not-authenticated__content"},"Vous n'Ãªtes pas authentifiÃ©.")))}},9032:(e,t,n)=>{"use strict";var r=n(7244),o=n(8184),i=n(5767),a=n(5680);function s(e){return e.call.bind(e)}var l="undefined"!=typeof BigInt,u="undefined"!=typeof Symbol,c=s(Object.prototype.toString),d=s(Number.prototype.valueOf),f=s(String.prototype.valueOf),p=s(Boolean.prototype.valueOf);if(l)var h=s(BigInt.prototype.valueOf);if(u)var m=s(Symbol.prototype.valueOf);function g(e,t){if("object"!=typeof e)return!1;try{return t(e),!0}catch(e){return!1}}function v(e){return"[object Map]"===c(e)}function y(e){return"[object Set]"===c(e)}function b(e){return"[object WeakMap]"===c(e)}function w(e){return"[object WeakSet]"===c(e)}function E(e){return"[object ArrayBuffer]"===c(e)}function S(e){return"undefined"!=typeof ArrayBuffer&&(E.working?E(e):e instanceof ArrayBuffer)}function x(e){return"[object DataView]"===c(e)}function A(e){return"undefined"!=typeof DataView&&(x.working?x(e):e instanceof DataView)}t.isArgumentsObject=r,t.isGeneratorFunction=o,t.isTypedArray=a,t.isPromise=function(e){return"undefined"!=typeof Promise&&e instanceof Promise||null!==e&&"object"==typeof e&&"function"==typeof e.then&&"function"==typeof e.catch},t.isArrayBufferView=function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):a(e)||A(e)},t.isUint8Array=function(e){return"Uint8Array"===i(e)},t.isUint8ClampedArray=function(e){return"Uint8ClampedArray"===i(e)},t.isUint16Array=function(e){return"Uint16Array"===i(e)},t.isUint32Array=function(e){return"Uint32Array"===i(e)},t.isInt8Array=function(e){return"Int8Array"===i(e)},t.isInt16Array=function(e){return"Int16Array"===i(e)},t.isInt32Array=function(e){return"Int32Array"===i(e)},t.isFloat32Array=function(e){return"Float32Array"===i(e)},t.isFloat64Array=function(e){return"Float64Array"===i(e)},t.isBigInt64Array=function(e){return"BigInt64Array"===i(e)},t.isBigUint64Array=function(e){return"BigUint64Array"===i(e)},v.working="undefined"!=typeof Map&&v(new Map),t.isMap=function(e){return"undefined"!=typeof Map&&(v.working?v(e):e instanceof Map)},y.working="undefined"!=typeof Set&&y(new Set),t.isSet=function(e){return"undefined"!=typeof Set&&(y.working?y(e):e instanceof Set)},b.working="undefined"!=typeof WeakMap&&b(new WeakMap),t.isWeakMap=function(e){return"undefined"!=typeof WeakMap&&(b.working?b(e):e instanceof WeakMap)},w.working="undefined"!=typeof WeakSet&&w(new WeakSet),t.isWeakSet=function(e){return w(e)},E.working="undefined"!=typeof ArrayBuffer&&E(new ArrayBuffer),t.isArrayBuffer=S,x.working="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof DataView&&x(new DataView(new ArrayBuffer(1),0,1)),t.isDataView=A;var k="undefined"!=typeof SharedArrayBuffer?SharedArrayBuffer:void 0;function _(e){return"[object SharedArrayBuffer]"===c(e)}function C(e){return void 0!==k&&(void 0===_.working&&(_.working=_(new k)),_.working?_(e):e instanceof k)}function O(e){return g(e,d)}function P(e){return g(e,f)}function R(e){return g(e,p)}function T(e){return l&&g(e,h)}function F(e){return u&&g(e,m)}t.isSharedArrayBuffer=C,t.isAsyncFunction=function(e){return"[object AsyncFunction]"===c(e)},t.isMapIterator=function(e){return"[object Map Iterator]"===c(e)},t.isSetIterator=function(e){return"[object Set Iterator]"===c(e)},t.isGeneratorObject=function(e){return"[object Generator]"===c(e)},t.isWebAssemblyCompiledModule=function(e){return"[object WebAssembly.Module]"===c(e)},t.isNumberObject=O,t.isStringObject=P,t.isBooleanObject=R,t.isBigIntObject=T,t.isSymbolObject=F,t.isBoxedPrimitive=function(e){return O(e)||P(e)||R(e)||T(e)||F(e)},t.isAnyArrayBuffer=function(e){return"undefined"!=typeof Uint8Array&&(S(e)||C(e))},["isProxy","isExternal","isModuleNamespaceObject"].forEach((function(e){Object.defineProperty(t,e,{enumerable:!1,value:function(){throw new Error(e+" is not supported in userland")}})}))},9071:(e,t,n)=>{"use strict";n.d(t,{A:()=>o});const r=e=>e,o=(()=>{let e=r;return{configure(t){e=t},generate:t=>e(t),reset(){e=r}}})()},9092:(e,t,n)=>{"use strict";var r=n(1333);e.exports=function(){return r()&&!!Symbol.toStringTag}},9118:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M7.41 15.41 12 10.83l4.59 4.58L18 14l-6-6-6 6z"}),"KeyboardArrowUp");t.A=a},9149:function(e){var t,n;e.exports=(t={705:e=>{e.exports=function(e,t){const n=[];return 0===t.length?n.push({text:e,highlight:!1}):t[0][0]>0&&n.push({text:e.slice(0,t[0][0]),highlight:!1}),t.forEach(((r,o)=>{const i=r[0],a=r[1];n.push({text:e.slice(i,a),highlight:!0}),o===t.length-1?a<e.length&&n.push({text:e.slice(a,e.length),highlight:!1}):a<t[o+1][0]&&n.push({text:e.slice(a,t[o+1][0]),highlight:!1})})),n}}},n={},function e(r){var o=n[r];if(void 0!==o)return o.exports;var i=n[r]={exports:{}};return t[r](i,i.exports,e),i.exports}(705))},9209:(e,t,n)=>{"use strict";var r=["BigInt64Array","BigUint64Array","Float32Array","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Uint8Array","Uint8ClampedArray"],o="undefined"==typeof globalThis?n.g:globalThis;e.exports=function(){for(var e=[],t=0;t<r.length;t++)"function"==typeof o[r[t]]&&(e[e.length]=r[t]);return e}},9257:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M6 6h12v12H6z"}),"Stop");t.A=a},9321:e=>{"use strict";e.exports=function(e,t){if(e===t)return!0;if(!e||!t)return!1;var n=e.length;if(t.length!==n)return!1;for(var r=0;r<n;r++)if(e[r]!==t[r])return!1;return!0}},9353:e=>{"use strict";var t=Object.prototype.toString,n=Math.max,r=function(e,t){for(var n=[],r=0;r<e.length;r+=1)n[r]=e[r];for(var o=0;o<t.length;o+=1)n[o+e.length]=t[o];return n};e.exports=function(e){var o=this;if("function"!=typeof o||"[object Function]"!==t.apply(o))throw new TypeError("Function.prototype.bind called on incompatible "+o);for(var i,a=function(e){for(var t=[],n=1,r=0;n<e.length;n+=1,r+=1)t[r]=e[n];return t}(arguments),s=n(0,o.length-a.length),l=[],u=0;u<s;u++)l[u]="$"+u;if(i=Function("binder","return function ("+function(e){for(var t="",n=0;n<e.length;n+=1)t+=e[n],n+1<e.length&&(t+=",");return t}(l)+"){ return binder.apply(this,arguments); }")((function(){if(this instanceof i){var t=o.apply(this,r(a,arguments));return Object(t)===t?t:this}return o.apply(e,r(a,arguments))})),o.prototype){var c=function(){};c.prototype=o.prototype,i.prototype=new c,c.prototype=null}return i}},9375:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M20 8H4V6h16v2zm-2-6H6v2h12V2zm4 10v8c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2v-8c0-1.1.9-2 2-2h16c1.1 0 2 .9 2 2zm-6 4-6-3.27v6.53L16 16z"}),"Subscriptions");t.A=a},9452:(e,t,n)=>{"use strict";n.d(t,{EU:()=>s,NI:()=>a,iZ:()=>u,kW:()=>c,vf:()=>l,zu:()=>o});var r=n(3272);const o={xs:0,sm:600,md:900,lg:1200,xl:1536},i={keys:["xs","sm","md","lg","xl"],up:e=>`@media (min-width:${o[e]}px)`};function a(e,t,n){const r=e.theme||{};if(Array.isArray(t)){const e=r.breakpoints||i;return t.reduce(((r,o,i)=>(r[e.up(e.keys[i])]=n(t[i]),r)),{})}if("object"==typeof t){const e=r.breakpoints||i;return Object.keys(t).reduce(((r,i)=>{if(-1!==Object.keys(e.values||o).indexOf(i))r[e.up(i)]=n(t[i],i);else{const e=i;r[e]=t[e]}return r}),{})}return n(t)}function s(e={}){var t;return(null==(t=e.keys)?void 0:t.reduce(((t,n)=>(t[e.up(n)]={},t)),{}))||{}}function l(e,t){return e.reduce(((e,t)=>{const n=e[t];return(!n||0===Object.keys(n).length)&&delete e[t],e}),t)}function u(e,...t){const n=s(e),o=[n,...t].reduce(((e,t)=>(0,r.A)(e,t)),{});return l(Object.keys(n),o)}function c({values:e,breakpoints:t,base:n}){const r=n||function(e,t){if("object"!=typeof e)return{};const n={},r=Object.keys(t);return Array.isArray(e)?r.forEach(((t,r)=>{r<e.length&&(n[t]=!0)})):r.forEach((t=>{null!=e[t]&&(n[t]=!0)})),n}(e,t),o=Object.keys(r);if(0===o.length)return e;let i;return o.reduce(((t,n,r)=>(Array.isArray(e)?(t[n]=null!=e[r]?e[r]:e[i],i=r):"object"==typeof e?(t[n]=null!=e[n]?e[n]:e[i],i=n):t[n]=e,t)),{})}},9453:(e,t,n)=>{"use strict";function r(e){let t="https://mui.com/production-error/?code="+e;for(let e=1;e<arguments.length;e+=1)t+="&args[]="+encodeURIComponent(arguments[e]);return"Minified MUI error #"+e+"; visit "+t+" for the full message."}n.d(t,{A:()=>r})},9483:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"}),"KeyboardArrowDown");t.A=a},9550:(e,t,n)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(6554);t.AuthenticationProvider=r.default;var o=n(3093);t.AuthenticationContext=o.AuthenticationContext,t.useReactOidc=o.useReactOidc},9577:(e,t,n)=>{"use strict";n.d(t,{A:()=>r});const r={50:"#e1f5fe",100:"#b3e5fc",200:"#81d4fa",300:"#4fc3f7",400:"#29b6f6",500:"#03a9f4",600:"#039be5",700:"#0288d1",800:"#0277bd",900:"#01579b",A100:"#80d8ff",A200:"#40c4ff",A400:"#00b0ff",A700:"#0091ea"}},9592:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h4v-2H5V8h14v10h-4v2h4c1.1 0 2-.9 2-2V6c0-1.1-.89-2-2-2zm-7 6-4 4h3v6h2v-6h3l-4-4z"}),"OpenInBrowser");t.A=a},9600:e=>{"use strict";var t,n,r=Function.prototype.toString,o="object"==typeof Reflect&&null!==Reflect&&Reflect.apply;if("function"==typeof o&&"function"==typeof Object.defineProperty)try{t=Object.defineProperty({},"length",{get:function(){throw n}}),n={},o((function(){throw 42}),null,t)}catch(e){e!==n&&(o=null)}else o=null;var i=/^\s*class\b/,a=function(e){try{var t=r.call(e);return i.test(t)}catch(e){return!1}},s=function(e){try{return!a(e)&&(r.call(e),!0)}catch(e){return!1}},l=Object.prototype.toString,u="function"==typeof Symbol&&!!Symbol.toStringTag,c=!(0 in[,]),d=function(){return!1};if("object"==typeof document){var f=document.all;l.call(f)===l.call(document.all)&&(d=function(e){if((c||!e)&&(void 0===e||"object"==typeof e))try{var t=l.call(e);return("[object HTMLAllCollection]"===t||"[object HTML document.all class]"===t||"[object HTMLCollection]"===t||"[object Object]"===t)&&null==e("")}catch(e){}return!1})}e.exports=o?function(e){if(d(e))return!0;if(!e)return!1;if("function"!=typeof e&&"object"!=typeof e)return!1;try{o(e,null,t)}catch(e){if(e!==n)return!1}return!a(e)&&s(e)}:function(e){if(d(e))return!0;if(!e)return!1;if("function"!=typeof e&&"object"!=typeof e)return!1;if(u)return s(e);if(a(e))return!1;var t=l.call(e);return!("[object Function]"!==t&&"[object GeneratorFunction]"!==t&&!/^\[object HTML/.test(t))&&s(e)}},9648:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M2 20h20v-4H2v4zm2-3h2v2H4v-2zM2 4v4h20V4H2zm4 3H4V5h2v2zm-4 7h20v-4H2v4zm2-3h2v2H4v-2z"}),"Storage");t.A=a},9834:(e,t,n)=>{"use strict";n.d(t,{Ay:()=>k,MC:()=>b});var r=n(8587),o=n(8168),i=n(926),a=n(3272),s=n(7008),l=n(3967);const u=["variant"];function c(e){return 0===e.length}function d(e){const{variant:t}=e,n=(0,r.A)(e,u);let o=t||"";return Object.keys(n).sort().forEach((t=>{o+="color"===t?c(o)?e[t]:(0,l.A)(e[t]):`${c(o)?t:(0,l.A)(t)}${(0,l.A)(e[t].toString())}`})),o}var f=n(3571);const p=["name","slot","skipVariantsResolver","skipSx","overridesResolver"],h=(e,t)=>t.components&&t.components[e]&&t.components[e].styleOverrides?t.components[e].styleOverrides:null,m=e=>{let t=0;const n={};return e&&e.forEach((e=>{let r="";"function"==typeof e.props?(r=`callback${t}`,t+=1):r=d(e.props),n[r]=e.style})),n},g=(e,t)=>{let n=[];return t&&t.components&&t.components[e]&&t.components[e].variants&&(n=t.components[e].variants),m(n)},v=(e,t,n)=>{const{ownerState:r={}}=e,i=[];let a=0;return n&&n.forEach((n=>{let s=!0;if("function"==typeof n.props){const t=(0,o.A)({},e,r);s=n.props(t)}else Object.keys(n.props).forEach((t=>{r[t]!==n.props[t]&&e[t]!==n.props[t]&&(s=!1)}));s&&("function"==typeof n.props?i.push(t[`callback${a}`]):i.push(t[d(n.props)])),"function"==typeof n.props&&(a+=1)})),i},y=(e,t,n,r)=>{var o;const i=null==n||null==(o=n.components)||null==(o=o[r])?void 0:o.variants;return v(e,t,i)};function b(e){return"ownerState"!==e&&"theme"!==e&&"sx"!==e&&"as"!==e}const w=(0,s.A)(),E=e=>e?e.charAt(0).toLowerCase()+e.slice(1):e;function S({defaultTheme:e,theme:t,themeId:n}){return r=t,0===Object.keys(r).length?e:t[n]||t;var r}function x(e){return e?(t,n)=>n[e]:null}const A=({styledArg:e,props:t,defaultTheme:n,themeId:r})=>{const i=e((0,o.A)({},t,{theme:S((0,o.A)({},t,{defaultTheme:n,themeId:r}))}));let a;return i&&i.variants&&(a=i.variants,delete i.variants),a?[i,...v(t,m(a),a)]:i};function k(e={}){const{themeId:t,defaultTheme:n=w,rootShouldForwardProp:s=b,slotShouldForwardProp:l=b}=e,u=e=>(0,f.A)((0,o.A)({},e,{theme:S((0,o.A)({},e,{defaultTheme:n,themeId:t}))}));return u.__mui_systemSx=!0,(e,c={})=>{(0,i.q_)(e,(e=>e.filter((e=>!(null!=e&&e.__mui_systemSx)))));const{name:d,slot:f,skipVariantsResolver:w,skipSx:k,overridesResolver:_=x(E(f))}=c,C=(0,r.A)(c,p),O=void 0!==w?w:f&&"Root"!==f&&"root"!==f||!1,P=k||!1;let R=b;"Root"===f||"root"===f?R=s:f?R=l:function(e){return"string"==typeof e&&e.charCodeAt(0)>96}(e)&&(R=void 0);const T=(0,i.Ay)(e,(0,o.A)({shouldForwardProp:R,label:void 0},C)),F=(r,...i)=>{const s=i?i.map((e=>{if("function"==typeof e&&e.__emotion_real!==e)return r=>A({styledArg:e,props:r,defaultTheme:n,themeId:t});if((0,a.Q)(e)){let t,n=e;return e&&e.variants&&(t=e.variants,delete n.variants,n=n=>{let r=e;return v(n,m(t),t).forEach((e=>{r=(0,a.A)(r,e)})),r}),n}return e})):[];let l=r;if((0,a.Q)(r)){let e;r&&r.variants&&(e=r.variants,delete l.variants,l=t=>{let n=r;return v(t,m(e),e).forEach((e=>{n=(0,a.A)(n,e)})),n})}else"function"==typeof r&&r.__emotion_real!==r&&(l=e=>A({styledArg:r,props:e,defaultTheme:n,themeId:t}));d&&_&&s.push((e=>{const r=S((0,o.A)({},e,{defaultTheme:n,themeId:t})),i=h(d,r);if(i){const t={};return Object.entries(i).forEach((([n,i])=>{t[n]="function"==typeof i?i((0,o.A)({},e,{theme:r})):i})),_(e,t)}return null})),d&&!O&&s.push((e=>{const r=S((0,o.A)({},e,{defaultTheme:n,themeId:t}));return y(e,g(d,r),r,d)})),P||s.push(u);const c=s.length-i.length;if(Array.isArray(r)&&c>0){const e=new Array(c).fill("");l=[...r,...e],l.raw=[...r.raw,...e]}const f=T(l,...s);return e.muiName&&(f.muiName=e.muiName),f};return T.withConfig&&(F.withConfig=T.withConfig),F}}},9838:()=>{},9898:(e,t,n)=>{"use strict";var r=n(4994);t.A=void 0;var o=r(n(2032)),i=n(4848),a=(0,o.default)((0,i.jsx)("path",{d:"M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"}),"Lock");t.A=a},9903:function(e,t,n){"use strict";var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var o in t=arguments[n])Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o]);return e},r.apply(this,arguments)};Object.defineProperty(t,"__esModule",{value:!0});var o,i=n(6405);t.InMemoryWebStorage=i.InMemoryWebStorage,t.setUserManager=function(e){o=e},t.getUserManager=function(){return o},t.authenticationServiceInternal=function(e){return function(t,n){if(o)return o;var a=r({},t);return n&&(a.userStore=new e({store:new n})),o=new i.UserManager(a)}},t.authenticationService=t.authenticationServiceInternal(i.WebStorageStateStore)},9957:(e,t,n)=>{"use strict";var r=Function.prototype.call,o=Object.prototype.hasOwnProperty,i=n(6743);e.exports=i.call(r,o)},9982:(e,t,n)=>{"use strict";e.exports=n(7463)}},r={};function o(e){var t=r[e];if(void 0!==t)return t.exports;var i=r[e]={id:e,loaded:!1,exports:{}};return n[e].call(i.exports,i,i.exports,o),i.loaded=!0,i.exports}o.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return o.d(t,{a:t}),t},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,o.t=function(n,r){if(1&r&&(n=this(n)),8&r)return n;if("object"==typeof n&&n){if(4&r&&n.__esModule)return n;if(16&r&&"function"==typeof n.then)return n}var i=Object.create(null);o.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var s=2&r&&n;"object"==typeof s&&!~e.indexOf(s);s=t(s))Object.getOwnPropertyNames(s).forEach((e=>a[e]=()=>n[e]));return a.default=()=>n,o.d(i,a),i},o.d=(e,t)=>{for(var n in t)o.o(t,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{"use strict";o(6523);const e={type:"logger",log(e){this.output("log",e)},warn(e){this.output("warn",e)},error(e){this.output("error",e)},output(e,t){console&&console[e]&&console[e].apply(console,t)}};class t{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.init(e,t)}init(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.prefix=n.prefix||"i18next:",this.logger=t||e,this.options=n,this.debug=n.debug}log(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return this.forward(t,"log","",!0)}warn(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return this.forward(t,"warn","",!0)}error(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return this.forward(t,"error","")}deprecate(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return this.forward(t,"warn","WARNING DEPRECATED: ",!0)}forward(e,t,n,r){return r&&!this.debug?null:("string"==typeof e[0]&&(e[0]=`${n}${this.prefix} ${e[0]}`),this.logger[t](e))}create(e){return new t(this.logger,{prefix:`${this.prefix}:${e}:`,...this.options})}clone(e){return(e=e||this.options).prefix=e.prefix||this.prefix,new t(this.logger,e)}}var n=new t;class r{constructor(){this.observers={}}on(e,t){return e.split(" ").forEach((e=>{this.observers[e]=this.observers[e]||[],this.observers[e].push(t)})),this}off(e,t){this.observers[e]&&(t?this.observers[e]=this.observers[e].filter((e=>e!==t)):delete this.observers[e])}emit(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];this.observers[e]&&[].concat(this.observers[e]).forEach((e=>{e(...n)})),this.observers["*"]&&[].concat(this.observers["*"]).forEach((t=>{t.apply(t,[e,...n])}))}}function i(){let e,t;const n=new Promise(((n,r)=>{e=n,t=r}));return n.resolve=e,n.reject=t,n}function a(e){return null==e?"":""+e}function s(e,t,n){function r(e){return e&&e.indexOf("###")>-1?e.replace(/###/g,"."):e}function o(){return!e||"string"==typeof e}const i="string"!=typeof t?[].concat(t):t.split(".");for(;i.length>1;){if(o())return{};const t=r(i.shift());!e[t]&&n&&(e[t]=new n),e=Object.prototype.hasOwnProperty.call(e,t)?e[t]:{}}return o()?{}:{obj:e,k:r(i.shift())}}function l(e,t,n){const{obj:r,k:o}=s(e,t,Object);r[o]=n}function u(e,t){const{obj:n,k:r}=s(e,t);if(n)return n[r]}function c(e,t,n){for(const r in t)"__proto__"!==r&&"constructor"!==r&&(r in e?"string"==typeof e[r]||e[r]instanceof String||"string"==typeof t[r]||t[r]instanceof String?n&&(e[r]=t[r]):c(e[r],t[r],n):e[r]=t[r]);return e}function d(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}var f={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"};function p(e){return"string"==typeof e?e.replace(/[&<>"'\/]/g,(e=>f[e])):e}const h=[" ",",","?","!",";"];function m(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:".";if(!e)return;if(e[t])return e[t];const r=t.split(n);let o=e;for(let e=0;e<r.length;++e){if(!o)return;if("string"==typeof o[r[e]]&&e+1<r.length)return;if(void 0===o[r[e]]){let i=2,a=r.slice(e,e+i).join(n),s=o[a];for(;void 0===s&&r.length>e+i;)i++,a=r.slice(e,e+i).join(n),s=o[a];if(void 0===s)return;if(null===s)return null;if(t.endsWith(a)){if("string"==typeof s)return s;if(a&&"string"==typeof s[a])return s[a]}const l=r.slice(e+i).join(n);return l?m(s,l,n):void 0}o=o[r[e]]}return o}function g(e){return e&&e.indexOf("_")>0?e.replace("_","-"):e}class v extends r{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{ns:["translation"],defaultNS:"translation"};super(),this.data=e||{},this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator="."),void 0===this.options.ignoreJSONStructure&&(this.options.ignoreJSONStructure=!0)}addNamespaces(e){this.options.ns.indexOf(e)<0&&this.options.ns.push(e)}removeNamespaces(e){const t=this.options.ns.indexOf(e);t>-1&&this.options.ns.splice(t,1)}getResource(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};const o=void 0!==r.keySeparator?r.keySeparator:this.options.keySeparator,i=void 0!==r.ignoreJSONStructure?r.ignoreJSONStructure:this.options.ignoreJSONStructure;let a=[e,t];n&&"string"!=typeof n&&(a=a.concat(n)),n&&"string"==typeof n&&(a=a.concat(o?n.split(o):n)),e.indexOf(".")>-1&&(a=e.split("."));const s=u(this.data,a);return s||!i||"string"!=typeof n?s:m(this.data&&this.data[e]&&this.data[e][t],n,o)}addResource(e,t,n,r){let o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{silent:!1};const i=void 0!==o.keySeparator?o.keySeparator:this.options.keySeparator;let a=[e,t];n&&(a=a.concat(i?n.split(i):n)),e.indexOf(".")>-1&&(a=e.split("."),r=t,t=a[1]),this.addNamespaces(t),l(this.data,a,r),o.silent||this.emit("added",e,t,n,r)}addResources(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{silent:!1};for(const r in n)"string"!=typeof n[r]&&"[object Array]"!==Object.prototype.toString.apply(n[r])||this.addResource(e,t,r,n[r],{silent:!0});r.silent||this.emit("added",e,t,n)}addResourceBundle(e,t,n,r,o){let i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{silent:!1},a=[e,t];e.indexOf(".")>-1&&(a=e.split("."),r=n,n=t,t=a[1]),this.addNamespaces(t);let s=u(this.data,a)||{};r?c(s,n,o):s={...s,...n},l(this.data,a,s),i.silent||this.emit("added",e,t,n)}removeResourceBundle(e,t){this.hasResourceBundle(e,t)&&delete this.data[e][t],this.removeNamespaces(t),this.emit("removed",e,t)}hasResourceBundle(e,t){return void 0!==this.getResource(e,t)}getResourceBundle(e,t){return t||(t=this.options.defaultNS),"v1"===this.options.compatibilityAPI?{...this.getResource(e,t)}:this.getResource(e,t)}getDataByLanguage(e){return this.data[e]}hasLanguageSomeTranslations(e){const t=this.getDataByLanguage(e);return!!(t&&Object.keys(t)||[]).find((e=>t[e]&&Object.keys(t[e]).length>0))}toJSON(){return this.data}}var y={processors:{},addPostProcessor(e){this.processors[e.name]=e},handle(e,t,n,r,o){return e.forEach((e=>{this.processors[e]&&(t=this.processors[e].process(t,n,r,o))})),t}};const b={};class w extends r{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};var r,o;super(),r=e,o=this,["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"].forEach((e=>{r[e]&&(o[e]=r[e])})),this.options=t,void 0===this.options.keySeparator&&(this.options.keySeparator="."),this.logger=n.create("translator")}changeLanguage(e){e&&(this.language=e)}exists(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}};if(null==e)return!1;const n=this.resolve(e,t);return n&&void 0!==n.res}extractFromKey(e,t){let n=void 0!==t.nsSeparator?t.nsSeparator:this.options.nsSeparator;void 0===n&&(n=":");const r=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator;let o=t.ns||this.options.defaultNS||[];const i=n&&e.indexOf(n)>-1,a=!(this.options.userDefinedKeySeparator||t.keySeparator||this.options.userDefinedNsSeparator||t.nsSeparator||function(e,t,n){t=t||"",n=n||"";const r=h.filter((e=>t.indexOf(e)<0&&n.indexOf(e)<0));if(0===r.length)return!0;const o=new RegExp(`(${r.map((e=>"?"===e?"\\?":e)).join("|")})`);let i=!o.test(e);if(!i){const t=e.indexOf(n);t>0&&!o.test(e.substring(0,t))&&(i=!0)}return i}(e,n,r));if(i&&!a){const t=e.match(this.interpolator.nestingRegexp);if(t&&t.length>0)return{key:e,namespaces:o};const i=e.split(n);(n!==r||n===r&&this.options.ns.indexOf(i[0])>-1)&&(o=i.shift()),e=i.join(r)}return"string"==typeof o&&(o=[o]),{key:e,namespaces:o}}translate(e,t,n){if("object"!=typeof t&&this.options.overloadTranslationOptionHandler&&(t=this.options.overloadTranslationOptionHandler(arguments)),"object"==typeof t&&(t={...t}),t||(t={}),null==e)return"";Array.isArray(e)||(e=[String(e)]);const r=void 0!==t.returnDetails?t.returnDetails:this.options.returnDetails,o=void 0!==t.keySeparator?t.keySeparator:this.options.keySeparator,{key:i,namespaces:a}=this.extractFromKey(e[e.length-1],t),s=a[a.length-1],l=t.lng||this.language,u=t.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(l&&"cimode"===l.toLowerCase()){if(u){const e=t.nsSeparator||this.options.nsSeparator;return r?{res:`${s}${e}${i}`,usedKey:i,exactUsedKey:i,usedLng:l,usedNS:s,usedParams:this.getUsedParamsDetails(t)}:`${s}${e}${i}`}return r?{res:i,usedKey:i,exactUsedKey:i,usedLng:l,usedNS:s,usedParams:this.getUsedParamsDetails(t)}:i}const c=this.resolve(e,t);let d=c&&c.res;const f=c&&c.usedKey||i,p=c&&c.exactUsedKey||i,h=Object.prototype.toString.apply(d),m=void 0!==t.joinArrays?t.joinArrays:this.options.joinArrays,g=!this.i18nFormat||this.i18nFormat.handleAsObject;if(g&&d&&"string"!=typeof d&&"boolean"!=typeof d&&"number"!=typeof d&&["[object Number]","[object Function]","[object RegExp]"].indexOf(h)<0&&("string"!=typeof m||"[object Array]"!==h)){if(!t.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const e=this.options.returnedObjectHandler?this.options.returnedObjectHandler(f,d,{...t,ns:a}):`key '${i} (${this.language})' returned an object instead of string.`;return r?(c.res=e,c.usedParams=this.getUsedParamsDetails(t),c):e}if(o){const e="[object Array]"===h,n=e?[]:{},r=e?p:f;for(const e in d)if(Object.prototype.hasOwnProperty.call(d,e)){const i=`${r}${o}${e}`;n[e]=this.translate(i,{...t,joinArrays:!1,ns:a}),n[e]===i&&(n[e]=d[e])}d=n}}else if(g&&"string"==typeof m&&"[object Array]"===h)d=d.join(m),d&&(d=this.extendTranslation(d,e,t,n));else{let r=!1,a=!1;const u=void 0!==t.count&&"string"!=typeof t.count,f=w.hasDefaultValue(t),p=u?this.pluralResolver.getSuffix(l,t.count,t):"",h=t.ordinal&&u?this.pluralResolver.getSuffix(l,t.count,{ordinal:!1}):"",m=t[`defaultValue${p}`]||t[`defaultValue${h}`]||t.defaultValue;!this.isValidLookup(d)&&f&&(r=!0,d=m),this.isValidLookup(d)||(a=!0,d=i);const g=(t.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&a?void 0:d,v=f&&m!==d&&this.options.updateMissing;if(a||r||v){if(this.logger.log(v?"updateKey":"missingKey",l,s,i,v?m:d),o){const e=this.resolve(i,{...t,keySeparator:!1});e&&e.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let e=[];const n=this.languageUtils.getFallbackCodes(this.options.fallbackLng,t.lng||this.language);if("fallback"===this.options.saveMissingTo&&n&&n[0])for(let t=0;t<n.length;t++)e.push(n[t]);else"all"===this.options.saveMissingTo?e=this.languageUtils.toResolveHierarchy(t.lng||this.language):e.push(t.lng||this.language);const r=(e,n,r)=>{const o=f&&r!==d?r:g;this.options.missingKeyHandler?this.options.missingKeyHandler(e,s,n,o,v,t):this.backendConnector&&this.backendConnector.saveMissing&&this.backendConnector.saveMissing(e,s,n,o,v,t),this.emit("missingKey",e,s,n,d)};this.options.saveMissing&&(this.options.saveMissingPlurals&&u?e.forEach((e=>{this.pluralResolver.getSuffixes(e,t).forEach((n=>{r([e],i+n,t[`defaultValue${n}`]||m)}))})):r(e,i,m))}d=this.extendTranslation(d,e,t,c,n),a&&d===i&&this.options.appendNamespaceToMissingKey&&(d=`${s}:${i}`),(a||r)&&this.options.parseMissingKeyHandler&&(d="v1"!==this.options.compatibilityAPI?this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${s}:${i}`:i,r?d:void 0):this.options.parseMissingKeyHandler(d))}return r?(c.res=d,c.usedParams=this.getUsedParamsDetails(t),c):d}extendTranslation(e,t,n,r,o){var i=this;if(this.i18nFormat&&this.i18nFormat.parse)e=this.i18nFormat.parse(e,{...this.options.interpolation.defaultVariables,...n},n.lng||this.language||r.usedLng,r.usedNS,r.usedKey,{resolved:r});else if(!n.skipInterpolation){n.interpolation&&this.interpolator.init({...n,interpolation:{...this.options.interpolation,...n.interpolation}});const a="string"==typeof e&&(n&&n.interpolation&&void 0!==n.interpolation.skipOnVariables?n.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let s;if(a){const t=e.match(this.interpolator.nestingRegexp);s=t&&t.length}let l=n.replace&&"string"!=typeof n.replace?n.replace:n;if(this.options.interpolation.defaultVariables&&(l={...this.options.interpolation.defaultVariables,...l}),e=this.interpolator.interpolate(e,l,n.lng||this.language,n),a){const t=e.match(this.interpolator.nestingRegexp);s<(t&&t.length)&&(n.nest=!1)}!n.lng&&"v1"!==this.options.compatibilityAPI&&r&&r.res&&(n.lng=r.usedLng),!1!==n.nest&&(e=this.interpolator.nest(e,(function(){for(var e=arguments.length,r=new Array(e),a=0;a<e;a++)r[a]=arguments[a];return o&&o[0]===r[0]&&!n.context?(i.logger.warn(`It seems you are nesting recursively key: ${r[0]} in key: ${t[0]}`),null):i.translate(...r,t)}),n)),n.interpolation&&this.interpolator.reset()}const a=n.postProcess||this.options.postProcess,s="string"==typeof a?[a]:a;return null!=e&&s&&s.length&&!1!==n.applyPostProcessor&&(e=y.handle(s,e,t,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...r,usedParams:this.getUsedParamsDetails(n)},...n}:n,this)),e}resolve(e){let t,n,r,o,i,a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"string"==typeof e&&(e=[e]),e.forEach((e=>{if(this.isValidLookup(t))return;const s=this.extractFromKey(e,a),l=s.key;n=l;let u=s.namespaces;this.options.fallbackNS&&(u=u.concat(this.options.fallbackNS));const c=void 0!==a.count&&"string"!=typeof a.count,d=c&&!a.ordinal&&0===a.count&&this.pluralResolver.shouldUseIntlApi(),f=void 0!==a.context&&("string"==typeof a.context||"number"==typeof a.context)&&""!==a.context,p=a.lngs?a.lngs:this.languageUtils.toResolveHierarchy(a.lng||this.language,a.fallbackLng);u.forEach((e=>{this.isValidLookup(t)||(i=e,!b[`${p[0]}-${e}`]&&this.utils&&this.utils.hasLoadedNamespace&&!this.utils.hasLoadedNamespace(i)&&(b[`${p[0]}-${e}`]=!0,this.logger.warn(`key "${n}" for languages "${p.join(", ")}" won't get resolved as namespace "${i}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),p.forEach((n=>{if(this.isValidLookup(t))return;o=n;const i=[l];if(this.i18nFormat&&this.i18nFormat.addLookupKeys)this.i18nFormat.addLookupKeys(i,l,n,e,a);else{let e;c&&(e=this.pluralResolver.getSuffix(n,a.count,a));const t=`${this.options.pluralSeparator}zero`,r=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(c&&(i.push(l+e),a.ordinal&&0===e.indexOf(r)&&i.push(l+e.replace(r,this.options.pluralSeparator)),d&&i.push(l+t)),f){const n=`${l}${this.options.contextSeparator}${a.context}`;i.push(n),c&&(i.push(n+e),a.ordinal&&0===e.indexOf(r)&&i.push(n+e.replace(r,this.options.pluralSeparator)),d&&i.push(n+t))}}let s;for(;s=i.pop();)this.isValidLookup(t)||(r=s,t=this.getResource(n,e,s,a))})))}))})),{res:t,usedKey:n,exactUsedKey:r,usedLng:o,usedNS:i}}isValidLookup(e){return!(void 0===e||!this.options.returnNull&&null===e||!this.options.returnEmptyString&&""===e)}getResource(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return this.i18nFormat&&this.i18nFormat.getResource?this.i18nFormat.getResource(e,t,n,r):this.resourceStore.getResource(e,t,n,r)}getUsedParamsDetails(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const t=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],n=e.replace&&"string"!=typeof e.replace;let r=n?e.replace:e;if(n&&void 0!==e.count&&(r.count=e.count),this.options.interpolation.defaultVariables&&(r={...this.options.interpolation.defaultVariables,...r}),!n){r={...r};for(const e of t)delete r[e]}return r}static hasDefaultValue(e){for(const t in e)if(Object.prototype.hasOwnProperty.call(e,t)&&"defaultValue"===t.substring(0,12)&&void 0!==e[t])return!0;return!1}}function E(e){return e.charAt(0).toUpperCase()+e.slice(1)}class S{constructor(e){this.options=e,this.supportedLngs=this.options.supportedLngs||!1,this.logger=n.create("languageUtils")}getScriptPartFromCode(e){if(!(e=g(e))||e.indexOf("-")<0)return null;const t=e.split("-");return 2===t.length?null:(t.pop(),"x"===t[t.length-1].toLowerCase()?null:this.formatLanguageCode(t.join("-")))}getLanguagePartFromCode(e){if(!(e=g(e))||e.indexOf("-")<0)return e;const t=e.split("-");return this.formatLanguageCode(t[0])}formatLanguageCode(e){if("string"==typeof e&&e.indexOf("-")>-1){const t=["hans","hant","latn","cyrl","cans","mong","arab"];let n=e.split("-");return this.options.lowerCaseLng?n=n.map((e=>e.toLowerCase())):2===n.length?(n[0]=n[0].toLowerCase(),n[1]=n[1].toUpperCase(),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=E(n[1].toLowerCase()))):3===n.length&&(n[0]=n[0].toLowerCase(),2===n[1].length&&(n[1]=n[1].toUpperCase()),"sgn"!==n[0]&&2===n[2].length&&(n[2]=n[2].toUpperCase()),t.indexOf(n[1].toLowerCase())>-1&&(n[1]=E(n[1].toLowerCase())),t.indexOf(n[2].toLowerCase())>-1&&(n[2]=E(n[2].toLowerCase()))),n.join("-")}return this.options.cleanCode||this.options.lowerCaseLng?e.toLowerCase():e}isSupportedCode(e){return("languageOnly"===this.options.load||this.options.nonExplicitSupportedLngs)&&(e=this.getLanguagePartFromCode(e)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(e)>-1}getBestMatchFromCodes(e){if(!e)return null;let t;return e.forEach((e=>{if(t)return;const n=this.formatLanguageCode(e);this.options.supportedLngs&&!this.isSupportedCode(n)||(t=n)})),!t&&this.options.supportedLngs&&e.forEach((e=>{if(t)return;const n=this.getLanguagePartFromCode(e);if(this.isSupportedCode(n))return t=n;t=this.options.supportedLngs.find((e=>e===n?e:e.indexOf("-")<0&&n.indexOf("-")<0?void 0:0===e.indexOf(n)?e:void 0))})),t||(t=this.getFallbackCodes(this.options.fallbackLng)[0]),t}getFallbackCodes(e,t){if(!e)return[];if("function"==typeof e&&(e=e(t)),"string"==typeof e&&(e=[e]),"[object Array]"===Object.prototype.toString.apply(e))return e;if(!t)return e.default||[];let n=e[t];return n||(n=e[this.getScriptPartFromCode(t)]),n||(n=e[this.formatLanguageCode(t)]),n||(n=e[this.getLanguagePartFromCode(t)]),n||(n=e.default),n||[]}toResolveHierarchy(e,t){const n=this.getFallbackCodes(t||this.options.fallbackLng||[],e),r=[],o=e=>{e&&(this.isSupportedCode(e)?r.push(e):this.logger.warn(`rejecting language code not found in supportedLngs: ${e}`))};return"string"==typeof e&&(e.indexOf("-")>-1||e.indexOf("_")>-1)?("languageOnly"!==this.options.load&&o(this.formatLanguageCode(e)),"languageOnly"!==this.options.load&&"currentOnly"!==this.options.load&&o(this.getScriptPartFromCode(e)),"currentOnly"!==this.options.load&&o(this.getLanguagePartFromCode(e))):"string"==typeof e&&o(this.formatLanguageCode(e)),n.forEach((e=>{r.indexOf(e)<0&&o(this.formatLanguageCode(e))})),r}}let x=[{lngs:["ach","ak","am","arn","br","fil","gun","ln","mfe","mg","mi","oc","pt","pt-BR","tg","tl","ti","tr","uz","wa"],nr:[1,2],fc:1},{lngs:["af","an","ast","az","bg","bn","ca","da","de","dev","el","en","eo","es","et","eu","fi","fo","fur","fy","gl","gu","ha","hi","hu","hy","ia","it","kk","kn","ku","lb","mai","ml","mn","mr","nah","nap","nb","ne","nl","nn","no","nso","pa","pap","pms","ps","pt-PT","rm","sco","se","si","so","son","sq","sv","sw","ta","te","tk","ur","yo"],nr:[1,2],fc:2},{lngs:["ay","bo","cgg","fa","ht","id","ja","jbo","ka","km","ko","ky","lo","ms","sah","su","th","tt","ug","vi","wo","zh"],nr:[1],fc:3},{lngs:["be","bs","cnr","dz","hr","ru","sr","uk"],nr:[1,2,5],fc:4},{lngs:["ar"],nr:[0,1,2,3,11,100],fc:5},{lngs:["cs","sk"],nr:[1,2,5],fc:6},{lngs:["csb","pl"],nr:[1,2,5],fc:7},{lngs:["cy"],nr:[1,2,3,8],fc:8},{lngs:["fr"],nr:[1,2],fc:9},{lngs:["ga"],nr:[1,2,3,7,11],fc:10},{lngs:["gd"],nr:[1,2,3,20],fc:11},{lngs:["is"],nr:[1,2],fc:12},{lngs:["jv"],nr:[0,1],fc:13},{lngs:["kw"],nr:[1,2,3,4],fc:14},{lngs:["lt"],nr:[1,2,10],fc:15},{lngs:["lv"],nr:[1,2,0],fc:16},{lngs:["mk"],nr:[1,2],fc:17},{lngs:["mnk"],nr:[0,1,2],fc:18},{lngs:["mt"],nr:[1,2,11,20],fc:19},{lngs:["or"],nr:[2,1],fc:2},{lngs:["ro"],nr:[1,2,20],fc:20},{lngs:["sl"],nr:[5,1,2,3],fc:21},{lngs:["he","iw"],nr:[1,2,20,21],fc:22}],A={1:function(e){return Number(e>1)},2:function(e){return Number(1!=e)},3:function(e){return 0},4:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},5:function(e){return Number(0==e?0:1==e?1:2==e?2:e%100>=3&&e%100<=10?3:e%100>=11?4:5)},6:function(e){return Number(1==e?0:e>=2&&e<=4?1:2)},7:function(e){return Number(1==e?0:e%10>=2&&e%10<=4&&(e%100<10||e%100>=20)?1:2)},8:function(e){return Number(1==e?0:2==e?1:8!=e&&11!=e?2:3)},9:function(e){return Number(e>=2)},10:function(e){return Number(1==e?0:2==e?1:e<7?2:e<11?3:4)},11:function(e){return Number(1==e||11==e?0:2==e||12==e?1:e>2&&e<20?2:3)},12:function(e){return Number(e%10!=1||e%100==11)},13:function(e){return Number(0!==e)},14:function(e){return Number(1==e?0:2==e?1:3==e?2:3)},15:function(e){return Number(e%10==1&&e%100!=11?0:e%10>=2&&(e%100<10||e%100>=20)?1:2)},16:function(e){return Number(e%10==1&&e%100!=11?0:0!==e?1:2)},17:function(e){return Number(1==e||e%10==1&&e%100!=11?0:1)},18:function(e){return Number(0==e?0:1==e?1:2)},19:function(e){return Number(1==e?0:0==e||e%100>1&&e%100<11?1:e%100>10&&e%100<20?2:3)},20:function(e){return Number(1==e?0:0==e||e%100>0&&e%100<20?1:2)},21:function(e){return Number(e%100==1?1:e%100==2?2:e%100==3||e%100==4?3:0)},22:function(e){return Number(1==e?0:2==e?1:(e<0||e>10)&&e%10==0?2:3)}};const k=["v1","v2","v3"],_=["v4"],C={zero:0,one:1,two:2,few:3,many:4,other:5};class O{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};this.languageUtils=e,this.options=t,this.logger=n.create("pluralResolver"),this.options.compatibilityJSON&&!_.includes(this.options.compatibilityJSON)||"undefined"!=typeof Intl&&Intl.PluralRules||(this.options.compatibilityJSON="v3",this.logger.error("Your environment seems not to be Intl API compatible, use an Intl.PluralRules polyfill. Will fallback to the compatibilityJSON v3 format handling.")),this.rules=function(){const e={};return x.forEach((t=>{t.lngs.forEach((n=>{e[n]={numbers:t.nr,plurals:A[t.fc]}}))})),e}()}addRule(e,t){this.rules[e]=t}getRule(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(this.shouldUseIntlApi())try{return new Intl.PluralRules(g(e),{type:t.ordinal?"ordinal":"cardinal"})}catch(e){return}return this.rules[e]||this.rules[this.languageUtils.getLanguagePartFromCode(e)]}needsPlural(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=this.getRule(e,t);return this.shouldUseIntlApi()?n&&n.resolvedOptions().pluralCategories.length>1:n&&n.numbers.length>1}getPluralFormsOfKey(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this.getSuffixes(e,n).map((e=>`${t}${e}`))}getSuffixes(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=this.getRule(e,t);return n?this.shouldUseIntlApi()?n.resolvedOptions().pluralCategories.sort(((e,t)=>C[e]-C[t])).map((e=>`${this.options.prepend}${t.ordinal?`ordinal${this.options.prepend}`:""}${e}`)):n.numbers.map((n=>this.getSuffix(e,n,t))):[]}getSuffix(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const r=this.getRule(e,n);return r?this.shouldUseIntlApi()?`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:""}${r.select(t)}`:this.getSuffixRetroCompatible(r,t):(this.logger.warn(`no plural rule found for: ${e}`),"")}getSuffixRetroCompatible(e,t){const n=e.noAbs?e.plurals(t):e.plurals(Math.abs(t));let r=e.numbers[n];this.options.simplifyPluralSuffix&&2===e.numbers.length&&1===e.numbers[0]&&(2===r?r="plural":1===r&&(r=""));const o=()=>this.options.prepend&&r.toString()?this.options.prepend+r.toString():r.toString();return"v1"===this.options.compatibilityJSON?1===r?"":"number"==typeof r?`_plural_${r.toString()}`:o():"v2"===this.options.compatibilityJSON||this.options.simplifyPluralSuffix&&2===e.numbers.length&&1===e.numbers[0]?o():this.options.prepend&&n.toString()?this.options.prepend+n.toString():n.toString()}shouldUseIntlApi(){return!k.includes(this.options.compatibilityJSON)}}function P(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:".",o=!(arguments.length>4&&void 0!==arguments[4])||arguments[4],i=function(e,t,n){const r=u(e,n);return void 0!==r?r:u(t,n)}(e,t,n);return!i&&o&&"string"==typeof n&&(i=m(e,n,r),void 0===i&&(i=m(t,n,r))),i}class R{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=n.create("interpolator"),this.options=e,this.format=e.interpolation&&e.interpolation.format||(e=>e),this.init(e)}init(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};e.interpolation||(e.interpolation={escapeValue:!0});const t=e.interpolation;this.escape=void 0!==t.escape?t.escape:p,this.escapeValue=void 0===t.escapeValue||t.escapeValue,this.useRawValueToEscape=void 0!==t.useRawValueToEscape&&t.useRawValueToEscape,this.prefix=t.prefix?d(t.prefix):t.prefixEscaped||"{{",this.suffix=t.suffix?d(t.suffix):t.suffixEscaped||"}}",this.formatSeparator=t.formatSeparator?t.formatSeparator:t.formatSeparator||",",this.unescapePrefix=t.unescapeSuffix?"":t.unescapePrefix||"-",this.unescapeSuffix=this.unescapePrefix?"":t.unescapeSuffix||"",this.nestingPrefix=t.nestingPrefix?d(t.nestingPrefix):t.nestingPrefixEscaped||d("$t("),this.nestingSuffix=t.nestingSuffix?d(t.nestingSuffix):t.nestingSuffixEscaped||d(")"),this.nestingOptionsSeparator=t.nestingOptionsSeparator?t.nestingOptionsSeparator:t.nestingOptionsSeparator||",",this.maxReplaces=t.maxReplaces?t.maxReplaces:1e3,this.alwaysFormat=void 0!==t.alwaysFormat&&t.alwaysFormat,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const e=`${this.prefix}(.+?)${this.suffix}`;this.regexp=new RegExp(e,"g");const t=`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`;this.regexpUnescape=new RegExp(t,"g");const n=`${this.nestingPrefix}(.+?)${this.nestingSuffix}`;this.nestingRegexp=new RegExp(n,"g")}interpolate(e,t,n,r){let o,i,s;const l=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{};function u(e){return e.replace(/\$/g,"$$$$")}const c=e=>{if(e.indexOf(this.formatSeparator)<0){const o=P(t,l,e,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(o,void 0,n,{...r,...t,interpolationkey:e}):o}const o=e.split(this.formatSeparator),i=o.shift().trim(),a=o.join(this.formatSeparator).trim();return this.format(P(t,l,i,this.options.keySeparator,this.options.ignoreJSONStructure),a,n,{...r,...t,interpolationkey:i})};this.resetRegExp();const d=r&&r.missingInterpolationHandler||this.options.missingInterpolationHandler,f=r&&r.interpolation&&void 0!==r.interpolation.skipOnVariables?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:e=>u(e)},{regex:this.regexp,safeValue:e=>this.escapeValue?u(this.escape(e)):u(e)}].forEach((t=>{for(s=0;o=t.regex.exec(e);){const n=o[1].trim();if(i=c(n),void 0===i)if("function"==typeof d){const t=d(e,o,r);i="string"==typeof t?t:""}else if(r&&Object.prototype.hasOwnProperty.call(r,n))i="";else{if(f){i=o[0];continue}this.logger.warn(`missed to pass in variable ${n} for interpolating ${e}`),i=""}else"string"==typeof i||this.useRawValueToEscape||(i=a(i));const l=t.safeValue(i);if(e=e.replace(o[0],l),f?(t.regex.lastIndex+=i.length,t.regex.lastIndex-=o[0].length):t.regex.lastIndex=0,s++,s>=this.maxReplaces)break}})),e}nest(e,t){let n,r,o,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};function s(e,t){const n=this.nestingOptionsSeparator;if(e.indexOf(n)<0)return e;const r=e.split(new RegExp(`${n}[ ]*{`));let i=`{${r[1]}`;e=r[0],i=this.interpolate(i,o);const a=i.match(/'/g),s=i.match(/"/g);(a&&a.length%2==0&&!s||s.length%2!=0)&&(i=i.replace(/'/g,'"'));try{o=JSON.parse(i),t&&(o={...t,...o})}catch(t){return this.logger.warn(`failed parsing options string in nesting for key ${e}`,t),`${e}${n}${i}`}return delete o.defaultValue,e}for(;n=this.nestingRegexp.exec(e);){let l=[];o={...i},o=o.replace&&"string"!=typeof o.replace?o.replace:o,o.applyPostProcessor=!1,delete o.defaultValue;let u=!1;if(-1!==n[0].indexOf(this.formatSeparator)&&!/{.*}/.test(n[1])){const e=n[1].split(this.formatSeparator).map((e=>e.trim()));n[1]=e.shift(),l=e,u=!0}if(r=t(s.call(this,n[1].trim(),o),o),r&&n[0]===e&&"string"!=typeof r)return r;"string"!=typeof r&&(r=a(r)),r||(this.logger.warn(`missed to resolve ${n[1]} for nesting ${e}`),r=""),u&&(r=l.reduce(((e,t)=>this.format(e,t,i.lng,{...i,interpolationkey:n[1].trim()})),r.trim())),e=e.replace(n[0],r),this.regexp.lastIndex=0}return e}}function T(e){const t={};return function(n,r,o){const i=r+JSON.stringify(o);let a=t[i];return a||(a=e(g(r),o),t[i]=a),a(n)}}class F{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.logger=n.create("formatter"),this.options=e,this.formats={number:T(((e,t)=>{const n=new Intl.NumberFormat(e,{...t});return e=>n.format(e)})),currency:T(((e,t)=>{const n=new Intl.NumberFormat(e,{...t,style:"currency"});return e=>n.format(e)})),datetime:T(((e,t)=>{const n=new Intl.DateTimeFormat(e,{...t});return e=>n.format(e)})),relativetime:T(((e,t)=>{const n=new Intl.RelativeTimeFormat(e,{...t});return e=>n.format(e,t.range||"day")})),list:T(((e,t)=>{const n=new Intl.ListFormat(e,{...t});return e=>n.format(e)}))},this.init(e)}init(e){const t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{interpolation:{}}).interpolation;this.formatSeparator=t.formatSeparator?t.formatSeparator:t.formatSeparator||","}add(e,t){this.formats[e.toLowerCase().trim()]=t}addCached(e,t){this.formats[e.toLowerCase().trim()]=T(t)}format(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};return t.split(this.formatSeparator).reduce(((e,t)=>{const{formatName:o,formatOptions:i}=function(e){let t=e.toLowerCase().trim();const n={};if(e.indexOf("(")>-1){const r=e.split("(");t=r[0].toLowerCase().trim();const o=r[1].substring(0,r[1].length-1);"currency"===t&&o.indexOf(":")<0?n.currency||(n.currency=o.trim()):"relativetime"===t&&o.indexOf(":")<0?n.range||(n.range=o.trim()):o.split(";").forEach((e=>{if(!e)return;const[t,...r]=e.split(":"),o=r.join(":").trim().replace(/^'+|'+$/g,"");n[t.trim()]||(n[t.trim()]=o),"false"===o&&(n[t.trim()]=!1),"true"===o&&(n[t.trim()]=!0),isNaN(o)||(n[t.trim()]=parseInt(o,10))}))}return{formatName:t,formatOptions:n}}(t);if(this.formats[o]){let t=e;try{const a=r&&r.formatParams&&r.formatParams[r.interpolationkey]||{},s=a.locale||a.lng||r.locale||r.lng||n;t=this.formats[o](e,s,{...i,...r,...a})}catch(e){this.logger.warn(e)}return t}return this.logger.warn(`there was no format function for ${o}`),e}),e)}}class j extends r{constructor(e,t,r){let o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};super(),this.backend=e,this.store=t,this.services=r,this.languageUtils=r.languageUtils,this.options=o,this.logger=n.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=o.maxParallelReads||10,this.readingCalls=0,this.maxRetries=o.maxRetries>=0?o.maxRetries:5,this.retryTimeout=o.retryTimeout>=1?o.retryTimeout:350,this.state={},this.queue=[],this.backend&&this.backend.init&&this.backend.init(r,o.backend,o)}queueLoad(e,t,n,r){const o={},i={},a={},s={};return e.forEach((e=>{let r=!0;t.forEach((t=>{const a=`${e}|${t}`;!n.reload&&this.store.hasResourceBundle(e,t)?this.state[a]=2:this.state[a]<0||(1===this.state[a]?void 0===i[a]&&(i[a]=!0):(this.state[a]=1,r=!1,void 0===i[a]&&(i[a]=!0),void 0===o[a]&&(o[a]=!0),void 0===s[t]&&(s[t]=!0)))})),r||(a[e]=!0)})),(Object.keys(o).length||Object.keys(i).length)&&this.queue.push({pending:i,pendingCount:Object.keys(i).length,loaded:{},errors:[],callback:r}),{toLoad:Object.keys(o),pending:Object.keys(i),toLoadLanguages:Object.keys(a),toLoadNamespaces:Object.keys(s)}}loaded(e,t,n){const r=e.split("|"),o=r[0],i=r[1];t&&this.emit("failedLoading",o,i,t),n&&this.store.addResourceBundle(o,i,n),this.state[e]=t?-1:2;const a={};this.queue.forEach((n=>{!function(e,t,n){const{obj:r,k:o}=s(e,t,Object);r[o]=r[o]||[],r[o].push(n)}(n.loaded,[o],i),function(e,t){void 0!==e.pending[t]&&(delete e.pending[t],e.pendingCount--)}(n,e),t&&n.errors.push(t),0!==n.pendingCount||n.done||(Object.keys(n.loaded).forEach((e=>{a[e]||(a[e]={});const t=n.loaded[e];t.length&&t.forEach((t=>{void 0===a[e][t]&&(a[e][t]=!0)}))})),n.done=!0,n.errors.length?n.callback(n.errors):n.callback())})),this.emit("loaded",a),this.queue=this.queue.filter((e=>!e.done))}read(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:this.retryTimeout,i=arguments.length>5?arguments[5]:void 0;if(!e.length)return i(null,{});if(this.readingCalls>=this.maxParallelReads)return void this.waitingReads.push({lng:e,ns:t,fcName:n,tried:r,wait:o,callback:i});this.readingCalls++;const a=(a,s)=>{if(this.readingCalls--,this.waitingReads.length>0){const e=this.waitingReads.shift();this.read(e.lng,e.ns,e.fcName,e.tried,e.wait,e.callback)}a&&s&&r<this.maxRetries?setTimeout((()=>{this.read.call(this,e,t,n,r+1,2*o,i)}),o):i(a,s)},s=this.backend[n].bind(this.backend);if(2!==s.length)return s(e,t,a);try{const n=s(e,t);n&&"function"==typeof n.then?n.then((e=>a(null,e))).catch(a):a(null,n)}catch(e){a(e)}}prepareLoading(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=arguments.length>3?arguments[3]:void 0;if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),r&&r();"string"==typeof e&&(e=this.languageUtils.toResolveHierarchy(e)),"string"==typeof t&&(t=[t]);const o=this.queueLoad(e,t,n,r);if(!o.toLoad.length)return o.pending.length||r(),null;o.toLoad.forEach((e=>{this.loadOne(e)}))}load(e,t,n){this.prepareLoading(e,t,{},n)}reload(e,t,n){this.prepareLoading(e,t,{reload:!0},n)}loadOne(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const n=e.split("|"),r=n[0],o=n[1];this.read(r,o,"read",void 0,void 0,((n,i)=>{n&&this.logger.warn(`${t}loading namespace ${o} for language ${r} failed`,n),!n&&i&&this.logger.log(`${t}loaded namespace ${o} for language ${r}`,i),this.loaded(e,n,i)}))}saveMissing(e,t,n,r,o){let i=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{},a=arguments.length>6&&void 0!==arguments[6]?arguments[6]:()=>{};if(this.services.utils&&this.services.utils.hasLoadedNamespace&&!this.services.utils.hasLoadedNamespace(t))this.logger.warn(`did not save key "${n}" as the namespace "${t}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!");else if(null!=n&&""!==n){if(this.backend&&this.backend.create){const s={...i,isUpdate:o},l=this.backend.create.bind(this.backend);if(l.length<6)try{let o;o=5===l.length?l(e,t,n,r,s):l(e,t,n,r),o&&"function"==typeof o.then?o.then((e=>a(null,e))).catch(a):a(null,o)}catch(e){a(e)}else l(e,t,n,r,a,s)}e&&e[0]&&this.store.addResource(e[0],t,n,r)}}}function N(){return{debug:!1,initImmediate:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:function(e){let t={};if("object"==typeof e[1]&&(t=e[1]),"string"==typeof e[1]&&(t.defaultValue=e[1]),"string"==typeof e[2]&&(t.tDescription=e[2]),"object"==typeof e[2]||"object"==typeof e[3]){const n=e[3]||e[2];Object.keys(n).forEach((e=>{t[e]=n[e]}))}return t},interpolation:{escapeValue:!0,format:e=>e,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0}}}function I(e){return"string"==typeof e.ns&&(e.ns=[e.ns]),"string"==typeof e.fallbackLng&&(e.fallbackLng=[e.fallbackLng]),"string"==typeof e.fallbackNS&&(e.fallbackNS=[e.fallbackNS]),e.supportedLngs&&e.supportedLngs.indexOf("cimode")<0&&(e.supportedLngs=e.supportedLngs.concat(["cimode"])),e}function L(){}class M extends r{constructor(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;var r;if(super(),this.options=I(e),this.services={},this.logger=n,this.modules={external:[]},r=this,Object.getOwnPropertyNames(Object.getPrototypeOf(r)).forEach((e=>{"function"==typeof r[e]&&(r[e]=r[e].bind(r))})),t&&!this.isInitialized&&!e.isClone){if(!this.options.initImmediate)return this.init(e,t),this;setTimeout((()=>{this.init(e,t)}),0)}}init(){var e=this;let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=arguments.length>1?arguments[1]:void 0;"function"==typeof t&&(r=t,t={}),!t.defaultNS&&!1!==t.defaultNS&&t.ns&&("string"==typeof t.ns?t.defaultNS=t.ns:t.ns.indexOf("translation")<0&&(t.defaultNS=t.ns[0]));const o=N();function a(e){return e?"function"==typeof e?new e:e:null}if(this.options={...o,...this.options,...I(t)},"v1"!==this.options.compatibilityAPI&&(this.options.interpolation={...o.interpolation,...this.options.interpolation}),void 0!==t.keySeparator&&(this.options.userDefinedKeySeparator=t.keySeparator),void 0!==t.nsSeparator&&(this.options.userDefinedNsSeparator=t.nsSeparator),!this.options.isClone){let t;this.modules.logger?n.init(a(this.modules.logger),this.options):n.init(null,this.options),this.modules.formatter?t=this.modules.formatter:"undefined"!=typeof Intl&&(t=F);const r=new S(this.options);this.store=new v(this.options.resources,this.options);const i=this.services;i.logger=n,i.resourceStore=this.store,i.languageUtils=r,i.pluralResolver=new O(r,{prepend:this.options.pluralSeparator,compatibilityJSON:this.options.compatibilityJSON,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),!t||this.options.interpolation.format&&this.options.interpolation.format!==o.interpolation.format||(i.formatter=a(t),i.formatter.init(i,this.options),this.options.interpolation.format=i.formatter.format.bind(i.formatter)),i.interpolator=new R(this.options),i.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},i.backendConnector=new j(a(this.modules.backend),i.resourceStore,i,this.options),i.backendConnector.on("*",(function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];e.emit(t,...r)})),this.modules.languageDetector&&(i.languageDetector=a(this.modules.languageDetector),i.languageDetector.init&&i.languageDetector.init(i,this.options.detection,this.options)),this.modules.i18nFormat&&(i.i18nFormat=a(this.modules.i18nFormat),i.i18nFormat.init&&i.i18nFormat.init(this)),this.translator=new w(this.services,this.options),this.translator.on("*",(function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];e.emit(t,...r)})),this.modules.external.forEach((e=>{e.init&&e.init(this)}))}if(this.format=this.options.interpolation.format,r||(r=L),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const e=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);e.length>0&&"dev"!==e[0]&&(this.options.lng=e[0])}this.services.languageDetector||this.options.lng||this.logger.warn("init: no languageDetector is used and no lng is defined"),["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach((t=>{this[t]=function(){return e.store[t](...arguments)}})),["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach((t=>{this[t]=function(){return e.store[t](...arguments),e}}));const s=i(),l=()=>{const e=(e,t)=>{this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),s.resolve(t),r(e,t)};if(this.languages&&"v1"!==this.options.compatibilityAPI&&!this.isInitialized)return e(null,this.t.bind(this));this.changeLanguage(this.options.lng,e)};return this.options.resources||!this.options.initImmediate?l():setTimeout(l,0),s}loadResources(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:L;const n="string"==typeof e?e:this.language;if("function"==typeof e&&(t=e),!this.options.resources||this.options.partialBundledLanguages){if(n&&"cimode"===n.toLowerCase()&&(!this.options.preload||0===this.options.preload.length))return t();const e=[],r=t=>{t&&"cimode"!==t&&this.services.languageUtils.toResolveHierarchy(t).forEach((t=>{"cimode"!==t&&e.indexOf(t)<0&&e.push(t)}))};n?r(n):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach((e=>r(e))),this.options.preload&&this.options.preload.forEach((e=>r(e))),this.services.backendConnector.load(e,this.options.ns,(e=>{e||this.resolvedLanguage||!this.language||this.setResolvedLanguage(this.language),t(e)}))}else t(null)}reloadResources(e,t,n){const r=i();return e||(e=this.languages),t||(t=this.options.ns),n||(n=L),this.services.backendConnector.reload(e,t,(e=>{r.resolve(),n(e)})),r}use(e){if(!e)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!e.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return"backend"===e.type&&(this.modules.backend=e),("logger"===e.type||e.log&&e.warn&&e.error)&&(this.modules.logger=e),"languageDetector"===e.type&&(this.modules.languageDetector=e),"i18nFormat"===e.type&&(this.modules.i18nFormat=e),"postProcessor"===e.type&&y.addPostProcessor(e),"formatter"===e.type&&(this.modules.formatter=e),"3rdParty"===e.type&&this.modules.external.push(e),this}setResolvedLanguage(e){if(e&&this.languages&&!(["cimode","dev"].indexOf(e)>-1))for(let e=0;e<this.languages.length;e++){const t=this.languages[e];if(!(["cimode","dev"].indexOf(t)>-1)&&this.store.hasLanguageSomeTranslations(t)){this.resolvedLanguage=t;break}}}changeLanguage(e,t){var n=this;this.isLanguageChangingTo=e;const r=i();this.emit("languageChanging",e);const o=e=>{this.language=e,this.languages=this.services.languageUtils.toResolveHierarchy(e),this.resolvedLanguage=void 0,this.setResolvedLanguage(e)},a=(e,i)=>{i?(o(i),this.translator.changeLanguage(i),this.isLanguageChangingTo=void 0,this.emit("languageChanged",i),this.logger.log("languageChanged",i)):this.isLanguageChangingTo=void 0,r.resolve((function(){return n.t(...arguments)})),t&&t(e,(function(){return n.t(...arguments)}))},s=t=>{e||t||!this.services.languageDetector||(t=[]);const n="string"==typeof t?t:this.services.languageUtils.getBestMatchFromCodes(t);n&&(this.language||o(n),this.translator.language||this.translator.changeLanguage(n),this.services.languageDetector&&this.services.languageDetector.cacheUserLanguage&&this.services.languageDetector.cacheUserLanguage(n)),this.loadResources(n,(e=>{a(e,n)}))};return e||!this.services.languageDetector||this.services.languageDetector.async?!e&&this.services.languageDetector&&this.services.languageDetector.async?0===this.services.languageDetector.detect.length?this.services.languageDetector.detect().then(s):this.services.languageDetector.detect(s):s(e):s(this.services.languageDetector.detect()),r}getFixedT(e,t,n){var r=this;const o=function(e,t){let i;if("object"!=typeof t){for(var a=arguments.length,s=new Array(a>2?a-2:0),l=2;l<a;l++)s[l-2]=arguments[l];i=r.options.overloadTranslationOptionHandler([e,t].concat(s))}else i={...t};i.lng=i.lng||o.lng,i.lngs=i.lngs||o.lngs,i.ns=i.ns||o.ns,i.keyPrefix=i.keyPrefix||n||o.keyPrefix;const u=r.options.keySeparator||".";let c;return c=i.keyPrefix&&Array.isArray(e)?e.map((e=>`${i.keyPrefix}${u}${e}`)):i.keyPrefix?`${i.keyPrefix}${u}${e}`:e,r.t(c,i)};return"string"==typeof e?o.lng=e:o.lngs=e,o.ns=t,o.keyPrefix=n,o}t(){return this.translator&&this.translator.translate(...arguments)}exists(){return this.translator&&this.translator.exists(...arguments)}setDefaultNamespace(e){this.options.defaultNS=e}hasLoadedNamespace(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const n=t.lng||this.resolvedLanguage||this.languages[0],r=!!this.options&&this.options.fallbackLng,o=this.languages[this.languages.length-1];if("cimode"===n.toLowerCase())return!0;const i=(e,t)=>{const n=this.services.backendConnector.state[`${e}|${t}`];return-1===n||2===n};if(t.precheck){const e=t.precheck(this,i);if(void 0!==e)return e}return!(!this.hasResourceBundle(n,e)&&this.services.backendConnector.backend&&(!this.options.resources||this.options.partialBundledLanguages)&&(!i(n,e)||r&&!i(o,e)))}loadNamespaces(e,t){const n=i();return this.options.ns?("string"==typeof e&&(e=[e]),e.forEach((e=>{this.options.ns.indexOf(e)<0&&this.options.ns.push(e)})),this.loadResources((e=>{n.resolve(),t&&t(e)})),n):(t&&t(),Promise.resolve())}loadLanguages(e,t){const n=i();"string"==typeof e&&(e=[e]);const r=this.options.preload||[],o=e.filter((e=>r.indexOf(e)<0));return o.length?(this.options.preload=r.concat(o),this.loadResources((e=>{n.resolve(),t&&t(e)})),n):(t&&t(),Promise.resolve())}dir(e){if(e||(e=this.resolvedLanguage||(this.languages&&this.languages.length>0?this.languages[0]:this.language)),!e)return"rtl";const t=this.services&&this.services.languageUtils||new S(N());return["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"].indexOf(t.getLanguagePartFromCode(e))>-1||e.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(){return new M(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},arguments.length>1?arguments[1]:void 0)}cloneInstance(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:L;const n=e.forkResourceStore;n&&delete e.forkResourceStore;const r={...this.options,...e,isClone:!0},o=new M(r);return void 0===e.debug&&void 0===e.prefix||(o.logger=o.logger.clone(e)),["store","services","language"].forEach((e=>{o[e]=this[e]})),o.services={...this.services},o.services.utils={hasLoadedNamespace:o.hasLoadedNamespace.bind(o)},n&&(o.store=new v(this.store.data,r),o.services.resourceStore=o.store),o.translator=new w(o.services,r),o.translator.on("*",(function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];o.emit(e,...n)})),o.init(r,t),o.translator.options=r,o.translator.backendConnector.services.utils={hasLoadedNamespace:o.hasLoadedNamespace.bind(o)},o}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const D=M.createInstance();D.createInstance=M.createInstance,D.createInstance,D.dir,D.init,D.loadResources,D.reloadResources,D.use,D.changeLanguage,D.getFixedT,D.t,D.exists,D.setDefaultNamespace,D.hasLoadedNamespace,D.loadNamespaces,D.loadLanguages;var B=o(6540),U=o.t(B,2);o(4915),Object.create(null);const z={};function $(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];"string"==typeof t[0]&&z[t[0]]||("string"==typeof t[0]&&(z[t[0]]=new Date),function(){if(console&&console.warn){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];"string"==typeof t[0]&&(t[0]=`react-i18next:: ${t[0]}`),console.warn(...t)}}(...t))}const H=(e,t)=>()=>{if(e.isInitialized)t();else{const n=()=>{setTimeout((()=>{e.off("initialized",n)}),0),t()};e.on("initialized",n)}};function W(e,t,n){e.loadNamespaces(t,H(e,n))}function V(e,t,n,r){"string"==typeof n&&(n=[n]),n.forEach((t=>{e.options.ns.indexOf(t)<0&&e.options.ns.push(t)})),e.loadLanguages(t,H(e,r))}const q=/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g,K={"&amp;":"&","&#38;":"&","&lt;":"<","&#60;":"<","&gt;":">","&#62;":">","&apos;":"'","&#39;":"'","&quot;":'"',"&#34;":'"',"&nbsp;":" ","&#160;":" ","&copy;":"Â©","&#169;":"Â©","&reg;":"Â®","&#174;":"Â®","&hellip;":"â€¦","&#8230;":"â€¦","&#x2F;":"/","&#47;":"/"},G=e=>K[e];let J,Y={bindI18n:"languageChanged",bindI18nStore:"",transEmptyNodeValue:"",transSupportBasicHtmlNodes:!0,transWrapTextNodes:"",transKeepBasicHtmlNodesFor:["br","strong","i","p"],useSuspense:!0,unescape:e=>e.replace(q,G)};const X={type:"3rdParty",init(e){!function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};Y={...Y,...e}}(e.options.react),function(e){J=e}(e)}},Q=(0,B.createContext)();class Z{constructor(){this.usedNamespaces={}}addUsedNamespaces(e){e.forEach((e=>{this.usedNamespaces[e]||(this.usedNamespaces[e]=!0)}))}getUsedNamespaces(){return Object.keys(this.usedNamespaces)}}function ee(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{i18n:n}=t,{i18n:r,defaultNS:o}=(0,B.useContext)(Q)||{},i=n||r||J;if(i&&!i.reportNamespaces&&(i.reportNamespaces=new Z),!i){$("You will need to pass in an i18next instance by using initReactI18next");const e=(e,t)=>"string"==typeof t?t:t&&"object"==typeof t&&"string"==typeof t.defaultValue?t.defaultValue:Array.isArray(e)?e[e.length-1]:e,t=[e,{},!1];return t.t=e,t.i18n={},t.ready=!1,t}i.options.react&&void 0!==i.options.react.wait&&$("It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.");const a={...Y,...i.options.react,...t},{useSuspense:s,keyPrefix:l}=a;let u=e||o||i.options&&i.options.defaultNS;u="string"==typeof u?[u]:u||["translation"],i.reportNamespaces.addUsedNamespaces&&i.reportNamespaces.addUsedNamespaces(u);const c=(i.isInitialized||i.initializedStoreOnce)&&u.every((e=>function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return t.languages&&t.languages.length?void 0!==t.options.ignoreJSONStructure?t.hasLoadedNamespace(e,{lng:n.lng,precheck:(t,r)=>{if(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&t.services.backendConnector.backend&&t.isLanguageChangingTo&&!r(t.isLanguageChangingTo,e))return!1}}):function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const r=t.languages[0],o=!!t.options&&t.options.fallbackLng,i=t.languages[t.languages.length-1];if("cimode"===r.toLowerCase())return!0;const a=(e,n)=>{const r=t.services.backendConnector.state[`${e}|${n}`];return-1===r||2===r};return!(n.bindI18n&&n.bindI18n.indexOf("languageChanging")>-1&&t.services.backendConnector.backend&&t.isLanguageChangingTo&&!a(t.isLanguageChangingTo,e)||!t.hasResourceBundle(r,e)&&t.services.backendConnector.backend&&(!t.options.resources||t.options.partialBundledLanguages)&&(!a(r,e)||o&&!a(i,e)))}(e,t,n):($("i18n.languages were undefined or empty",t.languages),!0)}(e,i,a)));function d(){return i.getFixedT(t.lng||null,"fallback"===a.nsMode?u:u[0],l)}const[f,p]=(0,B.useState)(d);let h=u.join();t.lng&&(h=`${t.lng}${h}`);const m=((e,t)=>{const n=(0,B.useRef)();return(0,B.useEffect)((()=>{n.current=e}),[e,t]),n.current})(h),g=(0,B.useRef)(!0);(0,B.useEffect)((()=>{const{bindI18n:e,bindI18nStore:n}=a;function r(){g.current&&p(d)}return g.current=!0,c||s||(t.lng?V(i,t.lng,u,(()=>{g.current&&p(d)})):W(i,u,(()=>{g.current&&p(d)}))),c&&m&&m!==h&&g.current&&p(d),e&&i&&i.on(e,r),n&&i&&i.store.on(n,r),()=>{g.current=!1,e&&i&&e.split(" ").forEach((e=>i.off(e,r))),n&&i&&n.split(" ").forEach((e=>i.store.off(e,r)))}}),[i,h]);const v=(0,B.useRef)(!0);(0,B.useEffect)((()=>{g.current&&!v.current&&p(d),v.current=!1}),[i,l]);const y=[f,i,c];if(y.t=f,y.i18n=i,y.ready=c,c)return y;if(!c&&!s)return y;throw new Promise((e=>{t.lng?V(i,t.lng,u,(()=>e())):W(i,u,(()=>e()))}))}var te=o(8550);function ne(e){if(null==e)throw new TypeError("Cannot destructure "+e)}function re(e){return re="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},re(e)}function oe(e){var t=function(e){if("object"!=re(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var n=t.call(e,"string");if("object"!=re(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==re(t)?t:t+""}function ie(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,oe(r.key),r)}}function ae(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function se(e){return se=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},se(e)}function le(e,t){return le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},le(e,t)}function ue(e,t,n){return(t=oe(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function ce(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function de(e,t){if(e){if("string"==typeof e)return ce(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?ce(e,t):void 0}}function fe(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,a,s=[],l=!0,u=!1;try{if(i=(n=n.call(e)).next,0===t){if(Object(n)!==n)return;l=!1}else for(;!(l=(r=i.call(n)).done)&&(s.push(r.value),s.length!==t);l=!0);}catch(e){u=!0,o=e}finally{try{if(!l&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(u)throw o}}return s}}(e,t)||de(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}D.use(X).init({resources:te,lng:"en",keySeparator:!1,interpolation:{escapeValue:!1}});var pe=o(5338),he=o(2817),me=(0,B.createContext)(),ge=function(e){var t=e.reducer,n=e.initialState,r=e.children;return B.createElement(me.Provider,{value:(0,B.useReducer)(t,n)},r)},ve=function(){return(0,B.useContext)(me)},ye=(o(8232),o(5606),"popstate");function be(e={}){return function(e,t,n,r={}){let{window:o=document.defaultView,v5Compat:i=!1}=r,a=o.history,s="POP",l=null,u=c();function c(){return(a.state||{idx:null}).idx}function d(){s="POP";let e=c(),t=null==e?null:e-u;u=e,l&&l({action:s,location:p.location,delta:t})}function f(e){let t="null"!==o.location.origin?o.location.origin:o.location.href,n="string"==typeof e?e:Ae(e);return n=n.replace(/ $/,"%20"),we(t,`No window.location.(origin|href) available to create URL for href: ${n}`),new URL(n,t)}null==u&&(u=0,a.replaceState({...a.state,idx:u},""));let p={get action(){return s},get location(){return e(o,a)},listen(e){if(l)throw new Error("A history only accepts one active listener");return o.addEventListener(ye,d),l=e,()=>{o.removeEventListener(ye,d),l=null}},createHref:e=>t(o,e),createURL:f,encodeLocation(e){let t=f(e);return{pathname:t.pathname,search:t.search,hash:t.hash}},push:function(e,t){s="PUSH";let r=xe(p.location,e,t);n&&n(r,e),u=c()+1;let d=Se(r,u),f=p.createHref(r);try{a.pushState(d,"",f)}catch(e){if(e instanceof DOMException&&"DataCloneError"===e.name)throw e;o.location.assign(f)}i&&l&&l({action:s,location:p.location,delta:1})},replace:function(e,t){s="REPLACE";let r=xe(p.location,e,t);n&&n(r,e),u=c();let o=Se(r,u),d=p.createHref(r);a.replaceState(o,"",d),i&&l&&l({action:s,location:p.location,delta:0})},go:e=>a.go(e)};return p}((function(e,t){let{pathname:n,search:r,hash:o}=e.location;return xe("",{pathname:n,search:r,hash:o},t.state&&t.state.usr||null,t.state&&t.state.key||"default")}),(function(e,t){return"string"==typeof t?t:Ae(t)}),null,e)}function we(e,t){if(!1===e||null==e)throw new Error(t)}function Ee(e,t){if(!e){"undefined"!=typeof console&&console.warn(t);try{throw new Error(t)}catch(e){}}}function Se(e,t){return{usr:e.state,key:e.key,idx:t}}function xe(e,t,n=null,r){return{pathname:"string"==typeof e?e:e.pathname,search:"",hash:"",..."string"==typeof t?ke(t):t,state:n,key:t&&t.key||r||Math.random().toString(36).substring(2,10)}}function Ae({pathname:e="/",search:t="",hash:n=""}){return t&&"?"!==t&&(e+="?"===t.charAt(0)?t:"?"+t),n&&"#"!==n&&(e+="#"===n.charAt(0)?n:"#"+n),e}function ke(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substring(n),e=e.substring(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substring(r),e=e.substring(0,r)),e&&(t.pathname=e)}return t}function _e(e,t,n="/"){return function(e,t,n,r){let o=Ue(("string"==typeof t?ke(t):t).pathname||"/",n);if(null==o)return null;let i=Ce(e);!function(e){e.sort(((e,t)=>e.score!==t.score?t.score-e.score:function(e,t){return e.length===t.length&&e.slice(0,-1).every(((e,n)=>e===t[n]))?e[e.length-1]-t[t.length-1]:0}(e.routesMeta.map((e=>e.childrenIndex)),t.routesMeta.map((e=>e.childrenIndex)))))}(i);let a=null;for(let e=0;null==a&&e<i.length;++e){let t=Be(o);a=Me(i[e],t,r)}return a}(e,t,n,!1)}function Ce(e,t=[],n=[],r=""){let o=(e,o,i)=>{let a={relativePath:void 0===i?e.path||"":i,caseSensitive:!0===e.caseSensitive,childrenIndex:o,route:e};a.relativePath.startsWith("/")&&(we(a.relativePath.startsWith(r),`Absolute route path "${a.relativePath}" nested under path "${r}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),a.relativePath=a.relativePath.slice(r.length));let s=We([r,a.relativePath]),l=n.concat(a);e.children&&e.children.length>0&&(we(!0!==e.index,`Index routes must not have child routes. Please remove all child routes from route path "${s}".`),Ce(e.children,t,l,s)),(null!=e.path||e.index)&&t.push({path:s,score:Le(s,e.index),routesMeta:l})};return e.forEach(((e,t)=>{if(""!==e.path&&e.path?.includes("?"))for(let n of Oe(e.path))o(e,t,n);else o(e,t)})),t}function Oe(e){let t=e.split("/");if(0===t.length)return[];let[n,...r]=t,o=n.endsWith("?"),i=n.replace(/\?$/,"");if(0===r.length)return o?[i,""]:[i];let a=Oe(r.join("/")),s=[];return s.push(...a.map((e=>""===e?i:[i,e].join("/")))),o&&s.push(...a),s.map((t=>e.startsWith("/")&&""===t?"/":t))}new WeakMap;var Pe=/^:[\w-]+$/,Re=3,Te=2,Fe=1,je=10,Ne=-2,Ie=e=>"*"===e;function Le(e,t){let n=e.split("/"),r=n.length;return n.some(Ie)&&(r+=Ne),t&&(r+=Te),n.filter((e=>!Ie(e))).reduce(((e,t)=>e+(Pe.test(t)?Re:""===t?Fe:je)),r)}function Me(e,t,n=!1){let{routesMeta:r}=e,o={},i="/",a=[];for(let e=0;e<r.length;++e){let s=r[e],l=e===r.length-1,u="/"===i?t:t.slice(i.length)||"/",c=De({path:s.relativePath,caseSensitive:s.caseSensitive,end:l},u),d=s.route;if(!c&&l&&n&&!r[r.length-1].route.index&&(c=De({path:s.relativePath,caseSensitive:s.caseSensitive,end:!1},u)),!c)return null;Object.assign(o,c.params),a.push({params:o,pathname:We([i,c.pathname]),pathnameBase:Ve(We([i,c.pathnameBase])),route:d}),"/"!==c.pathnameBase&&(i=We([i,c.pathnameBase]))}return a}function De(e,t){"string"==typeof e&&(e={path:e,caseSensitive:!1,end:!0});let[n,r]=function(e,t=!1,n=!0){Ee("*"===e||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let r=[],o="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,((e,t,n)=>(r.push({paramName:t,isOptional:null!=n}),n?"/?([^\\/]+)?":"/([^\\/]+)")));return e.endsWith("*")?(r.push({paramName:"*"}),o+="*"===e||"/*"===e?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?o+="\\/*$":""!==e&&"/"!==e&&(o+="(?:(?=\\/|$))"),[new RegExp(o,t?void 0:"i"),r]}(e.path,e.caseSensitive,e.end),o=t.match(n);if(!o)return null;let i=o[0],a=i.replace(/(.)\/+$/,"$1"),s=o.slice(1);return{params:r.reduce(((e,{paramName:t,isOptional:n},r)=>{if("*"===t){let e=s[r]||"";a=i.slice(0,i.length-e.length).replace(/(.)\/+$/,"$1")}const o=s[r];return e[t]=n&&!o?void 0:(o||"").replace(/%2F/g,"/"),e}),{}),pathname:i,pathnameBase:a,pattern:e}}function Be(e){try{return e.split("/").map((e=>decodeURIComponent(e).replace(/\//g,"%2F"))).join("/")}catch(t){return Ee(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function Ue(e,t){if("/"===t)return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&"/"!==r?null:e.slice(n)||"/"}function ze(e,t,n,r){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(r)}].  Please separate it out to the \`to.${n}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`}function $e(e){let t=function(e){return e.filter(((e,t)=>0===t||e.route.path&&e.route.path.length>0))}(e);return t.map(((e,n)=>n===t.length-1?e.pathname:e.pathnameBase))}function He(e,t,n,r=!1){let o;"string"==typeof e?o=ke(e):(o={...e},we(!o.pathname||!o.pathname.includes("?"),ze("?","pathname","search",o)),we(!o.pathname||!o.pathname.includes("#"),ze("#","pathname","hash",o)),we(!o.search||!o.search.includes("#"),ze("#","search","hash",o)));let i,a=""===e||""===o.pathname,s=a?"/":o.pathname;if(null==s)i=n;else{let e=t.length-1;if(!r&&s.startsWith("..")){let t=s.split("/");for(;".."===t[0];)t.shift(),e-=1;o.pathname=t.join("/")}i=e>=0?t[e]:"/"}let l=function(e,t="/"){let{pathname:n,search:r="",hash:o=""}="string"==typeof e?ke(e):e,i=n?n.startsWith("/")?n:function(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach((e=>{".."===e?n.length>1&&n.pop():"."!==e&&n.push(e)})),n.length>1?n.join("/"):"/"}(n,t):t;return{pathname:i,search:qe(r),hash:Ke(o)}}(o,i),u=s&&"/"!==s&&s.endsWith("/"),c=(a||"."===s)&&n.endsWith("/");return l.pathname.endsWith("/")||!u&&!c||(l.pathname+="/"),l}var We=e=>e.join("/").replace(/\/\/+/g,"/"),Ve=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),qe=e=>e&&"?"!==e?e.startsWith("?")?e:"?"+e:"",Ke=e=>e&&"#"!==e?e.startsWith("#")?e:"#"+e:"";function Ge(e){return null!=e&&"number"==typeof e.status&&"string"==typeof e.statusText&&"boolean"==typeof e.internal&&"data"in e}var Je=["POST","PUT","PATCH","DELETE"],Ye=(new Set(Je),["GET",...Je]);new Set(Ye),Symbol("ResetLoaderData");var Xe=B.createContext(null);Xe.displayName="DataRouter";var Qe=B.createContext(null);Qe.displayName="DataRouterState";var Ze=B.createContext({isTransitioning:!1});Ze.displayName="ViewTransition",B.createContext(new Map).displayName="Fetchers",B.createContext(null).displayName="Await";var et=B.createContext(null);et.displayName="Navigation";var tt=B.createContext(null);tt.displayName="Location";var nt=B.createContext({outlet:null,matches:[],isDataRoute:!1});nt.displayName="Route";var rt=B.createContext(null);function ot(){return null!=B.useContext(tt)}function it(){return we(ot(),"useLocation() may be used only in the context of a <Router> component."),B.useContext(tt).location}rt.displayName="RouteError";var at="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function st(e){B.useContext(et).static||B.useLayoutEffect(e)}function lt(){let{isDataRoute:e}=B.useContext(nt);return e?function(){let{router:e}=function(e){let t=B.useContext(Xe);return we(t,mt(e)),t}("useNavigate"),t=gt("useNavigate"),n=B.useRef(!1);return st((()=>{n.current=!0})),B.useCallback((async(r,o={})=>{Ee(n.current,at),n.current&&("number"==typeof r?e.navigate(r):await e.navigate(r,{fromRouteId:t,...o}))}),[e,t])}():function(){we(ot(),"useNavigate() may be used only in the context of a <Router> component.");let e=B.useContext(Xe),{basename:t,navigator:n}=B.useContext(et),{matches:r}=B.useContext(nt),{pathname:o}=it(),i=JSON.stringify($e(r)),a=B.useRef(!1);return st((()=>{a.current=!0})),B.useCallback(((r,s={})=>{if(Ee(a.current,at),!a.current)return;if("number"==typeof r)return void n.go(r);let l=He(r,JSON.parse(i),o,"path"===s.relative);null==e&&"/"!==t&&(l.pathname="/"===l.pathname?t:We([t,l.pathname])),(s.replace?n.replace:n.push)(l,s.state,s)}),[t,n,i,o,e])}()}function ut(e,{relative:t}={}){let{matches:n}=B.useContext(nt),{pathname:r}=it(),o=JSON.stringify($e(n));return B.useMemo((()=>He(e,JSON.parse(o),r,"path"===t)),[e,o,r,t])}function ct(e,t,n,r){we(ot(),"useRoutes() may be used only in the context of a <Router> component.");let{navigator:o,static:i}=B.useContext(et),{matches:a}=B.useContext(nt),s=a[a.length-1],l=s?s.params:{},u=s?s.pathname:"/",c=s?s.pathnameBase:"/",d=s&&s.route;{let e=d&&d.path||"";yt(u,!d||e.endsWith("*")||e.endsWith("*?"),`You rendered descendant <Routes> (or called \`useRoutes()\`) at "${u}" (under <Route path="${e}">) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render.\n\nPlease change the parent <Route path="${e}"> to <Route path="${"/"===e?"*":`${e}/*`}">.`)}let f,p=it();if(t){let e="string"==typeof t?ke(t):t;we("/"===c||e.pathname?.startsWith(c),`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${c}" but pathname "${e.pathname}" was given in the \`location\` prop.`),f=e}else f=p;let h=f.pathname||"/",m=h;if("/"!==c){let e=c.replace(/^\//,"").split("/");m="/"+h.replace(/^\//,"").split("/").slice(e.length).join("/")}let g=!i&&n&&n.matches&&n.matches.length>0?n.matches:_e(e,{pathname:m});Ee(d||null!=g,`No routes matched location "${f.pathname}${f.search}${f.hash}" `),Ee(null==g||void 0!==g[g.length-1].route.element||void 0!==g[g.length-1].route.Component||void 0!==g[g.length-1].route.lazy,`Matched leaf route at location "${f.pathname}${f.search}${f.hash}" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`);let v=function(e,t=[],n=null){if(null==e){if(!n)return null;if(n.errors)e=n.matches;else{if(0!==t.length||n.initialized||!(n.matches.length>0))return null;e=n.matches}}let r=e,o=n?.errors;if(null!=o){let e=r.findIndex((e=>e.route.id&&void 0!==o?.[e.route.id]));we(e>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(o).join(",")}`),r=r.slice(0,Math.min(r.length,e+1))}let i=!1,a=-1;if(n)for(let e=0;e<r.length;e++){let t=r[e];if((t.route.HydrateFallback||t.route.hydrateFallbackElement)&&(a=e),t.route.id){let{loaderData:e,errors:o}=n,s=t.route.loader&&!e.hasOwnProperty(t.route.id)&&(!o||void 0===o[t.route.id]);if(t.route.lazy||s){i=!0,r=a>=0?r.slice(0,a+1):[r[0]];break}}}return r.reduceRight(((e,s,l)=>{let u,c=!1,d=null,f=null;n&&(u=o&&s.route.id?o[s.route.id]:void 0,d=s.route.errorElement||ft,i&&(a<0&&0===l?(yt("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),c=!0,f=null):a===l&&(c=!0,f=s.route.hydrateFallbackElement||null)));let p=t.concat(r.slice(0,l+1)),h=()=>{let t;return t=u?d:c?f:s.route.Component?B.createElement(s.route.Component,null):s.route.element?s.route.element:e,B.createElement(ht,{match:s,routeContext:{outlet:e,matches:p,isDataRoute:null!=n},children:t})};return n&&(s.route.ErrorBoundary||s.route.errorElement||0===l)?B.createElement(pt,{location:n.location,revalidation:n.revalidation,component:d,error:u,children:h(),routeContext:{outlet:null,matches:p,isDataRoute:!0}}):h()}),null)}(g&&g.map((e=>Object.assign({},e,{params:Object.assign({},l,e.params),pathname:We([c,o.encodeLocation?o.encodeLocation(e.pathname).pathname:e.pathname]),pathnameBase:"/"===e.pathnameBase?c:We([c,o.encodeLocation?o.encodeLocation(e.pathnameBase).pathname:e.pathnameBase])}))),a,n,r);return t&&v?B.createElement(tt.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...f},navigationType:"POP"}},v):v}function dt(){let e=function(){let e=B.useContext(rt),t=function(e){let t=B.useContext(Qe);return we(t,mt(e)),t}("useRouteError"),n=gt("useRouteError");return void 0!==e?e:t.errors?.[n]}(),t=Ge(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,r="rgba(200,200,200, 0.5)",o={padding:"0.5rem",backgroundColor:r},i={padding:"2px 4px",backgroundColor:r},a=null;return console.error("Error handled by React Router default ErrorBoundary:",e),a=B.createElement(B.Fragment,null,B.createElement("p",null,"ðŸ’¿ Hey developer ðŸ‘‹"),B.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",B.createElement("code",{style:i},"ErrorBoundary")," or"," ",B.createElement("code",{style:i},"errorElement")," prop on your route.")),B.createElement(B.Fragment,null,B.createElement("h2",null,"Unexpected Application Error!"),B.createElement("h3",{style:{fontStyle:"italic"}},t),n?B.createElement("pre",{style:o},n):null,a)}B.createContext(null);var ft=B.createElement(dt,null),pt=class extends B.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||"idle"!==t.revalidation&&"idle"===e.revalidation?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:void 0!==e.error?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){console.error("React Router caught the following error during render",e,t)}render(){return void 0!==this.state.error?B.createElement(nt.Provider,{value:this.props.routeContext},B.createElement(rt.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function ht({routeContext:e,match:t,children:n}){let r=B.useContext(Xe);return r&&r.static&&r.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(r.staticContext._deepestRenderedBoundaryId=t.route.id),B.createElement(nt.Provider,{value:e},n)}function mt(e){return`${e} must be used within a data router.  See https://reactrouter.com/en/main/routers/picking-a-router.`}function gt(e){let t=function(e){let t=B.useContext(nt);return we(t,mt(e)),t}(e),n=t.matches[t.matches.length-1];return we(n.route.id,`${e} can only be used on routes that contain a unique "id"`),n.route.id}var vt={};function yt(e,t,n){t||vt[e]||(vt[e]=!0,Ee(!1,n))}function bt(e){we(!1,"A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.")}function wt({basename:e="/",children:t=null,location:n,navigationType:r="POP",navigator:o,static:i=!1}){we(!ot(),"You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");let a=e.replace(/^\/*/,"/"),s=B.useMemo((()=>({basename:a,navigator:o,static:i,future:{}})),[a,o,i]);"string"==typeof n&&(n=ke(n));let{pathname:l="/",search:u="",hash:c="",state:d=null,key:f="default"}=n,p=B.useMemo((()=>{let e=Ue(l,a);return null==e?null:{location:{pathname:e,search:u,hash:c,state:d,key:f},navigationType:r}}),[a,l,u,c,d,f,r]);return Ee(null!=p,`<Router basename="${a}"> is not able to match the URL "${l}${u}${c}" because it does not start with the basename, so the <Router> won't render anything.`),null==p?null:B.createElement(et.Provider,{value:s},B.createElement(tt.Provider,{children:t,value:p}))}function Et({children:e,location:t}){return ct(St(e),t)}function St(e,t=[]){let n=[];return B.Children.forEach(e,((e,r)=>{if(!B.isValidElement(e))return;let o=[...t,r];if(e.type===B.Fragment)return void n.push.apply(n,St(e.props.children,o));we(e.type===bt,`[${"string"==typeof e.type?e.type:e.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`),we(!e.props.index||!e.props.children,"An index route cannot have child routes.");let i={id:e.props.id||o.join("-"),caseSensitive:e.props.caseSensitive,element:e.props.element,Component:e.props.Component,index:e.props.index,path:e.props.path,loader:e.props.loader,action:e.props.action,hydrateFallbackElement:e.props.hydrateFallbackElement,HydrateFallback:e.props.HydrateFallback,errorElement:e.props.errorElement,ErrorBoundary:e.props.ErrorBoundary,hasErrorBoundary:!0===e.props.hasErrorBoundary||null!=e.props.ErrorBoundary||null!=e.props.errorElement,shouldRevalidate:e.props.shouldRevalidate,handle:e.props.handle,lazy:e.props.lazy};e.props.children&&(i.children=St(e.props.children,o)),n.push(i)})),n}B.memo((function({routes:e,future:t,state:n}){return ct(e,void 0,n,t)})),B.Component;var xt="get",At="application/x-www-form-urlencoded";function kt(e){return null!=e&&"string"==typeof e.tagName}var _t=null,Ct=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Ot(e){return null==e||Ct.has(e)?e:(Ee(!1,`"${e}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${At}"`),null)}function Pt(e,t){if(!1===e||null==e)throw new Error(t)}function Rt(e){return null!=e&&(null==e.href?"preload"===e.rel&&"string"==typeof e.imageSrcSet&&"string"==typeof e.imageSizes:"string"==typeof e.rel&&"string"==typeof e.href)}function Tt(e,t,n,r,o,i){let a=(e,t)=>!n[t]||e.route.id!==n[t].route.id,s=(e,t)=>n[t].pathname!==e.pathname||n[t].route.path?.endsWith("*")&&n[t].params["*"]!==e.params["*"];return"assets"===i?t.filter(((e,t)=>a(e,t)||s(e,t))):"data"===i?t.filter(((t,i)=>{let l=r.routes[t.route.id];if(!l||!l.hasLoader)return!1;if(a(t,i)||s(t,i))return!0;if(t.route.shouldRevalidate){let r=t.route.shouldRevalidate({currentUrl:new URL(o.pathname+o.search+o.hash,window.origin),currentParams:n[0]?.params||{},nextUrl:new URL(e,window.origin),nextParams:t.params,defaultShouldRevalidate:!0});if("boolean"==typeof r)return r}return!0})):[]}function Ft(e,t,{includeHydrateFallback:n}={}){return r=e.map((e=>{let r=t.routes[e.route.id];if(!r)return[];let o=[r.module];return r.clientActionModule&&(o=o.concat(r.clientActionModule)),r.clientLoaderModule&&(o=o.concat(r.clientLoaderModule)),n&&r.hydrateFallbackModule&&(o=o.concat(r.hydrateFallbackModule)),r.imports&&(o=o.concat(r.imports)),o})).flat(1),[...new Set(r)];var r}Symbol("SingleFetchRedirect");function jt(){let e=B.useContext(Xe);return Pt(e,"You must render this element inside a <DataRouterContext.Provider> element"),e}function Nt(){let e=B.useContext(Qe);return Pt(e,"You must render this element inside a <DataRouterStateContext.Provider> element"),e}B.Component;var It=B.createContext(void 0);function Lt(){let e=B.useContext(It);return Pt(e,"You must render this element inside a <HydratedRouter> element"),e}function Mt(e,t){return n=>{e&&e(n),n.defaultPrevented||t(n)}}function Dt({page:e,...t}){let{router:n}=jt(),r=B.useMemo((()=>_e(n.routes,e,n.basename)),[n.routes,e,n.basename]);return r?B.createElement(Ut,{page:e,matches:r,...t}):null}function Bt(e){let{manifest:t,routeModules:n}=Lt(),[r,o]=B.useState([]);return B.useEffect((()=>{let r=!1;return async function(e,t,n){return function(e,t){let n=new Set,r=new Set(t);return e.reduce(((e,o)=>{if(t&&(null==(i=o)||"string"!=typeof i.page)&&"script"===o.as&&o.href&&r.has(o.href))return e;var i;let a=JSON.stringify(function(e){let t={},n=Object.keys(e).sort();for(let r of n)t[r]=e[r];return t}(o));return n.has(a)||(n.add(a),e.push({key:a,link:o})),e}),[])}((await Promise.all(e.map((async e=>{let r=t.routes[e.route.id];if(r){let e=await async function(e,t){if(e.id in t)return t[e.id];try{let n=await import(e.module);return t[e.id]=n,n}catch(t){return console.error(`Error loading route module \`${e.module}\`, reloading page...`),console.error(t),window.__reactRouterContext&&window.__reactRouterContext.isSpaMode,window.location.reload(),new Promise((()=>{}))}}(r,n);return e.links?e.links():[]}return[]})))).flat(1).filter(Rt).filter((e=>"stylesheet"===e.rel||"preload"===e.rel)).map((e=>"stylesheet"===e.rel?{...e,rel:"prefetch",as:"style"}:{...e,rel:"prefetch"})))}(e,t,n).then((e=>{r||o(e)})),()=>{r=!0}}),[e,t,n]),r}function Ut({page:e,matches:t,...n}){let r=it(),{manifest:o,routeModules:i}=Lt(),{basename:a}=jt(),{loaderData:s,matches:l}=Nt(),u=B.useMemo((()=>Tt(e,t,l,o,r,"data")),[e,t,l,o,r]),c=B.useMemo((()=>Tt(e,t,l,o,r,"assets")),[e,t,l,o,r]),d=B.useMemo((()=>{if(e===r.pathname+r.search+r.hash)return[];let n=new Set,l=!1;if(t.forEach((e=>{let t=o.routes[e.route.id];t&&t.hasLoader&&(!u.some((t=>t.route.id===e.route.id))&&e.route.id in s&&i[e.route.id]?.shouldRevalidate||t.hasClientLoader?l=!0:n.add(e.route.id))})),0===n.size)return[];let c=function(e,t){let n="string"==typeof e?new URL(e,"undefined"==typeof window?"server://singlefetch/":window.location.origin):e;return"/"===n.pathname?n.pathname="_root.data":t&&"/"===Ue(n.pathname,t)?n.pathname=`${t.replace(/\/$/,"")}/_root.data`:n.pathname=`${n.pathname.replace(/\/$/,"")}.data`,n}(e,a);return l&&n.size>0&&c.searchParams.set("_routes",t.filter((e=>n.has(e.route.id))).map((e=>e.route.id)).join(",")),[c.pathname+c.search]}),[a,s,r,o,u,t,e,i]),f=B.useMemo((()=>Ft(c,o)),[c,o]),p=Bt(c);return B.createElement(B.Fragment,null,d.map((e=>B.createElement("link",{key:e,rel:"prefetch",as:"fetch",href:e,...n}))),f.map((e=>B.createElement("link",{key:e,rel:"modulepreload",href:e,...n}))),p.map((({key:e,link:t})=>B.createElement("link",{key:e,...t}))))}It.displayName="FrameworkContext";function zt(...e){return t=>{e.forEach((e=>{"function"==typeof e?e(t):null!=e&&(e.current=t)}))}}var $t="undefined"!=typeof window&&void 0!==window.document&&void 0!==window.document.createElement;try{$t&&(window.__reactRouterVersion="7.5.2")}catch(e){}function Ht({basename:e,children:t,window:n}){let r=B.useRef();null==r.current&&(r.current=be({window:n,v5Compat:!0}));let o=r.current,[i,a]=B.useState({action:o.action,location:o.location}),s=B.useCallback((e=>{B.startTransition((()=>a(e)))}),[a]);return B.useLayoutEffect((()=>o.listen(s)),[o,s]),B.createElement(wt,{basename:e,children:t,location:i.location,navigationType:i.action,navigator:o})}var Wt=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Vt=B.forwardRef((function({onClick:e,discover:t="render",prefetch:n="none",relative:r,reloadDocument:o,replace:i,state:a,target:s,to:l,preventScrollReset:u,viewTransition:c,...d},f){let p,{basename:h}=B.useContext(et),m="string"==typeof l&&Wt.test(l),g=!1;if("string"==typeof l&&m&&(p=l,$t))try{let e=new URL(window.location.href),t=l.startsWith("//")?new URL(e.protocol+l):new URL(l),n=Ue(t.pathname,h);t.origin===e.origin&&null!=n?l=n+t.search+t.hash:g=!0}catch(e){Ee(!1,`<Link to="${l}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`)}let v=function(e,{relative:t}={}){we(ot(),"useHref() may be used only in the context of a <Router> component.");let{basename:n,navigator:r}=B.useContext(et),{hash:o,pathname:i,search:a}=ut(e,{relative:t}),s=i;return"/"!==n&&(s="/"===i?n:We([n,i])),r.createHref({pathname:s,search:a,hash:o})}(l,{relative:r}),[y,b,w]=function(e,t){let n=B.useContext(It),[r,o]=B.useState(!1),[i,a]=B.useState(!1),{onFocus:s,onBlur:l,onMouseEnter:u,onMouseLeave:c,onTouchStart:d}=t,f=B.useRef(null);B.useEffect((()=>{if("render"===e&&a(!0),"viewport"===e){let e=new IntersectionObserver((e=>{e.forEach((e=>{a(e.isIntersecting)}))}),{threshold:.5});return f.current&&e.observe(f.current),()=>{e.disconnect()}}}),[e]),B.useEffect((()=>{if(r){let e=setTimeout((()=>{a(!0)}),100);return()=>{clearTimeout(e)}}}),[r]);let p=()=>{o(!0)},h=()=>{o(!1),a(!1)};return n?"intent"!==e?[i,f,{}]:[i,f,{onFocus:Mt(s,p),onBlur:Mt(l,h),onMouseEnter:Mt(u,p),onMouseLeave:Mt(c,h),onTouchStart:Mt(d,p)}]:[!1,f,{}]}(n,d),E=function(e,{target:t,replace:n,state:r,preventScrollReset:o,relative:i,viewTransition:a}={}){let s=lt(),l=it(),u=ut(e,{relative:i});return B.useCallback((c=>{if(function(e,t){return!(0!==e.button||t&&"_self"!==t||function(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}(e))}(c,t)){c.preventDefault();let t=void 0!==n?n:Ae(l)===Ae(u);s(e,{replace:t,state:r,preventScrollReset:o,relative:i,viewTransition:a})}}),[l,s,u,n,r,t,e,o,i,a])}(l,{replace:i,state:a,target:s,preventScrollReset:u,relative:r,viewTransition:c}),S=B.createElement("a",{...d,...w,href:p||v,onClick:g||o?e:function(t){e&&e(t),t.defaultPrevented||E(t)},ref:zt(f,b),target:s,"data-discover":m||"render"!==t?void 0:"true"});return y&&!m?B.createElement(B.Fragment,null,S,B.createElement(Dt,{page:v})):S}));function qt(e){let t=B.useContext(Xe);return we(t,function(e){return`${e} must be used within a data router.  See https://reactrouter.com/en/main/routers/picking-a-router.`}(e)),t}Vt.displayName="Link",B.forwardRef((function({"aria-current":e="page",caseSensitive:t=!1,className:n="",end:r=!1,style:o,to:i,viewTransition:a,children:s,...l},u){let c=ut(i,{relative:l.relative}),d=it(),f=B.useContext(Qe),{navigator:p,basename:h}=B.useContext(et),m=null!=f&&function(e,t={}){let n=B.useContext(Ze);we(null!=n,"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`.  Did you accidentally import `RouterProvider` from `react-router`?");let{basename:r}=qt("useViewTransitionState"),o=ut(e,{relative:t.relative});if(!n.isTransitioning)return!1;let i=Ue(n.currentLocation.pathname,r)||n.currentLocation.pathname,a=Ue(n.nextLocation.pathname,r)||n.nextLocation.pathname;return null!=De(o.pathname,a)||null!=De(o.pathname,i)}(c)&&!0===a,g=p.encodeLocation?p.encodeLocation(c).pathname:c.pathname,v=d.pathname,y=f&&f.navigation&&f.navigation.location?f.navigation.location.pathname:null;t||(v=v.toLowerCase(),y=y?y.toLowerCase():null,g=g.toLowerCase()),y&&h&&(y=Ue(y,h)||y);const b="/"!==g&&g.endsWith("/")?g.length-1:g.length;let w,E=v===g||!r&&v.startsWith(g)&&"/"===v.charAt(b),S=null!=y&&(y===g||!r&&y.startsWith(g)&&"/"===y.charAt(g.length)),x={isActive:E,isPending:S,isTransitioning:m},A=E?e:void 0;w="function"==typeof n?n(x):[n,E?"active":null,S?"pending":null,m?"transitioning":null].filter(Boolean).join(" ");let k="function"==typeof o?o(x):o;return B.createElement(Vt,{...l,"aria-current":A,className:w,ref:u,style:k,to:i,viewTransition:a},"function"==typeof s?s(x):s)})).displayName="NavLink",B.forwardRef((({discover:e="render",fetcherKey:t,navigate:n,reloadDocument:r,replace:o,state:i,method:a=xt,action:s,onSubmit:l,relative:u,preventScrollReset:c,viewTransition:d,...f},p)=>{let h=function(){let{router:e}=qt("useSubmit"),{basename:t}=B.useContext(et),n=gt("useRouteId");return B.useCallback((async(r,o={})=>{let{action:i,method:a,encType:s,formData:l,body:u}=function(e,t){let n,r,o,i,a;if(kt(s=e)&&"form"===s.tagName.toLowerCase()){let a=e.getAttribute("action");r=a?Ue(a,t):null,n=e.getAttribute("method")||xt,o=Ot(e.getAttribute("enctype"))||At,i=new FormData(e)}else if(function(e){return kt(e)&&"button"===e.tagName.toLowerCase()}(e)||function(e){return kt(e)&&"input"===e.tagName.toLowerCase()}(e)&&("submit"===e.type||"image"===e.type)){let a=e.form;if(null==a)throw new Error('Cannot submit a <button> or <input type="submit"> without a <form>');let s=e.getAttribute("formaction")||a.getAttribute("action");if(r=s?Ue(s,t):null,n=e.getAttribute("formmethod")||a.getAttribute("method")||xt,o=Ot(e.getAttribute("formenctype"))||Ot(a.getAttribute("enctype"))||At,i=new FormData(a,e),!function(){if(null===_t)try{new FormData(document.createElement("form"),0),_t=!1}catch(e){_t=!0}return _t}()){let{name:t,type:n,value:r}=e;if("image"===n){let e=t?`${t}.`:"";i.append(`${e}x`,"0"),i.append(`${e}y`,"0")}else t&&i.append(t,r)}}else{if(kt(e))throw new Error('Cannot submit element that is not <form>, <button>, or <input type="submit|image">');n=xt,r=null,o=At,a=e}var s;return i&&"text/plain"===o&&(a=i,i=void 0),{action:r,method:n.toLowerCase(),encType:o,formData:i,body:a}}(r,t);if(!1===o.navigate){let t=o.fetcherKey||Gt();await e.fetch(t,n,o.action||i,{preventScrollReset:o.preventScrollReset,formData:l,body:u,formMethod:o.method||a,formEncType:o.encType||s,flushSync:o.flushSync})}else await e.navigate(o.action||i,{preventScrollReset:o.preventScrollReset,formData:l,body:u,formMethod:o.method||a,formEncType:o.encType||s,replace:o.replace,state:o.state,fromRouteId:n,flushSync:o.flushSync,viewTransition:o.viewTransition})}),[e,t,n])}(),m=function(e,{relative:t}={}){let{basename:n}=B.useContext(et),r=B.useContext(nt);we(r,"useFormAction must be used inside a RouteContext");let[o]=r.matches.slice(-1),i={...ut(e||".",{relative:t})},a=it();if(null==e){i.search=a.search;let e=new URLSearchParams(i.search),t=e.getAll("index");if(t.some((e=>""===e))){e.delete("index"),t.filter((e=>e)).forEach((t=>e.append("index",t)));let n=e.toString();i.search=n?`?${n}`:""}}return e&&"."!==e||!o.route.index||(i.search=i.search?i.search.replace(/^\?/,"?index&"):"?index"),"/"!==n&&(i.pathname="/"===i.pathname?n:We([n,i.pathname])),Ae(i)}(s,{relative:u}),g="get"===a.toLowerCase()?"get":"post",v="string"==typeof s&&Wt.test(s);return B.createElement("form",{ref:p,method:g,action:m,onSubmit:r?l:e=>{if(l&&l(e),e.defaultPrevented)return;e.preventDefault();let r=e.nativeEvent.submitter,s=r?.getAttribute("formmethod")||a;h(r||e.currentTarget,{fetcherKey:t,method:s,navigate:n,replace:o,state:i,relative:u,preventScrollReset:c,viewTransition:d})},...f,"data-discover":v||"render"!==e?void 0:"true"})})).displayName="Form";var Kt=0,Gt=()=>`__${String(++Kt)}__`;new TextEncoder;var Jt=o(8168);function Yt(e,t,n,r,o,i,a){try{var s=e[i](a),l=s.value}catch(e){return void n(e)}s.done?t(l):Promise.resolve(l).then(r,o)}function Xt(e){return function(){var t=this,n=arguments;return new Promise((function(r,o){var i=e.apply(t,n);function a(e){Yt(i,r,o,a,s,"next",e)}function s(e){Yt(i,r,o,a,s,"throw",e)}a(void 0)}))}}var Qt=o(4756),Zt=o.n(Qt),en=o(8287).Buffer;function tn(e,t){var n=function(e){var t=[];for(var n in e)t.push(encodeURIComponent(n)+"="+encodeURIComponent(e[n]));return t.join("&")}(t);return n?(e.indexOf("?")<0?e+="?":e+="&",e+n):e}function nn(e){return!!(e&&e.authChoice&&("basic"==e.authChoice?e.username&&e.password:"openid"==e.authChoice&&e.access_token))}function rn(e,t){return t&&t.authChoice?("basic"==t.authChoice?e.Authorization="Basic "+en.from(t.username+":"+t.password).toString("base64"):"openid"==t.authChoice&&(e.Authorization="Bearer "+t.access_token),e):e}function on(e,t,n,r,o){var i={Accept:"application/json","Content-Type":"application/json","o-node":e};i=rn(i,o),n||(n={}),fetch("/node_action",{headers:i,method:"POST",body:JSON.stringify({sync:!1,action:t,options:n})}).then((function(e){return e.json()})).then((function(e){r&&r(e)})).catch(console.log)}function an(e,t,n,r,o,i){var a={Accept:"application/json","Content-Type":"application/json","o-node":e};a=rn(a,i),fetch("/object_action",{headers:a,method:"POST",body:JSON.stringify({path:t,action:n,options:r,sync:!1})}).then((function(e){return e.json()})).then((function(e){o&&o(e)})).catch(console.log)}function sn(e,t,n,r){var o={Accept:"application/json","Content-Type":"application/json"};o=rn(o,r),fetch("/object_monitor",{headers:o,method:"POST",body:JSON.stringify({path:e,global_expect:t})}).then((function(e){return e.json()})).then((function(e){n&&n(e)})).catch(console.log)}function ln(e,t,n,r,o,i){var a={Accept:"application/json","Content-Type":"application/json","o-node":t},s={headers:a=rn(a,i),method:e};"GET"==e?n=tn(n,r):s.body=JSON.stringify(r),fetch(n,s).then((function(e){return e.json()})).then((function(e){o&&o(e)})).catch(console.log)}function un(e,t,n,r,o,i){ln(e,t,n,r,(function(e){var t=null;for(var n in e.nodes){t=e.nodes[n];break}o&&o(t)}),i)}function cn(e,t,n,r){return un("POST","ANY",e,t,n,r)}function dn(e,t,n,r){return un("GET","ANY",e,t,n,r)}function fn(e,t,n){var r={Accept:"application/json","Content-Type":"application/json","o-node":"ANY"};r=rn(r,n),fetch("/create",{headers:r,method:"POST",body:JSON.stringify(e)}).then((function(e){return e.json()})).then((function(e){console.log(e),t&&t(e)})).catch(console.log)}var pn={};const hn=function(e){var t=fe(ve(),2),n=t[0],r=n.user,o=n.basicLogin,i=n.authChoice,a=t[1],s=(0,he.km)().oidcUser,l={access_token:s?s.access_token:null,username:o.username,password:o.password,authChoice:i};function u(){pn.isLoading=!1,pn.auth=null,pn.error=null,a({type:"loadUser",data:{}}),a({type:"setAuthChoice",data:""}),a({type:"setBasicLogin",data:{}}),a({type:"setAuthenticated",data:!1})}return(0,B.useEffect)((function(){function e(){return(e=Xt(Zt().mark((function e(){var t,n,r;return Zt().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!pn.isLoading){e.next=2;break}return e.abrupt("return");case 2:if(!pn.auth||pn.auth.access_token!=l.access_token||pn.auth.username!=l.username){e.next=4;break}return e.abrupt("return");case 4:return pn.isLoading=!0,pn.auth=l,t=rn(t={Accept:"application/json","Content-Type":"application/json"},l),e.prev=8,e.next=11,fetch("/whoami",{headers:t,method:"GET"});case 11:return n=e.sent,e.next=14,n.json();case 14:r=e.sent,console.log("I am",r),"nobody"==r.name&&"x509"!=i?u():(a({type:"loadUser",data:r}),a({type:"setAuthenticated",data:!0})),e.next=24;break;case 19:e.prev=19,e.t0=e.catch(8),pn.auth=null,pn.error=e.t0,u();case 24:return e.prev=24,pn.isLoading=!1,e.finish(24);case 27:case"end":return e.stop()}}),e,null,[[8,19,24,27]])})))).apply(this,arguments)}!function(){e.apply(this,arguments)}()})),{user:r,auth:l,unloadUser:u}};var mn=o(1731),gn=o.n(mn);const vn={patch:function(e,t){var n;for(n=0;n<t.length;n++)e=this.patchStanza(e,t[n]);return e},diff:function(e,t,n,r){n=void 0===n||n;var o,i=[[r=void 0!==r?r:[],t]],a=[];return this.structureWorthInvestigating(e,t)?(o=this.commonality(e,t),a=n?this.needleDiff(e,t,n,r):o<.5?this.thisLevelDiff(e,t,r,o):this.keysetDiff(e,t,n,r)):a=this.thisLevelDiff(e,t,r,0),n&&JSON.stringify(i).length<JSON.stringify(a).length&&(a=i),0===r.length&&a.length>1&&(a=this.sortStanzas(a)),a},isStrictlyEqual:function(e,t){var n,r,o;if(this.isTerminal(e)&&this.isTerminal(t))return e===t;if(this.isTerminal(e)||this.isTerminal(t))return!1;if(e instanceof Array&&t instanceof Array){if(e.length!==t.length)return!1;for(n=0;n<e.length;n++)if(!this.isStrictlyEqual(e[n],t[n]))return!1;return!0}if(e instanceof Array||t instanceof Array)return!1;if(0!==(r=this.computeKeysets(e,t))[1].length||0!==r[2].length)return!1;for(n=0;n<r[0].length;n++)if(o=r[0][n],!this.isStrictlyEqual(e[o],t[o]))return!1;return!0},isTerminal:function(e){return"string"==typeof e||"number"==typeof e||"boolean"==typeof e||null===e},appendKey:function(e,t,n){n=void 0!==n?n:[];var r,o,i=t.length;for(o=0;o<e.length;o++)r=e[o][0],e[o].length>1&&r.length===n.length+1&&r[r.length-1]>=i&&(i=r[r.length-1]+1);return i},loopOver:function(e,t){var n,r;if(e instanceof Array)for(n=0;n<e.length;n++)t(e,n);else for(r in e)e.hasOwnProperty(r)&&t(e,r)},inArray:function(e){return"number"==typeof e[e.length-1]},inObject:function(e){return"string"==typeof e[e.length-1]},splitDiff:function(e){var t,n,r=[],o=[],i=[],a=[],s={3:a,1:i};if(0===e.length)return[[],e];for(t=0;t<e.length;t++)2===(n=e[t]).length?this.inObject(n[0])?r.push(n):o.push(n):s[n.length].push(n);return[r,o,i,a]},stableKeypathLengthSort:function(e){for(var t=0;t<e.length;t++)e[t][0].unshift(t);for(e.sort((function(e,t){return e[0].length===t[0].length?e[0][0]-t[0][0]:t[0].length-e[0].length})),t=0;t<e.length;t++)e[t][0].shift();return e},keypathCompare:function(e,t){if(e=e[0],t=t[0],e.length!==t.length)return e.length-t.length;for(var n=0;n<e.length;n++)if("number"==typeof e[n]&&e[n]!==t[n])return e[n]-t[n];return 0},keypathCompareReverse:function(e,t){if(e=e[0],t=t[0],e.length!==t.length)return t.length-e.length;for(var n=0;n<e.length;n++)if("number"==typeof e[n]&&e[n]!==t[n])return t[n]-e[n];return 0},sortStanzas:function(e){var t=this.splitDiff(e);return t[1].sort(this.keypathCompare),t[2].sort(this.keypathCompareReverse),t[3].sort(this.keypathCompare),e=t[0].concat(t[1],t[2],t[3]),this.stableKeypathLengthSort(e,!0)},computeKeysets:function(e,t){var n=[],r=[],o=[];return this.loopOver(e,(function(e,o){(void 0!==t[o]?n:r).push(o)})),this.loopOver(t,(function(t,n){void 0===e[n]&&o.push(n)})),[n,r,o]},structureWorthInvestigating:function(e,t){return!this.isTerminal(e)&&!this.isTerminal(t)&&0!==e.length&&0!==t.length&&(e instanceof Array&&t instanceof Array||!(e instanceof Array||t instanceof Array)&&"object"===re(e)&&"object"===re(t))},commonality:function(e,t){var n,r,o,i,a,s,l=0,u=0;if(this.isTerminal(e)||this.isTerminal(t))return 0;if(e instanceof Array&&t instanceof Array){for(s=0;s<e.length;s++)n=e[s],-1!==t.indexOf(n)&&l++;u=Math.max(e.length,t.length)}else{if(e instanceof Array||t instanceof Array)return 0;for(o=(r=this.computeKeysets(e,t))[0],i=r[1],a=r[2],l=o.length,u=o.length+i.length+a.length,s=0;s<a.length;s++)n=a[s],-1===i.indexOf(n)&&u++}return 0===u?0:l/u},thisLevelDiff:function(e,t,n,r){var o,i,a=[];if(n=void 0!==n?n:[],void 0===r&&(r=this.commonality(e,t)),r){var s=this.computeKeysets(e,t);for(o=0;o<s[0].length;o++)e[i=s[0][o]]!==t[i]&&a.push([n.concat([i]),t[i]]);for(o=0;o<s[1].length;o++)i=s[1][o],a.push([n.concat([i])]);for(o=0;o<s[2].length;o++)i=s[2][o],a.push([n.concat([i]),t[i]]);return a}return this.isStrictlyEqual(e,t)?[]:[[n,t]]},keysetDiff:function(e,t,n,r){n=void 0===n||n;var o,i=[],a=this.computeKeysets(e,t);for(o=0;o<a[1].length;o++)i.push([r.concat(a[1][o])]);for(o=0;o<a[2].length;o++)i.push([r.concat(a[2][o]),t[a[2][o]]]);for(o=0;o<a[0].length;o++)i=i.concat(this.diff(e[a[0][o]],t[a[0][o]],n,r.concat([a[0][o]])));return i},needleDiff:function(e,t,n,r){if(!(e instanceof Array&&t instanceof Array))return this.keysetDiff(e,t,n,r);n=void 0===n||n;var o,i,a,s,l,u,c,d,f,p,h,m,g=[],v=[function(){if(l+1<g.length)return g[l+1].concat(JSON_delta.diff(c,d,n,r.concat([a])))},function(){if(u.length>0)return u[0].concat([[r.concat([a])]])},function(){if(0===l)return g[l].concat([[r.concat([JSON_delta.appendKey(g[l],e,r)]),d]])},function(){if(0!==l)return g[l].concat([[r.concat([s]),d,"i"]])}];for(o=0;o<=e.length;o++)for(g.unshift([]),i=0;i<o;i++)g[0].push([r.concat([i])]);for(s=0;s<t.length;s++){for(d=t[s],u=[],a=0;a<e.length;a++){for(c=e[a],l=e.length-a-1,p=1/0,o=0;o<v.length;o++)void 0!==(h=v[o]())&&(f=JSON.stringify(h).length)<p&&(m=h,p=f);u.unshift(m)}g=u}return m},patchStanza:function(e,t){var n=t[0];switch(n.length){case 0:e=t[1];break;case 1:1===t.length?void 0===e.splice?delete e[n[0]]:e.splice(n[0],1):3===t.length?void 0===e.splice?e[n[0]]=t[1]:e.splice(n[0],0,t[1]):e[n[0]]=t[1];break;default:var r=n.slice(1),o=e[n[0]],i=[r].concat(t.slice(1));void 0===o&&(o="string"==typeof r[0]?{}:[]),e[n[0]]=this.patchStanza(o,i)}return e}};var yn={eventSource:null,timer:null,evtimer:null,cstat:null,lastReload:0,lastPatchId:0,auth:null};const bn=function(e){var t=fe(ve(),2),n=t[0],r=n.cstat,o=(n.user,n.eventSourceAlive),i=n.authenticated,a=t[1],s=hn().auth,l=(0,B.useRef)(Date.now());function u(e){a({type:"setEventSourceAlive",data:e})}function c(){null!==yn.eventSource&&(clearTimeout(yn.timer),clearTimeout(yn.evtimer),yn.eventSource.close(),yn.eventSource=null)}function d(){if(i&&(null===yn.eventSource||2==yn.eventSource.readyState)&&nn(s)){console.log("initEventSource",yn);var e={headers:{}};e.headers=rn(e.headers,s);var t=new(gn())(window.location.protocol+"//"+window.location.host+"/events",e);2!=t.readyState&&(yn.eventSource=t,u(!0),yn.eventSource.onmessage=function(e){u(!0),function(e){var t=JSON.parse(e.data);if(console.log("event",e.data),"patch"==t.kind)try{if(yn.lastPatchId&&yn.lastPatchId+1!=t.id)return console.log("broken patch chain"),f(),void(yn.lastPatchId=t.id);yn.cstat=vn.patch(yn.cstat,t.data),yn.evtimer=setTimeout((function(){Date.now()-l.current>=1e3&&(a({type:"loadCstat",data:yn.cstat}),l.current=Date.now())}),1e3-(Date.now()-l.current)),yn.lastPatchId=t.id,console.log("patched cluster status, id",t.id)}catch(e){console.log("error patching cstat:",e),f(),yn.lastPatchId=t.id}}(e)},yn.eventSource.onerror=function(e){console.log(e),u(!1)},yn.eventSource.addEventListener("closedConnection",(function(e){u(!1)})))}}function f(){return p.apply(this,arguments)}function p(){return(p=Xt(Zt().mark((function e(){var t,n,r,o;return Zt().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(i){e.next=2;break}return e.abrupt("return");case 2:if(nn(s)){e.next=5;break}return console.log("loadCstat",!1,s),e.abrupt("return");case 5:if(t=+new Date,!yn.isLoading){e.next=8;break}return e.abrupt("return");case 8:if(!(t-yn.lastReload<1e3)){e.next=12;break}return console.log("last reload too fresh ... delay",yn.lastReload,t,t-yn.lastReload),yn.timer=setTimeout(f,1e3),e.abrupt("return");case 12:return yn.isLoading=!0,yn.lastReload=t,n=rn(n={Accept:"application/json","Content-Type":"application/json"},s),e.prev=16,e.next=19,fetch("/daemon_status",{headers:n});case 19:return r=e.sent,e.next=22,r.json();case 22:o=e.sent,200==r.status&&(yn.cstat=o,a({type:"loadCstat",data:o})),e.next=29;break;case 26:e.prev=26,e.t0=e.catch(16),console.log("loadCstat:",e.t0);case 29:return e.prev=29,yn.isLoading=!1,e.finish(29);case 32:case"end":return e.stop()}}),e,null,[[16,26,29,32]])})))).apply(this,arguments)}function h(){c(),f(),d()}function m(){i&&(yn.cstat||f(),d())}return(0,B.useEffect)((function(){i&&(!yn.auth||yn.auth.access_token==s.access_token&&yn.auth.username==s.username?m():h(),yn.auth=s)}),[i]),{cstat:r,reset:h,init:m,close:function(){c(),a({type:"loadCstat",data:{}})},eventSourceAlive:o}},wn=function(e){var t=fe((0,B.useState)(),2),n=t[0],r=t[1];return(0,B.useEffect)((function(){function e(){return(e=Xt(Zt().mark((function e(){return Zt().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,fetch("/authinfo");case 2:e.sent.json().then((function(e){console.log(e),r(e)})).catch((function(e){console.log(e)}));case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}!function(){e.apply(this,arguments)}()}),[]),n};function En(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Sn(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Sn(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function Sn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var xn={OPTIMAL:{name:"optimal",color:"inherit"},WARNING:{name:"warning",color:"orange"},DANGER:{name:"danger",color:"red"},NOTAPPLICABLE:{name:"n/a",color:"lightgray"}};function An(e,t){return e==xn.NOTAPPLICABLE?t:t==xn.NOTAPPLICABLE?e:e==xn.DANGER||t==xn.DANGER?xn.DANGER:e==xn.WARNING||t==xn.WARNING?xn.WARNING:xn.OPTIMAL}function kn(e){var t=e.split("/"),n={};return"cluster"==e?(n.namespace="",n.kind="ccfg",n.name=e):1==t.length?(n.namespace="",n.kind="svc",n.name=e):2==t.length?(n.namespace="",n.kind=t[0],n.name=t[1]):3==t.length&&(n.namespace=t[0],n.kind=t[1],n.name=t[2]),n}function _n(e,t,n){return t&&"root"!=t?[t,n,e].join("/"):n&&"svc"!=n?[n,e].join("/"):e}function Cn(e){var t={},n=null,r=null;return e.split(/\n/).map((function(e){return e.replace(/\r/g,"")})).map((function(e){return e.replace(/\s+[#;].*$/g,"")})).forEach((function(e){return!e.match(/^\s*[#;]/)&&(e.match(/^\s+/)&&n&&r?(t[n][r.key]+=" "+e.trim(),!0):void((e=e.trim()).length&&(/^\[/.test(e)?(n=e.replace(/\[|\]/g,""),t[n]={}):n.length&&(r=function(e){var t=e.split("=").map((function(e){return e.trim()}));return{key:t[0],value:t[1]}}(e),t[n][r.key]=r.value))))}),{currentKey:""}),t}function On(e){if(e<0){var t="- ";e=-e}else t="";if(e<.001)var n="",r=1048576*e;else e<1?(n="k",r=1024*e):e<1024?(n="m",r=e):e<1048576?(n="g",r=e/1024):(n="t",r=e/1048576);return t+(r=r>=100?Math.round(r):r>=10?Math.round(10*r)/10:Math.round(100*r)/100)+n}function Pn(e){if(Array.isArray(e)){var t,n=En(e);try{for(n.s();!(t=n.n()).done;)if(!Pn(t.value))return!1}catch(e){n.e(e)}finally{n.f()}return!0}return void 0!==e&&!!e&&!!e.match(/^[a-z]+[a-z0-9_\-\.]*$/i)}function Rn(e){if(Array.isArray(e)){var t,n=En(e);try{for(n.s();!(t=n.n()).done;)if(!Rn(t.value))return!1}catch(e){n.e(e)}finally{n.f()}return!0}return void 0!==e&&!!e&&!!e.match(/^[a-z]+[a-z0-9_\-\.]*$/i)}function Tn(){try{return Object.keys(data.data)[0].match(/^[a-z]+[a-z0-9_\-\.]*\/[a-z]+\/[a-z]+[a-z0-9_\-\.]*$/i)}catch(e){return!1}}function Fn(e,t){if(!e.monitor)return null;for(var n in e.monitor.nodes){var r=void 0;try{r=e.monitor.nodes[n].services.config}catch(e){return null}if(t in r)return r[t].csum}}function jn(e){return void 0===e.monitor?xn.NOTAPPLICABLE:0==e.monitor.compat?xn.DANGER:xn.OPTIMAL}function Nn(e){var t;if(void 0===e.monitor)return xn.NOTAPPLICABLE;for(var n in e.monitor.nodes){if(t&&t!=e.monitor.nodes[n].agent)return xn.WARNING;t=e.monitor.nodes[n].agent}return xn.OPTIMAL}function In(e){try{if(e.min_avail_mem>e.stats.mem_avail)return xn.WARNING}catch(e){return xn.NOTAPPLICABLE}return xn.OPTIMAL}function Ln(e){try{if(e.min_avail_swap>e.stats.swap_avail)return xn.WARNING}catch(e){return xn.NOTAPPLICABLE}return xn.OPTIMAL}function Mn(e){var t=["listener","dns","monitor","scheduler"];for(var n in e)n.match(/^hb#/)&&t.push(n);for(var r=0;r<t.length;r++)if(t[r]in e&&"running"!=e[t[r]].state)return xn.DANGER;return xn.OPTIMAL}function Dn(e){if(!("monitor"in e))return xn.NOTAPPLICABLE;for(var t in e.monitor.nodes){var n=e.monitor.nodes[t];if(n.arbitrators)for(var r in n.arbitrators)if("up"!=n.arbitrators[r].status)return xn.WARNING}return xn.OPTIMAL}function Bn(e){var t,n,r=0,o=0;for(t in e)if(/^hb#/.test(t)){r+=1;var i=e[t];if("running"!=i.state){o+=1;break}for(n in i.peers){var a=i.peers[n];if(0==a.beating){o+=1;break}a.beating}}return 0==r?xn.OPTIMAL:o==r?xn.DANGER:0==o?xn.OPTIMAL:xn.WARNING}function Un(e,t,n){if(void 0===e.monitor)return xn.NOTAPPLICABLE;var r;try{r=e.monitor.nodes[n].services.status[t]}catch(e){return xn.WARNING}return void 0===r||void 0===r.monitor?xn.NOTAPPLICABLE:r.monitor.status.match(/failed/)?xn.WARNING:xn.OPTIMAL}function zn(e,t){if(void 0===e.monitor)return xn.NOTAPPLICABLE;var n=xn.OPTIMAL;for(var r in e.monitor.nodes)if((n=An(n,Un(e,t,r)))==xn.WARNING)break;return n}function $n(e,t){if(void 0===e.monitor)return xn.NOTAPPLICABLE;var n=e.monitor.services[t];return void 0===n.avail?xn.NOTAPPLICABLE:"down"==n.avail?xn.DANGER:"warn"==n.avail||"warn"==n.overall||"non-optimal"==n.placement?xn.WARNING:xn.OPTIMAL}function Hn(e,t){if(void 0===e.monitor)return xn.NOTAPPLICABLE;var n=xn.OPTIMAL;for(var r in e.monitor.services)if((!t||kn(r).kind==t)&&(n=An(n,$n(e,r)),(n=An(n,zn(e,r)))==xn.DANGER))break;return n}function Wn(e){var t=jn(e);return t=An(t,Nn(e)),t=An(t,function(e,t){if(void 0===e.monitor)return xn.NOTAPPLICABLE;var n=xn.OPTIMAL;for(var t in e.monitor.nodes){var r=e.monitor.nodes[t];if(n=An(n,In(r)),(n=An(n,Ln(r)))==xn.WARNING)break}return n}(e)),t}function Vn(e){if(!e)return xn.NOTAPPLICABLE;var t=Wn(e);return t=An(t,Mn(e)),t=An(t,Nn(e)),t=An(t,Dn(e)),An(t,Bn(e))}var qn=o(8587),Kn=o(4164),Gn=o(3272),Jn=o(8552);const Yn=["sx"];function Xn(e){const{sx:t}=e,n=(0,qn.A)(e,Yn),{systemProps:r,otherProps:o}=(e=>{var t,n;const r={systemProps:{},otherProps:{}},o=null!=(t=null==e||null==(n=e.theme)?void 0:n.unstable_sxConfig)?t:Jn.A;return Object.keys(e).forEach((t=>{o[t]?r.systemProps[t]=e[t]:r.otherProps[t]=e[t]})),r})(n);let i;return i=Array.isArray(t)?[r,...t]:"function"==typeof t?(...e)=>{const n=t(...e);return(0,Gn.Q)(n)?(0,Jt.A)({},r,n):r}:(0,Jt.A)({},r,t),(0,Jt.A)({},o,{sx:i})}var Qn=o(5659),Zn=o(1848),er=o(3541),tr=o(8466),nr=o(8413),rr=o(1609);function or(e){return(0,rr.A)("MuiTypography",e)}(0,nr.A)("MuiTypography",["root","h1","h2","h3","h4","h5","h6","subtitle1","subtitle2","body1","body2","inherit","button","caption","overline","alignLeft","alignRight","alignCenter","alignJustify","noWrap","gutterBottom","paragraph"]);var ir=o(4848);const ar=["align","className","component","gutterBottom","noWrap","paragraph","variant","variantMapping"],sr=(0,Zn.Ay)("span",{name:"MuiTypography",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.variant&&t[n.variant],"inherit"!==n.align&&t[`align${(0,tr.A)(n.align)}`],n.noWrap&&t.noWrap,n.gutterBottom&&t.gutterBottom,n.paragraph&&t.paragraph]}})((({theme:e,ownerState:t})=>(0,Jt.A)({margin:0},"inherit"===t.variant&&{font:"inherit"},"inherit"!==t.variant&&e.typography[t.variant],"inherit"!==t.align&&{textAlign:t.align},t.noWrap&&{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},t.gutterBottom&&{marginBottom:"0.35em"},t.paragraph&&{marginBottom:16}))),lr={h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",subtitle1:"h6",subtitle2:"h6",body1:"p",body2:"p",inherit:"p"},ur={primary:"primary.main",textPrimary:"text.primary",secondary:"secondary.main",textSecondary:"text.secondary",error:"error.main"},cr=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTypography"}),r=(e=>ur[e]||e)(n.color),o=Xn((0,Jt.A)({},n,{color:r})),{align:i="inherit",className:a,component:s,gutterBottom:l=!1,noWrap:u=!1,paragraph:c=!1,variant:d="body1",variantMapping:f=lr}=o,p=(0,qn.A)(o,ar),h=(0,Jt.A)({},o,{align:i,color:r,className:a,component:s,gutterBottom:l,noWrap:u,paragraph:c,variant:d,variantMapping:f}),m=s||(c?"p":f[d]||lr[d])||"span",g=(e=>{const{align:t,gutterBottom:n,noWrap:r,paragraph:o,variant:i,classes:a}=e,s={root:["root",i,"inherit"!==e.align&&`align${(0,tr.A)(t)}`,n&&"gutterBottom",r&&"noWrap",o&&"paragraph"]};return(0,Qn.A)(s,or,a)})(h);return(0,ir.jsx)(sr,(0,Jt.A)({as:m,ref:t,ownerState:h,className:(0,Kn.A)(g.root,a)},p))}));var dr=o(1523);function fr(e){return"string"==typeof e}function pr(e,t,n){return void 0===e||fr(e)?t:(0,Jt.A)({},t,{ownerState:(0,Jt.A)({},t.ownerState,n)})}function hr(e,t=[]){if(void 0===e)return{};const n={};return Object.keys(e).filter((n=>n.match(/^on[A-Z]/)&&"function"==typeof e[n]&&!t.includes(n))).forEach((t=>{n[t]=e[t]})),n}function mr(e){if(void 0===e)return{};const t={};return Object.keys(e).filter((t=>!(t.match(/^on[A-Z]/)&&"function"==typeof e[t]))).forEach((n=>{t[n]=e[n]})),t}const gr=["elementType","externalSlotProps","ownerState","skipResolvingSlotProps"];function vr(e){var t;const{elementType:n,externalSlotProps:r,ownerState:o,skipResolvingSlotProps:i=!1}=e,a=(0,qn.A)(e,gr),s=i?{}:function(e,t){return"function"==typeof e?e(t,void 0):e}(r,o),{props:l,internalRef:u}=function(e){const{getSlotProps:t,additionalProps:n,externalSlotProps:r,externalForwardedProps:o,className:i}=e;if(!t){const e=(0,Kn.A)(null==n?void 0:n.className,i,null==o?void 0:o.className,null==r?void 0:r.className),t=(0,Jt.A)({},null==n?void 0:n.style,null==o?void 0:o.style,null==r?void 0:r.style),a=(0,Jt.A)({},n,o,r);return e.length>0&&(a.className=e),Object.keys(t).length>0&&(a.style=t),{props:a,internalRef:void 0}}const a=hr((0,Jt.A)({},o,r)),s=mr(r),l=mr(o),u=t(a),c=(0,Kn.A)(null==u?void 0:u.className,null==n?void 0:n.className,i,null==o?void 0:o.className,null==r?void 0:r.className),d=(0,Jt.A)({},null==u?void 0:u.style,null==n?void 0:n.style,null==o?void 0:o.style,null==r?void 0:r.style),f=(0,Jt.A)({},u,n,l,s);return c.length>0&&(f.className=c),Object.keys(d).length>0&&(f.style=d),{props:f,internalRef:u.ref}}((0,Jt.A)({},a,{externalSlotProps:s})),c=(0,dr.A)(u,null==s?void 0:s.ref,null==(t=e.additionalProps)?void 0:t.ref);return pr(n,(0,Jt.A)({},l,{ref:c}),o)}var yr=o(2325),br=o(1547),wr=o(1210),Er=o(4877);function Sr(e){const t=e.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}function xr(e,t){t?e.setAttribute("aria-hidden","true"):e.removeAttribute("aria-hidden")}function Ar(e){return parseInt((0,Er.A)(e).getComputedStyle(e).paddingRight,10)||0}function kr(e,t,n,r,o){const i=[t,n,...r];[].forEach.call(e.children,(e=>{const t=-1===i.indexOf(e),n=!function(e){const t=-1!==["TEMPLATE","SCRIPT","STYLE","LINK","MAP","META","NOSCRIPT","PICTURE","COL","COLGROUP","PARAM","SLOT","SOURCE","TRACK"].indexOf(e.tagName),n="INPUT"===e.tagName&&"hidden"===e.getAttribute("type");return t||n}(e);t&&n&&xr(e,o)}))}function _r(e,t){let n=-1;return e.some(((e,r)=>!!t(e)&&(n=r,!0))),n}const Cr=new class{constructor(){this.containers=void 0,this.modals=void 0,this.modals=[],this.containers=[]}add(e,t){let n=this.modals.indexOf(e);if(-1!==n)return n;n=this.modals.length,this.modals.push(e),e.modalRef&&xr(e.modalRef,!1);const r=function(e){const t=[];return[].forEach.call(e.children,(e=>{"true"===e.getAttribute("aria-hidden")&&t.push(e)})),t}(t);kr(t,e.mount,e.modalRef,r,!0);const o=_r(this.containers,(e=>e.container===t));return-1!==o?(this.containers[o].modals.push(e),n):(this.containers.push({modals:[e],container:t,restore:null,hiddenSiblings:r}),n)}mount(e,t){const n=_r(this.containers,(t=>-1!==t.modals.indexOf(e))),r=this.containers[n];r.restore||(r.restore=function(e,t){const n=[],r=e.container;if(!t.disableScrollLock){if(function(e){const t=(0,yr.A)(e);return t.body===e?(0,Er.A)(e).innerWidth>t.documentElement.clientWidth:e.scrollHeight>e.clientHeight}(r)){const e=Sr((0,yr.A)(r));n.push({value:r.style.paddingRight,property:"padding-right",el:r}),r.style.paddingRight=`${Ar(r)+e}px`;const t=(0,yr.A)(r).querySelectorAll(".mui-fixed");[].forEach.call(t,(t=>{n.push({value:t.style.paddingRight,property:"padding-right",el:t}),t.style.paddingRight=`${Ar(t)+e}px`}))}let e;if(r.parentNode instanceof DocumentFragment)e=(0,yr.A)(r).body;else{const t=r.parentElement,n=(0,Er.A)(r);e="HTML"===(null==t?void 0:t.nodeName)&&"scroll"===n.getComputedStyle(t).overflowY?t:r}n.push({value:e.style.overflow,property:"overflow",el:e},{value:e.style.overflowX,property:"overflow-x",el:e},{value:e.style.overflowY,property:"overflow-y",el:e}),e.style.overflow="hidden"}return()=>{n.forEach((({value:e,el:t,property:n})=>{e?t.style.setProperty(n,e):t.style.removeProperty(n)}))}}(r,t))}remove(e,t=!0){const n=this.modals.indexOf(e);if(-1===n)return n;const r=_r(this.containers,(t=>-1!==t.modals.indexOf(e))),o=this.containers[r];if(o.modals.splice(o.modals.indexOf(e),1),this.modals.splice(n,1),0===o.modals.length)o.restore&&o.restore(),e.modalRef&&xr(e.modalRef,t),kr(o.container,e.mount,e.modalRef,o.hiddenSiblings,!1),this.containers.splice(r,1);else{const e=o.modals[o.modals.length-1];e.modalRef&&xr(e.modalRef,!1)}return n}isTopModal(e){return this.modals.length>0&&this.modals[this.modals.length-1]===e}};const Or=["input","select","textarea","a[href]","button","[tabindex]","audio[controls]","video[controls]",'[contenteditable]:not([contenteditable="false"])'].join(",");function Pr(e){const t=[],n=[];return Array.from(e.querySelectorAll(Or)).forEach(((e,r)=>{const o=function(e){const t=parseInt(e.getAttribute("tabindex")||"",10);return Number.isNaN(t)?"true"===e.contentEditable||("AUDIO"===e.nodeName||"VIDEO"===e.nodeName||"DETAILS"===e.nodeName)&&null===e.getAttribute("tabindex")?0:e.tabIndex:t}(e);-1!==o&&function(e){return!(e.disabled||"INPUT"===e.tagName&&"hidden"===e.type||function(e){if("INPUT"!==e.tagName||"radio"!==e.type)return!1;if(!e.name)return!1;const t=t=>e.ownerDocument.querySelector(`input[type="radio"]${t}`);let n=t(`[name="${e.name}"]:checked`);return n||(n=t(`[name="${e.name}"]`)),n!==e}(e))}(e)&&(0===o?t.push(e):n.push({documentOrder:r,tabIndex:o,node:e}))})),n.sort(((e,t)=>e.tabIndex===t.tabIndex?e.documentOrder-t.documentOrder:e.tabIndex-t.tabIndex)).map((e=>e.node)).concat(t)}function Rr(){return!0}function Tr(e){const{children:t,disableAutoFocus:n=!1,disableEnforceFocus:r=!1,disableRestoreFocus:o=!1,getTabbable:i=Pr,isEnabled:a=Rr,open:s}=e,l=B.useRef(!1),u=B.useRef(null),c=B.useRef(null),d=B.useRef(null),f=B.useRef(null),p=B.useRef(!1),h=B.useRef(null),m=(0,dr.A)(t.ref,h),g=B.useRef(null);B.useEffect((()=>{s&&h.current&&(p.current=!n)}),[n,s]),B.useEffect((()=>{if(!s||!h.current)return;const e=(0,yr.A)(h.current);return h.current.contains(e.activeElement)||(h.current.hasAttribute("tabIndex")||h.current.setAttribute("tabIndex","-1"),p.current&&h.current.focus()),()=>{o||(d.current&&d.current.focus&&(l.current=!0,d.current.focus()),d.current=null)}}),[s]),B.useEffect((()=>{if(!s||!h.current)return;const e=(0,yr.A)(h.current),t=t=>{g.current=t,!r&&a()&&"Tab"===t.key&&e.activeElement===h.current&&t.shiftKey&&(l.current=!0,c.current&&c.current.focus())},n=()=>{const t=h.current;if(null===t)return;if(!e.hasFocus()||!a()||l.current)return void(l.current=!1);if(t.contains(e.activeElement))return;if(r&&e.activeElement!==u.current&&e.activeElement!==c.current)return;if(e.activeElement!==f.current)f.current=null;else if(null!==f.current)return;if(!p.current)return;let n=[];if(e.activeElement!==u.current&&e.activeElement!==c.current||(n=i(h.current)),n.length>0){var o,s;const e=Boolean((null==(o=g.current)?void 0:o.shiftKey)&&"Tab"===(null==(s=g.current)?void 0:s.key)),t=n[0],r=n[n.length-1];"string"!=typeof t&&"string"!=typeof r&&(e?r.focus():t.focus())}else t.focus()};e.addEventListener("focusin",n),e.addEventListener("keydown",t,!0);const o=setInterval((()=>{e.activeElement&&"BODY"===e.activeElement.tagName&&n()}),50);return()=>{clearInterval(o),e.removeEventListener("focusin",n),e.removeEventListener("keydown",t,!0)}}),[n,r,o,a,s,i]);const v=e=>{null===d.current&&(d.current=e.relatedTarget),p.current=!0};return(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)("div",{tabIndex:s?0:-1,onFocus:v,ref:u,"data-testid":"sentinelStart"}),B.cloneElement(t,{ref:m,onFocus:e=>{null===d.current&&(d.current=e.relatedTarget),p.current=!0,f.current=e.target;const n=t.props.onFocus;n&&n(e)}}),(0,ir.jsx)("div",{tabIndex:s?0:-1,onFocus:v,ref:c,"data-testid":"sentinelEnd"})]})}var Fr=o(961),jr=o(1529),Nr=o(989);const Ir=B.forwardRef((function(e,t){const{children:n,container:r,disablePortal:o=!1}=e,[i,a]=B.useState(null),s=(0,dr.A)(B.isValidElement(n)?n.ref:null,t);if((0,jr.A)((()=>{o||a(function(e){return"function"==typeof e?e():e}(r)||document.body)}),[r,o]),(0,jr.A)((()=>{if(i&&!o)return(0,Nr.A)(t,i),()=>{(0,Nr.A)(t,null)}}),[t,i,o]),o){if(B.isValidElement(n)){const e={ref:s};return B.cloneElement(n,e)}return(0,ir.jsx)(B.Fragment,{children:n})}return(0,ir.jsx)(B.Fragment,{children:i?Fr.createPortal(n,i):i})}));function Lr(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,le(e,t)}const Mr=B.createContext(null);var Dr="unmounted",Br="exited",Ur="entering",zr="entered",$r="exiting",Hr=function(e){function t(t,n){var r;r=e.call(this,t,n)||this;var o,i=n&&!n.isMounting?t.enter:t.appear;return r.appearStatus=null,t.in?i?(o=Br,r.appearStatus=Ur):o=zr:o=t.unmountOnExit||t.mountOnEnter?Dr:Br,r.state={status:o},r.nextCallback=null,r}Lr(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===Dr?{status:Br}:null};var n=t.prototype;return n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==Ur&&n!==zr&&(t=Ur):n!==Ur&&n!==zr||(t=$r)}this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e,t,n,r=this.props.timeout;return e=t=n=r,null!=r&&"number"!=typeof r&&(e=r.exit,t=r.enter,n=void 0!==r.appear?r.appear:t),{exit:e,enter:t,appear:n}},n.updateStatus=function(e,t){if(void 0===e&&(e=!1),null!==t)if(this.cancelNextCallback(),t===Ur){if(this.props.unmountOnExit||this.props.mountOnEnter){var n=this.props.nodeRef?this.props.nodeRef.current:Fr.findDOMNode(this);n&&function(e){e.scrollTop}(n)}this.performEnter(e)}else this.performExit();else this.props.unmountOnExit&&this.state.status===Br&&this.setState({status:Dr})},n.performEnter=function(e){var t=this,n=this.props.enter,r=this.context?this.context.isMounting:e,o=this.props.nodeRef?[r]:[Fr.findDOMNode(this),r],i=o[0],a=o[1],s=this.getTimeouts(),l=r?s.appear:s.enter;e||n?(this.props.onEnter(i,a),this.safeSetState({status:Ur},(function(){t.props.onEntering(i,a),t.onTransitionEnd(l,(function(){t.safeSetState({status:zr},(function(){t.props.onEntered(i,a)}))}))}))):this.safeSetState({status:zr},(function(){t.props.onEntered(i)}))},n.performExit=function(){var e=this,t=this.props.exit,n=this.getTimeouts(),r=this.props.nodeRef?void 0:Fr.findDOMNode(this);t?(this.props.onExit(r),this.safeSetState({status:$r},(function(){e.props.onExiting(r),e.onTransitionEnd(n.exit,(function(){e.safeSetState({status:Br},(function(){e.props.onExited(r)}))}))}))):this.safeSetState({status:Br},(function(){e.props.onExited(r)}))},n.cancelNextCallback=function(){null!==this.nextCallback&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(r){n&&(n=!1,t.nextCallback=null,e(r))},this.nextCallback.cancel=function(){n=!1},this.nextCallback},n.onTransitionEnd=function(e,t){this.setNextCallback(t);var n=this.props.nodeRef?this.props.nodeRef.current:Fr.findDOMNode(this),r=null==e&&!this.props.addEndListener;if(n&&!r){if(this.props.addEndListener){var o=this.props.nodeRef?[this.nextCallback]:[n,this.nextCallback],i=o[0],a=o[1];this.props.addEndListener(i,a)}null!=e&&setTimeout(this.nextCallback,e)}else setTimeout(this.nextCallback,0)},n.render=function(){var e=this.state.status;if(e===Dr)return null;var t=this.props,n=t.children,r=(t.in,t.mountOnEnter,t.unmountOnExit,t.appear,t.enter,t.exit,t.timeout,t.addEndListener,t.onEnter,t.onEntering,t.onEntered,t.onExit,t.onExiting,t.onExited,t.nodeRef,(0,qn.A)(t,["children","in","mountOnEnter","unmountOnExit","appear","enter","exit","timeout","addEndListener","onEnter","onEntering","onEntered","onExit","onExiting","onExited","nodeRef"]));return B.createElement(Mr.Provider,{value:null},"function"==typeof n?n(e,r):B.cloneElement(B.Children.only(n),r))},t}(B.Component);function Wr(){}Hr.contextType=Mr,Hr.propTypes={},Hr.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:Wr,onEntering:Wr,onEntered:Wr,onExit:Wr,onExiting:Wr,onExited:Wr},Hr.UNMOUNTED=Dr,Hr.EXITED=Br,Hr.ENTERING=Ur,Hr.ENTERED=zr,Hr.EXITING=$r;const Vr=Hr;var qr=o(2858),Kr=o(2765),Gr=o(8312);function Jr(){const e=(0,qr.A)(Kr.A);return e[Gr.A]||e}const Yr=e=>e.scrollTop;function Xr(e,t){var n,r;const{timeout:o,easing:i,style:a={}}=e;return{duration:null!=(n=a.transitionDuration)?n:"number"==typeof o?o:o[t.mode]||0,easing:null!=(r=a.transitionTimingFunction)?r:"object"==typeof i?i[t.mode]:i,delay:a.transitionDelay}}var Qr=o(6852);const Zr=["addEndListener","appear","children","easing","in","onEnter","onEntered","onEntering","onExit","onExited","onExiting","style","timeout","TransitionComponent"],eo={entering:{opacity:1},entered:{opacity:1}},to=B.forwardRef((function(e,t){const n=Jr(),r={enter:n.transitions.duration.enteringScreen,exit:n.transitions.duration.leavingScreen},{addEndListener:o,appear:i=!0,children:a,easing:s,in:l,onEnter:u,onEntered:c,onEntering:d,onExit:f,onExited:p,onExiting:h,style:m,timeout:g=r,TransitionComponent:v=Vr}=e,y=(0,qn.A)(e,Zr),b=B.useRef(null),w=(0,Qr.A)(b,a.ref,t),E=e=>t=>{if(e){const n=b.current;void 0===t?e(n):e(n,t)}},S=E(d),x=E(((e,t)=>{Yr(e);const r=Xr({style:m,timeout:g,easing:s},{mode:"enter"});e.style.webkitTransition=n.transitions.create("opacity",r),e.style.transition=n.transitions.create("opacity",r),u&&u(e,t)})),A=E(c),k=E(h),_=E((e=>{const t=Xr({style:m,timeout:g,easing:s},{mode:"exit"});e.style.webkitTransition=n.transitions.create("opacity",t),e.style.transition=n.transitions.create("opacity",t),f&&f(e)})),C=E(p);return(0,ir.jsx)(v,(0,Jt.A)({appear:i,in:l,nodeRef:b,onEnter:x,onEntered:A,onEntering:S,onExit:_,onExited:C,onExiting:k,addEndListener:e=>{o&&o(b.current,e)},timeout:g},y,{children:(e,t)=>B.cloneElement(a,(0,Jt.A)({style:(0,Jt.A)({opacity:0,visibility:"exited"!==e||l?void 0:"hidden"},eo[e],m,a.props.style),ref:w},t))}))}));function no(e){return(0,rr.A)("MuiBackdrop",e)}(0,nr.A)("MuiBackdrop",["root","invisible"]);const ro=["children","className","component","components","componentsProps","invisible","open","slotProps","slots","TransitionComponent","transitionDuration"],oo=(0,Zn.Ay)("div",{name:"MuiBackdrop",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.invisible&&t.invisible]}})((({ownerState:e})=>(0,Jt.A)({position:"fixed",display:"flex",alignItems:"center",justifyContent:"center",right:0,bottom:0,top:0,left:0,backgroundColor:"rgba(0, 0, 0, 0.5)",WebkitTapHighlightColor:"transparent"},e.invisible&&{backgroundColor:"transparent"}))),io=B.forwardRef((function(e,t){var n,r,o;const i=(0,er.A)({props:e,name:"MuiBackdrop"}),{children:a,className:s,component:l="div",components:u={},componentsProps:c={},invisible:d=!1,open:f,slotProps:p={},slots:h={},TransitionComponent:m=to,transitionDuration:g}=i,v=(0,qn.A)(i,ro),y=(0,Jt.A)({},i,{component:l,invisible:d}),b=(e=>{const{classes:t,invisible:n}=e,r={root:["root",n&&"invisible"]};return(0,Qn.A)(r,no,t)})(y),w=null!=(n=p.root)?n:c.root;return(0,ir.jsx)(m,(0,Jt.A)({in:f,timeout:g},v,{children:(0,ir.jsx)(oo,(0,Jt.A)({"aria-hidden":!0},w,{as:null!=(r=null!=(o=h.root)?o:u.Root)?r:l,className:(0,Kn.A)(b.root,s,null==w?void 0:w.className),ownerState:(0,Jt.A)({},y,null==w?void 0:w.ownerState),classes:b,ref:t,children:a}))}))}));function ao(e){return(0,rr.A)("MuiModal",e)}(0,nr.A)("MuiModal",["root","hidden","backdrop"]);const so=["BackdropComponent","BackdropProps","classes","className","closeAfterTransition","children","container","component","components","componentsProps","disableAutoFocus","disableEnforceFocus","disableEscapeKeyDown","disablePortal","disableRestoreFocus","disableScrollLock","hideBackdrop","keepMounted","onBackdropClick","onClose","onTransitionEnter","onTransitionExited","open","slotProps","slots","theme"],lo=(0,Zn.Ay)("div",{name:"MuiModal",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,!n.open&&n.exited&&t.hidden]}})((({theme:e,ownerState:t})=>(0,Jt.A)({position:"fixed",zIndex:(e.vars||e).zIndex.modal,right:0,bottom:0,top:0,left:0},!t.open&&t.exited&&{visibility:"hidden"}))),uo=(0,Zn.Ay)(io,{name:"MuiModal",slot:"Backdrop",overridesResolver:(e,t)=>t.backdrop})({zIndex:-1}),co=B.forwardRef((function(e,t){var n,r,o,i,a,s;const l=(0,er.A)({name:"MuiModal",props:e}),{BackdropComponent:u=uo,BackdropProps:c,className:d,closeAfterTransition:f=!1,children:p,container:h,component:m,components:g={},componentsProps:v={},disableAutoFocus:y=!1,disableEnforceFocus:b=!1,disableEscapeKeyDown:w=!1,disablePortal:E=!1,disableRestoreFocus:S=!1,disableScrollLock:x=!1,hideBackdrop:A=!1,keepMounted:k=!1,onBackdropClick:_,open:C,slotProps:O,slots:P}=l,R=(0,qn.A)(l,so),T=(0,Jt.A)({},l,{closeAfterTransition:f,disableAutoFocus:y,disableEnforceFocus:b,disableEscapeKeyDown:w,disablePortal:E,disableRestoreFocus:S,disableScrollLock:x,hideBackdrop:A,keepMounted:k}),{getRootProps:F,getBackdropProps:j,getTransitionProps:N,portalRef:I,isTopModal:L,exited:M,hasTransition:D}=function(e){const{container:t,disableEscapeKeyDown:n=!1,disableScrollLock:r=!1,manager:o=Cr,closeAfterTransition:i=!1,onTransitionEnter:a,onTransitionExited:s,children:l,onClose:u,open:c,rootRef:d}=e,f=B.useRef({}),p=B.useRef(null),h=B.useRef(null),m=(0,dr.A)(h,d),[g,v]=B.useState(!c),y=function(e){return!!e&&e.props.hasOwnProperty("in")}(l);let b=!0;"false"!==e["aria-hidden"]&&!1!==e["aria-hidden"]||(b=!1);const w=()=>(f.current.modalRef=h.current,f.current.mount=p.current,f.current),E=()=>{o.mount(w(),{disableScrollLock:r}),h.current&&(h.current.scrollTop=0)},S=(0,br.A)((()=>{const e=function(e){return"function"==typeof e?e():e}(t)||(0,yr.A)(p.current).body;o.add(w(),e),h.current&&E()})),x=B.useCallback((()=>o.isTopModal(w())),[o]),A=(0,br.A)((e=>{p.current=e,e&&(c&&x()?E():h.current&&xr(h.current,b))})),k=B.useCallback((()=>{o.remove(w(),b)}),[b,o]);B.useEffect((()=>()=>{k()}),[k]),B.useEffect((()=>{c?S():y&&i||k()}),[c,k,y,i,S]);const _=e=>t=>{var r;null==(r=e.onKeyDown)||r.call(e,t),"Escape"===t.key&&229!==t.which&&x()&&(n||(t.stopPropagation(),u&&u(t,"escapeKeyDown")))},C=e=>t=>{var n;null==(n=e.onClick)||n.call(e,t),t.target===t.currentTarget&&u&&u(t,"backdropClick")};return{getRootProps:(t={})=>{const n=hr(e);delete n.onTransitionEnter,delete n.onTransitionExited;const r=(0,Jt.A)({},n,t);return(0,Jt.A)({role:"presentation"},r,{onKeyDown:_(r),ref:m})},getBackdropProps:(e={})=>{const t=e;return(0,Jt.A)({"aria-hidden":!0},t,{onClick:C(t),open:c})},getTransitionProps:()=>({onEnter:(0,wr.A)((()=>{v(!1),a&&a()}),null==l?void 0:l.props.onEnter),onExited:(0,wr.A)((()=>{v(!0),s&&s(),i&&k()}),null==l?void 0:l.props.onExited)}),rootRef:m,portalRef:A,isTopModal:x,exited:g,hasTransition:y}}((0,Jt.A)({},T,{rootRef:t})),U=(0,Jt.A)({},T,{exited:M}),z=(e=>{const{open:t,exited:n,classes:r}=e,o={root:["root",!t&&n&&"hidden"],backdrop:["backdrop"]};return(0,Qn.A)(o,ao,r)})(U),$={};if(void 0===p.props.tabIndex&&($.tabIndex="-1"),D){const{onEnter:e,onExited:t}=N();$.onEnter=e,$.onExited=t}const H=null!=(n=null!=(r=null==P?void 0:P.root)?r:g.Root)?n:lo,W=null!=(o=null!=(i=null==P?void 0:P.backdrop)?i:g.Backdrop)?o:u,V=null!=(a=null==O?void 0:O.root)?a:v.root,q=null!=(s=null==O?void 0:O.backdrop)?s:v.backdrop,K=vr({elementType:H,externalSlotProps:V,externalForwardedProps:R,getSlotProps:F,additionalProps:{ref:t,as:m},ownerState:U,className:(0,Kn.A)(d,null==V?void 0:V.className,null==z?void 0:z.root,!U.open&&U.exited&&(null==z?void 0:z.hidden))}),G=vr({elementType:W,externalSlotProps:q,additionalProps:c,getSlotProps:e=>j((0,Jt.A)({},e,{onClick:t=>{_&&_(t),null!=e&&e.onClick&&e.onClick(t)}})),className:(0,Kn.A)(null==q?void 0:q.className,null==c?void 0:c.className,null==z?void 0:z.backdrop),ownerState:U});return k||C||D&&!M?(0,ir.jsx)(Ir,{ref:I,container:h,disablePortal:E,children:(0,ir.jsxs)(H,(0,Jt.A)({},K,{children:[!A&&u?(0,ir.jsx)(W,(0,Jt.A)({},G)):null,(0,ir.jsx)(Tr,{disableEnforceFocus:b,disableAutoFocus:y,disableRestoreFocus:S,isEnabled:L,open:C,children:B.cloneElement(p,$)})]}))}):null}));var fo=o(1935),po=o(3749);const ho=["addEndListener","appear","children","container","direction","easing","in","onEnter","onEntered","onEntering","onExit","onExited","onExiting","style","timeout","TransitionComponent"];function mo(e,t,n){var r;const o=function(e,t,n){const r=t.getBoundingClientRect(),o=n&&n.getBoundingClientRect(),i=(0,po.A)(t);let a;if(t.fakeTransform)a=t.fakeTransform;else{const e=i.getComputedStyle(t);a=e.getPropertyValue("-webkit-transform")||e.getPropertyValue("transform")}let s=0,l=0;if(a&&"none"!==a&&"string"==typeof a){const e=a.split("(")[1].split(")")[0].split(",");s=parseInt(e[4],10),l=parseInt(e[5],10)}return"left"===e?o?`translateX(${o.right+s-r.left}px)`:`translateX(${i.innerWidth+s-r.left}px)`:"right"===e?o?`translateX(-${r.right-o.left-s}px)`:`translateX(-${r.left+r.width-s}px)`:"up"===e?o?`translateY(${o.bottom+l-r.top}px)`:`translateY(${i.innerHeight+l-r.top}px)`:o?`translateY(-${r.top-o.top+r.height-l}px)`:`translateY(-${r.top+r.height-l}px)`}(e,t,"function"==typeof(r=n)?r():r);o&&(t.style.webkitTransform=o,t.style.transform=o)}const go=B.forwardRef((function(e,t){const n=Jr(),r={enter:n.transitions.easing.easeOut,exit:n.transitions.easing.sharp},o={enter:n.transitions.duration.enteringScreen,exit:n.transitions.duration.leavingScreen},{addEndListener:i,appear:a=!0,children:s,container:l,direction:u="down",easing:c=r,in:d,onEnter:f,onEntered:p,onEntering:h,onExit:m,onExited:g,onExiting:v,style:y,timeout:b=o,TransitionComponent:w=Vr}=e,E=(0,qn.A)(e,ho),S=B.useRef(null),x=(0,Qr.A)(s.ref,S,t),A=e=>t=>{e&&(void 0===t?e(S.current):e(S.current,t))},k=A(((e,t)=>{mo(u,e,l),Yr(e),f&&f(e,t)})),_=A(((e,t)=>{const r=Xr({timeout:b,style:y,easing:c},{mode:"enter"});e.style.webkitTransition=n.transitions.create("-webkit-transform",(0,Jt.A)({},r)),e.style.transition=n.transitions.create("transform",(0,Jt.A)({},r)),e.style.webkitTransform="none",e.style.transform="none",h&&h(e,t)})),C=A(p),O=A(v),P=A((e=>{const t=Xr({timeout:b,style:y,easing:c},{mode:"exit"});e.style.webkitTransition=n.transitions.create("-webkit-transform",t),e.style.transition=n.transitions.create("transform",t),mo(u,e,l),m&&m(e)})),R=A((e=>{e.style.webkitTransition="",e.style.transition="",g&&g(e)})),T=B.useCallback((()=>{S.current&&mo(u,S.current,l)}),[u,l]);return B.useEffect((()=>{if(d||"down"===u||"right"===u)return;const e=(0,fo.A)((()=>{S.current&&mo(u,S.current,l)})),t=(0,po.A)(S.current);return t.addEventListener("resize",e),()=>{e.clear(),t.removeEventListener("resize",e)}}),[u,d,l]),B.useEffect((()=>{d||T()}),[d,T]),(0,ir.jsx)(w,(0,Jt.A)({nodeRef:S,onEnter:k,onEntered:C,onEntering:_,onExit:P,onExited:R,onExiting:O,addEndListener:e=>{i&&i(S.current,e)},appear:a,in:d,timeout:b},E,{children:(e,t)=>B.cloneElement(s,(0,Jt.A)({ref:x,style:(0,Jt.A)({visibility:"exited"!==e||d?void 0:"hidden"},y,s.props.style)},t))}))}));var vo=o(4279);const yo=e=>{let t;return t=e<1?5.11916*e**2:4.5*Math.log(e+1)+2,(t/100).toFixed(2)};function bo(e){return(0,rr.A)("MuiPaper",e)}(0,nr.A)("MuiPaper",["root","rounded","outlined","elevation","elevation0","elevation1","elevation2","elevation3","elevation4","elevation5","elevation6","elevation7","elevation8","elevation9","elevation10","elevation11","elevation12","elevation13","elevation14","elevation15","elevation16","elevation17","elevation18","elevation19","elevation20","elevation21","elevation22","elevation23","elevation24"]);const wo=["className","component","elevation","square","variant"],Eo=(0,Zn.Ay)("div",{name:"MuiPaper",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],!n.square&&t.rounded,"elevation"===n.variant&&t[`elevation${n.elevation}`]]}})((({theme:e,ownerState:t})=>{var n;return(0,Jt.A)({backgroundColor:(e.vars||e).palette.background.paper,color:(e.vars||e).palette.text.primary,transition:e.transitions.create("box-shadow")},!t.square&&{borderRadius:e.shape.borderRadius},"outlined"===t.variant&&{border:`1px solid ${(e.vars||e).palette.divider}`},"elevation"===t.variant&&(0,Jt.A)({boxShadow:(e.vars||e).shadows[t.elevation]},!e.vars&&"dark"===e.palette.mode&&{backgroundImage:`linear-gradient(${(0,vo.X4)("#fff",yo(t.elevation))}, ${(0,vo.X4)("#fff",yo(t.elevation))})`},e.vars&&{backgroundImage:null==(n=e.vars.overlays)?void 0:n[t.elevation]}))})),So=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiPaper"}),{className:r,component:o="div",elevation:i=1,square:a=!1,variant:s="elevation"}=n,l=(0,qn.A)(n,wo),u=(0,Jt.A)({},n,{component:o,elevation:i,square:a,variant:s}),c=(e=>{const{square:t,elevation:n,variant:r,classes:o}=e,i={root:["root",r,!t&&"rounded","elevation"===r&&`elevation${n}`]};return(0,Qn.A)(i,bo,o)})(u);return(0,ir.jsx)(Eo,(0,Jt.A)({as:o,ownerState:u,className:(0,Kn.A)(c.root,r),ref:t},l))}));function xo(e){return(0,rr.A)("MuiDrawer",e)}(0,nr.A)("MuiDrawer",["root","docked","paper","paperAnchorLeft","paperAnchorRight","paperAnchorTop","paperAnchorBottom","paperAnchorDockedLeft","paperAnchorDockedRight","paperAnchorDockedTop","paperAnchorDockedBottom","modal"]);const Ao=["BackdropProps"],ko=["anchor","BackdropProps","children","className","elevation","hideBackdrop","ModalProps","onClose","open","PaperProps","SlideProps","TransitionComponent","transitionDuration","variant"],_o=(e,t)=>{const{ownerState:n}=e;return[t.root,("permanent"===n.variant||"persistent"===n.variant)&&t.docked,t.modal]},Co=(0,Zn.Ay)(co,{name:"MuiDrawer",slot:"Root",overridesResolver:_o})((({theme:e})=>({zIndex:(e.vars||e).zIndex.drawer}))),Oo=(0,Zn.Ay)("div",{shouldForwardProp:Zn.ep,name:"MuiDrawer",slot:"Docked",skipVariantsResolver:!1,overridesResolver:_o})({flex:"0 0 auto"}),Po=(0,Zn.Ay)(So,{name:"MuiDrawer",slot:"Paper",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.paper,t[`paperAnchor${(0,tr.A)(n.anchor)}`],"temporary"!==n.variant&&t[`paperAnchorDocked${(0,tr.A)(n.anchor)}`]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({overflowY:"auto",display:"flex",flexDirection:"column",height:"100%",flex:"1 0 auto",zIndex:(e.vars||e).zIndex.drawer,WebkitOverflowScrolling:"touch",position:"fixed",top:0,outline:0},"left"===t.anchor&&{left:0},"top"===t.anchor&&{top:0,left:0,right:0,height:"auto",maxHeight:"100%"},"right"===t.anchor&&{right:0},"bottom"===t.anchor&&{top:"auto",left:0,bottom:0,right:0,height:"auto",maxHeight:"100%"},"left"===t.anchor&&"temporary"!==t.variant&&{borderRight:`1px solid ${(e.vars||e).palette.divider}`},"top"===t.anchor&&"temporary"!==t.variant&&{borderBottom:`1px solid ${(e.vars||e).palette.divider}`},"right"===t.anchor&&"temporary"!==t.variant&&{borderLeft:`1px solid ${(e.vars||e).palette.divider}`},"bottom"===t.anchor&&"temporary"!==t.variant&&{borderTop:`1px solid ${(e.vars||e).palette.divider}`}))),Ro={left:"right",right:"left",top:"down",bottom:"up"},To=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDrawer"}),r=Jr(),o={enter:r.transitions.duration.enteringScreen,exit:r.transitions.duration.leavingScreen},{anchor:i="left",BackdropProps:a,children:s,className:l,elevation:u=16,hideBackdrop:c=!1,ModalProps:{BackdropProps:d}={},onClose:f,open:p=!1,PaperProps:h={},SlideProps:m,TransitionComponent:g=go,transitionDuration:v=o,variant:y="temporary"}=n,b=(0,qn.A)(n.ModalProps,Ao),w=(0,qn.A)(n,ko),E=B.useRef(!1);B.useEffect((()=>{E.current=!0}),[]);const S=function(e,t){return"rtl"===e.direction&&function(e){return-1!==["left","right"].indexOf(e)}(t)?Ro[t]:t}(r,i),x=i,A=(0,Jt.A)({},n,{anchor:x,elevation:u,open:p,variant:y},w),k=(e=>{const{classes:t,anchor:n,variant:r}=e,o={root:["root"],docked:[("permanent"===r||"persistent"===r)&&"docked"],modal:["modal"],paper:["paper",`paperAnchor${(0,tr.A)(n)}`,"temporary"!==r&&`paperAnchorDocked${(0,tr.A)(n)}`]};return(0,Qn.A)(o,xo,t)})(A),_=(0,ir.jsx)(Po,(0,Jt.A)({elevation:"temporary"===y?u:0,square:!0},h,{className:(0,Kn.A)(k.paper,h.className),ownerState:A,children:s}));if("permanent"===y)return(0,ir.jsx)(Oo,(0,Jt.A)({className:(0,Kn.A)(k.root,k.docked,l),ownerState:A,ref:t},w,{children:_}));const C=(0,ir.jsx)(g,(0,Jt.A)({in:p,direction:Ro[S],timeout:v,appear:E.current},m,{children:_}));return"persistent"===y?(0,ir.jsx)(Oo,(0,Jt.A)({className:(0,Kn.A)(k.root,k.docked,l),ownerState:A,ref:t},w,{children:C})):(0,ir.jsx)(Co,(0,Jt.A)({BackdropProps:(0,Jt.A)({},a,d,{transitionDuration:v}),className:(0,Kn.A)(k.root,k.modal,l),open:p,ownerState:A,onClose:f,hideBackdrop:c,ref:t},w,b,{children:C}))}));var Fo=o(3034),jo=o(8851);function No(e,t){var n=Object.create(null);return e&&B.Children.map(e,(function(e){return e})).forEach((function(e){n[e.key]=function(e){return t&&(0,B.isValidElement)(e)?t(e):e}(e)})),n}function Io(e,t,n){return null!=n[t]?n[t]:e.props[t]}function Lo(e,t,n){var r=No(e.children),o=function(e,t){function n(n){return n in t?t[n]:e[n]}e=e||{},t=t||{};var r,o=Object.create(null),i=[];for(var a in e)a in t?i.length&&(o[a]=i,i=[]):i.push(a);var s={};for(var l in t){if(o[l])for(r=0;r<o[l].length;r++){var u=o[l][r];s[o[l][r]]=n(u)}s[l]=n(l)}for(r=0;r<i.length;r++)s[i[r]]=n(i[r]);return s}(t,r);return Object.keys(o).forEach((function(i){var a=o[i];if((0,B.isValidElement)(a)){var s=i in t,l=i in r,u=t[i],c=(0,B.isValidElement)(u)&&!u.props.in;!l||s&&!c?l||!s||c?l&&s&&(0,B.isValidElement)(u)&&(o[i]=(0,B.cloneElement)(a,{onExited:n.bind(null,a),in:u.props.in,exit:Io(a,"exit",e),enter:Io(a,"enter",e)})):o[i]=(0,B.cloneElement)(a,{in:!1}):o[i]=(0,B.cloneElement)(a,{onExited:n.bind(null,a),in:!0,exit:Io(a,"exit",e),enter:Io(a,"enter",e)})}})),o}var Mo=Object.values||function(e){return Object.keys(e).map((function(t){return e[t]}))},Do=function(e){function t(t,n){var r,o=(r=e.call(this,t,n)||this).handleExited.bind(ae(r));return r.state={contextValue:{isMounting:!0},handleExited:o,firstRender:!0},r}Lr(t,e);var n=t.prototype;return n.componentDidMount=function(){this.mounted=!0,this.setState({contextValue:{isMounting:!1}})},n.componentWillUnmount=function(){this.mounted=!1},t.getDerivedStateFromProps=function(e,t){var n,r,o=t.children,i=t.handleExited;return{children:t.firstRender?(n=e,r=i,No(n.children,(function(e){return(0,B.cloneElement)(e,{onExited:r.bind(null,e),in:!0,appear:Io(e,"appear",n),enter:Io(e,"enter",n),exit:Io(e,"exit",n)})}))):Lo(e,o,i),firstRender:!1}},n.handleExited=function(e,t){var n=No(this.props.children);e.key in n||(e.props.onExited&&e.props.onExited(t),this.mounted&&this.setState((function(t){var n=(0,Jt.A)({},t.children);return delete n[e.key],{children:n}})))},n.render=function(){var e=this.props,t=e.component,n=e.childFactory,r=(0,qn.A)(e,["component","childFactory"]),o=this.state.contextValue,i=Mo(this.state.children).map(n);return delete r.appear,delete r.enter,delete r.exit,null===t?B.createElement(Mr.Provider,{value:o},i):B.createElement(Mr.Provider,{value:o},B.createElement(t,r,i))},t}(B.Component);Do.propTypes={},Do.defaultProps={component:"div",childFactory:function(e){return e}};const Bo=Do;var Uo=o(5684),zo=o(2422),$o=o(1287),Ho=o(7308),Wo=o(8966),Vo=(o(4146),(0,Uo.w)((function(e,t){var n=e.styles,r=(0,Ho.J)([n],void 0,B.useContext(Uo.T));if(!Uo.i){for(var o,i=r.name,a=r.styles,s=r.next;void 0!==s;)i+=" "+s.name,a+=s.styles,s=s.next;var l=!0===t.compat,u=t.insert("",{name:i,styles:a},t.sheet,l);return l?null:B.createElement("style",((o={})["data-emotion"]=t.key+"-global "+i,o.dangerouslySetInnerHTML={__html:u},o.nonce=t.sheet.nonce,o))}var c=B.useRef();return(0,$o.i)((function(){var e=t.key+"-global",n=new t.sheet.constructor({key:e,nonce:t.sheet.nonce,container:t.sheet.container,speedy:t.sheet.isSpeedy}),o=!1,i=document.querySelector('style[data-emotion="'+e+" "+r.name+'"]');return t.sheet.tags.length&&(n.before=t.sheet.tags[0]),null!==i&&(o=!0,i.setAttribute("data-emotion",e),n.hydrate([i])),c.current=[n,o],function(){n.flush()}}),[t]),(0,$o.i)((function(){var e=c.current,n=e[0];if(e[1])e[1]=!1;else{if(void 0!==r.next&&(0,zo.sk)(t,r.next,!0),n.tags.length){var o=n.tags[n.tags.length-1].nextElementSibling;n.before=o,n.flush()}t.insert("",r,n,!1)}}),[t,r.name]),null})));function qo(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return(0,Ho.J)(t)}var Ko=function(){var e=qo.apply(void 0,arguments),t="animation-"+e.name;return{name:t,styles:"@keyframes "+t+"{"+e.styles+"}",anim:1,toString:function(){return"_EMO_"+this.name+"_"+this.styles+"_EMO_"}}};const Go=(0,nr.A)("MuiTouchRipple",["root","ripple","rippleVisible","ripplePulsate","child","childLeaving","childPulsate"]),Jo=["center","classes","className"];let Yo,Xo,Qo,Zo,ei=e=>e;const ti=Ko(Yo||(Yo=ei`
  0% {
    transform: scale(0);
    opacity: 0.1;
  }

  100% {
    transform: scale(1);
    opacity: 0.3;
  }
`)),ni=Ko(Xo||(Xo=ei`
  0% {
    opacity: 1;
  }

  100% {
    opacity: 0;
  }
`)),ri=Ko(Qo||(Qo=ei`
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(0.92);
  }

  100% {
    transform: scale(1);
  }
`)),oi=(0,Zn.Ay)("span",{name:"MuiTouchRipple",slot:"Root"})({overflow:"hidden",pointerEvents:"none",position:"absolute",zIndex:0,top:0,right:0,bottom:0,left:0,borderRadius:"inherit"}),ii=(0,Zn.Ay)((function(e){const{className:t,classes:n,pulsate:r=!1,rippleX:o,rippleY:i,rippleSize:a,in:s,onExited:l,timeout:u}=e,[c,d]=B.useState(!1),f=(0,Kn.A)(t,n.ripple,n.rippleVisible,r&&n.ripplePulsate),p={width:a,height:a,top:-a/2+i,left:-a/2+o},h=(0,Kn.A)(n.child,c&&n.childLeaving,r&&n.childPulsate);return s||c||d(!0),B.useEffect((()=>{if(!s&&null!=l){const e=setTimeout(l,u);return()=>{clearTimeout(e)}}}),[l,s,u]),(0,ir.jsx)("span",{className:f,style:p,children:(0,ir.jsx)("span",{className:h})})}),{name:"MuiTouchRipple",slot:"Ripple"})(Zo||(Zo=ei`
  opacity: 0;
  position: absolute;

  &.${0} {
    opacity: 0.3;
    transform: scale(1);
    animation-name: ${0};
    animation-duration: ${0}ms;
    animation-timing-function: ${0};
  }

  &.${0} {
    animation-duration: ${0}ms;
  }

  & .${0} {
    opacity: 1;
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: currentColor;
  }

  & .${0} {
    opacity: 0;
    animation-name: ${0};
    animation-duration: ${0}ms;
    animation-timing-function: ${0};
  }

  & .${0} {
    position: absolute;
    /* @noflip */
    left: 0px;
    top: 0;
    animation-name: ${0};
    animation-duration: 2500ms;
    animation-timing-function: ${0};
    animation-iteration-count: infinite;
    animation-delay: 200ms;
  }
`),Go.rippleVisible,ti,550,(({theme:e})=>e.transitions.easing.easeInOut),Go.ripplePulsate,(({theme:e})=>e.transitions.duration.shorter),Go.child,Go.childLeaving,ni,550,(({theme:e})=>e.transitions.easing.easeInOut),Go.childPulsate,ri,(({theme:e})=>e.transitions.easing.easeInOut)),ai=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTouchRipple"}),{center:r=!1,classes:o={},className:i}=n,a=(0,qn.A)(n,Jo),[s,l]=B.useState([]),u=B.useRef(0),c=B.useRef(null);B.useEffect((()=>{c.current&&(c.current(),c.current=null)}),[s]);const d=B.useRef(!1),f=B.useRef(0),p=B.useRef(null),h=B.useRef(null);B.useEffect((()=>()=>{f.current&&clearTimeout(f.current)}),[]);const m=B.useCallback((e=>{const{pulsate:t,rippleX:n,rippleY:r,rippleSize:i,cb:a}=e;l((e=>[...e,(0,ir.jsx)(ii,{classes:{ripple:(0,Kn.A)(o.ripple,Go.ripple),rippleVisible:(0,Kn.A)(o.rippleVisible,Go.rippleVisible),ripplePulsate:(0,Kn.A)(o.ripplePulsate,Go.ripplePulsate),child:(0,Kn.A)(o.child,Go.child),childLeaving:(0,Kn.A)(o.childLeaving,Go.childLeaving),childPulsate:(0,Kn.A)(o.childPulsate,Go.childPulsate)},timeout:550,pulsate:t,rippleX:n,rippleY:r,rippleSize:i},u.current)])),u.current+=1,c.current=a}),[o]),g=B.useCallback(((e={},t={},n=()=>{})=>{const{pulsate:o=!1,center:i=r||t.pulsate,fakeElement:a=!1}=t;if("mousedown"===(null==e?void 0:e.type)&&d.current)return void(d.current=!1);"touchstart"===(null==e?void 0:e.type)&&(d.current=!0);const s=a?null:h.current,l=s?s.getBoundingClientRect():{width:0,height:0,left:0,top:0};let u,c,g;if(i||void 0===e||0===e.clientX&&0===e.clientY||!e.clientX&&!e.touches)u=Math.round(l.width/2),c=Math.round(l.height/2);else{const{clientX:t,clientY:n}=e.touches&&e.touches.length>0?e.touches[0]:e;u=Math.round(t-l.left),c=Math.round(n-l.top)}if(i)g=Math.sqrt((2*l.width**2+l.height**2)/3),g%2==0&&(g+=1);else{const e=2*Math.max(Math.abs((s?s.clientWidth:0)-u),u)+2,t=2*Math.max(Math.abs((s?s.clientHeight:0)-c),c)+2;g=Math.sqrt(e**2+t**2)}null!=e&&e.touches?null===p.current&&(p.current=()=>{m({pulsate:o,rippleX:u,rippleY:c,rippleSize:g,cb:n})},f.current=setTimeout((()=>{p.current&&(p.current(),p.current=null)}),80)):m({pulsate:o,rippleX:u,rippleY:c,rippleSize:g,cb:n})}),[r,m]),v=B.useCallback((()=>{g({},{pulsate:!0})}),[g]),y=B.useCallback(((e,t)=>{if(clearTimeout(f.current),"touchend"===(null==e?void 0:e.type)&&p.current)return p.current(),p.current=null,void(f.current=setTimeout((()=>{y(e,t)})));p.current=null,l((e=>e.length>0?e.slice(1):e)),c.current=t}),[]);return B.useImperativeHandle(t,(()=>({pulsate:v,start:g,stop:y})),[v,g,y]),(0,ir.jsx)(oi,(0,Jt.A)({className:(0,Kn.A)(Go.root,o.root,i),ref:h},a,{children:(0,ir.jsx)(Bo,{component:null,exit:!0,children:s})}))}));function si(e){return(0,rr.A)("MuiButtonBase",e)}const li=(0,nr.A)("MuiButtonBase",["root","disabled","focusVisible"]),ui=["action","centerRipple","children","className","component","disabled","disableRipple","disableTouchRipple","focusRipple","focusVisibleClassName","LinkComponent","onBlur","onClick","onContextMenu","onDragLeave","onFocus","onFocusVisible","onKeyDown","onKeyUp","onMouseDown","onMouseLeave","onMouseUp","onTouchEnd","onTouchMove","onTouchStart","tabIndex","TouchRippleProps","touchRippleRef","type"],ci=(0,Zn.Ay)("button",{name:"MuiButtonBase",slot:"Root",overridesResolver:(e,t)=>t.root})({display:"inline-flex",alignItems:"center",justifyContent:"center",position:"relative",boxSizing:"border-box",WebkitTapHighlightColor:"transparent",backgroundColor:"transparent",outline:0,border:0,margin:0,borderRadius:0,padding:0,cursor:"pointer",userSelect:"none",verticalAlign:"middle",MozAppearance:"none",WebkitAppearance:"none",textDecoration:"none",color:"inherit","&::-moz-focus-inner":{borderStyle:"none"},[`&.${li.disabled}`]:{pointerEvents:"none",cursor:"default"},"@media print":{colorAdjust:"exact"}}),di=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiButtonBase"}),{action:r,centerRipple:o=!1,children:i,className:a,component:s="button",disabled:l=!1,disableRipple:u=!1,disableTouchRipple:c=!1,focusRipple:d=!1,LinkComponent:f="a",onBlur:p,onClick:h,onContextMenu:m,onDragLeave:g,onFocus:v,onFocusVisible:y,onKeyDown:b,onKeyUp:w,onMouseDown:E,onMouseLeave:S,onMouseUp:x,onTouchEnd:A,onTouchMove:k,onTouchStart:_,tabIndex:C=0,TouchRippleProps:O,touchRippleRef:P,type:R}=n,T=(0,qn.A)(n,ui),F=B.useRef(null),j=B.useRef(null),N=(0,Qr.A)(j,P),{isFocusVisibleRef:I,onFocus:L,onBlur:M,ref:D}=(0,jo.A)(),[U,z]=B.useState(!1);l&&U&&z(!1),B.useImperativeHandle(r,(()=>({focusVisible:()=>{z(!0),F.current.focus()}})),[]);const[$,H]=B.useState(!1);B.useEffect((()=>{H(!0)}),[]);const W=$&&!u&&!l;function V(e,t,n=c){return(0,Fo.A)((r=>(t&&t(r),!n&&j.current&&j.current[e](r),!0)))}B.useEffect((()=>{U&&d&&!u&&$&&j.current.pulsate()}),[u,d,U,$]);const q=V("start",E),K=V("stop",m),G=V("stop",g),J=V("stop",x),Y=V("stop",(e=>{U&&e.preventDefault(),S&&S(e)})),X=V("start",_),Q=V("stop",A),Z=V("stop",k),ee=V("stop",(e=>{M(e),!1===I.current&&z(!1),p&&p(e)}),!1),te=(0,Fo.A)((e=>{F.current||(F.current=e.currentTarget),L(e),!0===I.current&&(z(!0),y&&y(e)),v&&v(e)})),ne=()=>{const e=F.current;return s&&"button"!==s&&!("A"===e.tagName&&e.href)},re=B.useRef(!1),oe=(0,Fo.A)((e=>{d&&!re.current&&U&&j.current&&" "===e.key&&(re.current=!0,j.current.stop(e,(()=>{j.current.start(e)}))),e.target===e.currentTarget&&ne()&&" "===e.key&&e.preventDefault(),b&&b(e),e.target===e.currentTarget&&ne()&&"Enter"===e.key&&!l&&(e.preventDefault(),h&&h(e))})),ie=(0,Fo.A)((e=>{d&&" "===e.key&&j.current&&U&&!e.defaultPrevented&&(re.current=!1,j.current.stop(e,(()=>{j.current.pulsate(e)}))),w&&w(e),h&&e.target===e.currentTarget&&ne()&&" "===e.key&&!e.defaultPrevented&&h(e)}));let ae=s;"button"===ae&&(T.href||T.to)&&(ae=f);const se={};"button"===ae?(se.type=void 0===R?"button":R,se.disabled=l):(T.href||T.to||(se.role="button"),l&&(se["aria-disabled"]=l));const le=(0,Qr.A)(t,D,F),ue=(0,Jt.A)({},n,{centerRipple:o,component:s,disabled:l,disableRipple:u,disableTouchRipple:c,focusRipple:d,tabIndex:C,focusVisible:U}),ce=(e=>{const{disabled:t,focusVisible:n,focusVisibleClassName:r,classes:o}=e,i={root:["root",t&&"disabled",n&&"focusVisible"]},a=(0,Qn.A)(i,si,o);return n&&r&&(a.root+=` ${r}`),a})(ue);return(0,ir.jsxs)(ci,(0,Jt.A)({as:ae,className:(0,Kn.A)(ce.root,a),ownerState:ue,onBlur:ee,onClick:h,onContextMenu:K,onFocus:te,onKeyDown:oe,onKeyUp:ie,onMouseDown:q,onMouseLeave:Y,onMouseUp:J,onDragLeave:G,onTouchEnd:Q,onTouchMove:Z,onTouchStart:X,ref:le,tabIndex:l?-1:C,type:R},se,T,{children:[i,W?(0,ir.jsx)(ai,(0,Jt.A)({ref:N,center:o},O)):null]}))}));function fi(e){return(0,rr.A)("MuiIconButton",e)}const pi=(0,nr.A)("MuiIconButton",["root","disabled","colorInherit","colorPrimary","colorSecondary","colorError","colorInfo","colorSuccess","colorWarning","edgeStart","edgeEnd","sizeSmall","sizeMedium","sizeLarge"]),hi=["edge","children","className","color","disabled","disableFocusRipple","size"],mi=(0,Zn.Ay)(di,{name:"MuiIconButton",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,"default"!==n.color&&t[`color${(0,tr.A)(n.color)}`],n.edge&&t[`edge${(0,tr.A)(n.edge)}`],t[`size${(0,tr.A)(n.size)}`]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({textAlign:"center",flex:"0 0 auto",fontSize:e.typography.pxToRem(24),padding:8,borderRadius:"50%",overflow:"visible",color:(e.vars||e).palette.action.active,transition:e.transitions.create("background-color",{duration:e.transitions.duration.shortest})},!t.disableRipple&&{"&:hover":{backgroundColor:e.vars?`rgba(${e.vars.palette.action.activeChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette.action.active,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},"start"===t.edge&&{marginLeft:"small"===t.size?-3:-12},"end"===t.edge&&{marginRight:"small"===t.size?-3:-12})),(({theme:e,ownerState:t})=>{var n;const r=null==(n=(e.vars||e).palette)?void 0:n[t.color];return(0,Jt.A)({},"inherit"===t.color&&{color:"inherit"},"inherit"!==t.color&&"default"!==t.color&&(0,Jt.A)({color:null==r?void 0:r.main},!t.disableRipple&&{"&:hover":(0,Jt.A)({},r&&{backgroundColor:e.vars?`rgba(${r.mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(r.main,e.palette.action.hoverOpacity)},{"@media (hover: none)":{backgroundColor:"transparent"}})}),"small"===t.size&&{padding:5,fontSize:e.typography.pxToRem(18)},"large"===t.size&&{padding:12,fontSize:e.typography.pxToRem(28)},{[`&.${pi.disabled}`]:{backgroundColor:"transparent",color:(e.vars||e).palette.action.disabled}})})),gi=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiIconButton"}),{edge:r=!1,children:o,className:i,color:a="default",disabled:s=!1,disableFocusRipple:l=!1,size:u="medium"}=n,c=(0,qn.A)(n,hi),d=(0,Jt.A)({},n,{edge:r,color:a,disabled:s,disableFocusRipple:l,size:u}),f=(e=>{const{classes:t,disabled:n,color:r,edge:o,size:i}=e,a={root:["root",n&&"disabled","default"!==r&&`color${(0,tr.A)(r)}`,o&&`edge${(0,tr.A)(o)}`,`size${(0,tr.A)(i)}`]};return(0,Qn.A)(a,fi,t)})(d);return(0,ir.jsx)(mi,(0,Jt.A)({className:(0,Kn.A)(f.root,i),centerRipple:!0,focusRipple:!l,disabled:s,ref:t,ownerState:d},c,{children:o}))})),vi=B.createContext({});function yi(e){return(0,rr.A)("MuiList",e)}(0,nr.A)("MuiList",["root","padding","dense","subheader"]);const bi=["children","className","component","dense","disablePadding","subheader"],wi=(0,Zn.Ay)("ul",{name:"MuiList",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,!n.disablePadding&&t.padding,n.dense&&t.dense,n.subheader&&t.subheader]}})((({ownerState:e})=>(0,Jt.A)({listStyle:"none",margin:0,padding:0,position:"relative"},!e.disablePadding&&{paddingTop:8,paddingBottom:8},e.subheader&&{paddingTop:0}))),Ei=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiList"}),{children:r,className:o,component:i="ul",dense:a=!1,disablePadding:s=!1,subheader:l}=n,u=(0,qn.A)(n,bi),c=B.useMemo((()=>({dense:a})),[a]),d=(0,Jt.A)({},n,{component:i,dense:a,disablePadding:s}),f=(e=>{const{classes:t,disablePadding:n,dense:r,subheader:o}=e,i={root:["root",!n&&"padding",r&&"dense",o&&"subheader"]};return(0,Qn.A)(i,yi,t)})(d);return(0,ir.jsx)(vi.Provider,{value:c,children:(0,ir.jsxs)(wi,(0,Jt.A)({as:i,className:(0,Kn.A)(f.root,o),ref:t,ownerState:d},u,{children:[l,r]}))})}));var Si=o(6288),xi=o(2778);function Ai(e){return(0,rr.A)("MuiListItem",e)}const ki=(0,nr.A)("MuiListItem",["root","container","focusVisible","dense","alignItemsFlexStart","disabled","divider","gutters","padding","button","secondaryAction","selected"]),_i=(0,nr.A)("MuiListItemButton",["root","focusVisible","dense","alignItemsFlexStart","disabled","divider","gutters","selected"]);function Ci(e){return(0,rr.A)("MuiListItemSecondaryAction",e)}(0,nr.A)("MuiListItemSecondaryAction",["root","disableGutters"]);const Oi=["className"],Pi=(0,Zn.Ay)("div",{name:"MuiListItemSecondaryAction",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.disableGutters&&t.disableGutters]}})((({ownerState:e})=>(0,Jt.A)({position:"absolute",right:16,top:"50%",transform:"translateY(-50%)"},e.disableGutters&&{right:0}))),Ri=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiListItemSecondaryAction"}),{className:r}=n,o=(0,qn.A)(n,Oi),i=B.useContext(vi),a=(0,Jt.A)({},n,{disableGutters:i.disableGutters}),s=(e=>{const{disableGutters:t,classes:n}=e,r={root:["root",t&&"disableGutters"]};return(0,Qn.A)(r,Ci,n)})(a);return(0,ir.jsx)(Pi,(0,Jt.A)({className:(0,Kn.A)(s.root,r),ownerState:a,ref:t},o))}));Ri.muiName="ListItemSecondaryAction";const Ti=Ri,Fi=["className"],ji=["alignItems","autoFocus","button","children","className","component","components","componentsProps","ContainerComponent","ContainerProps","dense","disabled","disableGutters","disablePadding","divider","focusVisibleClassName","secondaryAction","selected","slotProps","slots"],Ni=(0,Zn.Ay)("div",{name:"MuiListItem",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.dense&&t.dense,"flex-start"===n.alignItems&&t.alignItemsFlexStart,n.divider&&t.divider,!n.disableGutters&&t.gutters,!n.disablePadding&&t.padding,n.button&&t.button,n.hasSecondaryAction&&t.secondaryAction]}})((({theme:e,ownerState:t})=>(0,Jt.A)({display:"flex",justifyContent:"flex-start",alignItems:"center",position:"relative",textDecoration:"none",width:"100%",boxSizing:"border-box",textAlign:"left"},!t.disablePadding&&(0,Jt.A)({paddingTop:8,paddingBottom:8},t.dense&&{paddingTop:4,paddingBottom:4},!t.disableGutters&&{paddingLeft:16,paddingRight:16},!!t.secondaryAction&&{paddingRight:48}),!!t.secondaryAction&&{[`& > .${_i.root}`]:{paddingRight:48}},{[`&.${ki.focusVisible}`]:{backgroundColor:(e.vars||e).palette.action.focus},[`&.${ki.selected}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity),[`&.${ki.focusVisible}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.focusOpacity}))`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity+e.palette.action.focusOpacity)}},[`&.${ki.disabled}`]:{opacity:(e.vars||e).palette.action.disabledOpacity}},"flex-start"===t.alignItems&&{alignItems:"flex-start"},t.divider&&{borderBottom:`1px solid ${(e.vars||e).palette.divider}`,backgroundClip:"padding-box"},t.button&&{transition:e.transitions.create("background-color",{duration:e.transitions.duration.shortest}),"&:hover":{textDecoration:"none",backgroundColor:(e.vars||e).palette.action.hover,"@media (hover: none)":{backgroundColor:"transparent"}},[`&.${ki.selected}:hover`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.hoverOpacity}))`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity+e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity)}}},t.hasSecondaryAction&&{paddingRight:48}))),Ii=(0,Zn.Ay)("li",{name:"MuiListItem",slot:"Container",overridesResolver:(e,t)=>t.container})({position:"relative"}),Li=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiListItem"}),{alignItems:r="center",autoFocus:o=!1,button:i=!1,children:a,className:s,component:l,components:u={},componentsProps:c={},ContainerComponent:d="li",ContainerProps:{className:f}={},dense:p=!1,disabled:h=!1,disableGutters:m=!1,disablePadding:g=!1,divider:v=!1,focusVisibleClassName:y,secondaryAction:b,selected:w=!1,slotProps:E={},slots:S={}}=n,x=(0,qn.A)(n.ContainerProps,Fi),A=(0,qn.A)(n,ji),k=B.useContext(vi),_=B.useMemo((()=>({dense:p||k.dense||!1,alignItems:r,disableGutters:m})),[r,k.dense,p,m]),C=B.useRef(null);(0,xi.A)((()=>{o&&C.current&&C.current.focus()}),[o]);const O=B.Children.toArray(a),P=O.length&&(0,Si.A)(O[O.length-1],["ListItemSecondaryAction"]),R=(0,Jt.A)({},n,{alignItems:r,autoFocus:o,button:i,dense:_.dense,disabled:h,disableGutters:m,disablePadding:g,divider:v,hasSecondaryAction:P,selected:w}),T=(e=>{const{alignItems:t,button:n,classes:r,dense:o,disabled:i,disableGutters:a,disablePadding:s,divider:l,hasSecondaryAction:u,selected:c}=e,d={root:["root",o&&"dense",!a&&"gutters",!s&&"padding",l&&"divider",i&&"disabled",n&&"button","flex-start"===t&&"alignItemsFlexStart",u&&"secondaryAction",c&&"selected"],container:["container"]};return(0,Qn.A)(d,Ai,r)})(R),F=(0,Qr.A)(C,t),j=S.root||u.Root||Ni,N=E.root||c.root||{},I=(0,Jt.A)({className:(0,Kn.A)(T.root,N.className,s),disabled:h},A);let L=l||"li";return i&&(I.component=l||"div",I.focusVisibleClassName=(0,Kn.A)(ki.focusVisible,y),L=di),P?(L=I.component||l?L:"div","li"===d&&("li"===L?L="div":"li"===I.component&&(I.component="div")),(0,ir.jsx)(vi.Provider,{value:_,children:(0,ir.jsxs)(Ii,(0,Jt.A)({as:d,className:(0,Kn.A)(T.container,f),ref:F,ownerState:R},x,{children:[(0,ir.jsx)(j,(0,Jt.A)({},N,!fr(j)&&{as:L,ownerState:(0,Jt.A)({},R,N.ownerState)},I,{children:O})),O.pop()]}))})):(0,ir.jsx)(vi.Provider,{value:_,children:(0,ir.jsxs)(j,(0,Jt.A)({},N,{as:L,ref:F},!fr(j)&&{ownerState:(0,Jt.A)({},R,N.ownerState)},I,{children:[O,b&&(0,ir.jsx)(Ti,{children:b})]}))})}));function Mi(e){return(0,rr.A)("MuiListItemIcon",e)}const Di=(0,nr.A)("MuiListItemIcon",["root","alignItemsFlexStart"]),Bi=["className"],Ui=(0,Zn.Ay)("div",{name:"MuiListItemIcon",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,"flex-start"===n.alignItems&&t.alignItemsFlexStart]}})((({theme:e,ownerState:t})=>(0,Jt.A)({minWidth:56,color:(e.vars||e).palette.action.active,flexShrink:0,display:"inline-flex"},"flex-start"===t.alignItems&&{marginTop:8}))),zi=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiListItemIcon"}),{className:r}=n,o=(0,qn.A)(n,Bi),i=B.useContext(vi),a=(0,Jt.A)({},n,{alignItems:i.alignItems}),s=(e=>{const{alignItems:t,classes:n}=e,r={root:["root","flex-start"===t&&"alignItemsFlexStart"]};return(0,Qn.A)(r,Mi,n)})(a);return(0,ir.jsx)(Ui,(0,Jt.A)({className:(0,Kn.A)(s.root,r),ownerState:a,ref:t},o))}));function $i(e){return(0,rr.A)("MuiListItemText",e)}const Hi=(0,nr.A)("MuiListItemText",["root","multiline","dense","inset","primary","secondary"]),Wi=["children","className","disableTypography","inset","primary","primaryTypographyProps","secondary","secondaryTypographyProps"],Vi=(0,Zn.Ay)("div",{name:"MuiListItemText",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`& .${Hi.primary}`]:t.primary},{[`& .${Hi.secondary}`]:t.secondary},t.root,n.inset&&t.inset,n.primary&&n.secondary&&t.multiline,n.dense&&t.dense]}})((({ownerState:e})=>(0,Jt.A)({flex:"1 1 auto",minWidth:0,marginTop:4,marginBottom:4},e.primary&&e.secondary&&{marginTop:6,marginBottom:6},e.inset&&{paddingLeft:56}))),qi=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiListItemText"}),{children:r,className:o,disableTypography:i=!1,inset:a=!1,primary:s,primaryTypographyProps:l,secondary:u,secondaryTypographyProps:c}=n,d=(0,qn.A)(n,Wi),{dense:f}=B.useContext(vi);let p=null!=s?s:r,h=u;const m=(0,Jt.A)({},n,{disableTypography:i,inset:a,primary:!!p,secondary:!!h,dense:f}),g=(e=>{const{classes:t,inset:n,primary:r,secondary:o,dense:i}=e,a={root:["root",n&&"inset",i&&"dense",r&&o&&"multiline"],primary:["primary"],secondary:["secondary"]};return(0,Qn.A)(a,$i,t)})(m);return null==p||p.type===cr||i||(p=(0,ir.jsx)(cr,(0,Jt.A)({variant:f?"body2":"body1",className:g.primary,component:null!=l&&l.variant?void 0:"span",display:"block"},l,{children:p}))),null==h||h.type===cr||i||(h=(0,ir.jsx)(cr,(0,Jt.A)({variant:"body2",className:g.secondary,color:"text.secondary",display:"block"},c,{children:h}))),(0,ir.jsxs)(Vi,(0,Jt.A)({className:(0,Kn.A)(g.root,o),ownerState:m,ref:t},d,{children:[p,h]}))}));var Ki=o(3341);const Gi=e=>{const t=B.useRef({});return B.useEffect((()=>{t.current=e})),t.current};function Ji(e){return(0,rr.A)("MuiBadge",e)}const Yi=(0,nr.A)("MuiBadge",["root","badge","dot","standard","anchorOriginTopRight","anchorOriginBottomRight","anchorOriginTopLeft","anchorOriginBottomLeft","invisible","colorError","colorInfo","colorPrimary","colorSecondary","colorSuccess","colorWarning","overlapRectangular","overlapCircular","anchorOriginTopLeftCircular","anchorOriginTopLeftRectangular","anchorOriginTopRightCircular","anchorOriginTopRightRectangular","anchorOriginBottomLeftCircular","anchorOriginBottomLeftRectangular","anchorOriginBottomRightCircular","anchorOriginBottomRightRectangular"]),Xi=["anchorOrigin","className","classes","component","components","componentsProps","children","overlap","color","invisible","max","badgeContent","slots","slotProps","showZero","variant"],Qi=(0,Zn.Ay)("span",{name:"MuiBadge",slot:"Root",overridesResolver:(e,t)=>t.root})({position:"relative",display:"inline-flex",verticalAlign:"middle",flexShrink:0}),Zi=(0,Zn.Ay)("span",{name:"MuiBadge",slot:"Badge",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.badge,t[n.variant],t[`anchorOrigin${(0,tr.A)(n.anchorOrigin.vertical)}${(0,tr.A)(n.anchorOrigin.horizontal)}${(0,tr.A)(n.overlap)}`],"default"!==n.color&&t[`color${(0,tr.A)(n.color)}`],n.invisible&&t.invisible]}})((({theme:e})=>{var t;return{display:"flex",flexDirection:"row",flexWrap:"wrap",justifyContent:"center",alignContent:"center",alignItems:"center",position:"absolute",boxSizing:"border-box",fontFamily:e.typography.fontFamily,fontWeight:e.typography.fontWeightMedium,fontSize:e.typography.pxToRem(12),minWidth:20,lineHeight:1,padding:"0 6px",height:20,borderRadius:10,zIndex:1,transition:e.transitions.create("transform",{easing:e.transitions.easing.easeInOut,duration:e.transitions.duration.enteringScreen}),variants:[...Object.keys((null!=(t=e.vars)?t:e).palette).filter((t=>{var n,r;return(null!=(n=e.vars)?n:e).palette[t].main&&(null!=(r=e.vars)?r:e).palette[t].contrastText})).map((t=>({props:{color:t},style:{backgroundColor:(e.vars||e).palette[t].main,color:(e.vars||e).palette[t].contrastText}}))),{props:{variant:"dot"},style:{borderRadius:4,height:8,minWidth:8,padding:0}},{props:({ownerState:e})=>"top"===e.anchorOrigin.vertical&&"right"===e.anchorOrigin.horizontal&&"rectangular"===e.overlap,style:{top:0,right:0,transform:"scale(1) translate(50%, -50%)",transformOrigin:"100% 0%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(50%, -50%)"}}},{props:({ownerState:e})=>"bottom"===e.anchorOrigin.vertical&&"right"===e.anchorOrigin.horizontal&&"rectangular"===e.overlap,style:{bottom:0,right:0,transform:"scale(1) translate(50%, 50%)",transformOrigin:"100% 100%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(50%, 50%)"}}},{props:({ownerState:e})=>"top"===e.anchorOrigin.vertical&&"left"===e.anchorOrigin.horizontal&&"rectangular"===e.overlap,style:{top:0,left:0,transform:"scale(1) translate(-50%, -50%)",transformOrigin:"0% 0%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(-50%, -50%)"}}},{props:({ownerState:e})=>"bottom"===e.anchorOrigin.vertical&&"left"===e.anchorOrigin.horizontal&&"rectangular"===e.overlap,style:{bottom:0,left:0,transform:"scale(1) translate(-50%, 50%)",transformOrigin:"0% 100%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(-50%, 50%)"}}},{props:({ownerState:e})=>"top"===e.anchorOrigin.vertical&&"right"===e.anchorOrigin.horizontal&&"circular"===e.overlap,style:{top:"14%",right:"14%",transform:"scale(1) translate(50%, -50%)",transformOrigin:"100% 0%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(50%, -50%)"}}},{props:({ownerState:e})=>"bottom"===e.anchorOrigin.vertical&&"right"===e.anchorOrigin.horizontal&&"circular"===e.overlap,style:{bottom:"14%",right:"14%",transform:"scale(1) translate(50%, 50%)",transformOrigin:"100% 100%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(50%, 50%)"}}},{props:({ownerState:e})=>"top"===e.anchorOrigin.vertical&&"left"===e.anchorOrigin.horizontal&&"circular"===e.overlap,style:{top:"14%",left:"14%",transform:"scale(1) translate(-50%, -50%)",transformOrigin:"0% 0%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(-50%, -50%)"}}},{props:({ownerState:e})=>"bottom"===e.anchorOrigin.vertical&&"left"===e.anchorOrigin.horizontal&&"circular"===e.overlap,style:{bottom:"14%",left:"14%",transform:"scale(1) translate(-50%, 50%)",transformOrigin:"0% 100%",[`&.${Yi.invisible}`]:{transform:"scale(0) translate(-50%, 50%)"}}},{props:{invisible:!0},style:{transition:e.transitions.create("transform",{easing:e.transitions.easing.easeInOut,duration:e.transitions.duration.leavingScreen})}}]}})),ea=B.forwardRef((function(e,t){var n,r,o,i,a,s;const l=(0,er.A)({props:e,name:"MuiBadge"}),{anchorOrigin:u={vertical:"top",horizontal:"right"},className:c,component:d,components:f={},componentsProps:p={},children:h,overlap:m="rectangular",color:g="default",invisible:v=!1,max:y=99,badgeContent:b,slots:w,slotProps:E,showZero:S=!1,variant:x="standard"}=l,A=(0,qn.A)(l,Xi),{badgeContent:k,invisible:_,max:C,displayValue:O}=function(e){const{badgeContent:t,invisible:n=!1,max:r=99,showZero:o=!1}=e,i=Gi({badgeContent:t,max:r});let a=n;!1!==n||0!==t||o||(a=!0);const{badgeContent:s,max:l=r}=a?i:e;return{badgeContent:s,invisible:a,max:l,displayValue:s&&Number(s)>l?`${l}+`:s}}({max:y,invisible:v,badgeContent:b,showZero:S}),P=Gi({anchorOrigin:u,color:g,overlap:m,variant:x,badgeContent:b}),R=_||null==k&&"dot"!==x,{color:T=g,overlap:F=m,anchorOrigin:j=u,variant:N=x}=R?P:l,I="dot"!==N?O:void 0,L=(0,Jt.A)({},l,{badgeContent:k,invisible:R,max:C,displayValue:I,showZero:S,anchorOrigin:j,color:T,overlap:F,variant:N}),M=(e=>{const{color:t,anchorOrigin:n,invisible:r,overlap:o,variant:i,classes:a={}}=e,s={root:["root"],badge:["badge",i,r&&"invisible",`anchorOrigin${(0,tr.A)(n.vertical)}${(0,tr.A)(n.horizontal)}`,`anchorOrigin${(0,tr.A)(n.vertical)}${(0,tr.A)(n.horizontal)}${(0,tr.A)(o)}`,`overlap${(0,tr.A)(o)}`,"default"!==t&&`color${(0,tr.A)(t)}`]};return(0,Qn.A)(s,Ji,a)})(L),D=null!=(n=null!=(r=null==w?void 0:w.root)?r:f.Root)?n:Qi,B=null!=(o=null!=(i=null==w?void 0:w.badge)?i:f.Badge)?o:Zi,U=null!=(a=null==E?void 0:E.root)?a:p.root,z=null!=(s=null==E?void 0:E.badge)?s:p.badge,$=vr({elementType:D,externalSlotProps:U,externalForwardedProps:A,additionalProps:{ref:t,as:d},ownerState:L,className:(0,Kn.A)(null==U?void 0:U.className,M.root,c)}),H=vr({elementType:B,externalSlotProps:z,ownerState:L,className:(0,Kn.A)(M.badge,null==z?void 0:z.className)});return(0,ir.jsxs)(D,(0,Jt.A)({},$,{children:[h,(0,ir.jsx)(B,(0,Jt.A)({},H,{children:I}))]}))}));var ta=o(7034),na=o(684);function ra(e,t){if(void 0===e.inserted[t.name])return e.insert("",t,e.sheet,!0)}function oa(e,t,n){var r=[],o=(0,zo.Rk)(e,r,n);return r.length<2?n:o+t(r)}var ia=function e(t){for(var n="",r=0;r<t.length;r++){var o=t[r];if(null!=o){var i=void 0;switch(typeof o){case"boolean":break;case"object":if(Array.isArray(o))i=e(o);else for(var a in i="",o)o[a]&&a&&(i&&(i+=" "),i+=a);break;default:i=o}i&&(n&&(n+=" "),n+=i)}}return n},aa=function(){var e=(0,Wo.A)({key:"css"});e.sheet.speedy=function(e){this.isSpeedy=e},e.compat=!0;var t=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];var o=(0,Ho.J)(n,e.registered,void 0);return(0,zo.sk)(e,o,!1),e.key+"-"+o.name};return{css:t,cx:function(){for(var n=arguments.length,r=new Array(n),o=0;o<n;o++)r[o]=arguments[o];return oa(e.registered,t,ia(r))},injectGlobal:function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];var o=(0,Ho.J)(n,e.registered);ra(e,o)},keyframes:function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];var o=(0,Ho.J)(n,e.registered),i="animation-"+o.name;return ra(e,{name:o.name,styles:"@keyframes "+i+"{"+o.styles+"}"}),i},hydrate:function(t){t.forEach((function(t){e.inserted[t]=!0}))},flush:function(){e.registered={},e.inserted={},e.sheet.flush()},sheet:e.sheet,cache:e,getRegisteredStyles:zo.Rk.bind(null,e.registered),merge:oa.bind(null,e.registered,t)}}(),sa=(aa.flush,aa.hydrate,aa.cx,aa.merge,aa.getRegisteredStyles,aa.injectGlobal,aa.keyframes,aa.css);aa.sheet,aa.cache;const la=function(e){var t=(0,Uo.u)();return(0,B.useMemo)((function(){var n="function"==typeof e?e(t):e,r={};return Object.entries(n).forEach((function(e){var t=fe(e,2),n=t[0],o=t[1],i=void 0===o?{}:o;r[n]=sa(i)})),r}),[e,t])};function ua(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var ca={closeButton:{alignSelf:"end"},list:{width:250},fullList:{width:"auto"}};function da(e){var t=fe(ve(),2),n=t[0].alerts;return t[1],n?B.createElement(Ei,null,n.map((function(e,t){return B.createElement(fa,{key:t,data:e,i:t})}))):B.createElement(Box,null,"No notifications")}function fa(e){var t=e.data,n=e.i,r=fe(ve(),2);ne(r[0]);var o=r[1];return B.createElement(Li,null,B.createElement(zi,null,B.createElement(Ki.A,{color:t.color})),B.createElement(qi,{primary:B.createElement(cr,{component:"span",variant:"caption"},t.date.toLocaleString()),secondary:t.body,secondaryTypographyProps:{component:"div"}}),B.createElement(Ti,null,B.createElement(gi,{edge:"end","aria-label":"Delete",onClick:function(e){return function(t){o({type:"closeAlert",i:e})}}(n)},B.createElement(ta.A,null))))}const pa=function(e){var t=fe(ve(),2),n=t[0].alerts,r=(t[1],fe(B.useState(!1),2)),o=r[0],i=r[1],a=la(ca);if(!n)return null;var s,l="primary",u=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return ua(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?ua(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(n);try{for(u.s();!(s=u.n()).done;)if("secondary"==s.value.color&&"primary"==l){l="secondary";break}}catch(e){u.e(e)}finally{u.f()}var c=function(e){return function(t){("keydown"!==t.type||"Tab"!==t.key&&"Shift"!==t.key)&&i(e)}};return B.createElement(B.Fragment,null,B.createElement(gi,{color:"inherit",onClick:c(!0)},B.createElement(ea,{max:9,badgeContent:n.length,color:l},B.createElement(Ki.A,null))),B.createElement(To,{anchor:"right",open:o,onClose:c(!1)},B.createElement(gi,{className:a.closeButton,edge:"end","aria-label":"Close",onClick:c(!1)},B.createElement(na.A,null)),B.createElement(da,null)))};var ha=function(e){return{optimal:{backgroundColor:e.status.up},danger:{backgroundColor:e.status.danger},warning:{backgroundColor:e.status.warning},"n/a":{backgroundColor:e.status.notapplicable}}},ma=function(e){return{running:{color:e.status.up},up:{color:e.status.up},"stdby up":{color:e.status.up},danger:{color:e.status.danger},down:{color:e.status.danger},stopped:{color:e.status.danger},"stdby down":{color:e.status.danger},warning:{color:e.status.warning},warn:{color:e.status.warning},"n/a":{color:e.status.notapplicable},undef:{color:e.status.notapplicable}}},ga=o(3544),va=o(790),ya=o(22),ba=o(9898),wa=o(8984),Ea=o(2162);const Sa=function(e){var t=e.path,n=e.kind,r=e.avail,o=la(ma);if(n)var i=n;else i=kn(t).kind;return function(e,t){return{svc:B.createElement(ga.A,{className:t}),vol:B.createElement(wa.A,{className:t}),cfg:B.createElement(ya.A,{className:t}),sec:B.createElement(ba.A,{className:t}),usr:B.createElement(va.A,{className:t})}[e]||B.createElement(Ea.A,{className:t})}(i,o[r])};var xa=o(9375),Aa=o(634),ka=o(7874),_a=o(9648),Ca=o(7888),Oa=o(8971),Pa=o(3289),Ra=o(4680),Ta=function(e){return{nested:{paddingLeft:e.spacing(4)}}};function Fa(e){var t=la(ma),n=ee(),r=n.t;return n.i18n,B.createElement(Li,{button:!0,onClick:function(t){e.onClick(),e.closeDrawer(t)},className:e.className},B.createElement(zi,{className:t[e.issue.name]},e.icon),B.createElement(qi,{primary:r(e.title)}))}function ja(e){var t=fe(ve(),2),n=t[0].cstat,r=(t[1],lt()),o=la(Ta);return B.createElement(Ei,null,B.createElement(Fa,{issue:Vn(n),href:"#cluster",title:"Cluster",icon:B.createElement(Oa.A,null),onClick:function(){return r("/")},closeDrawer:e.closeDrawer}),B.createElement(Fa,{issue:Mn(n),href:"#threads",title:"Threads",icon:B.createElement(xa.A,null),onClick:function(){return r("/threads")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:Bn(n),href:"#heartbeats",title:"Heartbeats",icon:B.createElement(Aa.A,null),onClick:function(){return r("/heartbeats")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:Dn(n),href:"#arbitrators",title:"Arbitrators",icon:B.createElement(ka.A,null),onClick:function(){return r("/arbitrators")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:Wn(n),href:"#nodes",title:"Nodes",icon:B.createElement(_a.A,null),onClick:function(){return r("/nodes")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#pools",title:"Pools",icon:B.createElement(wa.A,null),onClick:function(){return r("/pools")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#networks",title:"Networks",icon:B.createElement(Pa.A,null),onClick:function(){return r("/networks")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:Hn(n),href:"#objects",title:"Objects",icon:B.createElement(Ca.A,null),onClick:function(){return r("/objects")},closeDrawer:e.closeDrawer}),B.createElement(Fa,{issue:Hn(n,"svc"),href:"#svc",title:"Services",icon:B.createElement(Sa,{kind:"svc"}),onClick:function(){return r("/services")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:Hn(n,"vol"),href:"#vol",title:"Volumes",icon:B.createElement(Sa,{kind:"vol"}),onClick:function(){return r("/volumes")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#cfg",title:"Configs",icon:B.createElement(Sa,{kind:"cfg"}),onClick:function(){return r("/configs")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#sec",title:"Secrets",icon:B.createElement(Sa,{kind:"sec"}),onClick:function(){return r("/secrets")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#usr",title:"Users",icon:B.createElement(Sa,{kind:"usr"}),onClick:function(){return r("/users")},closeDrawer:e.closeDrawer,className:o.nested}),B.createElement(Fa,{issue:xn.OPTIMAL,href:"#api",title:"Api",icon:B.createElement(Ra.A,null),onClick:function(){return r("/api")},closeDrawer:e.closeDrawer}))}o(4363);var Na=o(561);const Ia=(0,Na.A)((0,ir.jsx)("path",{d:"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"}),"MoreHoriz"),La=["slots","slotProps"],Ma=(0,Zn.Ay)(di)((({theme:e})=>(0,Jt.A)({display:"flex",marginLeft:`calc(${e.spacing(1)} * 0.5)`,marginRight:`calc(${e.spacing(1)} * 0.5)`},"light"===e.palette.mode?{backgroundColor:e.palette.grey[100],color:e.palette.grey[700]}:{backgroundColor:e.palette.grey[700],color:e.palette.grey[100]},{borderRadius:2,"&:hover, &:focus":(0,Jt.A)({},"light"===e.palette.mode?{backgroundColor:e.palette.grey[200]}:{backgroundColor:e.palette.grey[600]}),"&:active":(0,Jt.A)({boxShadow:e.shadows[0]},"light"===e.palette.mode?{backgroundColor:(0,vo.tL)(e.palette.grey[200],.12)}:{backgroundColor:(0,vo.tL)(e.palette.grey[600],.12)})}))),Da=(0,Zn.Ay)(Ia)({width:24,height:16}),Ba=function(e){const{slots:t={},slotProps:n={}}=e,r=(0,qn.A)(e,La),o=e;return(0,ir.jsx)("li",{children:(0,ir.jsx)(Ma,(0,Jt.A)({focusRipple:!0},r,{ownerState:o,children:(0,ir.jsx)(Da,(0,Jt.A)({as:t.CollapsedIcon,ownerState:o},n.collapsedIcon))}))})};function Ua(e){return(0,rr.A)("MuiBreadcrumbs",e)}const za=(0,nr.A)("MuiBreadcrumbs",["root","ol","li","separator"]),$a=["children","className","component","slots","slotProps","expandText","itemsAfterCollapse","itemsBeforeCollapse","maxItems","separator"],Ha=(0,Zn.Ay)(cr,{name:"MuiBreadcrumbs",slot:"Root",overridesResolver:(e,t)=>[{[`& .${za.li}`]:t.li},t.root]})({}),Wa=(0,Zn.Ay)("ol",{name:"MuiBreadcrumbs",slot:"Ol",overridesResolver:(e,t)=>t.ol})({display:"flex",flexWrap:"wrap",alignItems:"center",padding:0,margin:0,listStyle:"none"}),Va=(0,Zn.Ay)("li",{name:"MuiBreadcrumbs",slot:"Separator",overridesResolver:(e,t)=>t.separator})({display:"flex",userSelect:"none",marginLeft:8,marginRight:8});function qa(e,t,n,r){return e.reduce(((o,i,a)=>(a<e.length-1?o=o.concat(i,(0,ir.jsx)(Va,{"aria-hidden":!0,className:t,ownerState:r,children:n},`separator-${a}`)):o.push(i),o)),[])}const Ka=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiBreadcrumbs"}),{children:r,className:o,component:i="nav",slots:a={},slotProps:s={},expandText:l="Show path",itemsAfterCollapse:u=1,itemsBeforeCollapse:c=1,maxItems:d=8,separator:f="/"}=n,p=(0,qn.A)(n,$a),[h,m]=B.useState(!1),g=(0,Jt.A)({},n,{component:i,expanded:h,expandText:l,itemsAfterCollapse:u,itemsBeforeCollapse:c,maxItems:d,separator:f}),v=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"],li:["li"],ol:["ol"],separator:["separator"]},Ua,t)})(g),y=vr({elementType:a.CollapsedIcon,externalSlotProps:s.collapsedIcon,ownerState:g}),b=B.useRef(null),w=B.Children.toArray(r).filter((e=>B.isValidElement(e))).map(((e,t)=>(0,ir.jsx)("li",{className:v.li,children:e},`child-${t}`)));return(0,ir.jsx)(Ha,(0,Jt.A)({ref:t,component:i,color:"text.secondary",className:(0,Kn.A)(v.root,o),ownerState:g},p,{children:(0,ir.jsx)(Wa,{className:v.ol,ref:b,ownerState:g,children:qa(h||d&&w.length<=d?w:(e=>c+u>=e.length?e:[...e.slice(0,c),(0,ir.jsx)(Ba,{"aria-label":l,slots:{CollapsedIcon:a.CollapsedIcon},slotProps:{collapsedIcon:y},onClick:()=>{m(!0);const e=b.current.querySelector("a[href],button,[tabindex]");e&&e.focus()}},"ellipsis"),...e.slice(e.length-u,e.length)])(w),v.separator,f,g)})}))}));var Ga=o(8727);function Ja(e){return(0,rr.A)("MuiToolbar",e)}(0,nr.A)("MuiToolbar",["root","gutters","regular","dense"]);const Ya=["className","component","disableGutters","variant"],Xa=(0,Zn.Ay)("div",{name:"MuiToolbar",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,!n.disableGutters&&t.gutters,t[n.variant]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({position:"relative",display:"flex",alignItems:"center"},!t.disableGutters&&{paddingLeft:e.spacing(2),paddingRight:e.spacing(2),[e.breakpoints.up("sm")]:{paddingLeft:e.spacing(3),paddingRight:e.spacing(3)}},"dense"===t.variant&&{minHeight:48})),(({theme:e,ownerState:t})=>"regular"===t.variant&&e.mixins.toolbar)),Qa=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiToolbar"}),{className:r,component:o="div",disableGutters:i=!1,variant:a="regular"}=n,s=(0,qn.A)(n,Ya),l=(0,Jt.A)({},n,{component:o,disableGutters:i,variant:a}),u=(e=>{const{classes:t,disableGutters:n,variant:r}=e,o={root:["root",!n&&"gutters",r]};return(0,Qn.A)(o,Ja,t)})(l);return(0,ir.jsx)(Xa,(0,Jt.A)({as:o,className:(0,Kn.A)(u.root,r),ref:t,ownerState:l},s))})),Za=(0,Na.A)((0,ir.jsx)("path",{d:"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"}),"Person");function es(e){return(0,rr.A)("MuiAvatar",e)}(0,nr.A)("MuiAvatar",["root","colorDefault","circular","rounded","square","img","fallback"]);const ts=["alt","children","className","component","imgProps","sizes","src","srcSet","variant"],ns=(0,Zn.Ay)("div",{name:"MuiAvatar",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],n.colorDefault&&t.colorDefault]}})((({theme:e,ownerState:t})=>(0,Jt.A)({position:"relative",display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,width:40,height:40,fontFamily:e.typography.fontFamily,fontSize:e.typography.pxToRem(20),lineHeight:1,borderRadius:"50%",overflow:"hidden",userSelect:"none"},"rounded"===t.variant&&{borderRadius:(e.vars||e).shape.borderRadius},"square"===t.variant&&{borderRadius:0},t.colorDefault&&(0,Jt.A)({color:(e.vars||e).palette.background.default},e.vars?{backgroundColor:e.vars.palette.Avatar.defaultBg}:{backgroundColor:"light"===e.palette.mode?e.palette.grey[400]:e.palette.grey[600]})))),rs=(0,Zn.Ay)("img",{name:"MuiAvatar",slot:"Img",overridesResolver:(e,t)=>t.img})({width:"100%",height:"100%",textAlign:"center",objectFit:"cover",color:"transparent",textIndent:1e4}),os=(0,Zn.Ay)(Za,{name:"MuiAvatar",slot:"Fallback",overridesResolver:(e,t)=>t.fallback})({width:"75%",height:"75%"}),is=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiAvatar"}),{alt:r,children:o,className:i,component:a="div",imgProps:s,sizes:l,src:u,srcSet:c,variant:d="circular"}=n,f=(0,qn.A)(n,ts);let p=null;const h=function({crossOrigin:e,referrerPolicy:t,src:n,srcSet:r}){const[o,i]=B.useState(!1);return B.useEffect((()=>{if(!n&&!r)return;i(!1);let o=!0;const a=new Image;return a.onload=()=>{o&&i("loaded")},a.onerror=()=>{o&&i("error")},a.crossOrigin=e,a.referrerPolicy=t,a.src=n,r&&(a.srcset=r),()=>{o=!1}}),[e,t,n,r]),o}((0,Jt.A)({},s,{src:u,srcSet:c})),m=u||c,g=m&&"error"!==h,v=(0,Jt.A)({},n,{colorDefault:!g,component:a,variant:d}),y=(e=>{const{classes:t,variant:n,colorDefault:r}=e,o={root:["root",n,r&&"colorDefault"],img:["img"],fallback:["fallback"]};return(0,Qn.A)(o,es,t)})(v);return p=g?(0,ir.jsx)(rs,(0,Jt.A)({alt:r,srcSet:c,src:u,sizes:l,ownerState:v,className:y.img},s)):null!=o?o:m&&r?r[0]:(0,ir.jsx)(os,{ownerState:v,className:y.fallback}),(0,ir.jsx)(ns,(0,Jt.A)({as:a,ownerState:v,className:(0,Kn.A)(y.root,i),ref:t},f,{children:p}))})),as=(0,Na.A)((0,ir.jsx)("path",{d:"M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"}),"Cancel");function ss(e){return(0,rr.A)("MuiChip",e)}const ls=(0,nr.A)("MuiChip",["root","sizeSmall","sizeMedium","colorError","colorInfo","colorPrimary","colorSecondary","colorSuccess","colorWarning","disabled","clickable","clickableColorPrimary","clickableColorSecondary","deletable","deletableColorPrimary","deletableColorSecondary","outlined","filled","outlinedPrimary","outlinedSecondary","filledPrimary","filledSecondary","avatar","avatarSmall","avatarMedium","avatarColorPrimary","avatarColorSecondary","icon","iconSmall","iconMedium","iconColorPrimary","iconColorSecondary","label","labelSmall","labelMedium","deleteIcon","deleteIconSmall","deleteIconMedium","deleteIconColorPrimary","deleteIconColorSecondary","deleteIconOutlinedColorPrimary","deleteIconOutlinedColorSecondary","deleteIconFilledColorPrimary","deleteIconFilledColorSecondary","focusVisible"]),us=["avatar","className","clickable","color","component","deleteIcon","disabled","icon","label","onClick","onDelete","onKeyDown","onKeyUp","size","variant","tabIndex","skipFocusWhenDisabled"],cs=(0,Zn.Ay)("div",{name:"MuiChip",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e,{color:r,iconColor:o,clickable:i,onDelete:a,size:s,variant:l}=n;return[{[`& .${ls.avatar}`]:t.avatar},{[`& .${ls.avatar}`]:t[`avatar${(0,tr.A)(s)}`]},{[`& .${ls.avatar}`]:t[`avatarColor${(0,tr.A)(r)}`]},{[`& .${ls.icon}`]:t.icon},{[`& .${ls.icon}`]:t[`icon${(0,tr.A)(s)}`]},{[`& .${ls.icon}`]:t[`iconColor${(0,tr.A)(o)}`]},{[`& .${ls.deleteIcon}`]:t.deleteIcon},{[`& .${ls.deleteIcon}`]:t[`deleteIcon${(0,tr.A)(s)}`]},{[`& .${ls.deleteIcon}`]:t[`deleteIconColor${(0,tr.A)(r)}`]},{[`& .${ls.deleteIcon}`]:t[`deleteIcon${(0,tr.A)(l)}Color${(0,tr.A)(r)}`]},t.root,t[`size${(0,tr.A)(s)}`],t[`color${(0,tr.A)(r)}`],i&&t.clickable,i&&"default"!==r&&t[`clickableColor${(0,tr.A)(r)})`],a&&t.deletable,a&&"default"!==r&&t[`deletableColor${(0,tr.A)(r)}`],t[l],t[`${l}${(0,tr.A)(r)}`]]}})((({theme:e,ownerState:t})=>{const n="light"===e.palette.mode?e.palette.grey[700]:e.palette.grey[300];return(0,Jt.A)({maxWidth:"100%",fontFamily:e.typography.fontFamily,fontSize:e.typography.pxToRem(13),display:"inline-flex",alignItems:"center",justifyContent:"center",height:32,color:(e.vars||e).palette.text.primary,backgroundColor:(e.vars||e).palette.action.selected,borderRadius:16,whiteSpace:"nowrap",transition:e.transitions.create(["background-color","box-shadow"]),cursor:"unset",outline:0,textDecoration:"none",border:0,padding:0,verticalAlign:"middle",boxSizing:"border-box",[`&.${ls.disabled}`]:{opacity:(e.vars||e).palette.action.disabledOpacity,pointerEvents:"none"},[`& .${ls.avatar}`]:{marginLeft:5,marginRight:-6,width:24,height:24,color:e.vars?e.vars.palette.Chip.defaultAvatarColor:n,fontSize:e.typography.pxToRem(12)},[`& .${ls.avatarColorPrimary}`]:{color:(e.vars||e).palette.primary.contrastText,backgroundColor:(e.vars||e).palette.primary.dark},[`& .${ls.avatarColorSecondary}`]:{color:(e.vars||e).palette.secondary.contrastText,backgroundColor:(e.vars||e).palette.secondary.dark},[`& .${ls.avatarSmall}`]:{marginLeft:4,marginRight:-4,width:18,height:18,fontSize:e.typography.pxToRem(10)},[`& .${ls.icon}`]:(0,Jt.A)({marginLeft:5,marginRight:-6},"small"===t.size&&{fontSize:18,marginLeft:4,marginRight:-4},t.iconColor===t.color&&(0,Jt.A)({color:e.vars?e.vars.palette.Chip.defaultIconColor:n},"default"!==t.color&&{color:"inherit"})),[`& .${ls.deleteIcon}`]:(0,Jt.A)({WebkitTapHighlightColor:"transparent",color:e.vars?`rgba(${e.vars.palette.text.primaryChannel} / 0.26)`:(0,vo.X4)(e.palette.text.primary,.26),fontSize:22,cursor:"pointer",margin:"0 5px 0 -6px","&:hover":{color:e.vars?`rgba(${e.vars.palette.text.primaryChannel} / 0.4)`:(0,vo.X4)(e.palette.text.primary,.4)}},"small"===t.size&&{fontSize:16,marginRight:4,marginLeft:-4},"default"!==t.color&&{color:e.vars?`rgba(${e.vars.palette[t.color].contrastTextChannel} / 0.7)`:(0,vo.X4)(e.palette[t.color].contrastText,.7),"&:hover, &:active":{color:(e.vars||e).palette[t.color].contrastText}})},"small"===t.size&&{height:24},"default"!==t.color&&{backgroundColor:(e.vars||e).palette[t.color].main,color:(e.vars||e).palette[t.color].contrastText},t.onDelete&&{[`&.${ls.focusVisible}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.action.selectedChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.focusOpacity}))`:(0,vo.X4)(e.palette.action.selected,e.palette.action.selectedOpacity+e.palette.action.focusOpacity)}},t.onDelete&&"default"!==t.color&&{[`&.${ls.focusVisible}`]:{backgroundColor:(e.vars||e).palette[t.color].dark}})}),(({theme:e,ownerState:t})=>(0,Jt.A)({},t.clickable&&{userSelect:"none",WebkitTapHighlightColor:"transparent",cursor:"pointer","&:hover":{backgroundColor:e.vars?`rgba(${e.vars.palette.action.selectedChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.hoverOpacity}))`:(0,vo.X4)(e.palette.action.selected,e.palette.action.selectedOpacity+e.palette.action.hoverOpacity)},[`&.${ls.focusVisible}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.action.selectedChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.focusOpacity}))`:(0,vo.X4)(e.palette.action.selected,e.palette.action.selectedOpacity+e.palette.action.focusOpacity)},"&:active":{boxShadow:(e.vars||e).shadows[1]}},t.clickable&&"default"!==t.color&&{[`&:hover, &.${ls.focusVisible}`]:{backgroundColor:(e.vars||e).palette[t.color].dark}})),(({theme:e,ownerState:t})=>(0,Jt.A)({},"outlined"===t.variant&&{backgroundColor:"transparent",border:e.vars?`1px solid ${e.vars.palette.Chip.defaultBorder}`:`1px solid ${"light"===e.palette.mode?e.palette.grey[400]:e.palette.grey[700]}`,[`&.${ls.clickable}:hover`]:{backgroundColor:(e.vars||e).palette.action.hover},[`&.${ls.focusVisible}`]:{backgroundColor:(e.vars||e).palette.action.focus},[`& .${ls.avatar}`]:{marginLeft:4},[`& .${ls.avatarSmall}`]:{marginLeft:2},[`& .${ls.icon}`]:{marginLeft:4},[`& .${ls.iconSmall}`]:{marginLeft:2},[`& .${ls.deleteIcon}`]:{marginRight:5},[`& .${ls.deleteIconSmall}`]:{marginRight:3}},"outlined"===t.variant&&"default"!==t.color&&{color:(e.vars||e).palette[t.color].main,border:`1px solid ${e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / 0.7)`:(0,vo.X4)(e.palette[t.color].main,.7)}`,[`&.${ls.clickable}:hover`]:{backgroundColor:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette[t.color].main,e.palette.action.hoverOpacity)},[`&.${ls.focusVisible}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.focusOpacity})`:(0,vo.X4)(e.palette[t.color].main,e.palette.action.focusOpacity)},[`& .${ls.deleteIcon}`]:{color:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / 0.7)`:(0,vo.X4)(e.palette[t.color].main,.7),"&:hover, &:active":{color:(e.vars||e).palette[t.color].main}}}))),ds=(0,Zn.Ay)("span",{name:"MuiChip",slot:"Label",overridesResolver:(e,t)=>{const{ownerState:n}=e,{size:r}=n;return[t.label,t[`label${(0,tr.A)(r)}`]]}})((({ownerState:e})=>(0,Jt.A)({overflow:"hidden",textOverflow:"ellipsis",paddingLeft:12,paddingRight:12,whiteSpace:"nowrap"},"outlined"===e.variant&&{paddingLeft:11,paddingRight:11},"small"===e.size&&{paddingLeft:8,paddingRight:8},"small"===e.size&&"outlined"===e.variant&&{paddingLeft:7,paddingRight:7})));function fs(e){return"Backspace"===e.key||"Delete"===e.key}const ps=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiChip"}),{avatar:r,className:o,clickable:i,color:a="default",component:s,deleteIcon:l,disabled:u=!1,icon:c,label:d,onClick:f,onDelete:p,onKeyDown:h,onKeyUp:m,size:g="medium",variant:v="filled",tabIndex:y,skipFocusWhenDisabled:b=!1}=n,w=(0,qn.A)(n,us),E=B.useRef(null),S=(0,Qr.A)(E,t),x=e=>{e.stopPropagation(),p&&p(e)},A=!(!1===i||!f)||i,k=A||p?di:s||"div",_=(0,Jt.A)({},n,{component:k,disabled:u,size:g,color:a,iconColor:B.isValidElement(c)&&c.props.color||a,onDelete:!!p,clickable:A,variant:v}),C=(e=>{const{classes:t,disabled:n,size:r,color:o,iconColor:i,onDelete:a,clickable:s,variant:l}=e,u={root:["root",l,n&&"disabled",`size${(0,tr.A)(r)}`,`color${(0,tr.A)(o)}`,s&&"clickable",s&&`clickableColor${(0,tr.A)(o)}`,a&&"deletable",a&&`deletableColor${(0,tr.A)(o)}`,`${l}${(0,tr.A)(o)}`],label:["label",`label${(0,tr.A)(r)}`],avatar:["avatar",`avatar${(0,tr.A)(r)}`,`avatarColor${(0,tr.A)(o)}`],icon:["icon",`icon${(0,tr.A)(r)}`,`iconColor${(0,tr.A)(i)}`],deleteIcon:["deleteIcon",`deleteIcon${(0,tr.A)(r)}`,`deleteIconColor${(0,tr.A)(o)}`,`deleteIcon${(0,tr.A)(l)}Color${(0,tr.A)(o)}`]};return(0,Qn.A)(u,ss,t)})(_),O=k===di?(0,Jt.A)({component:s||"div",focusVisibleClassName:C.focusVisible},p&&{disableRipple:!0}):{};let P=null;p&&(P=l&&B.isValidElement(l)?B.cloneElement(l,{className:(0,Kn.A)(l.props.className,C.deleteIcon),onClick:x}):(0,ir.jsx)(as,{className:(0,Kn.A)(C.deleteIcon),onClick:x}));let R=null;r&&B.isValidElement(r)&&(R=B.cloneElement(r,{className:(0,Kn.A)(C.avatar,r.props.className)}));let T=null;return c&&B.isValidElement(c)&&(T=B.cloneElement(c,{className:(0,Kn.A)(C.icon,c.props.className)})),(0,ir.jsxs)(cs,(0,Jt.A)({as:k,className:(0,Kn.A)(C.root,o),disabled:!(!A||!u)||void 0,onClick:f,onKeyDown:e=>{e.currentTarget===e.target&&fs(e)&&e.preventDefault(),h&&h(e)},onKeyUp:e=>{e.currentTarget===e.target&&(p&&fs(e)?p(e):"Escape"===e.key&&E.current&&E.current.blur()),m&&m(e)},ref:S,tabIndex:b&&u?-1:y,ownerState:_},O,w,{children:[R||T,(0,ir.jsx)(ds,{className:(0,Kn.A)(C.label),ownerState:_,children:d}),P]}))}));var hs=o(2274),ms=o(4736),gs=o(1823),vs="v7.2.0",ys=function(e){return{root:{backgroundColor:"inherit",height:e.spacing(3),color:"inherit",fontWeight:e.typography.fontWeightRegular,fontSize:"inherit","&:hover, &:focus":{backgroundColor:"rgba(255, 255, 255, 0.3)"},"&:active":{boxShadow:e.shadows[1],backgroundColor:"inherit"}}}},bs=function(e){var t=(0,Jt.A)({},(ne(e),e)),n=la(ys);return B.createElement(ps,(0,Jt.A)({className:n.root},t))},ws=function(e){return{breadcrumbs:{flexGrow:1},separator:{marginLeft:0,marginRight:0},menuButton:{marginRight:e.spacing(2)},root:{flexGrow:1}}};function Es(e){var t=la(ws),n=bn().cstat,r=function(){for(var e=it(),t=new URLSearchParams(e.search),n=[],r=0,o=[{path:"/heartbeats",text:"Heartbeats"},{path:"/threads",text:"Threads"},{path:"/arbitrators",text:"Arbitrators"},{path:"/networks",text:"Networks"},{path:"/pools",text:"Pools"},{path:"/nodes",text:"Nodes"},{path:"/objects",text:"Objects"},{path:"/services",text:"Services"},{path:"/volumes",text:"Volumes"},{path:"/configs",text:"Configs"},{path:"/secrets",text:"Secrets"},{path:"/users",text:"Users"},{path:"/stats",text:"Stats"},{path:"/api",text:"Api",to:"/api"}];r<o.length;r++){var i=o[r];if(De({path:i.path,end:!0,caseSensitive:!1},e.pathname))return n.push({text:i.text,to:i.to}),n}if(De({path:"/node",end:!0,caseSensitive:!1},e.pathname))return n.push({text:"Nodes",to:"/nodes"}),n.push({text:t.get("name")}),n;if(De({path:"/network",end:!0,caseSensitive:!1},e.pathname))return n.push({text:"Networks",to:"/networks"}),n.push({text:t.get("name")}),n;if(De({path:"/object",end:!0,caseSensitive:!1},e.pathname)){var a=null!==e.state?e.state.kind:"Objects";return n.push({text:a,to:"/"+a.toLowerCase()}),n.push({text:t.get("path")}),n}return De({path:"/instance",end:!0,caseSensitive:!1},e.pathname)?(a=null!==e.state?e.state.kind:"Objects",n.push({text:a,to:"/"+a.toLowerCase()}),n.push({text:t.get("path")+"@"+t.get("node")}),n):n}();try{var o=n.cluster.name}catch(e){o="/"}return B.createElement(Ka,{color:"inherit",className:t.breadcrumbs,separator:B.createElement(Ga.A,{fontSize:"small"}),classes:{separator:t.separator},"aria-label":"Breadcrumb"},B.createElement(As,{key:"cluster",text:o,to:"/"}),r.map((function(e,t){return B.createElement(As,{key:t,text:e.text,to:e.to})})))}function Ss(e){var t=la(ws);return B.createElement(Qa,{className:t.root},B.createElement(xs,null),B.createElement(Es,null),B.createElement(_s,null),B.createElement(pa,null),B.createElement(ks,null))}function xs(e){var t=fe(B.useState(!1),2),n=t[0],r=t[1],o=bn().cstat,i=la(ha),a=la(ws),s=function(e){return function(t){("keydown"!==t.type||"Tab"!==t.key&&"Shift"!==t.key)&&r(e)}},l=function(e){if(!e)return xn.NOTAPPLICABLE;var t=Wn(e);return t=An(t,Mn(e)),t=An(t,Nn(e)),t=An(t,Dn(e)),t=An(t,Bn(e)),An(t,Hn(e))}(o);if(l==xn.OPTIMAL)var u=0;else u=1;return B.createElement(B.Fragment,null,B.createElement(gi,{edge:"start",className:a.menuButton,color:"inherit","aria-label":"Menu",onClick:s(!n),title:"app version "+vs},B.createElement(ea,{badgeContent:u,classes:{badge:i[l.name]},variant:"dot"},B.createElement(hs.A,null))),B.createElement(To,{anchor:"left",open:n,onClose:s(!1)},B.createElement(ja,{closeDrawer:s(!1)})))}function As(e){var t=e.text,n=e.to,r=ee(),o=r.t,i=(r.i18n,lt());return B.createElement(bs,{onClick:function(e){e.preventDefault(),n&&i(n)},component:"a",label:e.children?null:o(t)},e.children)}function ks(e){var t=hn().user,n=lt();return wn()&&t?401==t.status?(localStorage.setItem("opensvc.authChoice",""),B.createElement(ms.A,null)):void 0===t.name?null:B.createElement(is,{color:"inherit",href:"#",onClick:function(e){n("/user")}},t.name[0].toUpperCase()):null}function _s(e){return bn().eventSourceAlive?null:B.createElement(gs.A,null)}var Cs=o(4661);function Os(e){return(0,rr.A)("MuiDialog",e)}const Ps=(0,nr.A)("MuiDialog",["root","scrollPaper","scrollBody","container","paper","paperScrollPaper","paperScrollBody","paperWidthFalse","paperWidthXs","paperWidthSm","paperWidthMd","paperWidthLg","paperWidthXl","paperFullWidth","paperFullScreen"]),Rs=B.createContext({}),Ts=["aria-describedby","aria-labelledby","BackdropComponent","BackdropProps","children","className","disableEscapeKeyDown","fullScreen","fullWidth","maxWidth","onBackdropClick","onClose","open","PaperComponent","PaperProps","scroll","TransitionComponent","transitionDuration","TransitionProps"],Fs=(0,Zn.Ay)(io,{name:"MuiDialog",slot:"Backdrop",overrides:(e,t)=>t.backdrop})({zIndex:-1}),js=(0,Zn.Ay)(co,{name:"MuiDialog",slot:"Root",overridesResolver:(e,t)=>t.root})({"@media print":{position:"absolute !important"}}),Ns=(0,Zn.Ay)("div",{name:"MuiDialog",slot:"Container",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.container,t[`scroll${(0,tr.A)(n.scroll)}`]]}})((({ownerState:e})=>(0,Jt.A)({height:"100%","@media print":{height:"auto"},outline:0},"paper"===e.scroll&&{display:"flex",justifyContent:"center",alignItems:"center"},"body"===e.scroll&&{overflowY:"auto",overflowX:"hidden",textAlign:"center","&::after":{content:'""',display:"inline-block",verticalAlign:"middle",height:"100%",width:"0"}}))),Is=(0,Zn.Ay)(So,{name:"MuiDialog",slot:"Paper",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.paper,t[`scrollPaper${(0,tr.A)(n.scroll)}`],t[`paperWidth${(0,tr.A)(String(n.maxWidth))}`],n.fullWidth&&t.paperFullWidth,n.fullScreen&&t.paperFullScreen]}})((({theme:e,ownerState:t})=>(0,Jt.A)({margin:32,position:"relative",overflowY:"auto","@media print":{overflowY:"visible",boxShadow:"none"}},"paper"===t.scroll&&{display:"flex",flexDirection:"column",maxHeight:"calc(100% - 64px)"},"body"===t.scroll&&{display:"inline-block",verticalAlign:"middle",textAlign:"left"},!t.maxWidth&&{maxWidth:"calc(100% - 64px)"},"xs"===t.maxWidth&&{maxWidth:"px"===e.breakpoints.unit?Math.max(e.breakpoints.values.xs,444):`max(${e.breakpoints.values.xs}${e.breakpoints.unit}, 444px)`,[`&.${Ps.paperScrollBody}`]:{[e.breakpoints.down(Math.max(e.breakpoints.values.xs,444)+64)]:{maxWidth:"calc(100% - 64px)"}}},t.maxWidth&&"xs"!==t.maxWidth&&{maxWidth:`${e.breakpoints.values[t.maxWidth]}${e.breakpoints.unit}`,[`&.${Ps.paperScrollBody}`]:{[e.breakpoints.down(e.breakpoints.values[t.maxWidth]+64)]:{maxWidth:"calc(100% - 64px)"}}},t.fullWidth&&{width:"calc(100% - 64px)"},t.fullScreen&&{margin:0,width:"100%",maxWidth:"100%",height:"100%",maxHeight:"none",borderRadius:0,[`&.${Ps.paperScrollBody}`]:{margin:0,maxWidth:"100%"}}))),Ls=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDialog"}),r=Jr(),o={enter:r.transitions.duration.enteringScreen,exit:r.transitions.duration.leavingScreen},{"aria-describedby":i,"aria-labelledby":a,BackdropComponent:s,BackdropProps:l,children:u,className:c,disableEscapeKeyDown:d=!1,fullScreen:f=!1,fullWidth:p=!1,maxWidth:h="sm",onBackdropClick:m,onClose:g,open:v,PaperComponent:y=So,PaperProps:b={},scroll:w="paper",TransitionComponent:E=to,transitionDuration:S=o,TransitionProps:x}=n,A=(0,qn.A)(n,Ts),k=(0,Jt.A)({},n,{disableEscapeKeyDown:d,fullScreen:f,fullWidth:p,maxWidth:h,scroll:w}),_=(e=>{const{classes:t,scroll:n,maxWidth:r,fullWidth:o,fullScreen:i}=e,a={root:["root"],container:["container",`scroll${(0,tr.A)(n)}`],paper:["paper",`paperScroll${(0,tr.A)(n)}`,`paperWidth${(0,tr.A)(String(r))}`,o&&"paperFullWidth",i&&"paperFullScreen"]};return(0,Qn.A)(a,Os,t)})(k),C=B.useRef(),O=(0,Cs.A)(a),P=B.useMemo((()=>({titleId:O})),[O]);return(0,ir.jsx)(js,(0,Jt.A)({className:(0,Kn.A)(_.root,c),closeAfterTransition:!0,components:{Backdrop:Fs},componentsProps:{backdrop:(0,Jt.A)({transitionDuration:S,as:s},l)},disableEscapeKeyDown:d,onClose:g,open:v,ref:t,onClick:e=>{C.current&&(C.current=null,m&&m(e),g&&g(e,"backdropClick"))},ownerState:k},A,{children:(0,ir.jsx)(E,(0,Jt.A)({appear:!0,in:v,timeout:S,role:"presentation"},x,{children:(0,ir.jsx)(Ns,{className:(0,Kn.A)(_.container),onMouseDown:e=>{C.current=e.target===e.currentTarget},ownerState:k,children:(0,ir.jsx)(Is,(0,Jt.A)({as:y,elevation:24,role:"dialog","aria-describedby":i,"aria-labelledby":O},b,{className:(0,Kn.A)(_.paper,b.className),ownerState:k,children:(0,ir.jsx)(Rs.Provider,{value:P,children:u})}))})}))}))}));function Ms(e){return(0,rr.A)("MuiDialogActions",e)}(0,nr.A)("MuiDialogActions",["root","spacing"]);const Ds=["className","disableSpacing"],Bs=(0,Zn.Ay)("div",{name:"MuiDialogActions",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,!n.disableSpacing&&t.spacing]}})((({ownerState:e})=>(0,Jt.A)({display:"flex",alignItems:"center",padding:8,justifyContent:"flex-end",flex:"0 0 auto"},!e.disableSpacing&&{"& > :not(style) ~ :not(style)":{marginLeft:8}}))),Us=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDialogActions"}),{className:r,disableSpacing:o=!1}=n,i=(0,qn.A)(n,Ds),a=(0,Jt.A)({},n,{disableSpacing:o}),s=(e=>{const{classes:t,disableSpacing:n}=e,r={root:["root",!n&&"spacing"]};return(0,Qn.A)(r,Ms,t)})(a);return(0,ir.jsx)(Bs,(0,Jt.A)({className:(0,Kn.A)(s.root,r),ownerState:a,ref:t},i))}));function zs(e){return(0,rr.A)("MuiDialogContent",e)}function $s(e){return(0,rr.A)("MuiDialogTitle",e)}(0,nr.A)("MuiDialogContent",["root","dividers"]);const Hs=(0,nr.A)("MuiDialogTitle",["root"]),Ws=["className","dividers"],Vs=(0,Zn.Ay)("div",{name:"MuiDialogContent",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.dividers&&t.dividers]}})((({theme:e,ownerState:t})=>(0,Jt.A)({flex:"1 1 auto",WebkitOverflowScrolling:"touch",overflowY:"auto",padding:"20px 24px"},t.dividers?{padding:"16px 24px",borderTop:`1px solid ${(e.vars||e).palette.divider}`,borderBottom:`1px solid ${(e.vars||e).palette.divider}`}:{[`.${Hs.root} + &`]:{paddingTop:0}}))),qs=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDialogContent"}),{className:r,dividers:o=!1}=n,i=(0,qn.A)(n,Ws),a=(0,Jt.A)({},n,{dividers:o}),s=(e=>{const{classes:t,dividers:n}=e,r={root:["root",n&&"dividers"]};return(0,Qn.A)(r,zs,t)})(a);return(0,ir.jsx)(Vs,(0,Jt.A)({className:(0,Kn.A)(s.root,r),ownerState:a,ref:t},i))}));function Ks(e){return(0,rr.A)("MuiDialogContentText",e)}(0,nr.A)("MuiDialogContentText",["root"]);const Gs=["children","className"],Js=(0,Zn.Ay)(cr,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiDialogContentText",slot:"Root",overridesResolver:(e,t)=>t.root})({}),Ys=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDialogContentText"}),{className:r}=n,o=(0,qn.A)(n,Gs),i=(e=>{const{classes:t}=e,n=(0,Qn.A)({root:["root"]},Ks,t);return(0,Jt.A)({},t,n)})(o);return(0,ir.jsx)(Js,(0,Jt.A)({component:"p",variant:"body1",color:"text.secondary",ref:t,ownerState:o,className:(0,Kn.A)(i.root,r)},n,{classes:i}))})),Xs=["className","id"],Qs=(0,Zn.Ay)(cr,{name:"MuiDialogTitle",slot:"Root",overridesResolver:(e,t)=>t.root})({padding:"16px 24px",flex:"0 0 auto"}),Zs=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDialogTitle"}),{className:r,id:o}=n,i=(0,qn.A)(n,Xs),a=n,s=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},$s,t)})(a),{titleId:l=o}=B.useContext(Rs);return(0,ir.jsx)(Qs,(0,Jt.A)({component:"h2",className:(0,Kn.A)(s.root,r),ownerState:a,ref:t,variant:"h6",id:null!=o?o:l},i))}));var el=o(3726);function tl(e){return(0,rr.A)("MuiButton",e)}const nl=(0,nr.A)("MuiButton",["root","text","textInherit","textPrimary","textSecondary","textSuccess","textError","textInfo","textWarning","outlined","outlinedInherit","outlinedPrimary","outlinedSecondary","outlinedSuccess","outlinedError","outlinedInfo","outlinedWarning","contained","containedInherit","containedPrimary","containedSecondary","containedSuccess","containedError","containedInfo","containedWarning","disableElevation","focusVisible","disabled","colorInherit","textSizeSmall","textSizeMedium","textSizeLarge","outlinedSizeSmall","outlinedSizeMedium","outlinedSizeLarge","containedSizeSmall","containedSizeMedium","containedSizeLarge","sizeMedium","sizeSmall","sizeLarge","fullWidth","startIcon","endIcon","iconSizeSmall","iconSizeMedium","iconSizeLarge"]),rl=B.createContext({}),ol=B.createContext(void 0),il=["children","color","component","className","disabled","disableElevation","disableFocusRipple","endIcon","focusVisibleClassName","fullWidth","size","startIcon","type","variant"],al=e=>(0,Jt.A)({},"small"===e.size&&{"& > *:nth-of-type(1)":{fontSize:18}},"medium"===e.size&&{"& > *:nth-of-type(1)":{fontSize:20}},"large"===e.size&&{"& > *:nth-of-type(1)":{fontSize:22}}),sl=(0,Zn.Ay)(di,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiButton",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],t[`${n.variant}${(0,tr.A)(n.color)}`],t[`size${(0,tr.A)(n.size)}`],t[`${n.variant}Size${(0,tr.A)(n.size)}`],"inherit"===n.color&&t.colorInherit,n.disableElevation&&t.disableElevation,n.fullWidth&&t.fullWidth]}})((({theme:e,ownerState:t})=>{var n,r;const o="light"===e.palette.mode?e.palette.grey[300]:e.palette.grey[800],i="light"===e.palette.mode?e.palette.grey.A100:e.palette.grey[700];return(0,Jt.A)({},e.typography.button,{minWidth:64,padding:"6px 16px",borderRadius:(e.vars||e).shape.borderRadius,transition:e.transitions.create(["background-color","box-shadow","border-color","color"],{duration:e.transitions.duration.short}),"&:hover":(0,Jt.A)({textDecoration:"none",backgroundColor:e.vars?`rgba(${e.vars.palette.text.primaryChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette.text.primary,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},"text"===t.variant&&"inherit"!==t.color&&{backgroundColor:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette[t.color].main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},"outlined"===t.variant&&"inherit"!==t.color&&{border:`1px solid ${(e.vars||e).palette[t.color].main}`,backgroundColor:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette[t.color].main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},"contained"===t.variant&&{backgroundColor:e.vars?e.vars.palette.Button.inheritContainedHoverBg:i,boxShadow:(e.vars||e).shadows[4],"@media (hover: none)":{boxShadow:(e.vars||e).shadows[2],backgroundColor:(e.vars||e).palette.grey[300]}},"contained"===t.variant&&"inherit"!==t.color&&{backgroundColor:(e.vars||e).palette[t.color].dark,"@media (hover: none)":{backgroundColor:(e.vars||e).palette[t.color].main}}),"&:active":(0,Jt.A)({},"contained"===t.variant&&{boxShadow:(e.vars||e).shadows[8]}),[`&.${nl.focusVisible}`]:(0,Jt.A)({},"contained"===t.variant&&{boxShadow:(e.vars||e).shadows[6]}),[`&.${nl.disabled}`]:(0,Jt.A)({color:(e.vars||e).palette.action.disabled},"outlined"===t.variant&&{border:`1px solid ${(e.vars||e).palette.action.disabledBackground}`},"contained"===t.variant&&{color:(e.vars||e).palette.action.disabled,boxShadow:(e.vars||e).shadows[0],backgroundColor:(e.vars||e).palette.action.disabledBackground})},"text"===t.variant&&{padding:"6px 8px"},"text"===t.variant&&"inherit"!==t.color&&{color:(e.vars||e).palette[t.color].main},"outlined"===t.variant&&{padding:"5px 15px",border:"1px solid currentColor"},"outlined"===t.variant&&"inherit"!==t.color&&{color:(e.vars||e).palette[t.color].main,border:e.vars?`1px solid rgba(${e.vars.palette[t.color].mainChannel} / 0.5)`:`1px solid ${(0,vo.X4)(e.palette[t.color].main,.5)}`},"contained"===t.variant&&{color:e.vars?e.vars.palette.text.primary:null==(n=(r=e.palette).getContrastText)?void 0:n.call(r,e.palette.grey[300]),backgroundColor:e.vars?e.vars.palette.Button.inheritContainedBg:o,boxShadow:(e.vars||e).shadows[2]},"contained"===t.variant&&"inherit"!==t.color&&{color:(e.vars||e).palette[t.color].contrastText,backgroundColor:(e.vars||e).palette[t.color].main},"inherit"===t.color&&{color:"inherit",borderColor:"currentColor"},"small"===t.size&&"text"===t.variant&&{padding:"4px 5px",fontSize:e.typography.pxToRem(13)},"large"===t.size&&"text"===t.variant&&{padding:"8px 11px",fontSize:e.typography.pxToRem(15)},"small"===t.size&&"outlined"===t.variant&&{padding:"3px 9px",fontSize:e.typography.pxToRem(13)},"large"===t.size&&"outlined"===t.variant&&{padding:"7px 21px",fontSize:e.typography.pxToRem(15)},"small"===t.size&&"contained"===t.variant&&{padding:"4px 10px",fontSize:e.typography.pxToRem(13)},"large"===t.size&&"contained"===t.variant&&{padding:"8px 22px",fontSize:e.typography.pxToRem(15)},t.fullWidth&&{width:"100%"})}),(({ownerState:e})=>e.disableElevation&&{boxShadow:"none","&:hover":{boxShadow:"none"},[`&.${nl.focusVisible}`]:{boxShadow:"none"},"&:active":{boxShadow:"none"},[`&.${nl.disabled}`]:{boxShadow:"none"}})),ll=(0,Zn.Ay)("span",{name:"MuiButton",slot:"StartIcon",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.startIcon,t[`iconSize${(0,tr.A)(n.size)}`]]}})((({ownerState:e})=>(0,Jt.A)({display:"inherit",marginRight:8,marginLeft:-4},"small"===e.size&&{marginLeft:-2},al(e)))),ul=(0,Zn.Ay)("span",{name:"MuiButton",slot:"EndIcon",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.endIcon,t[`iconSize${(0,tr.A)(n.size)}`]]}})((({ownerState:e})=>(0,Jt.A)({display:"inherit",marginRight:-4,marginLeft:8},"small"===e.size&&{marginRight:-2},al(e)))),cl=B.forwardRef((function(e,t){const n=B.useContext(rl),r=B.useContext(ol),o=(0,el.A)(n,e),i=(0,er.A)({props:o,name:"MuiButton"}),{children:a,color:s="primary",component:l="button",className:u,disabled:c=!1,disableElevation:d=!1,disableFocusRipple:f=!1,endIcon:p,focusVisibleClassName:h,fullWidth:m=!1,size:g="medium",startIcon:v,type:y,variant:b="text"}=i,w=(0,qn.A)(i,il),E=(0,Jt.A)({},i,{color:s,component:l,disabled:c,disableElevation:d,disableFocusRipple:f,fullWidth:m,size:g,type:y,variant:b}),S=(e=>{const{color:t,disableElevation:n,fullWidth:r,size:o,variant:i,classes:a}=e,s={root:["root",i,`${i}${(0,tr.A)(t)}`,`size${(0,tr.A)(o)}`,`${i}Size${(0,tr.A)(o)}`,"inherit"===t&&"colorInherit",n&&"disableElevation",r&&"fullWidth"],label:["label"],startIcon:["startIcon",`iconSize${(0,tr.A)(o)}`],endIcon:["endIcon",`iconSize${(0,tr.A)(o)}`]},l=(0,Qn.A)(s,tl,a);return(0,Jt.A)({},a,l)})(E),x=v&&(0,ir.jsx)(ll,{className:S.startIcon,ownerState:E,children:v}),A=p&&(0,ir.jsx)(ul,{className:S.endIcon,ownerState:E,children:p}),k=r||"";return(0,ir.jsxs)(sl,(0,Jt.A)({ownerState:E,className:(0,Kn.A)(n.className,S.root,u,k),component:l,disabled:c,focusRipple:!f,focusVisibleClassName:(0,Kn.A)(S.focusVisible,h),ref:t,type:y},w,{classes:S,children:[x,a,A]}))})),dl=function(e){var t=ee(),n=(t.i18n,t.t),r=wn(),o=fe(ve(),2),i=(o[0].authChoice,o[1]);return B.createElement(Ls,{open:!0,"aria-labelledby":"dialog-title"},B.createElement(Zs,{id:"dialog-title"},n("Authentication Methods")),B.createElement(qs,null,B.createElement(Ys,null,n("Please select one of the following authentication method the cluster advertizes."))),B.createElement(Us,null,r&&r.openid&&r.openid.well_known_uri&&B.createElement(cl,{onClick:function(){return i({type:"setAuthChoice",data:"openid"})}},"OpenId"),r&&r.methods&&r.methods.indexOf("basic")>=0&&B.createElement(cl,{onClick:function(){return i({type:"setAuthChoice",data:"basic"})}},"Basic")))},fl=function(e){var t=ee(),n=(t.i18n,t.t);return B.createElement(Ls,{open:!0,"aria-labelledby":"dialog-title"},B.createElement(Zs,{id:"dialog-title"},n("Authentication")),B.createElement(qs,null,B.createElement(Ys,null,n("You are not authenticated. Choose an authentication method."))))},pl=function(e){var t=ee(),n=(t.i18n,t.t),r=wn(),o=fe(ve(),2),i=o[0].authChoice,a=o[1];return B.createElement(Ls,{open:!0,"aria-labelledby":"dialog-title"},B.createElement(Zs,{id:"dialog-title"},i),B.createElement(qs,null,B.createElement(Ys,null,n("You are not authorized. Choose another authentication method, or another user certificate."))),B.createElement(Us,null,r&&r.openid&&r.openid.well_known_uri&&B.createElement(cl,{onClick:function(){return a({type:"setAuthChoice",data:"openid"})}},"OpenId")))},hl=function(e){var t=ee(),n=(t.i18n,t.t);return B.createElement(Ls,{open:!0,"aria-labelledby":"dialog-title"},B.createElement(Zs,{id:"dialog-title"},n("Authentication")),B.createElement(qs,null,B.createElement(Ys,null,n("You are being redirected to the openid provider."))),B.createElement(Us,null,B.createElement(cl,{onClick:function(){return location.reload()}},n("Reload")),B.createElement(cl,{onClick:function(){localStorage.setItem("opensvc.authChoice",""),window.location.reload()}},n("Reset authentication method"))))};function ml(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function gl(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?ml(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):ml(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var vl=window.location.protocol+"//"+window.location.host,yl={client_id:"ringfs",redirect_uri:vl+"/authentication/callback",response_type:"code",scope:"openid profile email offline_access opensvc:om2 opensvc:om2:root opensvc:om2:guest grant",silent_redirect_uri:vl+"/authentication/silent_callback",automaticSilentRenew:!0,loadUserInfo:!1,triggerAuthFlow:!0,post_logout_redirect_uri:vl+"/",authority:""};const bl=function(e){return e&&void 0!==e.openid&&void 0!==e.openid.well_known_uri?gl(gl({},yl),{},{authority:e.openid.well_known_uri,client_id:e.openid.client_id}):yl};function wl(){var e=fe(ve(),1)[0].authenticated,t=fe((0,B.useState)(null),2),n=t[0],r=t[1],o=hn().auth;return(0,B.useEffect)((function(){e&&null===n&&dn("/pools",{},(function(e){r(e)}),o)}),[e]),n}function El(){var e=fe(ve(),1)[0].authenticated,t=fe((0,B.useState)(null),2),n=t[0],r=t[1],o=hn().auth;return(0,B.useEffect)((function(){e&&null===n&&dn("/networks",{},(function(e){r(e)}),o)}),[e]),n}var Sl="I understand the service will be interrupted.",xl="I understand the configuration will be lost.",Al="I understand data will be lost.",kl="I understand the selected services may be temporarily interrupted during failover, or durably interrupted if no failover is configured.",_l="I understand the selected services will be unavailable during move.",Cl="I understand this action will be orchestrated clusterwide.";function Ol(e){return(0,rr.A)("MuiDivider",e)}const Pl=(0,nr.A)("MuiDivider",["root","absolute","fullWidth","inset","middle","flexItem","light","vertical","withChildren","withChildrenVertical","textAlignRight","textAlignLeft","wrapper","wrapperVertical"]),Rl=["absolute","children","className","component","flexItem","light","orientation","role","textAlign","variant"],Tl=(0,Zn.Ay)("div",{name:"MuiDivider",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.absolute&&t.absolute,t[n.variant],n.light&&t.light,"vertical"===n.orientation&&t.vertical,n.flexItem&&t.flexItem,n.children&&t.withChildren,n.children&&"vertical"===n.orientation&&t.withChildrenVertical,"right"===n.textAlign&&"vertical"!==n.orientation&&t.textAlignRight,"left"===n.textAlign&&"vertical"!==n.orientation&&t.textAlignLeft]}})((({theme:e,ownerState:t})=>(0,Jt.A)({margin:0,flexShrink:0,borderWidth:0,borderStyle:"solid",borderColor:(e.vars||e).palette.divider,borderBottomWidth:"thin"},t.absolute&&{position:"absolute",bottom:0,left:0,width:"100%"},t.light&&{borderColor:e.vars?`rgba(${e.vars.palette.dividerChannel} / 0.08)`:(0,vo.X4)(e.palette.divider,.08)},"inset"===t.variant&&{marginLeft:72},"middle"===t.variant&&"horizontal"===t.orientation&&{marginLeft:e.spacing(2),marginRight:e.spacing(2)},"middle"===t.variant&&"vertical"===t.orientation&&{marginTop:e.spacing(1),marginBottom:e.spacing(1)},"vertical"===t.orientation&&{height:"100%",borderBottomWidth:0,borderRightWidth:"thin"},t.flexItem&&{alignSelf:"stretch",height:"auto"})),(({ownerState:e})=>(0,Jt.A)({},e.children&&{display:"flex",whiteSpace:"nowrap",textAlign:"center",border:0,"&::before, &::after":{content:'""',alignSelf:"center"}})),(({theme:e,ownerState:t})=>(0,Jt.A)({},t.children&&"vertical"!==t.orientation&&{"&::before, &::after":{width:"100%",borderTop:`thin solid ${(e.vars||e).palette.divider}`}})),(({theme:e,ownerState:t})=>(0,Jt.A)({},t.children&&"vertical"===t.orientation&&{flexDirection:"column","&::before, &::after":{height:"100%",borderLeft:`thin solid ${(e.vars||e).palette.divider}`}})),(({ownerState:e})=>(0,Jt.A)({},"right"===e.textAlign&&"vertical"!==e.orientation&&{"&::before":{width:"90%"},"&::after":{width:"10%"}},"left"===e.textAlign&&"vertical"!==e.orientation&&{"&::before":{width:"10%"},"&::after":{width:"90%"}}))),Fl=(0,Zn.Ay)("span",{name:"MuiDivider",slot:"Wrapper",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.wrapper,"vertical"===n.orientation&&t.wrapperVertical]}})((({theme:e,ownerState:t})=>(0,Jt.A)({display:"inline-block",paddingLeft:`calc(${e.spacing(1)} * 1.2)`,paddingRight:`calc(${e.spacing(1)} * 1.2)`},"vertical"===t.orientation&&{paddingTop:`calc(${e.spacing(1)} * 1.2)`,paddingBottom:`calc(${e.spacing(1)} * 1.2)`}))),jl=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiDivider"}),{absolute:r=!1,children:o,className:i,component:a=(o?"div":"hr"),flexItem:s=!1,light:l=!1,orientation:u="horizontal",role:c=("hr"!==a?"separator":void 0),textAlign:d="center",variant:f="fullWidth"}=n,p=(0,qn.A)(n,Rl),h=(0,Jt.A)({},n,{absolute:r,component:a,flexItem:s,light:l,orientation:u,role:c,textAlign:d,variant:f}),m=(e=>{const{absolute:t,children:n,classes:r,flexItem:o,light:i,orientation:a,textAlign:s,variant:l}=e,u={root:["root",t&&"absolute",l,i&&"light","vertical"===a&&"vertical",o&&"flexItem",n&&"withChildren",n&&"vertical"===a&&"withChildrenVertical","right"===s&&"vertical"!==a&&"textAlignRight","left"===s&&"vertical"!==a&&"textAlignLeft"],wrapper:["wrapper","vertical"===a&&"wrapperVertical"]};return(0,Qn.A)(u,Ol,r)})(h);return(0,ir.jsx)(Tl,(0,Jt.A)({as:a,className:(0,Kn.A)(m.root,i),role:c,ref:t,ownerState:h},p,{children:o?(0,ir.jsx)(Fl,{className:m.wrapper,ownerState:h,children:o}):null}))}));jl.muiSkipListHighlight=!0;const Nl=jl,Il=B.createContext(void 0);function Ll(){return B.useContext(Il)}const Ml=(0,o(9834).Ay)();var Dl=o(4467),Bl=o(7008),Ul=o(9452),zl=o(6468);const $l=["component","direction","spacing","divider","children","className","useFlexGap"],Hl=(0,Bl.A)(),Wl=Ml("div",{name:"MuiStack",slot:"Root",overridesResolver:(e,t)=>t.root});function Vl(e){return(0,Dl.A)({props:e,name:"MuiStack",defaultTheme:Hl})}function ql(e,t){const n=B.Children.toArray(e).filter(Boolean);return n.reduce(((e,r,o)=>(e.push(r),o<n.length-1&&e.push(B.cloneElement(t,{key:`separator-${o}`})),e)),[])}const Kl=({ownerState:e,theme:t})=>{let n=(0,Jt.A)({display:"flex",flexDirection:"column"},(0,Ul.NI)({theme:t},(0,Ul.kW)({values:e.direction,breakpoints:t.breakpoints.values}),(e=>({flexDirection:e}))));if(e.spacing){const r=(0,zl.LX)(t),o=Object.keys(t.breakpoints.values).reduce(((t,n)=>(("object"==typeof e.spacing&&null!=e.spacing[n]||"object"==typeof e.direction&&null!=e.direction[n])&&(t[n]=!0),t)),{}),i=(0,Ul.kW)({values:e.direction,base:o}),a=(0,Ul.kW)({values:e.spacing,base:o});"object"==typeof i&&Object.keys(i).forEach(((e,t,n)=>{if(!i[e]){const r=t>0?i[n[t-1]]:"column";i[e]=r}}));const s=(t,n)=>{return e.useFlexGap?{gap:(0,zl._W)(r,t)}:{"& > :not(style):not(style)":{margin:0},"& > :not(style) ~ :not(style)":{[`margin${o=n?i[n]:e.direction,{row:"Left","row-reverse":"Right",column:"Top","column-reverse":"Bottom"}[o]}`]:(0,zl._W)(r,t)}};var o};n=(0,Gn.A)(n,(0,Ul.NI)({theme:t},a,s))}return n=(0,Ul.iZ)(t.breakpoints,n),n},Gl=function(e={}){const{createStyledComponent:t=Wl,useThemeProps:n=Vl,componentName:r="MuiStack"}=e,o=t(Kl);return B.forwardRef((function(e,t){const i=Xn(n(e)),{component:a="div",direction:s="column",spacing:l=0,divider:u,children:c,className:d,useFlexGap:f=!1}=i,p=(0,qn.A)(i,$l),h={direction:s,spacing:l,useFlexGap:f},m=(0,Qn.A)({root:["root"]},(e=>(0,rr.A)(r,e)),{});return(0,ir.jsx)(o,(0,Jt.A)({as:a,ownerState:h,ref:t,className:(0,Kn.A)(m.root,d)},p,{children:u?ql(c,u):c}))}))}({createStyledComponent:(0,Zn.Ay)("div",{name:"MuiStack",slot:"Root",overridesResolver:(e,t)=>t.root}),useThemeProps:e=>(0,er.A)({props:e,name:"MuiStack"})});function Jl(e){return(0,rr.A)("MuiFormControlLabel",e)}const Yl=(0,nr.A)("MuiFormControlLabel",["root","labelPlacementStart","labelPlacementTop","labelPlacementBottom","disabled","label","error","required","asterisk"]);function Xl({props:e,states:t,muiFormControl:n}){return t.reduce(((t,r)=>(t[r]=e[r],n&&void 0===e[r]&&(t[r]=n[r]),t)),{})}const Ql=["checked","className","componentsProps","control","disabled","disableTypography","inputRef","label","labelPlacement","name","onChange","required","slotProps","value"],Zl=(0,Zn.Ay)("label",{name:"MuiFormControlLabel",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`& .${Yl.label}`]:t.label},t.root,t[`labelPlacement${(0,tr.A)(n.labelPlacement)}`]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({display:"inline-flex",alignItems:"center",cursor:"pointer",verticalAlign:"middle",WebkitTapHighlightColor:"transparent",marginLeft:-11,marginRight:16,[`&.${Yl.disabled}`]:{cursor:"default"}},"start"===t.labelPlacement&&{flexDirection:"row-reverse",marginLeft:16,marginRight:-11},"top"===t.labelPlacement&&{flexDirection:"column-reverse",marginLeft:16},"bottom"===t.labelPlacement&&{flexDirection:"column",marginLeft:16},{[`& .${Yl.label}`]:{[`&.${Yl.disabled}`]:{color:(e.vars||e).palette.text.disabled}}}))),eu=(0,Zn.Ay)("span",{name:"MuiFormControlLabel",slot:"Asterisk",overridesResolver:(e,t)=>t.asterisk})((({theme:e})=>({[`&.${Yl.error}`]:{color:(e.vars||e).palette.error.main}}))),tu=B.forwardRef((function(e,t){var n,r;const o=(0,er.A)({props:e,name:"MuiFormControlLabel"}),{className:i,componentsProps:a={},control:s,disabled:l,disableTypography:u,label:c,labelPlacement:d="end",required:f,slotProps:p={}}=o,h=(0,qn.A)(o,Ql),m=Ll(),g=null!=(n=null!=l?l:s.props.disabled)?n:null==m?void 0:m.disabled,v=null!=f?f:s.props.required,y={disabled:g,required:v};["checked","name","onChange","value","inputRef"].forEach((e=>{void 0===s.props[e]&&void 0!==o[e]&&(y[e]=o[e])}));const b=Xl({props:o,muiFormControl:m,states:["error"]}),w=(0,Jt.A)({},o,{disabled:g,labelPlacement:d,required:v,error:b.error}),E=(e=>{const{classes:t,disabled:n,labelPlacement:r,error:o,required:i}=e,a={root:["root",n&&"disabled",`labelPlacement${(0,tr.A)(r)}`,o&&"error",i&&"required"],label:["label",n&&"disabled"],asterisk:["asterisk",o&&"error"]};return(0,Qn.A)(a,Jl,t)})(w),S=null!=(r=p.typography)?r:a.typography;let x=c;return null==x||x.type===cr||u||(x=(0,ir.jsx)(cr,(0,Jt.A)({component:"span"},S,{className:(0,Kn.A)(E.label,null==S?void 0:S.className),children:x}))),(0,ir.jsxs)(Zl,(0,Jt.A)({className:(0,Kn.A)(E.root,i),ownerState:w,ref:t},h,{children:[B.cloneElement(s,y),v?(0,ir.jsxs)(Gl,{display:"block",children:[x,(0,ir.jsxs)(eu,{ownerState:w,"aria-hidden":!0,className:E.asterisk,children:["â€‰","*"]})]}):x]}))}));var nu=o(1159);function ru(e){return(0,rr.A)("PrivateSwitchBase",e)}(0,nr.A)("PrivateSwitchBase",["root","checked","disabled","input","edgeStart","edgeEnd"]);const ou=["autoFocus","checked","checkedIcon","className","defaultChecked","disabled","disableFocusRipple","edge","icon","id","inputProps","inputRef","name","onBlur","onChange","onFocus","readOnly","required","tabIndex","type","value"],iu=(0,Zn.Ay)(di)((({ownerState:e})=>(0,Jt.A)({padding:9,borderRadius:"50%"},"start"===e.edge&&{marginLeft:"small"===e.size?-3:-12},"end"===e.edge&&{marginRight:"small"===e.size?-3:-12}))),au=(0,Zn.Ay)("input",{shouldForwardProp:Zn.ep})({cursor:"inherit",position:"absolute",opacity:0,width:"100%",height:"100%",top:0,left:0,margin:0,padding:0,zIndex:1}),su=B.forwardRef((function(e,t){const{autoFocus:n,checked:r,checkedIcon:o,className:i,defaultChecked:a,disabled:s,disableFocusRipple:l=!1,edge:u=!1,icon:c,id:d,inputProps:f,inputRef:p,name:h,onBlur:m,onChange:g,onFocus:v,readOnly:y,required:b=!1,tabIndex:w,type:E,value:S}=e,x=(0,qn.A)(e,ou),[A,k]=(0,nu.A)({controlled:r,default:Boolean(a),name:"SwitchBase",state:"checked"}),_=Ll();let C=s;_&&void 0===C&&(C=_.disabled);const O="checkbox"===E||"radio"===E,P=(0,Jt.A)({},e,{checked:A,disabled:C,disableFocusRipple:l,edge:u}),R=(e=>{const{classes:t,checked:n,disabled:r,edge:o}=e,i={root:["root",n&&"checked",r&&"disabled",o&&`edge${(0,tr.A)(o)}`],input:["input"]};return(0,Qn.A)(i,ru,t)})(P);return(0,ir.jsxs)(iu,(0,Jt.A)({component:"span",className:(0,Kn.A)(R.root,i),centerRipple:!0,focusRipple:!l,disabled:C,tabIndex:null,role:void 0,onFocus:e=>{v&&v(e),_&&_.onFocus&&_.onFocus(e)},onBlur:e=>{m&&m(e),_&&_.onBlur&&_.onBlur(e)},ownerState:P,ref:t},x,{children:[(0,ir.jsx)(au,(0,Jt.A)({autoFocus:n,checked:r,defaultChecked:a,className:R.input,disabled:C,id:O?d:void 0,name:h,onChange:e=>{if(e.nativeEvent.defaultPrevented)return;const t=e.target.checked;k(t),g&&g(e,t)},readOnly:y,ref:p,required:b,ownerState:P,tabIndex:w,type:E},"checkbox"===E&&void 0===S?{}:{value:S},f)),A?o:c]}))})),lu=(0,Na.A)((0,ir.jsx)("path",{d:"M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"}),"CheckBoxOutlineBlank"),uu=(0,Na.A)((0,ir.jsx)("path",{d:"M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"}),"CheckBox"),cu=(0,Na.A)((0,ir.jsx)("path",{d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z"}),"IndeterminateCheckBox");function du(e){return(0,rr.A)("MuiCheckbox",e)}const fu=(0,nr.A)("MuiCheckbox",["root","checked","disabled","indeterminate","colorPrimary","colorSecondary","sizeSmall","sizeMedium"]),pu=["checkedIcon","color","icon","indeterminate","indeterminateIcon","inputProps","size","className"],hu=(0,Zn.Ay)(su,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiCheckbox",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.indeterminate&&t.indeterminate,t[`size${(0,tr.A)(n.size)}`],"default"!==n.color&&t[`color${(0,tr.A)(n.color)}`]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({color:(e.vars||e).palette.text.secondary},!t.disableRipple&&{"&:hover":{backgroundColor:e.vars?`rgba(${"default"===t.color?e.vars.palette.action.activeChannel:e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)("default"===t.color?e.palette.action.active:e.palette[t.color].main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},"default"!==t.color&&{[`&.${fu.checked}, &.${fu.indeterminate}`]:{color:(e.vars||e).palette[t.color].main},[`&.${fu.disabled}`]:{color:(e.vars||e).palette.action.disabled}}))),mu=(0,ir.jsx)(uu,{}),gu=(0,ir.jsx)(lu,{}),vu=(0,ir.jsx)(cu,{}),yu=B.forwardRef((function(e,t){var n,r;const o=(0,er.A)({props:e,name:"MuiCheckbox"}),{checkedIcon:i=mu,color:a="primary",icon:s=gu,indeterminate:l=!1,indeterminateIcon:u=vu,inputProps:c,size:d="medium",className:f}=o,p=(0,qn.A)(o,pu),h=l?u:s,m=l?u:i,g=(0,Jt.A)({},o,{color:a,indeterminate:l,size:d}),v=(e=>{const{classes:t,indeterminate:n,color:r,size:o}=e,i={root:["root",n&&"indeterminate",`color${(0,tr.A)(r)}`,`size${(0,tr.A)(o)}`]},a=(0,Qn.A)(i,du,t);return(0,Jt.A)({},t,a)})(g);return(0,ir.jsx)(hu,(0,Jt.A)({type:"checkbox",inputProps:(0,Jt.A)({"data-indeterminate":l},c),icon:B.cloneElement(h,{fontSize:null!=(n=h.props.fontSize)?n:d}),checkedIcon:B.cloneElement(m,{fontSize:null!=(r=m.props.fontSize)?r:d}),ownerState:g,ref:t,className:(0,Kn.A)(v.root,f)},p,{classes:v}))}));function bu(e){return null!=e&&!(Array.isArray(e)&&0===e.length)}function wu(e,t=!1){return e&&(bu(e.value)&&""!==e.value||t&&bu(e.defaultValue)&&""!==e.defaultValue)}function Eu(e){return(0,rr.A)("MuiFormControl",e)}(0,nr.A)("MuiFormControl",["root","marginNone","marginNormal","marginDense","fullWidth","disabled"]);const Su=["children","className","color","component","disabled","error","focused","fullWidth","hiddenLabel","margin","required","size","variant"],xu=(0,Zn.Ay)("div",{name:"MuiFormControl",slot:"Root",overridesResolver:({ownerState:e},t)=>(0,Jt.A)({},t.root,t[`margin${(0,tr.A)(e.margin)}`],e.fullWidth&&t.fullWidth)})((({ownerState:e})=>(0,Jt.A)({display:"inline-flex",flexDirection:"column",position:"relative",minWidth:0,padding:0,margin:0,border:0,verticalAlign:"top"},"normal"===e.margin&&{marginTop:16,marginBottom:8},"dense"===e.margin&&{marginTop:8,marginBottom:4},e.fullWidth&&{width:"100%"}))),Au=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiFormControl"}),{children:r,className:o,color:i="primary",component:a="div",disabled:s=!1,error:l=!1,focused:u,fullWidth:c=!1,hiddenLabel:d=!1,margin:f="none",required:p=!1,size:h="medium",variant:m="outlined"}=n,g=(0,qn.A)(n,Su),v=(0,Jt.A)({},n,{color:i,component:a,disabled:s,error:l,fullWidth:c,hiddenLabel:d,margin:f,required:p,size:h,variant:m}),y=(e=>{const{classes:t,margin:n,fullWidth:r}=e,o={root:["root","none"!==n&&`margin${(0,tr.A)(n)}`,r&&"fullWidth"]};return(0,Qn.A)(o,Eu,t)})(v),[b,w]=B.useState((()=>{let e=!1;return r&&B.Children.forEach(r,(t=>{if(!(0,Si.A)(t,["Input","Select"]))return;const n=(0,Si.A)(t,["Select"])?t.props.input:t;n&&n.props.startAdornment&&(e=!0)})),e})),[E,S]=B.useState((()=>{let e=!1;return r&&B.Children.forEach(r,(t=>{(0,Si.A)(t,["Input","Select"])&&(wu(t.props,!0)||wu(t.props.inputProps,!0))&&(e=!0)})),e})),[x,A]=B.useState(!1);s&&x&&A(!1);const k=void 0===u||s?x:u;let _;const C=B.useMemo((()=>({adornedStart:b,setAdornedStart:w,color:i,disabled:s,error:l,filled:E,focused:k,fullWidth:c,hiddenLabel:d,size:h,onBlur:()=>{A(!1)},onEmpty:()=>{S(!1)},onFilled:()=>{S(!0)},onFocus:()=>{A(!0)},registerEffect:_,required:p,variant:m})),[b,i,s,l,E,k,c,d,_,p,h,m]);return(0,ir.jsx)(Il.Provider,{value:C,children:(0,ir.jsx)(xu,(0,Jt.A)({as:a,ownerState:v,className:(0,Kn.A)(y.root,o),ref:t},g,{children:r}))})}));function ku(e){return(0,rr.A)("MuiFormGroup",e)}(0,nr.A)("MuiFormGroup",["root","row","error"]);const _u=["className","row"],Cu=(0,Zn.Ay)("div",{name:"MuiFormGroup",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.row&&t.row]}})((({ownerState:e})=>(0,Jt.A)({display:"flex",flexDirection:"column",flexWrap:"wrap"},e.row&&{flexDirection:"row"}))),Ou=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiFormGroup"}),{className:r,row:o=!1}=n,i=(0,qn.A)(n,_u),a=Xl({props:n,muiFormControl:Ll(),states:["error"]}),s=(0,Jt.A)({},n,{row:o,error:a.error}),l=(e=>{const{classes:t,row:n,error:r}=e,o={root:["root",n&&"row",r&&"error"]};return(0,Qn.A)(o,ku,t)})(s);return(0,ir.jsx)(Cu,(0,Jt.A)({className:(0,Kn.A)(l.root,r),ownerState:s,ref:t},i))}));function Pu(e){return(0,rr.A)("MuiFab",e)}const Ru=(0,nr.A)("MuiFab",["root","primary","secondary","extended","circular","focusVisible","disabled","colorInherit","sizeSmall","sizeMedium","sizeLarge","info","error","warning","success"]),Tu=["children","className","color","component","disabled","disableFocusRipple","focusVisibleClassName","size","variant"],Fu=(0,Zn.Ay)(di,{name:"MuiFab",slot:"Root",shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],t[`size${(0,tr.A)(n.size)}`],"inherit"===n.color&&t.colorInherit,t[(0,tr.A)(n.size)],t[n.color]]}})((({theme:e,ownerState:t})=>{var n,r;return(0,Jt.A)({},e.typography.button,{minHeight:36,transition:e.transitions.create(["background-color","box-shadow","border-color"],{duration:e.transitions.duration.short}),borderRadius:"50%",padding:0,minWidth:0,width:56,height:56,zIndex:(e.vars||e).zIndex.fab,boxShadow:(e.vars||e).shadows[6],"&:active":{boxShadow:(e.vars||e).shadows[12]},color:e.vars?e.vars.palette.text.primary:null==(n=(r=e.palette).getContrastText)?void 0:n.call(r,e.palette.grey[300]),backgroundColor:(e.vars||e).palette.grey[300],"&:hover":{backgroundColor:(e.vars||e).palette.grey.A100,"@media (hover: none)":{backgroundColor:(e.vars||e).palette.grey[300]},textDecoration:"none"},[`&.${Ru.focusVisible}`]:{boxShadow:(e.vars||e).shadows[6]}},"small"===t.size&&{width:40,height:40},"medium"===t.size&&{width:48,height:48},"extended"===t.variant&&{borderRadius:24,padding:"0 16px",width:"auto",minHeight:"auto",minWidth:48,height:48},"extended"===t.variant&&"small"===t.size&&{width:"auto",padding:"0 8px",borderRadius:17,minWidth:34,height:34},"extended"===t.variant&&"medium"===t.size&&{width:"auto",padding:"0 16px",borderRadius:20,minWidth:40,height:40},"inherit"===t.color&&{color:"inherit"})}),(({theme:e,ownerState:t})=>(0,Jt.A)({},"inherit"!==t.color&&"default"!==t.color&&null!=(e.vars||e).palette[t.color]&&{color:(e.vars||e).palette[t.color].contrastText,backgroundColor:(e.vars||e).palette[t.color].main,"&:hover":{backgroundColor:(e.vars||e).palette[t.color].dark,"@media (hover: none)":{backgroundColor:(e.vars||e).palette[t.color].main}}})),(({theme:e})=>({[`&.${Ru.disabled}`]:{color:(e.vars||e).palette.action.disabled,boxShadow:(e.vars||e).shadows[0],backgroundColor:(e.vars||e).palette.action.disabledBackground}}))),ju=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiFab"}),{children:r,className:o,color:i="default",component:a="button",disabled:s=!1,disableFocusRipple:l=!1,focusVisibleClassName:u,size:c="large",variant:d="circular"}=n,f=(0,qn.A)(n,Tu),p=(0,Jt.A)({},n,{color:i,component:a,disabled:s,disableFocusRipple:l,size:c,variant:d}),h=(e=>{const{color:t,variant:n,classes:r,size:o}=e,i={root:["root",n,`size${(0,tr.A)(o)}`,"inherit"===t?"colorInherit":t]},a=(0,Qn.A)(i,Pu,r);return(0,Jt.A)({},r,a)})(p);return(0,ir.jsx)(Fu,(0,Jt.A)({className:(0,Kn.A)(h.root,o),component:a,disabled:s,focusRipple:!l,focusVisibleClassName:(0,Kn.A)(h.focusVisible,u),ownerState:p,ref:t},f,{classes:h,children:r}))}));var Nu=o(8586);const Iu=["addEndListener","appear","children","easing","in","onEnter","onEntered","onEntering","onExit","onExited","onExiting","style","timeout","TransitionComponent"];function Lu(e){return`scale(${e}, ${e**2})`}const Mu={entering:{opacity:1,transform:Lu(1)},entered:{opacity:1,transform:"none"}},Du="undefined"!=typeof navigator&&/^((?!chrome|android).)*(safari|mobile)/i.test(navigator.userAgent)&&/(os |version\/)15(.|_)4/i.test(navigator.userAgent),Bu=B.forwardRef((function(e,t){const{addEndListener:n,appear:r=!0,children:o,easing:i,in:a,onEnter:s,onEntered:l,onEntering:u,onExit:c,onExited:d,onExiting:f,style:p,timeout:h="auto",TransitionComponent:m=Vr}=e,g=(0,qn.A)(e,Iu),v=B.useRef(),y=B.useRef(),b=Jr(),w=B.useRef(null),E=(0,Qr.A)(w,o.ref,t),S=e=>t=>{if(e){const n=w.current;void 0===t?e(n):e(n,t)}},x=S(u),A=S(((e,t)=>{Yr(e);const{duration:n,delay:r,easing:o}=Xr({style:p,timeout:h,easing:i},{mode:"enter"});let a;"auto"===h?(a=b.transitions.getAutoHeightDuration(e.clientHeight),y.current=a):a=n,e.style.transition=[b.transitions.create("opacity",{duration:a,delay:r}),b.transitions.create("transform",{duration:Du?a:.666*a,delay:r,easing:o})].join(","),s&&s(e,t)})),k=S(l),_=S(f),C=S((e=>{const{duration:t,delay:n,easing:r}=Xr({style:p,timeout:h,easing:i},{mode:"exit"});let o;"auto"===h?(o=b.transitions.getAutoHeightDuration(e.clientHeight),y.current=o):o=t,e.style.transition=[b.transitions.create("opacity",{duration:o,delay:n}),b.transitions.create("transform",{duration:Du?o:.666*o,delay:Du?n:n||.333*o,easing:r})].join(","),e.style.opacity=0,e.style.transform=Lu(.75),c&&c(e)})),O=S(d);return B.useEffect((()=>()=>{clearTimeout(v.current)}),[]),(0,ir.jsx)(m,(0,Jt.A)({appear:r,in:a,nodeRef:w,onEnter:A,onEntered:k,onEntering:x,onExit:C,onExited:O,onExiting:_,addEndListener:e=>{"auto"===h&&(v.current=setTimeout(e,y.current||0)),n&&n(w.current,e)},timeout:"auto"===h?null:h},g,{children:(e,t)=>B.cloneElement(o,(0,Jt.A)({style:(0,Jt.A)({opacity:0,transform:Lu(.75),visibility:"exited"!==e||a?void 0:"hidden"},Mu[e],p,o.props.style),ref:E},t))}))}));Bu.muiSupportAuto=!0;const Uu=Bu;function zu(e){if(null==e)return window;if("[object Window]"!==e.toString()){var t=e.ownerDocument;return t&&t.defaultView||window}return e}function $u(e){return e instanceof zu(e).Element||e instanceof Element}function Hu(e){return e instanceof zu(e).HTMLElement||e instanceof HTMLElement}function Wu(e){return"undefined"!=typeof ShadowRoot&&(e instanceof zu(e).ShadowRoot||e instanceof ShadowRoot)}var Vu=Math.max,qu=Math.min,Ku=Math.round;function Gu(){var e=navigator.userAgentData;return null!=e&&e.brands&&Array.isArray(e.brands)?e.brands.map((function(e){return e.brand+"/"+e.version})).join(" "):navigator.userAgent}function Ju(){return!/^((?!chrome|android).)*safari/i.test(Gu())}function Yu(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=!1);var r=e.getBoundingClientRect(),o=1,i=1;t&&Hu(e)&&(o=e.offsetWidth>0&&Ku(r.width)/e.offsetWidth||1,i=e.offsetHeight>0&&Ku(r.height)/e.offsetHeight||1);var a=($u(e)?zu(e):window).visualViewport,s=!Ju()&&n,l=(r.left+(s&&a?a.offsetLeft:0))/o,u=(r.top+(s&&a?a.offsetTop:0))/i,c=r.width/o,d=r.height/i;return{width:c,height:d,top:u,right:l+c,bottom:u+d,left:l,x:l,y:u}}function Xu(e){var t=zu(e);return{scrollLeft:t.pageXOffset,scrollTop:t.pageYOffset}}function Qu(e){return e?(e.nodeName||"").toLowerCase():null}function Zu(e){return(($u(e)?e.ownerDocument:e.document)||window.document).documentElement}function ec(e){return Yu(Zu(e)).left+Xu(e).scrollLeft}function tc(e){return zu(e).getComputedStyle(e)}function nc(e){var t=tc(e),n=t.overflow,r=t.overflowX,o=t.overflowY;return/auto|scroll|overlay|hidden/.test(n+o+r)}function rc(e,t,n){void 0===n&&(n=!1);var r,o,i=Hu(t),a=Hu(t)&&function(e){var t=e.getBoundingClientRect(),n=Ku(t.width)/e.offsetWidth||1,r=Ku(t.height)/e.offsetHeight||1;return 1!==n||1!==r}(t),s=Zu(t),l=Yu(e,a,n),u={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(i||!i&&!n)&&(("body"!==Qu(t)||nc(s))&&(u=(r=t)!==zu(r)&&Hu(r)?{scrollLeft:(o=r).scrollLeft,scrollTop:o.scrollTop}:Xu(r)),Hu(t)?((c=Yu(t,!0)).x+=t.clientLeft,c.y+=t.clientTop):s&&(c.x=ec(s))),{x:l.left+u.scrollLeft-c.x,y:l.top+u.scrollTop-c.y,width:l.width,height:l.height}}function oc(e){var t=Yu(e),n=e.offsetWidth,r=e.offsetHeight;return Math.abs(t.width-n)<=1&&(n=t.width),Math.abs(t.height-r)<=1&&(r=t.height),{x:e.offsetLeft,y:e.offsetTop,width:n,height:r}}function ic(e){return"html"===Qu(e)?e:e.assignedSlot||e.parentNode||(Wu(e)?e.host:null)||Zu(e)}function ac(e){return["html","body","#document"].indexOf(Qu(e))>=0?e.ownerDocument.body:Hu(e)&&nc(e)?e:ac(ic(e))}function sc(e,t){var n;void 0===t&&(t=[]);var r=ac(e),o=r===(null==(n=e.ownerDocument)?void 0:n.body),i=zu(r),a=o?[i].concat(i.visualViewport||[],nc(r)?r:[]):r,s=t.concat(a);return o?s:s.concat(sc(ic(a)))}function lc(e){return["table","td","th"].indexOf(Qu(e))>=0}function uc(e){return Hu(e)&&"fixed"!==tc(e).position?e.offsetParent:null}function cc(e){for(var t=zu(e),n=uc(e);n&&lc(n)&&"static"===tc(n).position;)n=uc(n);return n&&("html"===Qu(n)||"body"===Qu(n)&&"static"===tc(n).position)?t:n||function(e){var t=/firefox/i.test(Gu());if(/Trident/i.test(Gu())&&Hu(e)&&"fixed"===tc(e).position)return null;var n=ic(e);for(Wu(n)&&(n=n.host);Hu(n)&&["html","body"].indexOf(Qu(n))<0;){var r=tc(n);if("none"!==r.transform||"none"!==r.perspective||"paint"===r.contain||-1!==["transform","perspective"].indexOf(r.willChange)||t&&"filter"===r.willChange||t&&r.filter&&"none"!==r.filter)return n;n=n.parentNode}return null}(e)||t}var dc="top",fc="bottom",pc="right",hc="left",mc="auto",gc=[dc,fc,pc,hc],vc="start",yc="end",bc="viewport",wc="popper",Ec=gc.reduce((function(e,t){return e.concat([t+"-"+vc,t+"-"+yc])}),[]),Sc=[].concat(gc,[mc]).reduce((function(e,t){return e.concat([t,t+"-"+vc,t+"-"+yc])}),[]),xc=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function Ac(e){var t=new Map,n=new Set,r=[];function o(e){n.add(e.name),[].concat(e.requires||[],e.requiresIfExists||[]).forEach((function(e){if(!n.has(e)){var r=t.get(e);r&&o(r)}})),r.push(e)}return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||o(e)})),r}var kc={placement:"bottom",modifiers:[],strategy:"absolute"};function _c(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return!t.some((function(e){return!(e&&"function"==typeof e.getBoundingClientRect)}))}function Cc(e){void 0===e&&(e={});var t=e,n=t.defaultModifiers,r=void 0===n?[]:n,o=t.defaultOptions,i=void 0===o?kc:o;return function(e,t,n){void 0===n&&(n=i);var o,a,s={placement:"bottom",orderedModifiers:[],options:Object.assign({},kc,i),modifiersData:{},elements:{reference:e,popper:t},attributes:{},styles:{}},l=[],u=!1,c={state:s,setOptions:function(n){var o="function"==typeof n?n(s.options):n;d(),s.options=Object.assign({},i,s.options,o),s.scrollParents={reference:$u(e)?sc(e):e.contextElement?sc(e.contextElement):[],popper:sc(t)};var a,u,f=function(e){var t=Ac(e);return xc.reduce((function(e,n){return e.concat(t.filter((function(e){return e.phase===n})))}),[])}((a=[].concat(r,s.options.modifiers),u=a.reduce((function(e,t){var n=e[t.name];return e[t.name]=n?Object.assign({},n,t,{options:Object.assign({},n.options,t.options),data:Object.assign({},n.data,t.data)}):t,e}),{}),Object.keys(u).map((function(e){return u[e]}))));return s.orderedModifiers=f.filter((function(e){return e.enabled})),s.orderedModifiers.forEach((function(e){var t=e.name,n=e.options,r=void 0===n?{}:n,o=e.effect;if("function"==typeof o){var i=o({state:s,name:t,instance:c,options:r});l.push(i||function(){})}})),c.update()},forceUpdate:function(){if(!u){var e=s.elements,t=e.reference,n=e.popper;if(_c(t,n)){s.rects={reference:rc(t,cc(n),"fixed"===s.options.strategy),popper:oc(n)},s.reset=!1,s.placement=s.options.placement,s.orderedModifiers.forEach((function(e){return s.modifiersData[e.name]=Object.assign({},e.data)}));for(var r=0;r<s.orderedModifiers.length;r++)if(!0!==s.reset){var o=s.orderedModifiers[r],i=o.fn,a=o.options,l=void 0===a?{}:a,d=o.name;"function"==typeof i&&(s=i({state:s,options:l,name:d,instance:c})||s)}else s.reset=!1,r=-1}}},update:(o=function(){return new Promise((function(e){c.forceUpdate(),e(s)}))},function(){return a||(a=new Promise((function(e){Promise.resolve().then((function(){a=void 0,e(o())}))}))),a}),destroy:function(){d(),u=!0}};if(!_c(e,t))return c;function d(){l.forEach((function(e){return e()})),l=[]}return c.setOptions(n).then((function(e){!u&&n.onFirstUpdate&&n.onFirstUpdate(e)})),c}}var Oc={passive:!0};function Pc(e){return e.split("-")[0]}function Rc(e){return e.split("-")[1]}function Tc(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}function Fc(e){var t,n=e.reference,r=e.element,o=e.placement,i=o?Pc(o):null,a=o?Rc(o):null,s=n.x+n.width/2-r.width/2,l=n.y+n.height/2-r.height/2;switch(i){case dc:t={x:s,y:n.y-r.height};break;case fc:t={x:s,y:n.y+n.height};break;case pc:t={x:n.x+n.width,y:l};break;case hc:t={x:n.x-r.width,y:l};break;default:t={x:n.x,y:n.y}}var u=i?Tc(i):null;if(null!=u){var c="y"===u?"height":"width";switch(a){case vc:t[u]=t[u]-(n[c]/2-r[c]/2);break;case yc:t[u]=t[u]+(n[c]/2-r[c]/2)}}return t}var jc={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Nc(e){var t,n=e.popper,r=e.popperRect,o=e.placement,i=e.variation,a=e.offsets,s=e.position,l=e.gpuAcceleration,u=e.adaptive,c=e.roundOffsets,d=e.isFixed,f=a.x,p=void 0===f?0:f,h=a.y,m=void 0===h?0:h,g="function"==typeof c?c({x:p,y:m}):{x:p,y:m};p=g.x,m=g.y;var v=a.hasOwnProperty("x"),y=a.hasOwnProperty("y"),b=hc,w=dc,E=window;if(u){var S=cc(n),x="clientHeight",A="clientWidth";S===zu(n)&&"static"!==tc(S=Zu(n)).position&&"absolute"===s&&(x="scrollHeight",A="scrollWidth"),(o===dc||(o===hc||o===pc)&&i===yc)&&(w=fc,m-=(d&&S===E&&E.visualViewport?E.visualViewport.height:S[x])-r.height,m*=l?1:-1),o!==hc&&(o!==dc&&o!==fc||i!==yc)||(b=pc,p-=(d&&S===E&&E.visualViewport?E.visualViewport.width:S[A])-r.width,p*=l?1:-1)}var k,_=Object.assign({position:s},u&&jc),C=!0===c?function(e,t){var n=e.x,r=e.y,o=t.devicePixelRatio||1;return{x:Ku(n*o)/o||0,y:Ku(r*o)/o||0}}({x:p,y:m},zu(n)):{x:p,y:m};return p=C.x,m=C.y,l?Object.assign({},_,((k={})[w]=y?"0":"",k[b]=v?"0":"",k.transform=(E.devicePixelRatio||1)<=1?"translate("+p+"px, "+m+"px)":"translate3d("+p+"px, "+m+"px, 0)",k)):Object.assign({},_,((t={})[w]=y?m+"px":"",t[b]=v?p+"px":"",t.transform="",t))}var Ic={left:"right",right:"left",bottom:"top",top:"bottom"};function Lc(e){return e.replace(/left|right|bottom|top/g,(function(e){return Ic[e]}))}var Mc={start:"end",end:"start"};function Dc(e){return e.replace(/start|end/g,(function(e){return Mc[e]}))}function Bc(e,t){var n=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if(n&&Wu(n)){var r=t;do{if(r&&e.isSameNode(r))return!0;r=r.parentNode||r.host}while(r)}return!1}function Uc(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function zc(e,t,n){return t===bc?Uc(function(e,t){var n=zu(e),r=Zu(e),o=n.visualViewport,i=r.clientWidth,a=r.clientHeight,s=0,l=0;if(o){i=o.width,a=o.height;var u=Ju();(u||!u&&"fixed"===t)&&(s=o.offsetLeft,l=o.offsetTop)}return{width:i,height:a,x:s+ec(e),y:l}}(e,n)):$u(t)?function(e,t){var n=Yu(e,!1,"fixed"===t);return n.top=n.top+e.clientTop,n.left=n.left+e.clientLeft,n.bottom=n.top+e.clientHeight,n.right=n.left+e.clientWidth,n.width=e.clientWidth,n.height=e.clientHeight,n.x=n.left,n.y=n.top,n}(t,n):Uc(function(e){var t,n=Zu(e),r=Xu(e),o=null==(t=e.ownerDocument)?void 0:t.body,i=Vu(n.scrollWidth,n.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),a=Vu(n.scrollHeight,n.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),s=-r.scrollLeft+ec(e),l=-r.scrollTop;return"rtl"===tc(o||n).direction&&(s+=Vu(n.clientWidth,o?o.clientWidth:0)-i),{width:i,height:a,x:s,y:l}}(Zu(e)))}function $c(e){return Object.assign({},{top:0,right:0,bottom:0,left:0},e)}function Hc(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function Wc(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=void 0===r?e.placement:r,i=n.strategy,a=void 0===i?e.strategy:i,s=n.boundary,l=void 0===s?"clippingParents":s,u=n.rootBoundary,c=void 0===u?bc:u,d=n.elementContext,f=void 0===d?wc:d,p=n.altBoundary,h=void 0!==p&&p,m=n.padding,g=void 0===m?0:m,v=$c("number"!=typeof g?g:Hc(g,gc)),y=f===wc?"reference":wc,b=e.rects.popper,w=e.elements[h?y:f],E=function(e,t,n,r){var o="clippingParents"===t?function(e){var t=sc(ic(e)),n=["absolute","fixed"].indexOf(tc(e).position)>=0&&Hu(e)?cc(e):e;return $u(n)?t.filter((function(e){return $u(e)&&Bc(e,n)&&"body"!==Qu(e)})):[]}(e):[].concat(t),i=[].concat(o,[n]),a=i[0],s=i.reduce((function(t,n){var o=zc(e,n,r);return t.top=Vu(o.top,t.top),t.right=qu(o.right,t.right),t.bottom=qu(o.bottom,t.bottom),t.left=Vu(o.left,t.left),t}),zc(e,a,r));return s.width=s.right-s.left,s.height=s.bottom-s.top,s.x=s.left,s.y=s.top,s}($u(w)?w:w.contextElement||Zu(e.elements.popper),l,c,a),S=Yu(e.elements.reference),x=Fc({reference:S,element:b,strategy:"absolute",placement:o}),A=Uc(Object.assign({},b,x)),k=f===wc?A:S,_={top:E.top-k.top+v.top,bottom:k.bottom-E.bottom+v.bottom,left:E.left-k.left+v.left,right:k.right-E.right+v.right},C=e.modifiersData.offset;if(f===wc&&C){var O=C[o];Object.keys(_).forEach((function(e){var t=[pc,fc].indexOf(e)>=0?1:-1,n=[dc,fc].indexOf(e)>=0?"y":"x";_[e]+=O[n]*t}))}return _}function Vc(e,t,n){return Vu(e,qu(t,n))}function qc(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function Kc(e){return[dc,pc,fc,hc].some((function(t){return e[t]>=0}))}var Gc=Cc({defaultModifiers:[{name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var t=e.state,n=e.instance,r=e.options,o=r.scroll,i=void 0===o||o,a=r.resize,s=void 0===a||a,l=zu(t.elements.popper),u=[].concat(t.scrollParents.reference,t.scrollParents.popper);return i&&u.forEach((function(e){e.addEventListener("scroll",n.update,Oc)})),s&&l.addEventListener("resize",n.update,Oc),function(){i&&u.forEach((function(e){e.removeEventListener("scroll",n.update,Oc)})),s&&l.removeEventListener("resize",n.update,Oc)}},data:{}},{name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state,n=e.name;t.modifiersData[n]=Fc({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},{name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options,r=n.gpuAcceleration,o=void 0===r||r,i=n.adaptive,a=void 0===i||i,s=n.roundOffsets,l=void 0===s||s,u={placement:Pc(t.placement),variation:Rc(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:o,isFixed:"fixed"===t.options.strategy};null!=t.modifiersData.popperOffsets&&(t.styles.popper=Object.assign({},t.styles.popper,Nc(Object.assign({},u,{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:a,roundOffsets:l})))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign({},t.styles.arrow,Nc(Object.assign({},u,{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-placement":t.placement})},data:{}},{name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach((function(e){var n=t.styles[e]||{},r=t.attributes[e]||{},o=t.elements[e];Hu(o)&&Qu(o)&&(Object.assign(o.style,n),Object.keys(r).forEach((function(e){var t=r[e];!1===t?o.removeAttribute(e):o.setAttribute(e,!0===t?"":t)})))}))},effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.styles=n,t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach((function(e){var r=t.elements[e],o=t.attributes[e]||{},i=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce((function(e,t){return e[t]="",e}),{});Hu(r)&&Qu(r)&&(Object.assign(r.style,i),Object.keys(o).forEach((function(e){r.removeAttribute(e)})))}))}},requires:["computeStyles"]},{name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.offset,i=void 0===o?[0,0]:o,a=Sc.reduce((function(e,n){return e[n]=function(e,t,n){var r=Pc(e),o=[hc,dc].indexOf(r)>=0?-1:1,i="function"==typeof n?n(Object.assign({},t,{placement:e})):n,a=i[0],s=i[1];return a=a||0,s=(s||0)*o,[hc,pc].indexOf(r)>=0?{x:s,y:a}:{x:a,y:s}}(n,t.rects,i),e}),{}),s=a[t.placement],l=s.x,u=s.y;null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=l,t.modifiersData.popperOffsets.y+=u),t.modifiersData[r]=a}},{name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name;if(!t.modifiersData[r]._skip){for(var o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0===a||a,l=n.fallbackPlacements,u=n.padding,c=n.boundary,d=n.rootBoundary,f=n.altBoundary,p=n.flipVariations,h=void 0===p||p,m=n.allowedAutoPlacements,g=t.options.placement,v=Pc(g),y=l||(v!==g&&h?function(e){if(Pc(e)===mc)return[];var t=Lc(e);return[Dc(e),t,Dc(t)]}(g):[Lc(g)]),b=[g].concat(y).reduce((function(e,n){return e.concat(Pc(n)===mc?function(e,t){void 0===t&&(t={});var n=t,r=n.placement,o=n.boundary,i=n.rootBoundary,a=n.padding,s=n.flipVariations,l=n.allowedAutoPlacements,u=void 0===l?Sc:l,c=Rc(r),d=c?s?Ec:Ec.filter((function(e){return Rc(e)===c})):gc,f=d.filter((function(e){return u.indexOf(e)>=0}));0===f.length&&(f=d);var p=f.reduce((function(t,n){return t[n]=Wc(e,{placement:n,boundary:o,rootBoundary:i,padding:a})[Pc(n)],t}),{});return Object.keys(p).sort((function(e,t){return p[e]-p[t]}))}(t,{placement:n,boundary:c,rootBoundary:d,padding:u,flipVariations:h,allowedAutoPlacements:m}):n)}),[]),w=t.rects.reference,E=t.rects.popper,S=new Map,x=!0,A=b[0],k=0;k<b.length;k++){var _=b[k],C=Pc(_),O=Rc(_)===vc,P=[dc,fc].indexOf(C)>=0,R=P?"width":"height",T=Wc(t,{placement:_,boundary:c,rootBoundary:d,altBoundary:f,padding:u}),F=P?O?pc:hc:O?fc:dc;w[R]>E[R]&&(F=Lc(F));var j=Lc(F),N=[];if(i&&N.push(T[C]<=0),s&&N.push(T[F]<=0,T[j]<=0),N.every((function(e){return e}))){A=_,x=!1;break}S.set(_,N)}if(x)for(var I=function(e){var t=b.find((function(t){var n=S.get(t);if(n)return n.slice(0,e).every((function(e){return e}))}));if(t)return A=t,"break"},L=h?3:1;L>0&&"break"!==I(L);L--);t.placement!==A&&(t.modifiersData[r]._skip=!0,t.placement=A,t.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}},{name:"preventOverflow",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options,r=e.name,o=n.mainAxis,i=void 0===o||o,a=n.altAxis,s=void 0!==a&&a,l=n.boundary,u=n.rootBoundary,c=n.altBoundary,d=n.padding,f=n.tether,p=void 0===f||f,h=n.tetherOffset,m=void 0===h?0:h,g=Wc(t,{boundary:l,rootBoundary:u,padding:d,altBoundary:c}),v=Pc(t.placement),y=Rc(t.placement),b=!y,w=Tc(v),E="x"===w?"y":"x",S=t.modifiersData.popperOffsets,x=t.rects.reference,A=t.rects.popper,k="function"==typeof m?m(Object.assign({},t.rects,{placement:t.placement})):m,_="number"==typeof k?{mainAxis:k,altAxis:k}:Object.assign({mainAxis:0,altAxis:0},k),C=t.modifiersData.offset?t.modifiersData.offset[t.placement]:null,O={x:0,y:0};if(S){if(i){var P,R="y"===w?dc:hc,T="y"===w?fc:pc,F="y"===w?"height":"width",j=S[w],N=j+g[R],I=j-g[T],L=p?-A[F]/2:0,M=y===vc?x[F]:A[F],D=y===vc?-A[F]:-x[F],B=t.elements.arrow,U=p&&B?oc(B):{width:0,height:0},z=t.modifiersData["arrow#persistent"]?t.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},$=z[R],H=z[T],W=Vc(0,x[F],U[F]),V=b?x[F]/2-L-W-$-_.mainAxis:M-W-$-_.mainAxis,q=b?-x[F]/2+L+W+H+_.mainAxis:D+W+H+_.mainAxis,K=t.elements.arrow&&cc(t.elements.arrow),G=K?"y"===w?K.clientTop||0:K.clientLeft||0:0,J=null!=(P=null==C?void 0:C[w])?P:0,Y=j+q-J,X=Vc(p?qu(N,j+V-J-G):N,j,p?Vu(I,Y):I);S[w]=X,O[w]=X-j}if(s){var Q,Z="x"===w?dc:hc,ee="x"===w?fc:pc,te=S[E],ne="y"===E?"height":"width",re=te+g[Z],oe=te-g[ee],ie=-1!==[dc,hc].indexOf(v),ae=null!=(Q=null==C?void 0:C[E])?Q:0,se=ie?re:te-x[ne]-A[ne]-ae+_.altAxis,le=ie?te+x[ne]+A[ne]-ae-_.altAxis:oe,ue=p&&ie?function(e,t,n){var r=Vc(e,t,n);return r>n?n:r}(se,te,le):Vc(p?se:re,te,p?le:oe);S[E]=ue,O[E]=ue-te}t.modifiersData[r]=O}},requiresIfExists:["offset"]},{name:"arrow",enabled:!0,phase:"main",fn:function(e){var t,n=e.state,r=e.name,o=e.options,i=n.elements.arrow,a=n.modifiersData.popperOffsets,s=Pc(n.placement),l=Tc(s),u=[hc,pc].indexOf(s)>=0?"height":"width";if(i&&a){var c=function(e,t){return $c("number"!=typeof(e="function"==typeof e?e(Object.assign({},t.rects,{placement:t.placement})):e)?e:Hc(e,gc))}(o.padding,n),d=oc(i),f="y"===l?dc:hc,p="y"===l?fc:pc,h=n.rects.reference[u]+n.rects.reference[l]-a[l]-n.rects.popper[u],m=a[l]-n.rects.reference[l],g=cc(i),v=g?"y"===l?g.clientHeight||0:g.clientWidth||0:0,y=h/2-m/2,b=c[f],w=v-d[u]-c[p],E=v/2-d[u]/2+y,S=Vc(b,E,w),x=l;n.modifiersData[r]=((t={})[x]=S,t.centerOffset=S-E,t)}},effect:function(e){var t=e.state,n=e.options.element,r=void 0===n?"[data-popper-arrow]":n;null!=r&&("string"!=typeof r||(r=t.elements.popper.querySelector(r)))&&Bc(t.elements.popper,r)&&(t.elements.arrow=r)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]},{name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(e){var t=e.state,n=e.name,r=t.rects.reference,o=t.rects.popper,i=t.modifiersData.preventOverflow,a=Wc(t,{elementContext:"reference"}),s=Wc(t,{altBoundary:!0}),l=qc(a,r),u=qc(s,o,i),c=Kc(l),d=Kc(u);t.modifiersData[n]={referenceClippingOffsets:l,popperEscapeOffsets:u,isReferenceHidden:c,hasPopperEscaped:d},t.attributes.popper=Object.assign({},t.attributes.popper,{"data-popper-reference-hidden":c,"data-popper-escaped":d})}}]});function Jc(e){return(0,rr.A)("MuiPopper",e)}(0,nr.A)("MuiPopper",["root"]);const Yc={disableDefaultClasses:!1},Xc=B.createContext(Yc),Qc=["anchorEl","children","direction","disablePortal","modifiers","open","placement","popperOptions","popperRef","slotProps","slots","TransitionProps","ownerState"],Zc=["anchorEl","children","container","direction","disablePortal","keepMounted","modifiers","open","placement","popperOptions","popperRef","style","transition","slotProps","slots"];function ed(e){return"function"==typeof e?e():e}const td={},nd=B.forwardRef((function(e,t){var n;const{anchorEl:r,children:o,direction:i,disablePortal:a,modifiers:s,open:l,placement:u,popperOptions:c,popperRef:d,slotProps:f={},slots:p={},TransitionProps:h}=e,m=(0,qn.A)(e,Qc),g=B.useRef(null),v=(0,dr.A)(g,t),y=B.useRef(null),b=(0,dr.A)(y,d),w=B.useRef(b);(0,jr.A)((()=>{w.current=b}),[b]),B.useImperativeHandle(d,(()=>y.current),[]);const E=function(e,t){if("ltr"===t)return e;switch(e){case"bottom-end":return"bottom-start";case"bottom-start":return"bottom-end";case"top-end":return"top-start";case"top-start":return"top-end";default:return e}}(u,i),[S,x]=B.useState(E),[A,k]=B.useState(ed(r));B.useEffect((()=>{y.current&&y.current.forceUpdate()})),B.useEffect((()=>{r&&k(ed(r))}),[r]),(0,jr.A)((()=>{if(!A||!l)return;let e=[{name:"preventOverflow",options:{altBoundary:a}},{name:"flip",options:{altBoundary:a}},{name:"onUpdate",enabled:!0,phase:"afterWrite",fn:({state:e})=>{(e=>{x(e.placement)})(e)}}];null!=s&&(e=e.concat(s)),c&&null!=c.modifiers&&(e=e.concat(c.modifiers));const t=Gc(A,g.current,(0,Jt.A)({placement:E},c,{modifiers:e}));return w.current(t),()=>{t.destroy(),w.current(null)}}),[A,a,s,l,c,E]);const _={placement:S};null!==h&&(_.TransitionProps=h);const C=(0,Qn.A)({root:["root"]},function(e){const{disableDefaultClasses:t}=B.useContext(Xc);return n=>t?"":e(n)}(Jc)),O=null!=(n=p.root)?n:"div",P=vr({elementType:O,externalSlotProps:f.root,externalForwardedProps:m,additionalProps:{role:"tooltip",ref:v},ownerState:e,className:C.root});return(0,ir.jsx)(O,(0,Jt.A)({},P,{children:"function"==typeof o?o(_):o}))})),rd=B.forwardRef((function(e,t){const{anchorEl:n,children:r,container:o,direction:i="ltr",disablePortal:a=!1,keepMounted:s=!1,modifiers:l,open:u,placement:c="bottom",popperOptions:d=td,popperRef:f,style:p,transition:h=!1,slotProps:m={},slots:g={}}=e,v=(0,qn.A)(e,Zc),[y,b]=B.useState(!0);if(!s&&!u&&(!h||y))return null;let w;if(o)w=o;else if(n){const e=ed(n);w=e&&void 0!==e.nodeType?(0,yr.A)(e).body:(0,yr.A)(null).body}const E=u||!s||h&&!y?void 0:"none",S=h?{in:u,onEnter:()=>{b(!1)},onExited:()=>{b(!0)}}:void 0;return(0,ir.jsx)(Ir,{disablePortal:a,container:w,children:(0,ir.jsx)(nd,(0,Jt.A)({anchorEl:n,direction:i,disablePortal:a,modifiers:l,ref:t,open:h?!y:u,placement:c,popperOptions:d,popperRef:f,slotProps:m,slots:g},v,{style:(0,Jt.A)({position:"fixed",top:0,left:0,display:E},p),TransitionProps:S,children:r}))})}));var od=o(3951);const id=["anchorEl","component","components","componentsProps","container","disablePortal","keepMounted","modifiers","open","placement","popperOptions","popperRef","transition","slots","slotProps"],ad=(0,Zn.Ay)(rd,{name:"MuiPopper",slot:"Root",overridesResolver:(e,t)=>t.root})({}),sd=B.forwardRef((function(e,t){var n;const r=(0,od.A)(),o=(0,er.A)({props:e,name:"MuiPopper"}),{anchorEl:i,component:a,components:s,componentsProps:l,container:u,disablePortal:c,keepMounted:d,modifiers:f,open:p,placement:h,popperOptions:m,popperRef:g,transition:v,slots:y,slotProps:b}=o,w=(0,qn.A)(o,id),E=null!=(n=null==y?void 0:y.root)?n:null==s?void 0:s.Root,S=(0,Jt.A)({anchorEl:i,container:u,disablePortal:c,keepMounted:d,modifiers:f,open:p,placement:h,popperOptions:m,popperRef:g,transition:v},w);return(0,ir.jsx)(ad,(0,Jt.A)({as:a,direction:null==r?void 0:r.direction,slots:{root:E},slotProps:null!=b?b:l},S,{ref:t}))}));var ld=o(1668);function ud(e){return(0,rr.A)("MuiTooltip",e)}const cd=(0,nr.A)("MuiTooltip",["popper","popperInteractive","popperArrow","popperClose","tooltip","tooltipArrow","touch","tooltipPlacementLeft","tooltipPlacementRight","tooltipPlacementTop","tooltipPlacementBottom","arrow"]),dd=["arrow","children","classes","components","componentsProps","describeChild","disableFocusListener","disableHoverListener","disableInteractive","disableTouchListener","enterDelay","enterNextDelay","enterTouchDelay","followCursor","id","leaveDelay","leaveTouchDelay","onClose","onOpen","open","placement","PopperComponent","PopperProps","slotProps","slots","title","TransitionComponent","TransitionProps"],fd=(0,Zn.Ay)(sd,{name:"MuiTooltip",slot:"Popper",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.popper,!n.disableInteractive&&t.popperInteractive,n.arrow&&t.popperArrow,!n.open&&t.popperClose]}})((({theme:e,ownerState:t,open:n})=>(0,Jt.A)({zIndex:(e.vars||e).zIndex.tooltip,pointerEvents:"none"},!t.disableInteractive&&{pointerEvents:"auto"},!n&&{pointerEvents:"none"},t.arrow&&{[`&[data-popper-placement*="bottom"] .${cd.arrow}`]:{top:0,marginTop:"-0.71em","&::before":{transformOrigin:"0 100%"}},[`&[data-popper-placement*="top"] .${cd.arrow}`]:{bottom:0,marginBottom:"-0.71em","&::before":{transformOrigin:"100% 0"}},[`&[data-popper-placement*="right"] .${cd.arrow}`]:(0,Jt.A)({},t.isRtl?{right:0,marginRight:"-0.71em"}:{left:0,marginLeft:"-0.71em"},{height:"1em",width:"0.71em","&::before":{transformOrigin:"100% 100%"}}),[`&[data-popper-placement*="left"] .${cd.arrow}`]:(0,Jt.A)({},t.isRtl?{left:0,marginLeft:"-0.71em"}:{right:0,marginRight:"-0.71em"},{height:"1em",width:"0.71em","&::before":{transformOrigin:"0 0"}})}))),pd=(0,Zn.Ay)("div",{name:"MuiTooltip",slot:"Tooltip",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.tooltip,n.touch&&t.touch,n.arrow&&t.tooltipArrow,t[`tooltipPlacement${(0,tr.A)(n.placement.split("-")[0])}`]]}})((({theme:e,ownerState:t})=>{return(0,Jt.A)({backgroundColor:e.vars?e.vars.palette.Tooltip.bg:(0,vo.X4)(e.palette.grey[700],.92),borderRadius:(e.vars||e).shape.borderRadius,color:(e.vars||e).palette.common.white,fontFamily:e.typography.fontFamily,padding:"4px 8px",fontSize:e.typography.pxToRem(11),maxWidth:300,margin:2,wordWrap:"break-word",fontWeight:e.typography.fontWeightMedium},t.arrow&&{position:"relative",margin:0},t.touch&&{padding:"8px 16px",fontSize:e.typography.pxToRem(14),lineHeight:(n=16/14,Math.round(1e5*n)/1e5+"em"),fontWeight:e.typography.fontWeightRegular},{[`.${cd.popper}[data-popper-placement*="left"] &`]:(0,Jt.A)({transformOrigin:"right center"},t.isRtl?(0,Jt.A)({marginLeft:"14px"},t.touch&&{marginLeft:"24px"}):(0,Jt.A)({marginRight:"14px"},t.touch&&{marginRight:"24px"})),[`.${cd.popper}[data-popper-placement*="right"] &`]:(0,Jt.A)({transformOrigin:"left center"},t.isRtl?(0,Jt.A)({marginRight:"14px"},t.touch&&{marginRight:"24px"}):(0,Jt.A)({marginLeft:"14px"},t.touch&&{marginLeft:"24px"})),[`.${cd.popper}[data-popper-placement*="top"] &`]:(0,Jt.A)({transformOrigin:"center bottom",marginBottom:"14px"},t.touch&&{marginBottom:"24px"}),[`.${cd.popper}[data-popper-placement*="bottom"] &`]:(0,Jt.A)({transformOrigin:"center top",marginTop:"14px"},t.touch&&{marginTop:"24px"})});var n})),hd=(0,Zn.Ay)("span",{name:"MuiTooltip",slot:"Arrow",overridesResolver:(e,t)=>t.arrow})((({theme:e})=>({overflow:"hidden",position:"absolute",width:"1em",height:"0.71em",boxSizing:"border-box",color:e.vars?e.vars.palette.Tooltip.bg:(0,vo.X4)(e.palette.grey[700],.9),"&::before":{content:'""',margin:"auto",display:"block",width:"100%",height:"100%",backgroundColor:"currentColor",transform:"rotate(45deg)"}})));let md=!1,gd=null,vd={x:0,y:0};function yd(e,t){return n=>{t&&t(n),e(n)}}const bd=B.forwardRef((function(e,t){var n,r,o,i,a,s,l,u,c,d,f,p,h,m,g,v,y,b,w;const E=(0,er.A)({props:e,name:"MuiTooltip"}),{arrow:S=!1,children:x,components:A={},componentsProps:k={},describeChild:_=!1,disableFocusListener:C=!1,disableHoverListener:O=!1,disableInteractive:P=!1,disableTouchListener:R=!1,enterDelay:T=100,enterNextDelay:F=0,enterTouchDelay:j=700,followCursor:N=!1,id:I,leaveDelay:L=0,leaveTouchDelay:M=1500,onClose:D,onOpen:U,open:z,placement:$="bottom",PopperComponent:H,PopperProps:W={},slotProps:V={},slots:q={},title:K,TransitionComponent:G=Uu,TransitionProps:J}=E,Y=(0,qn.A)(E,dd),X=B.isValidElement(x)?x:(0,ir.jsx)("span",{children:x}),Q=Jr(),Z="rtl"===Q.direction,[ee,te]=B.useState(),[ne,re]=B.useState(null),oe=B.useRef(!1),ie=P||N,ae=B.useRef(),se=B.useRef(),le=B.useRef(),ue=B.useRef(),[ce,de]=(0,nu.A)({controlled:z,default:!1,name:"Tooltip",state:"open"});let fe=ce;const pe=(0,ld.A)(I),he=B.useRef(),me=B.useCallback((()=>{void 0!==he.current&&(document.body.style.WebkitUserSelect=he.current,he.current=void 0),clearTimeout(ue.current)}),[]);B.useEffect((()=>()=>{clearTimeout(ae.current),clearTimeout(se.current),clearTimeout(le.current),me()}),[me]);const ge=e=>{clearTimeout(gd),md=!0,de(!0),U&&!fe&&U(e)},ve=(0,Fo.A)((e=>{clearTimeout(gd),gd=setTimeout((()=>{md=!1}),800+L),de(!1),D&&fe&&D(e),clearTimeout(ae.current),ae.current=setTimeout((()=>{oe.current=!1}),Q.transitions.duration.shortest)})),ye=e=>{oe.current&&"touchstart"!==e.type||(ee&&ee.removeAttribute("title"),clearTimeout(se.current),clearTimeout(le.current),T||md&&F?se.current=setTimeout((()=>{ge(e)}),md?F:T):ge(e))},be=e=>{clearTimeout(se.current),clearTimeout(le.current),le.current=setTimeout((()=>{ve(e)}),L)},{isFocusVisibleRef:we,onBlur:Ee,onFocus:Se,ref:xe}=(0,jo.A)(),[,Ae]=B.useState(!1),ke=e=>{Ee(e),!1===we.current&&(Ae(!1),be(e))},_e=e=>{ee||te(e.currentTarget),Se(e),!0===we.current&&(Ae(!0),ye(e))},Ce=e=>{oe.current=!0;const t=X.props;t.onTouchStart&&t.onTouchStart(e)},Oe=ye,Pe=be;B.useEffect((()=>{if(fe)return document.addEventListener("keydown",e),()=>{document.removeEventListener("keydown",e)};function e(e){"Escape"!==e.key&&"Esc"!==e.key||ve(e)}}),[ve,fe]);const Re=(0,Qr.A)(X.ref,xe,te,t);K||0===K||(fe=!1);const Te=B.useRef(),Fe={},je="string"==typeof K;_?(Fe.title=fe||!je||O?null:K,Fe["aria-describedby"]=fe?pe:null):(Fe["aria-label"]=je?K:null,Fe["aria-labelledby"]=fe&&!je?pe:null);const Ne=(0,Jt.A)({},Fe,Y,X.props,{className:(0,Kn.A)(Y.className,X.props.className),onTouchStart:Ce,ref:Re},N?{onMouseMove:e=>{const t=X.props;t.onMouseMove&&t.onMouseMove(e),vd={x:e.clientX,y:e.clientY},Te.current&&Te.current.update()}}:{}),Ie={};R||(Ne.onTouchStart=e=>{Ce(e),clearTimeout(le.current),clearTimeout(ae.current),me(),he.current=document.body.style.WebkitUserSelect,document.body.style.WebkitUserSelect="none",ue.current=setTimeout((()=>{document.body.style.WebkitUserSelect=he.current,ye(e)}),j)},Ne.onTouchEnd=e=>{X.props.onTouchEnd&&X.props.onTouchEnd(e),me(),clearTimeout(le.current),le.current=setTimeout((()=>{ve(e)}),M)}),O||(Ne.onMouseOver=yd(Oe,Ne.onMouseOver),Ne.onMouseLeave=yd(Pe,Ne.onMouseLeave),ie||(Ie.onMouseOver=Oe,Ie.onMouseLeave=Pe)),C||(Ne.onFocus=yd(_e,Ne.onFocus),Ne.onBlur=yd(ke,Ne.onBlur),ie||(Ie.onFocus=_e,Ie.onBlur=ke));const Le=B.useMemo((()=>{var e;let t=[{name:"arrow",enabled:Boolean(ne),options:{element:ne,padding:4}}];return null!=(e=W.popperOptions)&&e.modifiers&&(t=t.concat(W.popperOptions.modifiers)),(0,Jt.A)({},W.popperOptions,{modifiers:t})}),[ne,W]),Me=(0,Jt.A)({},E,{isRtl:Z,arrow:S,disableInteractive:ie,placement:$,PopperComponentProp:H,touch:oe.current}),De=(e=>{const{classes:t,disableInteractive:n,arrow:r,touch:o,placement:i}=e,a={popper:["popper",!n&&"popperInteractive",r&&"popperArrow"],tooltip:["tooltip",r&&"tooltipArrow",o&&"touch",`tooltipPlacement${(0,tr.A)(i.split("-")[0])}`],arrow:["arrow"]};return(0,Qn.A)(a,ud,t)})(Me),Be=null!=(n=null!=(r=q.popper)?r:A.Popper)?n:fd,Ue=null!=(o=null!=(i=null!=(a=q.transition)?a:A.Transition)?i:G)?o:Uu,ze=null!=(s=null!=(l=q.tooltip)?l:A.Tooltip)?s:pd,$e=null!=(u=null!=(c=q.arrow)?c:A.Arrow)?u:hd,He=pr(Be,(0,Jt.A)({},W,null!=(d=V.popper)?d:k.popper,{className:(0,Kn.A)(De.popper,null==W?void 0:W.className,null==(f=null!=(p=V.popper)?p:k.popper)?void 0:f.className)}),Me),We=pr(Ue,(0,Jt.A)({},J,null!=(h=V.transition)?h:k.transition),Me),Ve=pr(ze,(0,Jt.A)({},null!=(m=V.tooltip)?m:k.tooltip,{className:(0,Kn.A)(De.tooltip,null==(g=null!=(v=V.tooltip)?v:k.tooltip)?void 0:g.className)}),Me),qe=pr($e,(0,Jt.A)({},null!=(y=V.arrow)?y:k.arrow,{className:(0,Kn.A)(De.arrow,null==(b=null!=(w=V.arrow)?w:k.arrow)?void 0:b.className)}),Me);return(0,ir.jsxs)(B.Fragment,{children:[B.cloneElement(X,Ne),(0,ir.jsx)(Be,(0,Jt.A)({as:null!=H?H:sd,placement:$,anchorEl:N?{getBoundingClientRect:()=>({top:vd.y,left:vd.x,right:vd.x,bottom:vd.y,width:0,height:0})}:ee,popperRef:Te,open:!!ee&&fe,id:pe,transition:!0},Ie,He,{popperOptions:Le,children:({TransitionProps:e})=>(0,ir.jsx)(Ue,(0,Jt.A)({timeout:Q.transitions.duration.shorter},e,We,{children:(0,ir.jsxs)(ze,(0,Jt.A)({},Ve,{children:[K,S?(0,ir.jsx)($e,(0,Jt.A)({},qe,{ref:re})):null]}))}))}))]})}));function wd(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Ed(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?wd(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):wd(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var Sd=function(e){return{root:{width:"100%",maxWidth:360,backgroundColor:e.palette.background.paper},paper:{width:"80%",maxHeight:435},fab:{marginTop:e.spacing(2)}}};function xd(e){return e.data.action?null:B.createElement(Nl,null)}function Ad(e){var t=la(Sd),n=fe((0,B.useState)({action:null,acks:[],confirmations:[]}),2),r=n[0],o=n[1],i=function(e){return function(t){if(t.target.checked&&r.acks.indexOf(e)<0){var n=[e].concat(r.acks);o(Ed(Ed({},r),{},{acks:n}))}else!t.target.checked&&r.acks.indexOf(e)>-1&&((n=[].concat(r.acks)).splice(r.acks.indexOf(e),1),o(Ed(Ed({},r),{},{acks:n})))}};function a(t,n){e.handleClose(),o(Ed(Ed({},r),{},{acks:[],confirmations:[],action:null}))}return B.createElement(Ls,{disableBackdropClick:!0,maxWidth:"xs",onClose:a,"aria-labelledby":"confirmation-dialog-title",open:e.open},B.createElement(Zs,{id:"confirmation-dialog-title"},"Action"),B.createElement(qs,{dividers:!0},B.createElement(Ei,null,B.Children.toArray(e.children).map((function(e){return B.cloneElement(e,{data:r,setData:o})}))),r.confirmations&&r.confirmations.length>0&&B.createElement(Au,{component:"fieldset",className:t.formControl},r.confirmations.map((function(e,t){return B.createElement(Ou,{key:t},B.createElement(tu,{control:B.createElement(yu,{checked:r.acks.indexOf(e)>-1,onChange:i(e),value:e}),label:e}))})))),B.createElement(Us,null,B.createElement(cl,{onClick:a,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(t,n){e.submit({value:r.action}),e.handleClose(),o(Ed(Ed({},r),{},{acks:[],confirmations:[],action:null}))},disabled:!r.action||!!r.confirmations&&!!r.confirmations.length&&r.confirmations.length!=r.acks.length,color:"secondary"},"Ok")))}function kd(e){var t=fe(B.useState(!1),2),n=t[0],r=t[1],o=la(Sd),i=ee().t;function a(e){e.stopPropagation(),r(!0)}if(e.title)var s=B.createElement(cl,{color:"primary","aria-label":"More","aria-haspopup":"true",onClick:a},e.title);else s=e.fab?B.createElement(ju,{color:"primary",onClick:a,className:o.fab},B.createElement(Nu.A,null)):B.createElement(gi,{"aria-label":"More","aria-haspopup":"true",onClick:a},B.createElement(Nu.A,null));return B.createElement(B.Fragment,null,B.createElement(bd,{title:i("Actions")},s),B.createElement(Ad,{submit:e.submit,path:e.path,selected:e.selected,node:e.node,handleClose:function(e){r(!1)},open:n,children:e.children}))}function _d(e){return B.createElement("div",null,B.Children.toArray(e.children).map((function(t){return B.cloneElement(t,{data:e.data,setData:e.setData})})))}function Cd(e){var t,n,r=fe(ve(),2),o=r[0].user;return r[1],e.data.action&&e.value!=e.data.action?null:B.createElement(Li,{button:!0,disabled:!(!e.disabled&&!e.data.action&&(void 0===e.requires||void 0!==o.grant&&("root"in o.grant||(!e.requires.role||e.requires.role in o.grant)&&(""==e.requires.namespace&&(e.requires.namespace="root"),!e.requires.namespace||(t=o.grant[e.requires.role],n=e.requires.namespace,t.filter((function(e){return n.includes(e)})),t.length))))),onClick:function(t){e.setData(Ed(Ed({},e.data),{},{action:e.value,confirmations:e.confirmations}))}},B.createElement(zi,null,e.icon),B.createElement(qi,{primary:e.text}))}function Od(e){var t,n,r="";if("string"==typeof e||"number"==typeof e)r+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(n=Od(e[t]))&&(r&&(r+=" "),r+=n);else for(t in e)e[t]&&(r&&(r+=" "),r+=t);return r}const Pd=function(){for(var e,t,n=0,r="";n<arguments.length;)(e=arguments[n++])&&(t=Od(e))&&(r&&(r+=" "),r+=t);return r};let Rd={data:""},Td=/(?:([\u0080-\uFFFF\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\s*)/g,Fd=/\/\*[^]*?\*\/|  +/g,jd=/\n+/g,Nd=(e,t)=>{let n="",r="",o="";for(let i in e){let a=e[i];"@"==i[0]?"i"==i[1]?n=i+" "+a+";":r+="f"==i[1]?Nd(a,i):i+"{"+Nd(a,"k"==i[1]?"":t)+"}":"object"==typeof a?r+=Nd(a,t?t.replace(/([^,])+/g,(e=>i.replace(/(^:.*)|([^,])+/g,(t=>/&/.test(t)?t.replace(/&/g,e):e?e+" "+t:t)))):i):null!=a&&(i=/^--/.test(i)?i:i.replace(/[A-Z]/g,"-$&").toLowerCase(),o+=Nd.p?Nd.p(i,a):i+":"+a+";")}return n+(t&&o?t+"{"+o+"}":o)+r},Id={},Ld=e=>{if("object"==typeof e){let t="";for(let n in e)t+=n+Ld(e[n]);return t}return e};function Md(e){let t=this||{},n=e.call?e(t.p):e;return((e,t,n,r,o)=>{let i=Ld(e),a=Id[i]||(Id[i]=(e=>{let t=0,n=11;for(;t<e.length;)n=101*n+e.charCodeAt(t++)>>>0;return"go"+n})(i));if(!Id[a]){let t=i!==e?e:(e=>{let t,n,r=[{}];for(;t=Td.exec(e.replace(Fd,""));)t[4]?r.shift():t[3]?(n=t[3].replace(jd," ").trim(),r.unshift(r[0][n]=r[0][n]||{})):r[0][t[1]]=t[2].replace(jd," ").trim();return r[0]})(e);Id[a]=Nd(o?{["@keyframes "+a]:t}:t,n?"":"."+a)}let s=n&&Id.g?Id.g:null;return n&&(Id.g=Id[a]),((e,t,n,r)=>{r?t.data=t.data.replace(r,e):-1===t.data.indexOf(e)&&(t.data=n?e+t.data:t.data+e)})(Id[a],t,r,s),a})(n.unshift?n.raw?((e,t,n)=>e.reduce(((e,r,o)=>{let i=t[o];if(i&&i.call){let e=i(n),t=e&&e.props&&e.props.className||/^go/.test(e)&&e;i=t?"."+t:e&&"object"==typeof e?e.props?"":Nd(e,""):!1===e?"":e}return e+r+(null==i?"":i)}),""))(n,[].slice.call(arguments,1),t.p):n.reduce(((e,n)=>Object.assign(e,n&&n.call?n(t.p):n)),{}):n,(r=t.target,"object"==typeof window?((r?r.querySelector("#_goober"):window._goober)||Object.assign((r||document.head).appendChild(document.createElement("style")),{innerHTML:" ",id:"_goober"})).firstChild:r||Rd),t.g,t.o,t.k);var r}function Dd(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function Bd(e,t,n){return t&&Dd(e.prototype,t),n&&Dd(e,n),e}function Ud(){return Ud=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},Ud.apply(this,arguments)}function zd(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function $d(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r<i.length;r++)n=i[r],t.indexOf(n)>=0||(o[n]=e[n]);return o}function Hd(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}Md.bind({g:1}),Md.bind({k:1});var Wd=function(){return""},Vd=B.createContext({enqueueSnackbar:Wd,closeSnackbar:Wd}),qd="@media (max-width:599.95px)",Kd="@media (min-width:600px)",Gd=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},Jd=function(e){return""+Gd(e.vertical)+Gd(e.horizontal)},Yd=function(e){return!!e||0===e},Xd="unmounted",Qd="exited",Zd="entering",ef="entered",tf="exiting",nf=function(e){function t(t){var n;n=e.call(this,t)||this;var r,o=t.appear;return n.appearStatus=null,t.in?o?(r=Qd,n.appearStatus=Zd):r=ef:r=t.unmountOnExit||t.mountOnEnter?Xd:Qd,n.state={status:r},n.nextCallback=null,n}zd(t,e),t.getDerivedStateFromProps=function(e,t){return e.in&&t.status===Xd?{status:Qd}:null};var n=t.prototype;return n.componentDidMount=function(){this.updateStatus(!0,this.appearStatus)},n.componentDidUpdate=function(e){var t=null;if(e!==this.props){var n=this.state.status;this.props.in?n!==Zd&&n!==ef&&(t=Zd):n!==Zd&&n!==ef||(t=tf)}this.updateStatus(!1,t)},n.componentWillUnmount=function(){this.cancelNextCallback()},n.getTimeouts=function(){var e=this.props.timeout,t=e,n=e;return null!=e&&"number"!=typeof e&&"string"!=typeof e&&(n=e.exit,t=e.enter),{exit:n,enter:t}},n.updateStatus=function(e,t){void 0===e&&(e=!1),null!==t?(this.cancelNextCallback(),t===Zd?this.performEnter(e):this.performExit()):this.props.unmountOnExit&&this.state.status===Qd&&this.setState({status:Xd})},n.performEnter=function(e){var t=this,n=this.props.enter,r=e,o=this.getTimeouts();e||n?(this.props.onEnter&&this.props.onEnter(this.node,r),this.safeSetState({status:Zd},(function(){t.props.onEntering&&t.props.onEntering(t.node,r),t.onTransitionEnd(o.enter,(function(){t.safeSetState({status:ef},(function(){t.props.onEntered&&t.props.onEntered(t.node,r)}))}))}))):this.safeSetState({status:ef},(function(){t.props.onEntered&&t.props.onEntered(t.node,r)}))},n.performExit=function(){var e=this,t=this.props.exit,n=this.getTimeouts();t?(this.props.onExit&&this.props.onExit(this.node),this.safeSetState({status:tf},(function(){e.props.onExiting&&e.props.onExiting(e.node),e.onTransitionEnd(n.exit,(function(){e.safeSetState({status:Qd},(function(){e.props.onExited&&e.props.onExited(e.node)}))}))}))):this.safeSetState({status:Qd},(function(){e.props.onExited&&e.props.onExited(e.node)}))},n.cancelNextCallback=function(){null!==this.nextCallback&&this.nextCallback.cancel&&(this.nextCallback.cancel(),this.nextCallback=null)},n.safeSetState=function(e,t){t=this.setNextCallback(t),this.setState(e,t)},n.setNextCallback=function(e){var t=this,n=!0;return this.nextCallback=function(){n&&(n=!1,t.nextCallback=null,e())},this.nextCallback.cancel=function(){n=!1},this.nextCallback},n.onTransitionEnd=function(e,t){this.setNextCallback(t);var n=null==e&&!this.props.addEndListener;this.node&&!n?(this.props.addEndListener&&this.props.addEndListener(this.node,this.nextCallback),null!=e&&setTimeout(this.nextCallback,e)):setTimeout(this.nextCallback,0)},n.render=function(){var e=this.state.status;if(e===Xd)return null;var t=this.props;return(0,t.children)(e,$d(t,["children","in","mountOnEnter","unmountOnExit","appear","enter","exit","timeout","addEndListener","onEnter","onEntering","onEntered","onExit","onExiting","onExited","nodeRef"]))},Bd(t,[{key:"node",get:function(){var e,t=null===(e=this.props.nodeRef)||void 0===e?void 0:e.current;if(!t)throw new Error("notistack - Custom snackbar is not refForwarding");return t}}]),t}(B.Component);function rf(){}function of(e,t){"function"==typeof e?e(t):e&&(e.current=t)}function af(e,t){return(0,B.useMemo)((function(){return null==e&&null==t?null:function(n){of(e,n),of(t,n)}}),[e,t])}function sf(e){var t=e.timeout,n=e.style,r=void 0===n?{}:n,o=e.mode;return{duration:"object"==typeof t?t[o]||0:t,easing:r.transitionTimingFunction,delay:r.transitionDelay}}nf.defaultProps={in:!1,mountOnEnter:!1,unmountOnExit:!1,appear:!1,enter:!0,exit:!0,onEnter:rf,onEntering:rf,onEntered:rf,onExit:rf,onExiting:rf,onExited:rf};var lf=function(e){e.scrollTop=e.scrollTop},uf=function(e){return Math.round(e)+"ms"};function cf(e,t){void 0===e&&(e=["all"]);var n=t||{},r=n.duration,o=void 0===r?300:r,i=n.easing,a=void 0===i?"cubic-bezier(0.4, 0, 0.2, 1)":i,s=n.delay,l=void 0===s?0:s;return(Array.isArray(e)?e:[e]).map((function(e){var t="string"==typeof o?o:uf(o),n="string"==typeof l?l:uf(l);return e+" "+t+" "+a+" "+n})).join(",")}function df(e){var t=function(e){return e&&e.ownerDocument||document}(e);return t.defaultView||window}function ff(e,t){if(t){var n=function(e,t){var n,r=t.getBoundingClientRect(),o=df(t);if(t.fakeTransform)n=t.fakeTransform;else{var i=o.getComputedStyle(t);n=i.getPropertyValue("-webkit-transform")||i.getPropertyValue("transform")}var a=0,s=0;if(n&&"none"!==n&&"string"==typeof n){var l=n.split("(")[1].split(")")[0].split(",");a=parseInt(l[4],10),s=parseInt(l[5],10)}switch(e){case"left":return"translateX("+(o.innerWidth+a-r.left)+"px)";case"right":return"translateX(-"+(r.left+r.width-a)+"px)";case"up":return"translateY("+(o.innerHeight+s-r.top)+"px)";default:return"translateY(-"+(r.top+r.height-s)+"px)"}}(e,t);n&&(t.style.webkitTransform=n,t.style.transform=n)}}var pf=(0,B.forwardRef)((function(e,t){var n=e.children,r=e.direction,o=void 0===r?"down":r,i=e.in,a=e.style,s=e.timeout,l=void 0===s?0:s,u=e.onEnter,c=e.onEntered,d=e.onExit,f=e.onExited,p=$d(e,["children","direction","in","style","timeout","onEnter","onEntered","onExit","onExited"]),h=(0,B.useRef)(null),m=af(n.ref,h),g=af(m,t),v=(0,B.useCallback)((function(){h.current&&ff(o,h.current)}),[o]);return(0,B.useEffect)((function(){if(!i&&"down"!==o&&"right"!==o){var e=function(e,t){var n;function r(){for(var r=this,o=arguments.length,i=new Array(o),a=0;a<o;a++)i[a]=arguments[a];clearTimeout(n),n=setTimeout((function(){e.apply(r,i)}),t)}return void 0===t&&(t=166),r.clear=function(){clearTimeout(n)},r}((function(){h.current&&ff(o,h.current)})),t=df(h.current);return t.addEventListener("resize",e),function(){e.clear(),t.removeEventListener("resize",e)}}}),[o,i]),(0,B.useEffect)((function(){i||v()}),[i,v]),(0,B.createElement)(nf,Object.assign({appear:!0,nodeRef:h,onEnter:function(e,t){ff(o,e),lf(e),u&&u(e,t)},onEntered:c,onEntering:function(e){var t=(null==a?void 0:a.transitionTimingFunction)||"cubic-bezier(0.0, 0, 0.2, 1)",n=sf({timeout:l,mode:"enter",style:Ud({},a,{transitionTimingFunction:t})});e.style.webkitTransition=cf("-webkit-transform",n),e.style.transition=cf("transform",n),e.style.webkitTransform="none",e.style.transform="none"},onExit:function(e){var t=(null==a?void 0:a.transitionTimingFunction)||"cubic-bezier(0.4, 0, 0.6, 1)",n=sf({timeout:l,mode:"exit",style:Ud({},a,{transitionTimingFunction:t})});e.style.webkitTransition=cf("-webkit-transform",n),e.style.transition=cf("transform",n),ff(o,e),d&&d(e)},onExited:function(e){e.style.webkitTransition="",e.style.transition="",f&&f(e)},in:i,timeout:l},p),(function(e,t){return(0,B.cloneElement)(n,Ud({ref:g,style:Ud({visibility:"exited"!==e||i?void 0:"hidden"},a,{},n.props.style)},t))}))}));pf.displayName="Slide";var hf=function(e){return B.createElement("svg",Object.assign({viewBox:"0 0 24 24",focusable:"false",style:{fontSize:20,marginInlineEnd:8,userSelect:"none",width:"1em",height:"1em",display:"inline-block",fill:"currentColor",flexShrink:0}},e))},mf=function(){return B.createElement(hf,null,B.createElement("path",{d:"M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41\n        10.59L10 14.17L17.59 6.58L19 8L10 17Z"}))},gf=function(){return B.createElement(hf,null,B.createElement("path",{d:"M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"}))},vf=function(){return B.createElement(hf,null,B.createElement("path",{d:"M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,\n        6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,\n        13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"}))},yf=function(){return B.createElement(hf,null,B.createElement("path",{d:"M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,\n        0 22,12A10,10 0 0,0 12,2Z"}))},bf={maxSnack:3,persist:!1,hideIconVariant:!1,disableWindowBlurListener:!1,variant:"default",autoHideDuration:5e3,iconVariant:{default:void 0,success:B.createElement(mf,null),warning:B.createElement(gf,null),error:B.createElement(vf,null),info:B.createElement(yf,null)},anchorOrigin:{vertical:"bottom",horizontal:"left"},TransitionComponent:pf,transitionDuration:{enter:225,exit:195}};function wf(e){return Object.entries(e).reduce((function(e,t){var n,r=t[0],o=t[1];return Ud({},e,((n={})[r]=Md(o),n))}),{})}var Ef="notistack-CollapseWrapper",Sf=function(e){return"notistack-MuiContent-"+e},xf=wf({root:{height:0},entered:{height:"auto"}}),Af="0px",kf=(0,B.forwardRef)((function(e,t){var n=e.children,r=e.in,o=e.onExited,i=(0,B.useRef)(null),a=(0,B.useRef)(null),s=af(t,a),l=function(){return i.current?i.current.clientHeight:0};return(0,B.createElement)(nf,{in:r,unmountOnExit:!0,onEnter:function(e){e.style.height=Af},onEntered:function(e){e.style.height="auto"},onEntering:function(e){var t=l(),n=sf({timeout:175,mode:"enter"}),r=n.duration,o=n.easing;e.style.transitionDuration="string"==typeof r?r:r+"ms",e.style.height=t+"px",e.style.transitionTimingFunction=o||""},onExit:function(e){e.style.height=l()+"px"},onExited:o,onExiting:function(e){lf(e);var t=sf({timeout:175,mode:"exit"}),n=t.duration,r=t.easing;e.style.transitionDuration="string"==typeof n?n:n+"ms",e.style.height=Af,e.style.transitionTimingFunction=r||""},nodeRef:a,timeout:175},(function(e,t){return(0,B.createElement)("div",Object.assign({ref:s,className:Pd(xf.root,"entered"===e&&xf.entered),style:Ud({pointerEvents:"all",overflow:"hidden",minHeight:Af,transition:cf("height")},"entered"===e&&{overflow:"visible"},{},"exited"===e&&!r&&{visibility:"hidden"})},t),(0,B.createElement)("div",{ref:i,className:Ef,style:{display:"flex",width:"100%"}},n))}))}));kf.displayName="Collapse";var _f={right:"left",left:"right",bottom:"up",top:"down"},Cf=function(e){return"anchorOrigin"+Jd(e)},Of=function(){};function Pf(e,t){return e.reduce((function(e,n){return null==n?e:function(){for(var r=arguments.length,o=new Array(r),i=0;i<r;i++)o[i]=arguments[i];var a=[].concat(o);t&&-1===a.indexOf(t)&&a.push(t),e.apply(this,a),n.apply(this,a)}}),Of)}var Rf="undefined"!=typeof window?B.useLayoutEffect:B.useEffect;function Tf(e){var t=(0,B.useRef)(e);return Rf((function(){t.current=e})),(0,B.useCallback)((function(){return t.current.apply(void 0,arguments)}),[])}var Ff,jf=(0,B.forwardRef)((function(e,t){var n=e.children,r=e.className,o=e.autoHideDuration,i=e.disableWindowBlurListener,a=void 0!==i&&i,s=e.onClose,l=e.id,u=e.open,c=e.SnackbarProps,d=void 0===c?{}:c,f=(0,B.useRef)(),p=Tf((function(){s&&s.apply(void 0,arguments)})),h=Tf((function(e){s&&null!=e&&(f.current&&clearTimeout(f.current),f.current=setTimeout((function(){p(null,"timeout",l)}),e))}));(0,B.useEffect)((function(){return u&&h(o),function(){f.current&&clearTimeout(f.current)}}),[u,o,h]);var m=function(){f.current&&clearTimeout(f.current)},g=(0,B.useCallback)((function(){null!=o&&h(.5*o)}),[o,h]);return(0,B.useEffect)((function(){if(!a&&u)return window.addEventListener("focus",g),window.addEventListener("blur",m),function(){window.removeEventListener("focus",g),window.removeEventListener("blur",m)}}),[a,g,u]),(0,B.createElement)("div",Object.assign({ref:t},d,{className:Pd("notistack-Snackbar",r),onMouseEnter:function(e){d.onMouseEnter&&d.onMouseEnter(e),m()},onMouseLeave:function(e){d.onMouseLeave&&d.onMouseLeave(e),g()}}),n)}));jf.displayName="Snackbar";var Nf=wf({root:(Ff={display:"flex",flexWrap:"wrap",flexGrow:1},Ff[Kd]={flexGrow:"initial",minWidth:"288px"},Ff)}),If=(0,B.forwardRef)((function(e,t){var n=e.className,r=$d(e,["className"]);return B.createElement("div",Object.assign({ref:t,className:Pd(Nf.root,n)},r))}));If.displayName="SnackbarContent";var Lf=wf({root:{backgroundColor:"#313131",fontSize:"0.875rem",lineHeight:1.43,letterSpacing:"0.01071em",color:"#fff",alignItems:"center",padding:"6px 16px",borderRadius:"4px",boxShadow:"0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)"},lessPadding:{paddingLeft:"20px"},default:{backgroundColor:"#313131"},success:{backgroundColor:"#43a047"},error:{backgroundColor:"#d32f2f"},warning:{backgroundColor:"#ff9800"},info:{backgroundColor:"#2196f3"},message:{display:"flex",alignItems:"center",padding:"8px 0"},action:{display:"flex",alignItems:"center",marginLeft:"auto",paddingLeft:"16px",marginRight:"-8px"}}),Mf="notistack-snackbar",Df=(0,B.forwardRef)((function(e,t){var n=e.id,r=e.message,o=e.action,i=e.iconVariant,a=e.variant,s=e.hideIconVariant,l=e.style,u=e.className,c=i[a],d=o;return"function"==typeof d&&(d=d(n)),B.createElement(If,{ref:t,role:"alert","aria-describedby":Mf,style:l,className:Pd("notistack-MuiContent",Sf(a),Lf.root,Lf[a],u,!s&&c&&Lf.lessPadding)},B.createElement("div",{id:Mf,className:Lf.message},s?null:c,r),d&&B.createElement("div",{className:Lf.action},d))}));Df.displayName="MaterialDesignContent";var Bf,Uf,zf,$f,Hf,Wf=(0,B.memo)(Df),Vf=wf({wrappedRoot:{width:"100%",position:"relative",transform:"translateX(0)",top:0,right:0,bottom:0,left:0,minWidth:"288px"}}),qf=function(e){var t=(0,B.useRef)(),n=(0,B.useState)(!0),r=n[0],o=n[1],i=Pf([e.snack.onClose,e.onClose]),a=(0,B.useCallback)((function(){t.current=setTimeout((function(){o((function(e){return!e}))}),125)}),[]);(0,B.useEffect)((function(){return function(){t.current&&clearTimeout(t.current)}}),[]);var s,l=e.snack,u=e.classes,c=e.Component,d=void 0===c?Wf:c,f=(0,B.useMemo)((function(){return function(e){void 0===e&&(e={});var t={containerRoot:!0,containerAnchorOriginTopCenter:!0,containerAnchorOriginBottomCenter:!0,containerAnchorOriginTopRight:!0,containerAnchorOriginBottomRight:!0,containerAnchorOriginTopLeft:!0,containerAnchorOriginBottomLeft:!0};return Object.keys(e).filter((function(e){return!t[e]})).reduce((function(t,n){var r;return Ud({},t,((r={})[n]=e[n],r))}),{})}(u)}),[u]),p=l.open,h=l.SnackbarProps,m=l.TransitionComponent,g=l.TransitionProps,v=l.transitionDuration,y=l.disableWindowBlurListener,b=l.content,w=$d(l,["open","SnackbarProps","TransitionComponent","TransitionProps","transitionDuration","disableWindowBlurListener","content","entered","requestClose","onEnter","onEntered","onExit","onExited"]),E=Ud({direction:(s=w.anchorOrigin,"center"!==s.horizontal?_f[s.horizontal]:_f[s.vertical]),timeout:v},g),S=b;"function"==typeof S&&(S=S(w.id,w.message));var x=["onEnter","onEntered","onExit","onExited"].reduce((function(t,n){var r;return Ud({},t,((r={})[n]=Pf([e.snack[n],e[n]],w.id),r))}),{});return B.createElement(kf,{in:r,onExited:x.onExited},B.createElement(jf,{open:p,id:w.id,disableWindowBlurListener:y,autoHideDuration:w.autoHideDuration,className:Pd(Vf.wrappedRoot,f.root,f[Cf(w.anchorOrigin)]),SnackbarProps:h,onClose:i},B.createElement(m,Object.assign({},E,{appear:!0,in:p,onExit:x.onExit,onExited:a,onEnter:x.onEnter,onEntered:Pf([x.onEntered,function(){e.snack.requestClose&&i(null,"instructed",e.snack.id)}],w.id)}),S||B.createElement(d,Object.assign({},w)))))},Kf=20,Gf=6,Jf=2,Yf="."+Ef,Xf=wf({root:(Bf={boxSizing:"border-box",display:"flex",maxHeight:"100%",position:"fixed",zIndex:1400,height:"auto",width:"auto",transition:cf(["top","right","bottom","left","max-width"],{duration:300,easing:"ease"}),pointerEvents:"none"},Bf[Yf]={padding:Gf+"px 0px",transition:"padding 300ms ease 0ms"},Bf.maxWidth="calc(100% - "+2*Kf+"px)",Bf[qd]={width:"100%",maxWidth:"calc(100% - 32px)"},Bf),rootDense:(Uf={},Uf[Yf]={padding:Jf+"px 0px"},Uf),top:{top:Kf-Gf+"px",flexDirection:"column"},bottom:{bottom:Kf-Gf+"px",flexDirection:"column-reverse"},left:(zf={left:Kf+"px"},zf[Kd]={alignItems:"flex-start"},zf[qd]={left:"16px"},zf),right:($f={right:Kf+"px"},$f[Kd]={alignItems:"flex-end"},$f[qd]={right:"16px"},$f),center:(Hf={left:"50%",transform:"translateX(-50%)"},Hf[Kd]={alignItems:"center"},Hf)}),Qf=function(e){var t=e.classes,n=void 0===t?{}:t,r=e.anchorOrigin,o=e.dense,i=e.children,a=Pd("notistack-SnackbarContainer",Xf[r.vertical],Xf[r.horizontal],Xf.root,n.containerRoot,n["containerAnchorOrigin"+Jd(r)],o&&Xf.rootDense);return B.createElement("div",{className:a},i)},Zf=(0,B.memo)(Qf),ep=function(e){return!("string"==typeof e||(0,B.isValidElement)(e))},tp=function(e){function t(t){var n;return(n=e.call(this,t)||this).enqueueSnackbar=function(e,t){if(void 0===t&&(t={}),null==e)throw new Error("enqueueSnackbar called with invalid argument");var r=ep(e)?e:t,o=ep(e)?e.message:e,i=r.key,a=r.preventDuplicate,s=$d(r,["key","preventDuplicate"]),l=Yd(i),u=l?i:(new Date).getTime()+Math.random(),c=function(e,t){return function(n,r){return void 0===r&&(r=!1),r?Ud({},bf[n],{},t[n],{},e[n]):"autoHideDuration"===n?(o=e.autoHideDuration,i=t.autoHideDuration,(a=function(e){return"number"==typeof e||null===e})(o)?o:a(i)?i:bf.autoHideDuration):"transitionDuration"===n?function(e,t){var n=function(e,t){return t.some((function(t){return typeof e===t}))};return n(e,["string","number"])?e:n(e,["object"])?Ud({},bf.transitionDuration,{},n(t,["object"])&&t,{},e):n(t,["string","number"])?t:n(t,["object"])?Ud({},bf.transitionDuration,{},t):bf.transitionDuration}(e.transitionDuration,t.transitionDuration):e[n]||t[n]||bf[n];var o,i,a}}(s,n.props),d=Ud({id:u},s,{message:o,open:!0,entered:!1,requestClose:!1,persist:c("persist"),action:c("action"),content:c("content"),variant:c("variant"),anchorOrigin:c("anchorOrigin"),disableWindowBlurListener:c("disableWindowBlurListener"),autoHideDuration:c("autoHideDuration"),hideIconVariant:c("hideIconVariant"),TransitionComponent:c("TransitionComponent"),transitionDuration:c("transitionDuration"),TransitionProps:c("TransitionProps",!0),iconVariant:c("iconVariant",!0),style:c("style",!0),SnackbarProps:c("SnackbarProps",!0),className:Pd(n.props.className,s.className)});return d.persist&&(d.autoHideDuration=void 0),n.setState((function(e){if(void 0===a&&n.props.preventDuplicate||a){var t=function(e){return l?e.id===u:e.message===o},r=e.queue.findIndex(t)>-1,i=e.snacks.findIndex(t)>-1;if(r||i)return e}return n.handleDisplaySnack(Ud({},e,{queue:[].concat(e.queue,[d])}))})),u},n.handleDisplaySnack=function(e){return e.snacks.length>=n.maxSnack?n.handleDismissOldest(e):n.processQueue(e)},n.processQueue=function(e){var t=e.queue,n=e.snacks;return t.length>0?Ud({},e,{snacks:[].concat(n,[t[0]]),queue:t.slice(1,t.length)}):e},n.handleDismissOldest=function(e){if(e.snacks.some((function(e){return!e.open||e.requestClose})))return e;var t=!1,r=!1;e.snacks.reduce((function(e,t){return e+(t.open&&t.persist?1:0)}),0)===n.maxSnack&&(r=!0);var o=e.snacks.map((function(e){return t||e.persist&&!r?Ud({},e):(t=!0,e.entered?(e.onClose&&e.onClose(null,"maxsnack",e.id),n.props.onClose&&n.props.onClose(null,"maxsnack",e.id),Ud({},e,{open:!1})):Ud({},e,{requestClose:!0}))}));return Ud({},e,{snacks:o})},n.handleEnteredSnack=function(e,t,r){if(!Yd(r))throw new Error("handleEnteredSnack Cannot be called with undefined key");n.setState((function(e){return{snacks:e.snacks.map((function(e){return e.id===r?Ud({},e,{entered:!0}):Ud({},e)}))}}))},n.handleCloseSnack=function(e,t,r){n.props.onClose&&n.props.onClose(e,t,r);var o=void 0===r;n.setState((function(e){var t=e.snacks,n=e.queue;return{snacks:t.map((function(e){return o||e.id===r?e.entered?Ud({},e,{open:!1}):Ud({},e,{requestClose:!0}):Ud({},e)})),queue:n.filter((function(e){return e.id!==r}))}}))},n.closeSnackbar=function(e){var t=n.state.snacks.find((function(t){return t.id===e}));Yd(e)&&t&&t.onClose&&t.onClose(null,"instructed",e),n.handleCloseSnack(null,"instructed",e)},n.handleExitedSnack=function(e,t){if(!Yd(t))throw new Error("handleExitedSnack Cannot be called with undefined key");n.setState((function(e){var r=n.processQueue(Ud({},e,{snacks:e.snacks.filter((function(e){return e.id!==t}))}));return 0===r.queue.length?r:n.handleDismissOldest(r)}))},n.enqueueSnackbar,n.closeSnackbar,n.state={snacks:[],queue:[],contextValue:{enqueueSnackbar:n.enqueueSnackbar.bind(Hd(n)),closeSnackbar:n.closeSnackbar.bind(Hd(n))}},n}return zd(t,e),t.prototype.render=function(){var e=this,t=this.state.contextValue,n=this.props,r=n.domRoot,o=n.children,i=n.dense,a=void 0!==i&&i,s=n.Components,l=void 0===s?{}:s,u=n.classes,c=this.state.snacks.reduce((function(e,t){var n,r=Jd(t.anchorOrigin),o=e[r]||[];return Ud({},e,((n={})[r]=[].concat(o,[t]),n))}),{}),d=Object.keys(c).map((function(t){var n=c[t],r=n[0];return B.createElement(Zf,{key:t,dense:a,anchorOrigin:r.anchorOrigin,classes:u},n.map((function(t){return B.createElement(qf,{key:t.id,snack:t,classes:u,Component:l[t.variant],onClose:e.handleCloseSnack,onEnter:e.props.onEnter,onExit:e.props.onExit,onExited:Pf([e.handleExitedSnack,e.props.onExited],t.id),onEntered:Pf([e.handleEnteredSnack,e.props.onEntered],t.id)})})))}));return B.createElement(Vd.Provider,{value:t},o,r?(0,Fr.createPortal)(d,r):d)},Bd(t,[{key:"maxSnack",get:function(){return this.props.maxSnack||bf.maxSnack}}]),t}(B.Component);function np(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}const rp=function(e){var t=(0,B.useContext)(Vd),n=t.enqueueSnackbar,r=(t.closeSnackbar,fe(ve(),2));ne(r[0]);var o=r[1];return{dispatchAlerts:function(e){console.log(1,e);var t=function(e,t){var n=[],r=new Date;function o(e){return Array.isArray(e)||(e=[e]),e.map((function(e,t){return B.createElement("div",{key:t},e)}))}if(console.log("parse api response",e),void 0!==e.status)if("nodes"in e)for(var i in e.nodes){var a=e.nodes[i];a.status&&n.push({date:r,level:"error",color:"secondary",body:B.createElement("div",null,B.createElement("strong",null,i),B.createElement("br",null),B.createElement("div",null,o(a.error)))}),a.info&&n.push({date:r,level:"success",color:"primary",body:B.createElement("div",null,B.createElement("strong",null,i),B.createElement("br",null),B.createElement("div",null,o(a.info)))})}else e.error&&n.push({date:r,level:"error",color:"secondary",body:B.createElement("div",null,o(e.error))});return e.info&&n.push({date:r,level:"success",color:"primary",body:B.createElement("div",null,o(e.info))}),0==e.status&&0==n.length&&n.push({date:r,level:"success",color:"primary",body:B.createElement("div",null,t)}),n}(e.data,!0);console.log(2,t);var r,i=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return np(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?np(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(t);try{for(i.s();!(r=i.n()).done;){var a=r.value;console.log(3,a),n(a.body,{variant:a.level})}}catch(e){i.e(e)}finally{i.f()}console.log(4,a),o({type:"pushAlerts",data:t})}}};var op=o(3419),ip=o(8277);function ap(e){var t=hn().auth,n=fe(ve(),2),r=n[0].cstat,o=(n[1],rp().dispatchAlerts);if(void 0===r.monitor)return null;var i=r.monitor.nodes;return B.createElement(kd,{title:e.title,submit:function(e){var n,r,i;n=e.value,r=function(e){return o({data:e})},i=rn(i={Accept:"application/json","Content-Type":"application/json"},t),fetch("/node_monitor",{headers:i,method:"POST",body:JSON.stringify({global_expect:n})}).then((function(e){return e.json()})).then((function(e){r&&r(e)})).catch(console.log)},position:e.position,fab:e.fab},B.createElement(_d,{name:"safe",color:"secondary"},B.createElement(Cd,{value:"frozen",text:"Freeze Nodes",disabled:function(){for(var e in i)if(!i[e].frozen)return!1;return!0}(),requires:{role:"root"},icon:B.createElement(op.A,null),confirmations:["I understand all services orchestration will be paused."]}),B.createElement(Cd,{value:"thawed",text:"Thaw Nodes",disabled:function(){for(var e in i)if(i[e].frozen)return!1;return!0}(),requires:{role:"root"},icon:B.createElement(ip.A,null)})))}function sp(e){return(0,rr.A)("MuiToggleButton",e)}const lp=(0,nr.A)("MuiToggleButton",["root","disabled","selected","standard","primary","secondary","sizeSmall","sizeMedium","sizeLarge","fullWidth"]),up=["children","className","color","disabled","disableFocusRipple","fullWidth","onChange","onClick","selected","size","value"],cp=(0,Zn.Ay)(di,{name:"MuiToggleButton",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`size${(0,tr.A)(n.size)}`]]}})((({theme:e,ownerState:t})=>{let n,r="standard"===t.color?e.palette.text.primary:e.palette[t.color].main;return e.vars&&(r="standard"===t.color?e.vars.palette.text.primary:e.vars.palette[t.color].main,n="standard"===t.color?e.vars.palette.text.primaryChannel:e.vars.palette[t.color].mainChannel),(0,Jt.A)({},e.typography.button,{borderRadius:(e.vars||e).shape.borderRadius,padding:11,border:`1px solid ${(e.vars||e).palette.divider}`,color:(e.vars||e).palette.action.active},t.fullWidth&&{width:"100%"},{[`&.${lp.disabled}`]:{color:(e.vars||e).palette.action.disabled,border:`1px solid ${(e.vars||e).palette.action.disabledBackground}`},"&:hover":{textDecoration:"none",backgroundColor:e.vars?`rgba(${e.vars.palette.text.primaryChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette.text.primary,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},[`&.${lp.selected}`]:{color:r,backgroundColor:e.vars?`rgba(${n} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(r,e.palette.action.selectedOpacity),"&:hover":{backgroundColor:e.vars?`rgba(${n} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.hoverOpacity}))`:(0,vo.X4)(r,e.palette.action.selectedOpacity+e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:e.vars?`rgba(${n} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(r,e.palette.action.selectedOpacity)}}}},"small"===t.size&&{padding:7,fontSize:e.typography.pxToRem(13)},"large"===t.size&&{padding:15,fontSize:e.typography.pxToRem(15)})})),dp=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiToggleButton"}),{children:r,className:o,color:i="standard",disabled:a=!1,disableFocusRipple:s=!1,fullWidth:l=!1,onChange:u,onClick:c,selected:d,size:f="medium",value:p}=n,h=(0,qn.A)(n,up),m=(0,Jt.A)({},n,{color:i,disabled:a,disableFocusRipple:s,fullWidth:l,size:f}),g=(e=>{const{classes:t,fullWidth:n,selected:r,disabled:o,size:i,color:a}=e,s={root:["root",r&&"selected",o&&"disabled",n&&"fullWidth",`size${(0,tr.A)(i)}`,a]};return(0,Qn.A)(s,sp,t)})(m);return(0,ir.jsx)(cp,(0,Jt.A)({className:(0,Kn.A)(g.root,o),disabled:a,focusRipple:!s,ref:t,onClick:e=>{c&&(c(e,p),e.defaultPrevented)||u&&u(e,p)},onChange:u,value:p,ownerState:m,"aria-pressed":d},h,{children:r}))}));function fp(e,t){return void 0!==t&&void 0!==e&&(Array.isArray(t)?t.indexOf(e)>=0:e===t)}function pp(e){return(0,rr.A)("MuiToggleButtonGroup",e)}const hp=(0,nr.A)("MuiToggleButtonGroup",["root","selected","vertical","disabled","grouped","groupedHorizontal","groupedVertical","fullWidth"]),mp=["children","className","color","disabled","exclusive","fullWidth","onChange","orientation","size","value"],gp=(0,Zn.Ay)("div",{name:"MuiToggleButtonGroup",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`& .${hp.grouped}`]:t.grouped},{[`& .${hp.grouped}`]:t[`grouped${(0,tr.A)(n.orientation)}`]},t.root,"vertical"===n.orientation&&t.vertical,n.fullWidth&&t.fullWidth]}})((({ownerState:e,theme:t})=>(0,Jt.A)({display:"inline-flex",borderRadius:(t.vars||t).shape.borderRadius},"vertical"===e.orientation&&{flexDirection:"column"},e.fullWidth&&{width:"100%"},{[`& .${hp.grouped}`]:(0,Jt.A)({},"horizontal"===e.orientation?{"&:not(:first-of-type)":{marginLeft:-1,borderLeft:"1px solid transparent",borderTopLeftRadius:0,borderBottomLeftRadius:0},"&:not(:last-of-type)":{borderTopRightRadius:0,borderBottomRightRadius:0},[`&.${hp.selected} + .${hp.grouped}.${hp.selected}`]:{borderLeft:0,marginLeft:0}}:{"&:not(:first-of-type)":{marginTop:-1,borderTop:"1px solid transparent",borderTopLeftRadius:0,borderTopRightRadius:0},"&:not(:last-of-type)":{borderBottomLeftRadius:0,borderBottomRightRadius:0},[`&.${hp.selected} + .${hp.grouped}.${hp.selected}`]:{borderTop:0,marginTop:0}})}))),vp=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiToggleButtonGroup"}),{children:r,className:o,color:i="standard",disabled:a=!1,exclusive:s=!1,fullWidth:l=!1,onChange:u,orientation:c="horizontal",size:d="medium",value:f}=n,p=(0,qn.A)(n,mp),h=(0,Jt.A)({},n,{disabled:a,fullWidth:l,orientation:c,size:d}),m=(e=>{const{classes:t,orientation:n,fullWidth:r,disabled:o}=e,i={root:["root","vertical"===n&&"vertical",r&&"fullWidth"],grouped:["grouped",`grouped${(0,tr.A)(n)}`,o&&"disabled"]};return(0,Qn.A)(i,pp,t)})(h),g=(e,t)=>{if(!u)return;const n=f&&f.indexOf(t);let r;f&&n>=0?(r=f.slice(),r.splice(n,1)):r=f?f.concat(t):[t],u(e,r)},v=(e,t)=>{u&&u(e,f===t?null:t)};return(0,ir.jsx)(gp,(0,Jt.A)({role:"group",className:(0,Kn.A)(m.root,o),ref:t,ownerState:h},p,{children:B.Children.map(r,(e=>B.isValidElement(e)?B.cloneElement(e,{className:(0,Kn.A)(m.grouped,e.props.className),onChange:s?v:g,selected:void 0===e.props.selected?fp(e.props.value,f):e.props.selected,size:e.props.size||d,fullWidth:l,color:e.props.color||i,disabled:e.props.disabled||a}):null))}))}));var yp=[{name:"svc",icon:B.createElement(ga.A,null)},{name:"vol",icon:B.createElement(wa.A,null)},{name:"cfg",icon:B.createElement(ya.A,null)},{name:"sec",icon:B.createElement(ba.A,null)},{name:"usr",icon:B.createElement(va.A,null)},{name:"ccfg",requires:"root"}];function bp(e){var t=fe(ve(),2),n=t[0].user;return t[1],B.createElement(vp,{value:e.value,exclusive:!0,onChange:e.onChange},yp.map((function(e){return e.requires&&!(e.requires in n.grant)?null:B.createElement(dp,{value:e.name,key:e.name},e.icon,e.name)})))}function wp(e,t){if(null==e)return{};var n,r,o=(0,qn.A)(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)n=i[r],-1===t.indexOf(n)&&{}.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var Ep=o(2243),Sp=o.n(Ep),xp=o(1183),Ap=o.n(xp),kp=o(9149),_p=o.n(kp),Cp=o(9453),Op=o(4705);const Pp=["onChange","maxRows","minRows","style","value"];function Rp(e){return parseInt(e,10)||0}const Tp={visibility:"hidden",position:"absolute",overflow:"hidden",height:0,top:0,left:0,transform:"translateZ(0)"};function Fp(e){return null==e||0===Object.keys(e).length||0===e.outerHeightStyle&&!e.overflow}const jp=B.forwardRef((function(e,t){const{onChange:n,maxRows:r,minRows:o=1,style:i,value:a}=e,s=(0,qn.A)(e,Pp),{current:l}=B.useRef(null!=a),u=B.useRef(null),c=(0,dr.A)(t,u),d=B.useRef(null),f=B.useRef(0),[p,h]=B.useState({outerHeightStyle:0}),m=B.useCallback((()=>{const t=u.current,n=(0,Er.A)(t).getComputedStyle(t);if("0px"===n.width)return{outerHeightStyle:0};const i=d.current;i.style.width=n.width,i.value=t.value||e.placeholder||"x","\n"===i.value.slice(-1)&&(i.value+=" ");const a=n.boxSizing,s=Rp(n.paddingBottom)+Rp(n.paddingTop),l=Rp(n.borderBottomWidth)+Rp(n.borderTopWidth),c=i.scrollHeight;i.value="x";const f=i.scrollHeight;let p=c;return o&&(p=Math.max(Number(o)*f,p)),r&&(p=Math.min(Number(r)*f,p)),p=Math.max(p,f),{outerHeightStyle:p+("border-box"===a?s+l:0),overflow:Math.abs(p-c)<=1}}),[r,o,e.placeholder]),g=(e,t)=>{const{outerHeightStyle:n,overflow:r}=t;return f.current<20&&(n>0&&Math.abs((e.outerHeightStyle||0)-n)>1||e.overflow!==r)?(f.current+=1,{overflow:r,outerHeightStyle:n}):e},v=B.useCallback((()=>{const e=m();Fp(e)||h((t=>g(t,e)))}),[m]);return(0,jr.A)((()=>{const e=()=>{f.current=0,(()=>{const e=m();Fp(e)||Fr.flushSync((()=>{h((t=>g(t,e)))}))})()};const t=(0,Op.A)(e),n=u.current,r=(0,Er.A)(n);let o;return r.addEventListener("resize",t),"undefined"!=typeof ResizeObserver&&(o=new ResizeObserver(e),o.observe(n)),()=>{t.clear(),cancelAnimationFrame(undefined),r.removeEventListener("resize",t),o&&o.disconnect()}}),[m]),(0,jr.A)((()=>{v()})),B.useEffect((()=>{f.current=0}),[a]),(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)("textarea",(0,Jt.A)({value:a,onChange:e=>{f.current=0,l||v(),n&&n(e)},ref:c,rows:o,style:(0,Jt.A)({height:p.outerHeightStyle,overflow:p.overflow?"hidden":void 0},i)},s)),(0,ir.jsx)("textarea",{"aria-hidden":!0,className:e.className,readOnly:!0,ref:d,tabIndex:-1,style:(0,Jt.A)({},Tp,i,{paddingTop:0,paddingBottom:0})})]})}));function Np(e){const{styles:t,defaultTheme:n={}}=e,r="function"==typeof t?e=>{return t(null==(r=e)||0===Object.keys(r).length?n:e);var r}:t;return(0,ir.jsx)(Vo,{styles:r})}const Ip=function({styles:e,themeId:t,defaultTheme:n={}}){const r=(0,qr.A)(n),o="function"==typeof e?e(t&&r[t]||r):e;return(0,ir.jsx)(Np,{styles:o})},Lp=function(e){return(0,ir.jsx)(Ip,(0,Jt.A)({},e,{defaultTheme:Kr.A,themeId:Gr.A}))};function Mp(e){return(0,rr.A)("MuiInputBase",e)}const Dp=(0,nr.A)("MuiInputBase",["root","formControl","focused","disabled","adornedStart","adornedEnd","error","sizeSmall","multiline","colorSecondary","fullWidth","hiddenLabel","readOnly","input","inputSizeSmall","inputMultiline","inputTypeSearch","inputAdornedStart","inputAdornedEnd","inputHiddenLabel"]),Bp=["aria-describedby","autoComplete","autoFocus","className","color","components","componentsProps","defaultValue","disabled","disableInjectingGlobalStyles","endAdornment","error","fullWidth","id","inputComponent","inputProps","inputRef","margin","maxRows","minRows","multiline","name","onBlur","onChange","onClick","onFocus","onKeyDown","onKeyUp","placeholder","readOnly","renderSuffix","rows","size","slotProps","slots","startAdornment","type","value"],Up=(e,t)=>{const{ownerState:n}=e;return[t.root,n.formControl&&t.formControl,n.startAdornment&&t.adornedStart,n.endAdornment&&t.adornedEnd,n.error&&t.error,"small"===n.size&&t.sizeSmall,n.multiline&&t.multiline,n.color&&t[`color${(0,tr.A)(n.color)}`],n.fullWidth&&t.fullWidth,n.hiddenLabel&&t.hiddenLabel]},zp=(e,t)=>{const{ownerState:n}=e;return[t.input,"small"===n.size&&t.inputSizeSmall,n.multiline&&t.inputMultiline,"search"===n.type&&t.inputTypeSearch,n.startAdornment&&t.inputAdornedStart,n.endAdornment&&t.inputAdornedEnd,n.hiddenLabel&&t.inputHiddenLabel]},$p=(0,Zn.Ay)("div",{name:"MuiInputBase",slot:"Root",overridesResolver:Up})((({theme:e,ownerState:t})=>(0,Jt.A)({},e.typography.body1,{color:(e.vars||e).palette.text.primary,lineHeight:"1.4375em",boxSizing:"border-box",position:"relative",cursor:"text",display:"inline-flex",alignItems:"center",[`&.${Dp.disabled}`]:{color:(e.vars||e).palette.text.disabled,cursor:"default"}},t.multiline&&(0,Jt.A)({padding:"4px 0 5px"},"small"===t.size&&{paddingTop:1}),t.fullWidth&&{width:"100%"}))),Hp=(0,Zn.Ay)("input",{name:"MuiInputBase",slot:"Input",overridesResolver:zp})((({theme:e,ownerState:t})=>{const n="light"===e.palette.mode,r=(0,Jt.A)({color:"currentColor"},e.vars?{opacity:e.vars.opacity.inputPlaceholder}:{opacity:n?.42:.5},{transition:e.transitions.create("opacity",{duration:e.transitions.duration.shorter})}),o={opacity:"0 !important"},i=e.vars?{opacity:e.vars.opacity.inputPlaceholder}:{opacity:n?.42:.5};return(0,Jt.A)({font:"inherit",letterSpacing:"inherit",color:"currentColor",padding:"4px 0 5px",border:0,boxSizing:"content-box",background:"none",height:"1.4375em",margin:0,WebkitTapHighlightColor:"transparent",display:"block",minWidth:0,width:"100%",animationName:"mui-auto-fill-cancel",animationDuration:"10ms","&::-webkit-input-placeholder":r,"&::-moz-placeholder":r,"&:-ms-input-placeholder":r,"&::-ms-input-placeholder":r,"&:focus":{outline:0},"&:invalid":{boxShadow:"none"},"&::-webkit-search-decoration":{WebkitAppearance:"none"},[`label[data-shrink=false] + .${Dp.formControl} &`]:{"&::-webkit-input-placeholder":o,"&::-moz-placeholder":o,"&:-ms-input-placeholder":o,"&::-ms-input-placeholder":o,"&:focus::-webkit-input-placeholder":i,"&:focus::-moz-placeholder":i,"&:focus:-ms-input-placeholder":i,"&:focus::-ms-input-placeholder":i},[`&.${Dp.disabled}`]:{opacity:1,WebkitTextFillColor:(e.vars||e).palette.text.disabled},"&:-webkit-autofill":{animationDuration:"5000s",animationName:"mui-auto-fill"}},"small"===t.size&&{paddingTop:1},t.multiline&&{height:"auto",resize:"none",padding:0,paddingTop:0},"search"===t.type&&{MozAppearance:"textfield"})})),Wp=(0,ir.jsx)(Lp,{styles:{"@keyframes mui-auto-fill":{from:{display:"block"}},"@keyframes mui-auto-fill-cancel":{from:{display:"block"}}}}),Vp=B.forwardRef((function(e,t){var n;const r=(0,er.A)({props:e,name:"MuiInputBase"}),{"aria-describedby":o,autoComplete:i,autoFocus:a,className:s,components:l={},componentsProps:u={},defaultValue:c,disabled:d,disableInjectingGlobalStyles:f,endAdornment:p,fullWidth:h=!1,id:m,inputComponent:g="input",inputProps:v={},inputRef:y,maxRows:b,minRows:w,multiline:E=!1,name:S,onBlur:x,onChange:A,onClick:k,onFocus:_,onKeyDown:C,onKeyUp:O,placeholder:P,readOnly:R,renderSuffix:T,rows:F,slotProps:j={},slots:N={},startAdornment:I,type:L="text",value:M}=r,D=(0,qn.A)(r,Bp),U=null!=v.value?v.value:M,{current:z}=B.useRef(null!=U),$=B.useRef(),H=B.useCallback((e=>{}),[]),W=(0,Qr.A)($,y,v.ref,H),[V,q]=B.useState(!1),K=Ll(),G=Xl({props:r,muiFormControl:K,states:["color","disabled","error","hiddenLabel","size","required","filled"]});G.focused=K?K.focused:V,B.useEffect((()=>{!K&&d&&V&&(q(!1),x&&x())}),[K,d,V,x]);const J=K&&K.onFilled,Y=K&&K.onEmpty,X=B.useCallback((e=>{wu(e)?J&&J():Y&&Y()}),[J,Y]);(0,xi.A)((()=>{z&&X({value:U})}),[U,X,z]),B.useEffect((()=>{X($.current)}),[]);let Q=g,Z=v;E&&"input"===Q&&(Z=F?(0,Jt.A)({type:void 0,minRows:F,maxRows:F},Z):(0,Jt.A)({type:void 0,maxRows:b,minRows:w},Z),Q=jp),B.useEffect((()=>{K&&K.setAdornedStart(Boolean(I))}),[K,I]);const ee=(0,Jt.A)({},r,{color:G.color||"primary",disabled:G.disabled,endAdornment:p,error:G.error,focused:G.focused,formControl:K,fullWidth:h,hiddenLabel:G.hiddenLabel,multiline:E,size:G.size,startAdornment:I,type:L}),te=(e=>{const{classes:t,color:n,disabled:r,error:o,endAdornment:i,focused:a,formControl:s,fullWidth:l,hiddenLabel:u,multiline:c,readOnly:d,size:f,startAdornment:p,type:h}=e,m={root:["root",`color${(0,tr.A)(n)}`,r&&"disabled",o&&"error",l&&"fullWidth",a&&"focused",s&&"formControl",f&&"medium"!==f&&`size${(0,tr.A)(f)}`,c&&"multiline",p&&"adornedStart",i&&"adornedEnd",u&&"hiddenLabel",d&&"readOnly"],input:["input",r&&"disabled","search"===h&&"inputTypeSearch",c&&"inputMultiline","small"===f&&"inputSizeSmall",u&&"inputHiddenLabel",p&&"inputAdornedStart",i&&"inputAdornedEnd",d&&"readOnly"]};return(0,Qn.A)(m,Mp,t)})(ee),ne=N.root||l.Root||$p,re=j.root||u.root||{},oe=N.input||l.Input||Hp;return Z=(0,Jt.A)({},Z,null!=(n=j.input)?n:u.input),(0,ir.jsxs)(B.Fragment,{children:[!f&&Wp,(0,ir.jsxs)(ne,(0,Jt.A)({},re,!fr(ne)&&{ownerState:(0,Jt.A)({},ee,re.ownerState)},{ref:t,onClick:e=>{$.current&&e.currentTarget===e.target&&$.current.focus(),k&&k(e)}},D,{className:(0,Kn.A)(te.root,re.className,s,R&&"MuiInputBase-readOnly"),children:[I,(0,ir.jsx)(Il.Provider,{value:null,children:(0,ir.jsx)(oe,(0,Jt.A)({ownerState:ee,"aria-invalid":G.error,"aria-describedby":o,autoComplete:i,autoFocus:a,defaultValue:c,disabled:G.disabled,id:m,onAnimationStart:e=>{X("mui-auto-fill-cancel"===e.animationName?$.current:{value:"x"})},name:S,placeholder:P,readOnly:R,required:G.required,rows:F,value:U,onKeyDown:C,onKeyUp:O,type:L},Z,!fr(oe)&&{as:Q,ownerState:(0,Jt.A)({},ee,Z.ownerState)},{ref:W,className:(0,Kn.A)(te.input,Z.className,R&&"MuiInputBase-readOnly"),onBlur:e=>{x&&x(e),v.onBlur&&v.onBlur(e),K&&K.onBlur?K.onBlur(e):q(!1)},onChange:(e,...t)=>{if(!z){const t=e.target||$.current;if(null==t)throw new Error((0,Cp.A)(1));X({value:t.value})}v.onChange&&v.onChange(e,...t),A&&A(e,...t)},onFocus:e=>{G.disabled?e.stopPropagation():(_&&_(e),v.onFocus&&v.onFocus(e),K&&K.onFocus?K.onFocus(e):q(!0))}}))}),p,T?T((0,Jt.A)({},G,{startAdornment:I})):null]}))]})}));function qp(e){return(0,rr.A)("MuiInput",e)}const Kp=(0,Jt.A)({},Dp,(0,nr.A)("MuiInput",["root","underline","input"])),Gp=["disableUnderline","components","componentsProps","fullWidth","inputComponent","multiline","slotProps","slots","type"],Jp=(0,Zn.Ay)($p,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiInput",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[...Up(e,t),!n.disableUnderline&&t.underline]}})((({theme:e,ownerState:t})=>{let n="light"===e.palette.mode?"rgba(0, 0, 0, 0.42)":"rgba(255, 255, 255, 0.7)";return e.vars&&(n=`rgba(${e.vars.palette.common.onBackgroundChannel} / ${e.vars.opacity.inputUnderline})`),(0,Jt.A)({position:"relative"},t.formControl&&{"label + &":{marginTop:16}},!t.disableUnderline&&{"&::after":{borderBottom:`2px solid ${(e.vars||e).palette[t.color].main}`,left:0,bottom:0,content:'""',position:"absolute",right:0,transform:"scaleX(0)",transition:e.transitions.create("transform",{duration:e.transitions.duration.shorter,easing:e.transitions.easing.easeOut}),pointerEvents:"none"},[`&.${Kp.focused}:after`]:{transform:"scaleX(1) translateX(0)"},[`&.${Kp.error}`]:{"&::before, &::after":{borderBottomColor:(e.vars||e).palette.error.main}},"&::before":{borderBottom:`1px solid ${n}`,left:0,bottom:0,content:'"\\00a0"',position:"absolute",right:0,transition:e.transitions.create("border-bottom-color",{duration:e.transitions.duration.shorter}),pointerEvents:"none"},[`&:hover:not(.${Kp.disabled}, .${Kp.error}):before`]:{borderBottom:`2px solid ${(e.vars||e).palette.text.primary}`,"@media (hover: none)":{borderBottom:`1px solid ${n}`}},[`&.${Kp.disabled}:before`]:{borderBottomStyle:"dotted"}})})),Yp=(0,Zn.Ay)(Hp,{name:"MuiInput",slot:"Input",overridesResolver:zp})({}),Xp=B.forwardRef((function(e,t){var n,r,o,i;const a=(0,er.A)({props:e,name:"MuiInput"}),{disableUnderline:s,components:l={},componentsProps:u,fullWidth:c=!1,inputComponent:d="input",multiline:f=!1,slotProps:p,slots:h={},type:m="text"}=a,g=(0,qn.A)(a,Gp),v=(e=>{const{classes:t,disableUnderline:n}=e,r={root:["root",!n&&"underline"],input:["input"]},o=(0,Qn.A)(r,qp,t);return(0,Jt.A)({},t,o)})(a),y={root:{ownerState:{disableUnderline:s}}},b=(null!=p?p:u)?(0,Gn.A)(null!=p?p:u,y):y,w=null!=(n=null!=(r=h.root)?r:l.Root)?n:Jp,E=null!=(o=null!=(i=h.input)?i:l.Input)?o:Yp;return(0,ir.jsx)(Vp,(0,Jt.A)({slots:{root:w,input:E},slotProps:b,fullWidth:c,inputComponent:d,multiline:f,ref:t,type:m},g,{classes:v}))}));Xp.muiName="Input";const Qp=Xp;function Zp(e){return(0,rr.A)("MuiFilledInput",e)}const eh=(0,Jt.A)({},Dp,(0,nr.A)("MuiFilledInput",["root","underline","input"])),th=["disableUnderline","components","componentsProps","fullWidth","hiddenLabel","inputComponent","multiline","slotProps","slots","type"],nh=(0,Zn.Ay)($p,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiFilledInput",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[...Up(e,t),!n.disableUnderline&&t.underline]}})((({theme:e,ownerState:t})=>{var n;const r="light"===e.palette.mode,o=r?"rgba(0, 0, 0, 0.42)":"rgba(255, 255, 255, 0.7)",i=r?"rgba(0, 0, 0, 0.06)":"rgba(255, 255, 255, 0.09)",a=r?"rgba(0, 0, 0, 0.09)":"rgba(255, 255, 255, 0.13)",s=r?"rgba(0, 0, 0, 0.12)":"rgba(255, 255, 255, 0.12)";return(0,Jt.A)({position:"relative",backgroundColor:e.vars?e.vars.palette.FilledInput.bg:i,borderTopLeftRadius:(e.vars||e).shape.borderRadius,borderTopRightRadius:(e.vars||e).shape.borderRadius,transition:e.transitions.create("background-color",{duration:e.transitions.duration.shorter,easing:e.transitions.easing.easeOut}),"&:hover":{backgroundColor:e.vars?e.vars.palette.FilledInput.hoverBg:a,"@media (hover: none)":{backgroundColor:e.vars?e.vars.palette.FilledInput.bg:i}},[`&.${eh.focused}`]:{backgroundColor:e.vars?e.vars.palette.FilledInput.bg:i},[`&.${eh.disabled}`]:{backgroundColor:e.vars?e.vars.palette.FilledInput.disabledBg:s}},!t.disableUnderline&&{"&::after":{borderBottom:`2px solid ${null==(n=(e.vars||e).palette[t.color||"primary"])?void 0:n.main}`,left:0,bottom:0,content:'""',position:"absolute",right:0,transform:"scaleX(0)",transition:e.transitions.create("transform",{duration:e.transitions.duration.shorter,easing:e.transitions.easing.easeOut}),pointerEvents:"none"},[`&.${eh.focused}:after`]:{transform:"scaleX(1) translateX(0)"},[`&.${eh.error}`]:{"&::before, &::after":{borderBottomColor:(e.vars||e).palette.error.main}},"&::before":{borderBottom:`1px solid ${e.vars?`rgba(${e.vars.palette.common.onBackgroundChannel} / ${e.vars.opacity.inputUnderline})`:o}`,left:0,bottom:0,content:'"\\00a0"',position:"absolute",right:0,transition:e.transitions.create("border-bottom-color",{duration:e.transitions.duration.shorter}),pointerEvents:"none"},[`&:hover:not(.${eh.disabled}, .${eh.error}):before`]:{borderBottom:`1px solid ${(e.vars||e).palette.text.primary}`},[`&.${eh.disabled}:before`]:{borderBottomStyle:"dotted"}},t.startAdornment&&{paddingLeft:12},t.endAdornment&&{paddingRight:12},t.multiline&&(0,Jt.A)({padding:"25px 12px 8px"},"small"===t.size&&{paddingTop:21,paddingBottom:4},t.hiddenLabel&&{paddingTop:16,paddingBottom:17},t.hiddenLabel&&"small"===t.size&&{paddingTop:8,paddingBottom:9}))})),rh=(0,Zn.Ay)(Hp,{name:"MuiFilledInput",slot:"Input",overridesResolver:zp})((({theme:e,ownerState:t})=>(0,Jt.A)({paddingTop:25,paddingRight:12,paddingBottom:8,paddingLeft:12},!e.vars&&{"&:-webkit-autofill":{WebkitBoxShadow:"light"===e.palette.mode?null:"0 0 0 100px #266798 inset",WebkitTextFillColor:"light"===e.palette.mode?null:"#fff",caretColor:"light"===e.palette.mode?null:"#fff",borderTopLeftRadius:"inherit",borderTopRightRadius:"inherit"}},e.vars&&{"&:-webkit-autofill":{borderTopLeftRadius:"inherit",borderTopRightRadius:"inherit"},[e.getColorSchemeSelector("dark")]:{"&:-webkit-autofill":{WebkitBoxShadow:"0 0 0 100px #266798 inset",WebkitTextFillColor:"#fff",caretColor:"#fff"}}},"small"===t.size&&{paddingTop:21,paddingBottom:4},t.hiddenLabel&&{paddingTop:16,paddingBottom:17},t.startAdornment&&{paddingLeft:0},t.endAdornment&&{paddingRight:0},t.hiddenLabel&&"small"===t.size&&{paddingTop:8,paddingBottom:9},t.multiline&&{paddingTop:0,paddingBottom:0,paddingLeft:0,paddingRight:0}))),oh=B.forwardRef((function(e,t){var n,r,o,i;const a=(0,er.A)({props:e,name:"MuiFilledInput"}),{components:s={},componentsProps:l,fullWidth:u=!1,inputComponent:c="input",multiline:d=!1,slotProps:f,slots:p={},type:h="text"}=a,m=(0,qn.A)(a,th),g=(0,Jt.A)({},a,{fullWidth:u,inputComponent:c,multiline:d,type:h}),v=(e=>{const{classes:t,disableUnderline:n}=e,r={root:["root",!n&&"underline"],input:["input"]},o=(0,Qn.A)(r,Zp,t);return(0,Jt.A)({},t,o)})(a),y={root:{ownerState:g},input:{ownerState:g}},b=(null!=f?f:l)?(0,Gn.A)(y,null!=f?f:l):y,w=null!=(n=null!=(r=p.root)?r:s.Root)?n:nh,E=null!=(o=null!=(i=p.input)?i:s.Input)?o:rh;return(0,ir.jsx)(Vp,(0,Jt.A)({slots:{root:w,input:E},componentsProps:b,fullWidth:u,inputComponent:c,multiline:d,ref:t,type:h},m,{classes:v}))}));oh.muiName="Input";const ih=oh;var ah;const sh=["children","classes","className","label","notched"],lh=(0,Zn.Ay)("fieldset",{shouldForwardProp:Zn.ep})({textAlign:"left",position:"absolute",bottom:0,right:0,top:-5,left:0,margin:0,padding:"0 8px",pointerEvents:"none",borderRadius:"inherit",borderStyle:"solid",borderWidth:1,overflow:"hidden",minWidth:"0%"}),uh=(0,Zn.Ay)("legend",{shouldForwardProp:Zn.ep})((({ownerState:e,theme:t})=>(0,Jt.A)({float:"unset",width:"auto",overflow:"hidden"},!e.withLabel&&{padding:0,lineHeight:"11px",transition:t.transitions.create("width",{duration:150,easing:t.transitions.easing.easeOut})},e.withLabel&&(0,Jt.A)({display:"block",padding:0,height:11,fontSize:"0.75em",visibility:"hidden",maxWidth:.01,transition:t.transitions.create("max-width",{duration:50,easing:t.transitions.easing.easeOut}),whiteSpace:"nowrap","& > span":{paddingLeft:5,paddingRight:5,display:"inline-block",opacity:0,visibility:"visible"}},e.notched&&{maxWidth:"100%",transition:t.transitions.create("max-width",{duration:100,easing:t.transitions.easing.easeOut,delay:50})}))));function ch(e){return(0,rr.A)("MuiOutlinedInput",e)}const dh=(0,Jt.A)({},Dp,(0,nr.A)("MuiOutlinedInput",["root","notchedOutline","input"])),fh=["components","fullWidth","inputComponent","label","multiline","notched","slots","type"],ph=(0,Zn.Ay)($p,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiOutlinedInput",slot:"Root",overridesResolver:Up})((({theme:e,ownerState:t})=>{const n="light"===e.palette.mode?"rgba(0, 0, 0, 0.23)":"rgba(255, 255, 255, 0.23)";return(0,Jt.A)({position:"relative",borderRadius:(e.vars||e).shape.borderRadius,[`&:hover .${dh.notchedOutline}`]:{borderColor:(e.vars||e).palette.text.primary},"@media (hover: none)":{[`&:hover .${dh.notchedOutline}`]:{borderColor:e.vars?`rgba(${e.vars.palette.common.onBackgroundChannel} / 0.23)`:n}},[`&.${dh.focused} .${dh.notchedOutline}`]:{borderColor:(e.vars||e).palette[t.color].main,borderWidth:2},[`&.${dh.error} .${dh.notchedOutline}`]:{borderColor:(e.vars||e).palette.error.main},[`&.${dh.disabled} .${dh.notchedOutline}`]:{borderColor:(e.vars||e).palette.action.disabled}},t.startAdornment&&{paddingLeft:14},t.endAdornment&&{paddingRight:14},t.multiline&&(0,Jt.A)({padding:"16.5px 14px"},"small"===t.size&&{padding:"8.5px 14px"}))})),hh=(0,Zn.Ay)((function(e){const{className:t,label:n,notched:r}=e,o=(0,qn.A)(e,sh),i=null!=n&&""!==n,a=(0,Jt.A)({},e,{notched:r,withLabel:i});return(0,ir.jsx)(lh,(0,Jt.A)({"aria-hidden":!0,className:t,ownerState:a},o,{children:(0,ir.jsx)(uh,{ownerState:a,children:i?(0,ir.jsx)("span",{children:n}):ah||(ah=(0,ir.jsx)("span",{className:"notranslate",children:"â€‹"}))})}))}),{name:"MuiOutlinedInput",slot:"NotchedOutline",overridesResolver:(e,t)=>t.notchedOutline})((({theme:e})=>{const t="light"===e.palette.mode?"rgba(0, 0, 0, 0.23)":"rgba(255, 255, 255, 0.23)";return{borderColor:e.vars?`rgba(${e.vars.palette.common.onBackgroundChannel} / 0.23)`:t}})),mh=(0,Zn.Ay)(Hp,{name:"MuiOutlinedInput",slot:"Input",overridesResolver:zp})((({theme:e,ownerState:t})=>(0,Jt.A)({padding:"16.5px 14px"},!e.vars&&{"&:-webkit-autofill":{WebkitBoxShadow:"light"===e.palette.mode?null:"0 0 0 100px #266798 inset",WebkitTextFillColor:"light"===e.palette.mode?null:"#fff",caretColor:"light"===e.palette.mode?null:"#fff",borderRadius:"inherit"}},e.vars&&{"&:-webkit-autofill":{borderRadius:"inherit"},[e.getColorSchemeSelector("dark")]:{"&:-webkit-autofill":{WebkitBoxShadow:"0 0 0 100px #266798 inset",WebkitTextFillColor:"#fff",caretColor:"#fff"}}},"small"===t.size&&{padding:"8.5px 14px"},t.multiline&&{padding:0},t.startAdornment&&{paddingLeft:0},t.endAdornment&&{paddingRight:0}))),gh=B.forwardRef((function(e,t){var n,r,o,i,a;const s=(0,er.A)({props:e,name:"MuiOutlinedInput"}),{components:l={},fullWidth:u=!1,inputComponent:c="input",label:d,multiline:f=!1,notched:p,slots:h={},type:m="text"}=s,g=(0,qn.A)(s,fh),v=(e=>{const{classes:t}=e,n=(0,Qn.A)({root:["root"],notchedOutline:["notchedOutline"],input:["input"]},ch,t);return(0,Jt.A)({},t,n)})(s),y=Ll(),b=Xl({props:s,muiFormControl:y,states:["color","disabled","error","focused","hiddenLabel","size","required"]}),w=(0,Jt.A)({},s,{color:b.color||"primary",disabled:b.disabled,error:b.error,focused:b.focused,formControl:y,fullWidth:u,hiddenLabel:b.hiddenLabel,multiline:f,size:b.size,type:m}),E=null!=(n=null!=(r=h.root)?r:l.Root)?n:ph,S=null!=(o=null!=(i=h.input)?i:l.Input)?o:mh;return(0,ir.jsx)(Vp,(0,Jt.A)({slots:{root:E,input:S},renderSuffix:e=>(0,ir.jsx)(hh,{ownerState:w,className:v.notchedOutline,label:null!=d&&""!==d&&b.required?a||(a=(0,ir.jsxs)(B.Fragment,{children:[d,"â€‰","*"]})):d,notched:void 0!==p?p:Boolean(e.startAdornment||e.filled||e.focused)}),fullWidth:u,inputComponent:c,multiline:f,ref:t,type:m},g,{classes:(0,Jt.A)({},v,{notchedOutline:null})}))}));gh.muiName="Input";const vh=gh;function yh(e){return(0,rr.A)("MuiFormLabel",e)}const bh=(0,nr.A)("MuiFormLabel",["root","colorSecondary","focused","disabled","error","filled","required","asterisk"]),wh=["children","className","color","component","disabled","error","filled","focused","required"],Eh=(0,Zn.Ay)("label",{name:"MuiFormLabel",slot:"Root",overridesResolver:({ownerState:e},t)=>(0,Jt.A)({},t.root,"secondary"===e.color&&t.colorSecondary,e.filled&&t.filled)})((({theme:e,ownerState:t})=>(0,Jt.A)({color:(e.vars||e).palette.text.secondary},e.typography.body1,{lineHeight:"1.4375em",padding:0,position:"relative",[`&.${bh.focused}`]:{color:(e.vars||e).palette[t.color].main},[`&.${bh.disabled}`]:{color:(e.vars||e).palette.text.disabled},[`&.${bh.error}`]:{color:(e.vars||e).palette.error.main}}))),Sh=(0,Zn.Ay)("span",{name:"MuiFormLabel",slot:"Asterisk",overridesResolver:(e,t)=>t.asterisk})((({theme:e})=>({[`&.${bh.error}`]:{color:(e.vars||e).palette.error.main}}))),xh=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiFormLabel"}),{children:r,className:o,component:i="label"}=n,a=(0,qn.A)(n,wh),s=Xl({props:n,muiFormControl:Ll(),states:["color","required","focused","disabled","error","filled"]}),l=(0,Jt.A)({},n,{color:s.color||"primary",component:i,disabled:s.disabled,error:s.error,filled:s.filled,focused:s.focused,required:s.required}),u=(e=>{const{classes:t,color:n,focused:r,disabled:o,error:i,filled:a,required:s}=e,l={root:["root",`color${(0,tr.A)(n)}`,o&&"disabled",i&&"error",a&&"filled",r&&"focused",s&&"required"],asterisk:["asterisk",i&&"error"]};return(0,Qn.A)(l,yh,t)})(l);return(0,ir.jsxs)(Eh,(0,Jt.A)({as:i,ownerState:l,className:(0,Kn.A)(u.root,o),ref:t},a,{children:[r,s.required&&(0,ir.jsxs)(Sh,{ownerState:l,"aria-hidden":!0,className:u.asterisk,children:["â€‰","*"]})]}))}));function Ah(e){return(0,rr.A)("MuiInputLabel",e)}(0,nr.A)("MuiInputLabel",["root","focused","disabled","error","required","asterisk","formControl","sizeSmall","shrink","animated","standard","filled","outlined"]);const kh=["disableAnimation","margin","shrink","variant","className"],_h=(0,Zn.Ay)(xh,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiInputLabel",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`& .${bh.asterisk}`]:t.asterisk},t.root,n.formControl&&t.formControl,"small"===n.size&&t.sizeSmall,n.shrink&&t.shrink,!n.disableAnimation&&t.animated,n.focused&&t.focused,t[n.variant]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({display:"block",transformOrigin:"top left",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",maxWidth:"100%"},t.formControl&&{position:"absolute",left:0,top:0,transform:"translate(0, 20px) scale(1)"},"small"===t.size&&{transform:"translate(0, 17px) scale(1)"},t.shrink&&{transform:"translate(0, -1.5px) scale(0.75)",transformOrigin:"top left",maxWidth:"133%"},!t.disableAnimation&&{transition:e.transitions.create(["color","transform","max-width"],{duration:e.transitions.duration.shorter,easing:e.transitions.easing.easeOut})},"filled"===t.variant&&(0,Jt.A)({zIndex:1,pointerEvents:"none",transform:"translate(12px, 16px) scale(1)",maxWidth:"calc(100% - 24px)"},"small"===t.size&&{transform:"translate(12px, 13px) scale(1)"},t.shrink&&(0,Jt.A)({userSelect:"none",pointerEvents:"auto",transform:"translate(12px, 7px) scale(0.75)",maxWidth:"calc(133% - 24px)"},"small"===t.size&&{transform:"translate(12px, 4px) scale(0.75)"})),"outlined"===t.variant&&(0,Jt.A)({zIndex:1,pointerEvents:"none",transform:"translate(14px, 16px) scale(1)",maxWidth:"calc(100% - 24px)"},"small"===t.size&&{transform:"translate(14px, 9px) scale(1)"},t.shrink&&{userSelect:"none",pointerEvents:"auto",maxWidth:"calc(133% - 32px)",transform:"translate(14px, -9px) scale(0.75)"})))),Ch=B.forwardRef((function(e,t){const n=(0,er.A)({name:"MuiInputLabel",props:e}),{disableAnimation:r=!1,shrink:o,className:i}=n,a=(0,qn.A)(n,kh),s=Ll();let l=o;void 0===l&&s&&(l=s.filled||s.focused||s.adornedStart);const u=Xl({props:n,muiFormControl:s,states:["size","variant","required","focused"]}),c=(0,Jt.A)({},n,{disableAnimation:r,formControl:s,shrink:l,size:u.size,variant:u.variant,required:u.required,focused:u.focused}),d=(e=>{const{classes:t,formControl:n,size:r,shrink:o,disableAnimation:i,variant:a,required:s}=e,l={root:["root",n&&"formControl",!i&&"animated",o&&"shrink",r&&"normal"!==r&&`size${(0,tr.A)(r)}`,a],asterisk:[s&&"asterisk"]},u=(0,Qn.A)(l,Ah,t);return(0,Jt.A)({},t,u)})(c);return(0,ir.jsx)(_h,(0,Jt.A)({"data-shrink":l,ownerState:c,ref:t,className:(0,Kn.A)(d.root,i)},a,{classes:d}))}));function Oh(e){return(0,rr.A)("MuiFormHelperText",e)}const Ph=(0,nr.A)("MuiFormHelperText",["root","error","disabled","sizeSmall","sizeMedium","contained","focused","filled","required"]);var Rh;const Th=["children","className","component","disabled","error","filled","focused","margin","required","variant"],Fh=(0,Zn.Ay)("p",{name:"MuiFormHelperText",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.size&&t[`size${(0,tr.A)(n.size)}`],n.contained&&t.contained,n.filled&&t.filled]}})((({theme:e,ownerState:t})=>(0,Jt.A)({color:(e.vars||e).palette.text.secondary},e.typography.caption,{textAlign:"left",marginTop:3,marginRight:0,marginBottom:0,marginLeft:0,[`&.${Ph.disabled}`]:{color:(e.vars||e).palette.text.disabled},[`&.${Ph.error}`]:{color:(e.vars||e).palette.error.main}},"small"===t.size&&{marginTop:4},t.contained&&{marginLeft:14,marginRight:14}))),jh=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiFormHelperText"}),{children:r,className:o,component:i="p"}=n,a=(0,qn.A)(n,Th),s=Xl({props:n,muiFormControl:Ll(),states:["variant","size","disabled","error","filled","focused","required"]}),l=(0,Jt.A)({},n,{component:i,contained:"filled"===s.variant||"outlined"===s.variant,variant:s.variant,size:s.size,disabled:s.disabled,error:s.error,filled:s.filled,focused:s.focused,required:s.required}),u=(e=>{const{classes:t,contained:n,size:r,disabled:o,error:i,filled:a,focused:s,required:l}=e,u={root:["root",o&&"disabled",i&&"error",r&&`size${(0,tr.A)(r)}`,n&&"contained",s&&"focused",a&&"filled",l&&"required"]};return(0,Qn.A)(u,Oh,t)})(l);return(0,ir.jsx)(Fh,(0,Jt.A)({as:i,ownerState:l,className:(0,Kn.A)(u.root,o),ref:t},a,{children:" "===r?Rh||(Rh=(0,ir.jsx)("span",{className:"notranslate",children:"â€‹"})):r}))}));var Nh=o(6248);const Ih=Sr,Lh=["actions","autoFocus","autoFocusItem","children","className","disabledItemsFocusable","disableListWrap","onKeyDown","variant"];function Mh(e,t,n){return e===t?e.firstChild:t&&t.nextElementSibling?t.nextElementSibling:n?null:e.firstChild}function Dh(e,t,n){return e===t?n?e.firstChild:e.lastChild:t&&t.previousElementSibling?t.previousElementSibling:n?null:e.lastChild}function Bh(e,t){if(void 0===t)return!0;let n=e.innerText;return void 0===n&&(n=e.textContent),n=n.trim().toLowerCase(),0!==n.length&&(t.repeating?n[0]===t.keys[0]:0===n.indexOf(t.keys.join("")))}function Uh(e,t,n,r,o,i){let a=!1,s=o(e,t,!!t&&n);for(;s;){if(s===e.firstChild){if(a)return!1;a=!0}const t=!r&&(s.disabled||"true"===s.getAttribute("aria-disabled"));if(s.hasAttribute("tabindex")&&Bh(s,i)&&!t)return s.focus(),!0;s=o(e,s,n)}return!1}const zh=B.forwardRef((function(e,t){const{actions:n,autoFocus:r=!1,autoFocusItem:o=!1,children:i,className:a,disabledItemsFocusable:s=!1,disableListWrap:l=!1,onKeyDown:u,variant:c="selectedMenu"}=e,d=(0,qn.A)(e,Lh),f=B.useRef(null),p=B.useRef({keys:[],repeating:!0,previousKeyMatched:!0,lastTime:null});(0,xi.A)((()=>{r&&f.current.focus()}),[r]),B.useImperativeHandle(n,(()=>({adjustStyleForScrollbar:(e,t)=>{const n=!f.current.style.width;if(e.clientHeight<f.current.clientHeight&&n){const n=`${Ih((0,Nh.A)(e))}px`;f.current.style["rtl"===t.direction?"paddingLeft":"paddingRight"]=n,f.current.style.width=`calc(100% + ${n})`}return f.current}})),[]);const h=(0,Qr.A)(f,t);let m=-1;B.Children.forEach(i,((e,t)=>{B.isValidElement(e)?(e.props.disabled||("selectedMenu"===c&&e.props.selected||-1===m)&&(m=t),m===t&&(e.props.disabled||e.props.muiSkipListHighlight||e.type.muiSkipListHighlight)&&(m+=1,m>=i.length&&(m=-1))):m===t&&(m+=1,m>=i.length&&(m=-1))}));const g=B.Children.map(i,((e,t)=>{if(t===m){const t={};return o&&(t.autoFocus=!0),void 0===e.props.tabIndex&&"selectedMenu"===c&&(t.tabIndex=0),B.cloneElement(e,t)}return e}));return(0,ir.jsx)(Ei,(0,Jt.A)({role:"menu",ref:h,className:a,onKeyDown:e=>{const t=f.current,n=e.key,r=(0,Nh.A)(t).activeElement;if("ArrowDown"===n)e.preventDefault(),Uh(t,r,l,s,Mh);else if("ArrowUp"===n)e.preventDefault(),Uh(t,r,l,s,Dh);else if("Home"===n)e.preventDefault(),Uh(t,null,l,s,Mh);else if("End"===n)e.preventDefault(),Uh(t,null,l,s,Dh);else if(1===n.length){const o=p.current,i=n.toLowerCase(),a=performance.now();o.keys.length>0&&(a-o.lastTime>500?(o.keys=[],o.repeating=!0,o.previousKeyMatched=!0):o.repeating&&i!==o.keys[0]&&(o.repeating=!1)),o.lastTime=a,o.keys.push(i);const l=r&&!o.repeating&&Bh(r,o);o.previousKeyMatched&&(l||Uh(t,r,!1,s,Mh,o))?e.preventDefault():o.previousKeyMatched=!1}u&&u(e)},tabIndex:r?0:-1},d,{children:g}))}));function $h(e){return(0,rr.A)("MuiPopover",e)}(0,nr.A)("MuiPopover",["root","paper"]);const Hh=["onEntering"],Wh=["action","anchorEl","anchorOrigin","anchorPosition","anchorReference","children","className","container","elevation","marginThreshold","open","PaperProps","slots","slotProps","transformOrigin","TransitionComponent","transitionDuration","TransitionProps","disableScrollLock"],Vh=["slotProps"];function qh(e,t){let n=0;return"number"==typeof t?n=t:"center"===t?n=e.height/2:"bottom"===t&&(n=e.height),n}function Kh(e,t){let n=0;return"number"==typeof t?n=t:"center"===t?n=e.width/2:"right"===t&&(n=e.width),n}function Gh(e){return[e.horizontal,e.vertical].map((e=>"number"==typeof e?`${e}px`:e)).join(" ")}function Jh(e){return"function"==typeof e?e():e}const Yh=(0,Zn.Ay)(co,{name:"MuiPopover",slot:"Root",overridesResolver:(e,t)=>t.root})({}),Xh=(0,Zn.Ay)(So,{name:"MuiPopover",slot:"Paper",overridesResolver:(e,t)=>t.paper})({position:"absolute",overflowY:"auto",overflowX:"hidden",minWidth:16,minHeight:16,maxWidth:"calc(100% - 32px)",maxHeight:"calc(100% - 32px)",outline:0}),Qh=B.forwardRef((function(e,t){var n,r,o;const i=(0,er.A)({props:e,name:"MuiPopover"}),{action:a,anchorEl:s,anchorOrigin:l={vertical:"top",horizontal:"left"},anchorPosition:u,anchorReference:c="anchorEl",children:d,className:f,container:p,elevation:h=8,marginThreshold:m=16,open:g,PaperProps:v={},slots:y,slotProps:b,transformOrigin:w={vertical:"top",horizontal:"left"},TransitionComponent:E=Uu,transitionDuration:S="auto",TransitionProps:{onEntering:x}={},disableScrollLock:A=!1}=i,k=(0,qn.A)(i.TransitionProps,Hh),_=(0,qn.A)(i,Wh),C=null!=(n=null==b?void 0:b.paper)?n:v,O=B.useRef(),P=(0,Qr.A)(O,C.ref),R=(0,Jt.A)({},i,{anchorOrigin:l,anchorReference:c,elevation:h,marginThreshold:m,externalPaperSlotProps:C,transformOrigin:w,TransitionComponent:E,transitionDuration:S,TransitionProps:k}),T=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"],paper:["paper"]},$h,t)})(R),F=B.useCallback((()=>{if("anchorPosition"===c)return u;const e=Jh(s),t=(e&&1===e.nodeType?e:(0,Nh.A)(O.current).body).getBoundingClientRect();return{top:t.top+qh(t,l.vertical),left:t.left+Kh(t,l.horizontal)}}),[s,l.horizontal,l.vertical,u,c]),j=B.useCallback((e=>({vertical:qh(e,w.vertical),horizontal:Kh(e,w.horizontal)})),[w.horizontal,w.vertical]),N=B.useCallback((e=>{const t={width:e.offsetWidth,height:e.offsetHeight},n=j(t);if("none"===c)return{top:null,left:null,transformOrigin:Gh(n)};const r=F();let o=r.top-n.vertical,i=r.left-n.horizontal;const a=o+t.height,l=i+t.width,u=(0,po.A)(Jh(s)),d=u.innerHeight-m,f=u.innerWidth-m;if(null!==m&&o<m){const e=o-m;o-=e,n.vertical+=e}else if(null!==m&&a>d){const e=a-d;o-=e,n.vertical+=e}if(null!==m&&i<m){const e=i-m;i-=e,n.horizontal+=e}else if(l>f){const e=l-f;i-=e,n.horizontal+=e}return{top:`${Math.round(o)}px`,left:`${Math.round(i)}px`,transformOrigin:Gh(n)}}),[s,c,F,j,m]),[I,L]=B.useState(g),M=B.useCallback((()=>{const e=O.current;if(!e)return;const t=N(e);null!==t.top&&(e.style.top=t.top),null!==t.left&&(e.style.left=t.left),e.style.transformOrigin=t.transformOrigin,L(!0)}),[N]);B.useEffect((()=>(A&&window.addEventListener("scroll",M),()=>window.removeEventListener("scroll",M))),[s,A,M]),B.useEffect((()=>{g&&M()})),B.useImperativeHandle(a,(()=>g?{updatePosition:()=>{M()}}:null),[g,M]),B.useEffect((()=>{if(!g)return;const e=(0,fo.A)((()=>{M()})),t=(0,po.A)(s);return t.addEventListener("resize",e),()=>{e.clear(),t.removeEventListener("resize",e)}}),[s,g,M]);let D=S;"auto"!==S||E.muiSupportAuto||(D=void 0);const U=p||(s?(0,Nh.A)(Jh(s)).body:void 0),z=null!=(r=null==y?void 0:y.root)?r:Yh,$=null!=(o=null==y?void 0:y.paper)?o:Xh,H=vr({elementType:$,externalSlotProps:(0,Jt.A)({},C,{style:I?C.style:(0,Jt.A)({},C.style,{opacity:0})}),additionalProps:{elevation:h,ref:P},ownerState:R,className:(0,Kn.A)(T.paper,null==C?void 0:C.className)}),W=vr({elementType:z,externalSlotProps:(null==b?void 0:b.root)||{},externalForwardedProps:_,additionalProps:{ref:t,slotProps:{backdrop:{invisible:!0}},container:U,open:g},ownerState:R,className:(0,Kn.A)(T.root,f)}),{slotProps:V}=W,q=(0,qn.A)(W,Vh);return(0,ir.jsx)(z,(0,Jt.A)({},q,!fr(z)&&{slotProps:V,disableScrollLock:A},{children:(0,ir.jsx)(E,(0,Jt.A)({appear:!0,in:g,onEntering:(e,t)=>{x&&x(e,t),M()},onExited:()=>{L(!1)},timeout:D},k,{children:(0,ir.jsx)($,(0,Jt.A)({},H,{children:d}))}))}))}));function Zh(e){return(0,rr.A)("MuiMenu",e)}(0,nr.A)("MuiMenu",["root","paper","list"]);const em=["onEntering"],tm=["autoFocus","children","className","disableAutoFocusItem","MenuListProps","onClose","open","PaperProps","PopoverClasses","transitionDuration","TransitionProps","variant","slots","slotProps"],nm={vertical:"top",horizontal:"right"},rm={vertical:"top",horizontal:"left"},om=(0,Zn.Ay)(Qh,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiMenu",slot:"Root",overridesResolver:(e,t)=>t.root})({}),im=(0,Zn.Ay)(Xh,{name:"MuiMenu",slot:"Paper",overridesResolver:(e,t)=>t.paper})({maxHeight:"calc(100% - 96px)",WebkitOverflowScrolling:"touch"}),am=(0,Zn.Ay)(zh,{name:"MuiMenu",slot:"List",overridesResolver:(e,t)=>t.list})({outline:0}),sm=B.forwardRef((function(e,t){var n,r;const o=(0,er.A)({props:e,name:"MuiMenu"}),{autoFocus:i=!0,children:a,className:s,disableAutoFocusItem:l=!1,MenuListProps:u={},onClose:c,open:d,PaperProps:f={},PopoverClasses:p,transitionDuration:h="auto",TransitionProps:{onEntering:m}={},variant:g="selectedMenu",slots:v={},slotProps:y={}}=o,b=(0,qn.A)(o.TransitionProps,em),w=(0,qn.A)(o,tm),E=Jr(),S="rtl"===E.direction,x=(0,Jt.A)({},o,{autoFocus:i,disableAutoFocusItem:l,MenuListProps:u,onEntering:m,PaperProps:f,transitionDuration:h,TransitionProps:b,variant:g}),A=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"],paper:["paper"],list:["list"]},Zh,t)})(x),k=i&&!l&&d,_=B.useRef(null);let C=-1;B.Children.map(a,((e,t)=>{B.isValidElement(e)&&(e.props.disabled||("selectedMenu"===g&&e.props.selected||-1===C)&&(C=t))}));const O=null!=(n=v.paper)?n:im,P=null!=(r=y.paper)?r:f,R=vr({elementType:v.root,externalSlotProps:y.root,ownerState:x,className:[A.root,s]}),T=vr({elementType:O,externalSlotProps:P,ownerState:x,className:A.paper});return(0,ir.jsx)(om,(0,Jt.A)({onClose:c,anchorOrigin:{vertical:"bottom",horizontal:S?"right":"left"},transformOrigin:S?nm:rm,slots:{paper:O,root:v.root},slotProps:{root:R,paper:T},open:d,ref:t,transitionDuration:h,TransitionProps:(0,Jt.A)({onEntering:(e,t)=>{_.current&&_.current.adjustStyleForScrollbar(e,E),m&&m(e,t)}},b),ownerState:x},w,{classes:p,children:(0,ir.jsx)(am,(0,Jt.A)({onKeyDown:e=>{"Tab"===e.key&&(e.preventDefault(),c&&c(e,"tabKeyDown"))},actions:_,autoFocus:i&&(-1===C||l),autoFocusItem:k,variant:g},u,{className:(0,Kn.A)(A.list,u.className),children:a}))}))}));function lm(e){return(0,rr.A)("MuiNativeSelect",e)}const um=(0,nr.A)("MuiNativeSelect",["root","select","multiple","filled","outlined","standard","disabled","icon","iconOpen","iconFilled","iconOutlined","iconStandard","nativeInput","error"]),cm=["className","disabled","error","IconComponent","inputRef","variant"],dm=({ownerState:e,theme:t})=>(0,Jt.A)({MozAppearance:"none",WebkitAppearance:"none",userSelect:"none",borderRadius:0,cursor:"pointer","&:focus":(0,Jt.A)({},t.vars?{backgroundColor:`rgba(${t.vars.palette.common.onBackgroundChannel} / 0.05)`}:{backgroundColor:"light"===t.palette.mode?"rgba(0, 0, 0, 0.05)":"rgba(255, 255, 255, 0.05)"},{borderRadius:0}),"&::-ms-expand":{display:"none"},[`&.${um.disabled}`]:{cursor:"default"},"&[multiple]":{height:"auto"},"&:not([multiple]) option, &:not([multiple]) optgroup":{backgroundColor:(t.vars||t).palette.background.paper},"&&&":{paddingRight:24,minWidth:16}},"filled"===e.variant&&{"&&&":{paddingRight:32}},"outlined"===e.variant&&{borderRadius:(t.vars||t).shape.borderRadius,"&:focus":{borderRadius:(t.vars||t).shape.borderRadius},"&&&":{paddingRight:32}}),fm=(0,Zn.Ay)("select",{name:"MuiNativeSelect",slot:"Select",shouldForwardProp:Zn.ep,overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.select,t[n.variant],n.error&&t.error,{[`&.${um.multiple}`]:t.multiple}]}})(dm),pm=({ownerState:e,theme:t})=>(0,Jt.A)({position:"absolute",right:0,top:"calc(50% - .5em)",pointerEvents:"none",color:(t.vars||t).palette.action.active,[`&.${um.disabled}`]:{color:(t.vars||t).palette.action.disabled}},e.open&&{transform:"rotate(180deg)"},"filled"===e.variant&&{right:7},"outlined"===e.variant&&{right:7}),hm=(0,Zn.Ay)("svg",{name:"MuiNativeSelect",slot:"Icon",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.icon,n.variant&&t[`icon${(0,tr.A)(n.variant)}`],n.open&&t.iconOpen]}})(pm),mm=B.forwardRef((function(e,t){const{className:n,disabled:r,error:o,IconComponent:i,inputRef:a,variant:s="standard"}=e,l=(0,qn.A)(e,cm),u=(0,Jt.A)({},e,{disabled:r,variant:s,error:o}),c=(e=>{const{classes:t,variant:n,disabled:r,multiple:o,open:i,error:a}=e,s={select:["select",n,r&&"disabled",o&&"multiple",a&&"error"],icon:["icon",`icon${(0,tr.A)(n)}`,i&&"iconOpen",r&&"disabled"]};return(0,Qn.A)(s,lm,t)})(u);return(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)(fm,(0,Jt.A)({ownerState:u,className:(0,Kn.A)(c.select,n),disabled:r,ref:a||t},l)),e.multiple?null:(0,ir.jsx)(hm,{as:i,ownerState:u,className:c.icon})]})}));function gm(e){return(0,rr.A)("MuiSelect",e)}const vm=(0,nr.A)("MuiSelect",["root","select","multiple","filled","outlined","standard","disabled","focused","icon","iconOpen","iconFilled","iconOutlined","iconStandard","nativeInput","error"]);var ym;const bm=["aria-describedby","aria-label","autoFocus","autoWidth","children","className","defaultOpen","defaultValue","disabled","displayEmpty","error","IconComponent","inputRef","labelId","MenuProps","multiple","name","onBlur","onChange","onClose","onFocus","onOpen","open","readOnly","renderValue","SelectDisplayProps","tabIndex","type","value","variant"],wm=(0,Zn.Ay)("div",{name:"MuiSelect",slot:"Select",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`&.${vm.select}`]:t.select},{[`&.${vm.select}`]:t[n.variant]},{[`&.${vm.error}`]:t.error},{[`&.${vm.multiple}`]:t.multiple}]}})(dm,{[`&.${vm.select}`]:{height:"auto",minHeight:"1.4375em",textOverflow:"ellipsis",whiteSpace:"nowrap",overflow:"hidden"}}),Em=(0,Zn.Ay)("svg",{name:"MuiSelect",slot:"Icon",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.icon,n.variant&&t[`icon${(0,tr.A)(n.variant)}`],n.open&&t.iconOpen]}})(pm),Sm=(0,Zn.Ay)("input",{shouldForwardProp:e=>(0,Zn._n)(e)&&"classes"!==e,name:"MuiSelect",slot:"NativeInput",overridesResolver:(e,t)=>t.nativeInput})({bottom:0,left:0,position:"absolute",opacity:0,pointerEvents:"none",width:"100%",boxSizing:"border-box"});function xm(e,t){return"object"==typeof t&&null!==t?e===t:String(e)===String(t)}function Am(e){return null==e||"string"==typeof e&&!e.trim()}const km=B.forwardRef((function(e,t){var n;const{"aria-describedby":r,"aria-label":o,autoFocus:i,autoWidth:a,children:s,className:l,defaultOpen:u,defaultValue:c,disabled:d,displayEmpty:f,error:p=!1,IconComponent:h,inputRef:m,labelId:g,MenuProps:v={},multiple:y,name:b,onBlur:w,onChange:E,onClose:S,onFocus:x,onOpen:A,open:k,readOnly:_,renderValue:C,SelectDisplayProps:O={},tabIndex:P,value:R,variant:T="standard"}=e,F=(0,qn.A)(e,bm),[j,N]=(0,nu.A)({controlled:R,default:c,name:"Select"}),[I,L]=(0,nu.A)({controlled:k,default:u,name:"Select"}),M=B.useRef(null),D=B.useRef(null),[U,z]=B.useState(null),{current:$}=B.useRef(null!=k),[H,W]=B.useState(),V=(0,Qr.A)(t,m),q=B.useCallback((e=>{D.current=e,e&&z(e)}),[]),K=null==U?void 0:U.parentNode;B.useImperativeHandle(V,(()=>({focus:()=>{D.current.focus()},node:M.current,value:j})),[j]),B.useEffect((()=>{u&&I&&U&&!$&&(W(a?null:K.clientWidth),D.current.focus())}),[U,a]),B.useEffect((()=>{i&&D.current.focus()}),[i]),B.useEffect((()=>{if(!g)return;const e=(0,Nh.A)(D.current).getElementById(g);if(e){const t=()=>{getSelection().isCollapsed&&D.current.focus()};return e.addEventListener("click",t),()=>{e.removeEventListener("click",t)}}}),[g]);const G=(e,t)=>{e?A&&A(t):S&&S(t),$||(W(a?null:K.clientWidth),L(e))},J=B.Children.toArray(s),Y=e=>t=>{let n;if(t.currentTarget.hasAttribute("tabindex")){if(y){n=Array.isArray(j)?j.slice():[];const t=j.indexOf(e.props.value);-1===t?n.push(e.props.value):n.splice(t,1)}else n=e.props.value;if(e.props.onClick&&e.props.onClick(t),j!==n&&(N(n),E)){const r=t.nativeEvent||t,o=new r.constructor(r.type,r);Object.defineProperty(o,"target",{writable:!0,value:{value:n,name:b}}),E(o,e)}y||G(!1,t)}},X=null!==U&&I;let Q,Z;delete F["aria-invalid"];const ee=[];let te=!1,ne=!1;(wu({value:j})||f)&&(C?Q=C(j):te=!0);const re=J.map((e=>{if(!B.isValidElement(e))return null;let t;if(y){if(!Array.isArray(j))throw new Error((0,Cp.A)(2));t=j.some((t=>xm(t,e.props.value))),t&&te&&ee.push(e.props.children)}else t=xm(j,e.props.value),t&&te&&(Z=e.props.children);return t&&(ne=!0),B.cloneElement(e,{"aria-selected":t?"true":"false",onClick:Y(e),onKeyUp:t=>{" "===t.key&&t.preventDefault(),e.props.onKeyUp&&e.props.onKeyUp(t)},role:"option",selected:t,value:void 0,"data-value":e.props.value})}));te&&(Q=y?0===ee.length?null:ee.reduce(((e,t,n)=>(e.push(t),n<ee.length-1&&e.push(", "),e)),[]):Z);let oe,ie=H;!a&&$&&U&&(ie=K.clientWidth),oe=void 0!==P?P:d?null:0;const ae=O.id||(b?`mui-component-select-${b}`:void 0),se=(0,Jt.A)({},e,{variant:T,value:j,open:X,error:p}),le=(e=>{const{classes:t,variant:n,disabled:r,multiple:o,open:i,error:a}=e,s={select:["select",n,r&&"disabled",o&&"multiple",a&&"error"],icon:["icon",`icon${(0,tr.A)(n)}`,i&&"iconOpen",r&&"disabled"],nativeInput:["nativeInput"]};return(0,Qn.A)(s,gm,t)})(se),ue=(0,Jt.A)({},v.PaperProps,null==(n=v.slotProps)?void 0:n.paper),ce=(0,Cs.A)();return(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)(wm,(0,Jt.A)({ref:q,tabIndex:oe,role:"combobox","aria-controls":ce,"aria-disabled":d?"true":void 0,"aria-expanded":X?"true":"false","aria-haspopup":"listbox","aria-label":o,"aria-labelledby":[g,ae].filter(Boolean).join(" ")||void 0,"aria-describedby":r,onKeyDown:e=>{_||-1!==[" ","ArrowUp","ArrowDown","Enter"].indexOf(e.key)&&(e.preventDefault(),G(!0,e))},onMouseDown:d||_?null:e=>{0===e.button&&(e.preventDefault(),D.current.focus(),G(!0,e))},onBlur:e=>{!X&&w&&(Object.defineProperty(e,"target",{writable:!0,value:{value:j,name:b}}),w(e))},onFocus:x},O,{ownerState:se,className:(0,Kn.A)(O.className,le.select,l),id:ae,children:Am(Q)?ym||(ym=(0,ir.jsx)("span",{className:"notranslate",children:"â€‹"})):Q})),(0,ir.jsx)(Sm,(0,Jt.A)({"aria-invalid":p,value:Array.isArray(j)?j.join(","):j,name:b,ref:M,"aria-hidden":!0,onChange:e=>{const t=J.find((t=>t.props.value===e.target.value));void 0!==t&&(N(t.props.value),E&&E(e,t))},tabIndex:-1,disabled:d,className:le.nativeInput,autoFocus:i,ownerState:se},F)),(0,ir.jsx)(Em,{as:h,className:le.icon,ownerState:se}),(0,ir.jsx)(sm,(0,Jt.A)({id:`menu-${b||""}`,anchorEl:K,open:X,onClose:e=>{G(!1,e)},anchorOrigin:{vertical:"bottom",horizontal:"center"},transformOrigin:{vertical:"top",horizontal:"center"}},v,{MenuListProps:(0,Jt.A)({"aria-labelledby":g,role:"listbox","aria-multiselectable":y?"true":void 0,disableListWrap:!0,id:ce},v.MenuListProps),slotProps:(0,Jt.A)({},v.slotProps,{paper:(0,Jt.A)({},ue,{style:(0,Jt.A)({minWidth:ie},null!=ue?ue.style:null)})}),children:re}))]})})),_m=(0,Na.A)((0,ir.jsx)("path",{d:"M7 10l5 5 5-5z"}),"ArrowDropDown"),Cm=["autoWidth","children","classes","className","defaultOpen","displayEmpty","IconComponent","id","input","inputProps","label","labelId","MenuProps","multiple","native","onClose","onOpen","open","renderValue","SelectDisplayProps","variant"],Om=["root"],Pm={name:"MuiSelect",overridesResolver:(e,t)=>t.root,shouldForwardProp:e=>(0,Zn.ep)(e)&&"variant"!==e,slot:"Root"},Rm=(0,Zn.Ay)(Qp,Pm)(""),Tm=(0,Zn.Ay)(vh,Pm)(""),Fm=(0,Zn.Ay)(ih,Pm)(""),jm=B.forwardRef((function(e,t){const n=(0,er.A)({name:"MuiSelect",props:e}),{autoWidth:r=!1,children:o,classes:i={},className:a,defaultOpen:s=!1,displayEmpty:l=!1,IconComponent:u=_m,id:c,input:d,inputProps:f,label:p,labelId:h,MenuProps:m,multiple:g=!1,native:v=!1,onClose:y,onOpen:b,open:w,renderValue:E,SelectDisplayProps:S,variant:x="outlined"}=n,A=(0,qn.A)(n,Cm),k=v?mm:km,_=Xl({props:n,muiFormControl:Ll(),states:["variant","error"]}),C=_.variant||x,O=(0,Jt.A)({},n,{variant:C,classes:i}),P=(e=>{const{classes:t}=e;return t})(O),R=(0,qn.A)(P,Om),T=d||{standard:(0,ir.jsx)(Rm,{ownerState:O}),outlined:(0,ir.jsx)(Tm,{label:p,ownerState:O}),filled:(0,ir.jsx)(Fm,{ownerState:O})}[C],F=(0,Qr.A)(t,T.ref);return(0,ir.jsx)(B.Fragment,{children:B.cloneElement(T,(0,Jt.A)({inputComponent:k,inputProps:(0,Jt.A)({children:o,error:_.error,IconComponent:u,variant:C,type:void 0,multiple:g},v?{id:c}:{autoWidth:r,defaultOpen:s,displayEmpty:l,labelId:h,MenuProps:m,onClose:y,onOpen:b,open:w,renderValue:E,SelectDisplayProps:(0,Jt.A)({id:c},S)},f,{classes:f?(0,Gn.A)(R,f.classes):R},d?d.props.inputProps:{})},g&&v&&"outlined"===C?{notched:!0}:{},{ref:F,className:(0,Kn.A)(T.props.className,a,P.root)},!d&&{variant:C},A))})}));jm.muiName="Select";const Nm=jm;function Im(e){return(0,rr.A)("MuiTextField",e)}(0,nr.A)("MuiTextField",["root"]);const Lm=["autoComplete","autoFocus","children","className","color","defaultValue","disabled","error","FormHelperTextProps","fullWidth","helperText","id","InputLabelProps","inputProps","InputProps","inputRef","label","maxRows","minRows","multiline","name","onBlur","onChange","onFocus","placeholder","required","rows","select","SelectProps","type","value","variant"],Mm={standard:Qp,filled:ih,outlined:vh},Dm=(0,Zn.Ay)(Au,{name:"MuiTextField",slot:"Root",overridesResolver:(e,t)=>t.root})({}),Bm=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTextField"}),{autoComplete:r,autoFocus:o=!1,children:i,className:a,color:s="primary",defaultValue:l,disabled:u=!1,error:c=!1,FormHelperTextProps:d,fullWidth:f=!1,helperText:p,id:h,InputLabelProps:m,inputProps:g,InputProps:v,inputRef:y,label:b,maxRows:w,minRows:E,multiline:S=!1,name:x,onBlur:A,onChange:k,onFocus:_,placeholder:C,required:O=!1,rows:P,select:R=!1,SelectProps:T,type:F,value:j,variant:N="outlined"}=n,I=(0,qn.A)(n,Lm),L=(0,Jt.A)({},n,{autoFocus:o,color:s,disabled:u,error:c,fullWidth:f,multiline:S,required:O,select:R,variant:N}),M=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},Im,t)})(L),D={};"outlined"===N&&(m&&void 0!==m.shrink&&(D.notched=m.shrink),D.label=b),R&&(T&&T.native||(D.id=void 0),D["aria-describedby"]=void 0);const B=(0,Cs.A)(h),U=p&&B?`${B}-helper-text`:void 0,z=b&&B?`${B}-label`:void 0,$=Mm[N],H=(0,ir.jsx)($,(0,Jt.A)({"aria-describedby":U,autoComplete:r,autoFocus:o,defaultValue:l,fullWidth:f,multiline:S,name:x,rows:P,maxRows:w,minRows:E,type:F,value:j,id:B,inputRef:y,onBlur:A,onChange:k,onFocus:_,placeholder:C,inputProps:g},D,v));return(0,ir.jsxs)(Dm,(0,Jt.A)({className:(0,Kn.A)(M.root,a),disabled:u,error:c,fullWidth:f,ref:t,required:O,color:s,variant:N,ownerState:L},I,{children:[null!=b&&""!==b&&(0,ir.jsx)(Ch,(0,Jt.A)({htmlFor:B,id:z},m,{children:b})),R?(0,ir.jsx)(Nm,(0,Jt.A)({"aria-describedby":U,id:B,labelId:z,value:j,input:H},T,{children:i})):H,p&&(0,ir.jsx)(jh,(0,Jt.A)({id:U},d,{children:p}))]}))}));function Um(e){return(0,rr.A)("MuiMenuItem",e)}const zm=(0,nr.A)("MuiMenuItem",["root","focusVisible","dense","disabled","divider","gutters","selected"]),$m=["autoFocus","component","dense","divider","disableGutters","focusVisibleClassName","role","tabIndex","className"],Hm=(0,Zn.Ay)(di,{shouldForwardProp:e=>(0,Zn.ep)(e)||"classes"===e,name:"MuiMenuItem",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.dense&&t.dense,n.divider&&t.divider,!n.disableGutters&&t.gutters]}})((({theme:e,ownerState:t})=>(0,Jt.A)({},e.typography.body1,{display:"flex",justifyContent:"flex-start",alignItems:"center",position:"relative",textDecoration:"none",minHeight:48,paddingTop:6,paddingBottom:6,boxSizing:"border-box",whiteSpace:"nowrap"},!t.disableGutters&&{paddingLeft:16,paddingRight:16},t.divider&&{borderBottom:`1px solid ${(e.vars||e).palette.divider}`,backgroundClip:"padding-box"},{"&:hover":{textDecoration:"none",backgroundColor:(e.vars||e).palette.action.hover,"@media (hover: none)":{backgroundColor:"transparent"}},[`&.${zm.selected}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity),[`&.${zm.focusVisible}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.focusOpacity}))`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity+e.palette.action.focusOpacity)}},[`&.${zm.selected}:hover`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.hoverOpacity}))`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity+e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity)}},[`&.${zm.focusVisible}`]:{backgroundColor:(e.vars||e).palette.action.focus},[`&.${zm.disabled}`]:{opacity:(e.vars||e).palette.action.disabledOpacity},[`& + .${Pl.root}`]:{marginTop:e.spacing(1),marginBottom:e.spacing(1)},[`& + .${Pl.inset}`]:{marginLeft:52},[`& .${Hi.root}`]:{marginTop:0,marginBottom:0},[`& .${Hi.inset}`]:{paddingLeft:36},[`& .${Di.root}`]:{minWidth:36}},!t.dense&&{[e.breakpoints.up("sm")]:{minHeight:"auto"}},t.dense&&(0,Jt.A)({minHeight:32,paddingTop:4,paddingBottom:4},e.typography.body2,{[`& .${Di.root} svg`]:{fontSize:"1.25rem"}})))),Wm=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiMenuItem"}),{autoFocus:r=!1,component:o="li",dense:i=!1,divider:a=!1,disableGutters:s=!1,focusVisibleClassName:l,role:u="menuitem",tabIndex:c,className:d}=n,f=(0,qn.A)(n,$m),p=B.useContext(vi),h=B.useMemo((()=>({dense:i||p.dense||!1,disableGutters:s})),[p.dense,i,s]),m=B.useRef(null);(0,xi.A)((()=>{r&&m.current&&m.current.focus()}),[r]);const g=(0,Jt.A)({},n,{dense:h.dense,divider:a,disableGutters:s}),v=(e=>{const{disabled:t,dense:n,divider:r,disableGutters:o,selected:i,classes:a}=e,s={root:["root",n&&"dense",t&&"disabled",!o&&"gutters",r&&"divider",i&&"selected"]},l=(0,Qn.A)(s,Um,a);return(0,Jt.A)({},a,l)})(n),y=(0,Qr.A)(m,t);let b;return n.disabled||(b=void 0!==c?c:-1),(0,ir.jsx)(vi.Provider,{value:h,children:(0,ir.jsx)(Hm,(0,Jt.A)({ref:y,role:u,tabIndex:b,component:o,focusVisibleClassName:(0,Kn.A)(v.focusVisible,l),className:(0,Kn.A)(v.root,d)},f,{ownerState:g,classes:v}))})}));var Vm=["classes","inputRef","ref"],qm=function(e){return{root:{flexGrow:1},container:{position:"relative"},suggestionsContainerOpen:{position:"absolute",zIndex:1,marginTop:e.spacing(1),left:0,right:0},suggestion:{display:"block"},suggestionsList:{margin:0,padding:0,listStyleType:"none"}}};function Km(e){return"squatter"in e.grant||"root"in e.grant}function Gm(e){var t=e.classes,n=e.inputRef,r=void 0===n?function(){}:n,o=e.ref,i=wp(e,Vm),a=!1,s=null;return Pn(i.value)?i.namespaces.indexOf(i.value)<0&&("true"==i.allownew?s="New namespace":(a=!0,s="You need the squatter or root role to create this new namespace")):(a=!0,s="Must start with an aplha and continue with aplhanum, dot, underscore or hyphen."),B.createElement(B.Fragment,null,B.createElement(Bm,(0,Jt.A)({fullWidth:!0,error:a,InputProps:{inputRef:function(e){o(e),r(e)},classes:{input:t.input}},helperText:s},i)))}function Jm(e,t){var n=t.query,r=t.isHighlighted,o=Ap()(e,n),i=_p()(e,o);return console.log("renderSuggestion",o,i),B.createElement(Wm,{selected:r,component:"div"},B.createElement("div",null,i.map((function(e){return B.createElement("span",{key:e.text,style:{fontWeight:e.highlight?500:400}},e.text)}))))}function Ym(e){return e}function Xm(e){var t=fe(ve(),2),n=t[0],r=n.cstat,o=n.user,i=t[1];i=e.role?function(e,t){if(void 0===e.grant)return[];if(!(t in e.grant))return[];var n=[].concat(e.grant[t]);return n.sort(),n}(o,e.role):getNamespaces(r);var a=la(qm),s=fe((0,B.useState)(null),2),l=s[0],u=s[1],c=fe((0,B.useState)([]),2),d=c[0],f=c[1],p={highlightFirstSuggestion:!0,renderInputComponent:Gm,suggestions:d,onSuggestionsFetchRequested:function(e){var t=e.value;f(function(e,t){var n=e.trim().toLowerCase(),r=n.length,o=0;return 0===r?[]:t.filter((function(e){var t=o<5&&e.slice(0,r).toLowerCase()===n;return t&&(o+=1),t}))}(t,i))},onSuggestionsClearRequested:function(){f([])},getSuggestionValue:Ym,renderSuggestion:Jm};return B.createElement("div",{className:a.root},B.createElement(Sp(),(0,Jt.A)({},p,{inputProps:{classes:a,id:e.id,label:"Namespace",value:void 0===e.selected?"":e.selected,onChange:function(t,n){var r=n.newValue;e.onChange(r)},inputRef:function(e){!function(e){u(e)}(e)},InputLabelProps:{shrink:!0},namespaces:i,allownew:Km(o).toString()},theme:{suggestionsList:a.suggestionsList,suggestion:a.suggestion},renderSuggestionsContainer:function(e){return B.createElement(sd,{anchorEl:l,open:Boolean(e.children)},B.createElement(So,(0,Jt.A)({square:!0},e.containerProps,{style:{width:l?l.clientWidth:void 0}}),e.children))}})))}function Qm(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Zm(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Qm(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Qm(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var eg=function(e){return{desc:{padding:e.spacing(3,0)},formcontrol:{margin:e.spacing(2,0)}}};function tg(e){var t=e.data,n=e.set,r=la(eg);return B.createElement("div",null,B.createElement(cr,{className:r.desc,component:"p",color:"textSecondary"},"Deploy an empty object to a single node, to configure later."),B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(Xm,{id:"namespace",role:"admin",placeholder:"test",onChange:function(e){return n(Zm(Zm({},t),{},{namespace:e}))},selected:t.namespace})),!t.kindForced&&B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(bp,{id:"kind",value:t.kind,onChange:function(e,r){n(Zm(Zm({},t),{},{kind:r}))}})),B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(Bm,{fullWidth:!0,error:!Rn(t.name),id:"name",label:"Name",onChange:function(e){return n(Zm(Zm({},t),{},{name:e.target.value}))},autoComplete:"off",helperText:!Rn(t.name)&&"Must start with an aplha and continue with aplhanum, dot, underscore or hyphen."})))}var ng=function(e){return{selected:{cursor:"pointer"},root:{width:"100%",maxWidth:360,backgroundColor:e.palette.background.paper},paper:{width:"80%",maxHeight:435}}};function rg(e){la(ng);var t=fe((0,B.useState)(""),2),n=t[0],r=t[1];function o(t,n){e.handleClose()}if(n.length>0)var i=RegExp(n,"i"),a=e.options.filter((function(e){return e.name.match(i)}));else a=e.options;return B.createElement(Ls,{disableBackdropClick:!0,maxWidth:"xs",onEntering:function(e){console.log(e)},"aria-labelledby":"confirmation-dialog-title",open:e.open,onClose:o},B.createElement(Zs,{id:"confirmation-dialog-title"},"Template"),B.createElement(qs,{dividers:!0},B.createElement(Bm,{fullWidth:!0,id:"search",label:"Search Regular Expression",type:"search",margin:"normal",variant:"outlined",onChange:function(e){r(e.target.value)},value:n}),B.createElement(Ei,null,a.map((function(t,n){return B.createElement(ig,{key:n,template:t,onChange:e.onChange,handleClose:e.handleClose})})))),B.createElement(Us,null,B.createElement(cl,{onClick:o,color:"primary"},"Cancel")))}function og(e){if(console.log("TemplateSelector, props",e),void 0===e.options)return null;var t=la(ng),n=fe(B.useState(!1),2),r=n[0],o=n[1];return B.createElement(B.Fragment,null,B.createElement("div",{className:t.selected,onClick:function(e){e.stopPropagation(),o(!0)}},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Template"),e.selected?B.createElement(cr,{component:"div"},e.selected.name):B.createElement(cr,{component:"div",color:"textSecondary"},"Click")),B.createElement(rg,{onChange:e.onChange,selected:e.selected,options:e.options,handleClose:function(e){o(!1)},open:r}))}function ig(e){return B.createElement(Li,{button:!0,onClick:function(t){e.onChange(e.template),e.handleClose()}},B.createElement(qi,{primary:e.template.name,secondary:e.template.desc}))}var ag=function(e){return{selected:{cursor:"pointer"},root:{width:"100%",maxWidth:360,backgroundColor:e.palette.background.paper},paper:{width:"80%",maxHeight:435}}};function sg(e){if(la(ag),!e.options)return null;function t(t,n){e.handleClose()}return B.createElement(Ls,{disableBackdropClick:!0,maxWidth:"xs",onEntering:function(e){console.log(e)},"aria-labelledby":"confirmation-dialog-title",open:e.open,onClose:t},B.createElement(Zs,{id:"confirmation-dialog-title"},"Catalog"),B.createElement(qs,{dividers:!0},B.createElement(Ei,null,e.options.map((function(t,n){return B.createElement(ug,{key:n,catalog:t,onChange:e.onChange,handleClose:e.handleClose})})))),B.createElement(Us,null,B.createElement(cl,{onClick:t,color:"primary"},"Cancel")))}function lg(e){if(void 0===e.options)return null;var t=la(ag),n=fe(B.useState(!1),2),r=n[0],o=n[1];return B.createElement(B.Fragment,null,B.createElement("div",{className:t.selected,onClick:function(e){e.stopPropagation(),o(!0)}},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Catalog"),e.selected?B.createElement(cr,{component:"div"},e.selected.name):B.createElement(cr,{component:"div",color:"textSecondary"},"Click")),B.createElement(sg,{onChange:e.onChange,selected:e.selected,options:e.options,handleClose:function(e){o(!1)},open:r}))}function ug(e){return B.createElement(Li,{button:!0,onClick:function(t){e.onChange(e.catalog),e.handleClose()}},B.createElement(qi,{primary:e.catalog.name,secondary:e.catalog.desc}))}function cg(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function dg(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?cg(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):cg(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var fg=function(e){return{desc:{padding:e.spacing(3,0)},textarea:{fontFamily:"monospace",width:"100%"},formcontrol:{margin:e.spacing(2,0)}}};function pg(e){var t=e.data,n=e.set,r=hn().auth,o=la(fg),i=function(){var e=fe((0,B.useState)([]),2),t=e[0],n=e[1],r=hn().auth;return(0,B.useEffect)((function(){t.length>0||dn("/catalogs",{},(function(e){console.log("catalogs",e),n(e)}),r)}),[]),t}();i.length>0&&!t.catalog&&n(dg(dg({},t),{},{catalog:i[0]}));var a=function(e){var t=fe((0,B.useState)([]),2),n=t[0],r=t[1],o=hn().auth;return n.length>0?console.log("useCatalogTemplates, already loaded"):e?dn("/templates",{catalog:e},(function(e){r(e)}),o):console.log("useCatalogTemplates, no name"),n}(t.catalog?t.catalog.name:null);function s(e){try{return JSON.parse(e)}catch(e){}try{return Cn(e)}catch(e){}}function l(e,r){var i=[];if(void 0===r)return[];for(var a in r)if(!a.match(/\./)){var s=a+".comment",l=r[a]?r[a]:"";try{var u=t.envs[e][a]}catch(e){}if(void 0===u){u=l;var c=dg({},t.envs);e in c||(c[e]={}),c[e][a]=u,n(dg(dg({},t),{},{envs:c}))}var d=a,f=e+"-"+a;s in r&&(d=r[s]),i.push(B.createElement(Au,{key:f,className:o.formcontrol,fullWidth:!0},B.createElement(Bm,{id:f,label:d,inputProps:{kw:a,path:e},value:u,onChange:function(e){var r=dg({},t.envs),o=e.target.getAttribute("kw"),i=e.target.getAttribute("path");i in r||(r[i]={}),r[i][o]=e.target.value,n(dg(dg({},t),{},{envs:r}))}})))}if(i.length){var p=B.createElement(cr,{variant:"h6",component:"h2",key:e},e," customization");i.splice(0,0,p)}return i}var u=[];if(t.data)if(Tn())for(var c in t.data)u=u.concat(l(c,t.data[c].env));else t.namespace&&t.name&&(c=t.namespace+"/svc/"+t.name,u=u.concat(l(c,t.data.env)));return B.createElement("div",null,B.createElement(cr,{className:o.desc,component:"p",color:"textSecondary"},"Deploy a service from a template served by a catalog. Templates served by the ",B.createElement("code",null,"collector")," catalog are read-only except for the ",B.createElement("code",null,"env")," section."),B.createElement("div",{className:"dropdown-divider"}),B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(lg,{options:i,onChange:function(e){console.log("selected catalog:",e),n(dg(dg({},t),{},{catalog:e}))},selected:t.catalog})),a.length>0&&B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(og,{options:a,onChange:function(e){var o;console.log("selected template:",e),void 0!==e.id&&(o=e,dn("/template",{catalog:t.catalog.name,template:o.id},(function(e){console.log(e),n(dg(dg({},t),{},{template:o,text:e,data:s(e)}))}),r))},selected:t.template})),t.text&&B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Definition"),B.createElement(jp,{placeholder:"Deployment Data",disabled:!0,className:o.textarea,rowsMax:20,id:"data",value:t.text,onChange:function(e){}}),null==t.data&&B.createElement(jh,{color:"error"},"This deployment data is not recognized as valid ini nor json dataset.")),B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(Xm,{id:"namespace",role:"admin",placeholder:"test",onChange:function(e){return n(dg(dg({},t),{},{namespace:e}))},selected:t.namespace})),!Tn()&&B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(Bm,{fullWidth:!0,error:!Rn(t.name),id:"name",label:"Name",onChange:function(e){var r=t.namespace+"/svc/"+t.name,o=t.namespace+"/svc/"+e.target.value,i=dg({},t.envs);c in t.envs&&(i[o]=t.envs[r],delete i[r]),n(dg(dg({},t),{},{name:e.target.value,envs:i}))},autoComplete:"off",value:t.name,helperText:!Rn(name)&&"Must start with an aplha and continue with aplhanum, dot, underscore or hyphen."})),u)}function hg(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function mg(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?hg(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):hg(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var gg=function(e){return{desc:{padding:e.spacing(3,0)},textarea:{fontFamily:"monospace",width:"100%"},formcontrol:{margin:e.spacing(2,0)}}};function vg(e){var t=e.data,n=e.set,r=la(gg);function o(e){try{return JSON.parse(e)}catch(e){}try{return Cn(e)}catch(e){}}return B.createElement("div",null,B.createElement(cr,{className:r.desc,component:"p",color:"textSecondary"},"Deploy a service from a configuration, pasted or loaded from an ",B.createElement("code",null,"uri"),"."),B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement("div",{style:{display:"inline-flex",alignItems:"flex-end"}},B.createElement(Bm,{type:"",id:"uri",label:"Deployment Data URI",onChange:function(e){n(mg(mg({},t),{},{uri:e.target.value}))},value:t.uri,style:{flexGrow:"1"}}),B.createElement(cl,{onClick:function(e){fetch(t.uri).then((function(e){return e.text()})).then((function(e){n(mg(mg({},t),{},{text:e,data:o(e)}))}))},color:"primary",style:{maxWidth:"6em",alignSelf:"bottom"}},"Load"))),B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Definition"),B.createElement(jp,{className:r.textarea,rowsMax:20,id:"data",onChange:function(e){n(mg(mg({},t),{},{text:e.target.value,data:o(e.target.value)}))},value:t.text}),null==t.data&&B.createElement(jh,{color:"error"},"This deployment data is not recognized as valid ini nor json dataset.")),B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(Xm,{id:"namespace",role:"admin",placeholder:"test",selected:t.namespace,onChange:function(e){return n(mg(mg({},t),{},{namespace:e}))}})),!Tn()&&B.createElement(Au,{className:r.formcontrol,fullWidth:!0},B.createElement(Bm,{id:"name",label:"Name",error:!Rn(t.name),onChange:function(e){n(mg(mg({},t),{},{name:e.target.value}))},value:t.name,autoComplete:"off",helperText:"Must start with an aplha and continue with aplhanum, dot, underscore or hyphen."})))}let yg;function bg(){if(yg)return yg;const e=document.createElement("div"),t=document.createElement("div");return t.style.width="10px",t.style.height="1px",e.appendChild(t),e.dir="rtl",e.style.fontSize="14px",e.style.width="4px",e.style.height="1px",e.style.position="absolute",e.style.top="-1000px",e.style.overflow="scroll",document.body.appendChild(e),yg="reverse",e.scrollLeft>0?yg="default":(e.scrollLeft=1,0===e.scrollLeft&&(yg="negative")),document.body.removeChild(e),yg}function wg(e,t){const n=e.scrollLeft;if("rtl"!==t)return n;switch(bg()){case"negative":return e.scrollWidth-e.clientWidth+n;case"reverse":return e.scrollWidth-e.clientWidth-n;default:return n}}function Eg(e){return(1+Math.sin(Math.PI*e-Math.PI/2))/2}const Sg=["onChange"],xg={width:99,height:99,position:"absolute",top:-9999,overflow:"scroll"},Ag=(0,Na.A)((0,ir.jsx)("path",{d:"M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"}),"KeyboardArrowLeft"),kg=(0,Na.A)((0,ir.jsx)("path",{d:"M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"}),"KeyboardArrowRight");function _g(e){return(0,rr.A)("MuiTabScrollButton",e)}const Cg=(0,nr.A)("MuiTabScrollButton",["root","vertical","horizontal","disabled"]),Og=["className","slots","slotProps","direction","orientation","disabled"],Pg=(0,Zn.Ay)(di,{name:"MuiTabScrollButton",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.orientation&&t[n.orientation]]}})((({ownerState:e})=>(0,Jt.A)({width:40,flexShrink:0,opacity:.8,[`&.${Cg.disabled}`]:{opacity:0}},"vertical"===e.orientation&&{width:"100%",height:40,"& svg":{transform:`rotate(${e.isRtl?-90:90}deg)`}}))),Rg=B.forwardRef((function(e,t){var n,r;const o=(0,er.A)({props:e,name:"MuiTabScrollButton"}),{className:i,slots:a={},slotProps:s={},direction:l}=o,u=(0,qn.A)(o,Og),c="rtl"===Jr().direction,d=(0,Jt.A)({isRtl:c},o),f=(e=>{const{classes:t,orientation:n,disabled:r}=e,o={root:["root",n,r&&"disabled"]};return(0,Qn.A)(o,_g,t)})(d),p=null!=(n=a.StartScrollButtonIcon)?n:Ag,h=null!=(r=a.EndScrollButtonIcon)?r:kg,m=vr({elementType:p,externalSlotProps:s.startScrollButtonIcon,additionalProps:{fontSize:"small"},ownerState:d}),g=vr({elementType:h,externalSlotProps:s.endScrollButtonIcon,additionalProps:{fontSize:"small"},ownerState:d});return(0,ir.jsx)(Pg,(0,Jt.A)({component:"div",className:(0,Kn.A)(f.root,i),ref:t,role:null,ownerState:d,tabIndex:null},u,{children:"left"===l?(0,ir.jsx)(p,(0,Jt.A)({},m)):(0,ir.jsx)(h,(0,Jt.A)({},g))}))}));function Tg(e){return(0,rr.A)("MuiTabs",e)}const Fg=(0,nr.A)("MuiTabs",["root","vertical","flexContainer","flexContainerVertical","centered","scroller","fixed","scrollableX","scrollableY","hideScrollbar","scrollButtons","scrollButtonsHideMobile","indicator"]),jg=["aria-label","aria-labelledby","action","centered","children","className","component","allowScrollButtonsMobile","indicatorColor","onChange","orientation","ScrollButtonComponent","scrollButtons","selectionFollowsFocus","slots","slotProps","TabIndicatorProps","TabScrollButtonProps","textColor","value","variant","visibleScrollbar"],Ng=(e,t)=>e===t?e.firstChild:t&&t.nextElementSibling?t.nextElementSibling:e.firstChild,Ig=(e,t)=>e===t?e.lastChild:t&&t.previousElementSibling?t.previousElementSibling:e.lastChild,Lg=(e,t,n)=>{let r=!1,o=n(e,t);for(;o;){if(o===e.firstChild){if(r)return;r=!0}const t=o.disabled||"true"===o.getAttribute("aria-disabled");if(o.hasAttribute("tabindex")&&!t)return void o.focus();o=n(e,o)}},Mg=(0,Zn.Ay)("div",{name:"MuiTabs",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[{[`& .${Fg.scrollButtons}`]:t.scrollButtons},{[`& .${Fg.scrollButtons}`]:n.scrollButtonsHideMobile&&t.scrollButtonsHideMobile},t.root,n.vertical&&t.vertical]}})((({ownerState:e,theme:t})=>(0,Jt.A)({overflow:"hidden",minHeight:48,WebkitOverflowScrolling:"touch",display:"flex"},e.vertical&&{flexDirection:"column"},e.scrollButtonsHideMobile&&{[`& .${Fg.scrollButtons}`]:{[t.breakpoints.down("sm")]:{display:"none"}}}))),Dg=(0,Zn.Ay)("div",{name:"MuiTabs",slot:"Scroller",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.scroller,n.fixed&&t.fixed,n.hideScrollbar&&t.hideScrollbar,n.scrollableX&&t.scrollableX,n.scrollableY&&t.scrollableY]}})((({ownerState:e})=>(0,Jt.A)({position:"relative",display:"inline-block",flex:"1 1 auto",whiteSpace:"nowrap"},e.fixed&&{overflowX:"hidden",width:"100%"},e.hideScrollbar&&{scrollbarWidth:"none","&::-webkit-scrollbar":{display:"none"}},e.scrollableX&&{overflowX:"auto",overflowY:"hidden"},e.scrollableY&&{overflowY:"auto",overflowX:"hidden"}))),Bg=(0,Zn.Ay)("div",{name:"MuiTabs",slot:"FlexContainer",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.flexContainer,n.vertical&&t.flexContainerVertical,n.centered&&t.centered]}})((({ownerState:e})=>(0,Jt.A)({display:"flex"},e.vertical&&{flexDirection:"column"},e.centered&&{justifyContent:"center"}))),Ug=(0,Zn.Ay)("span",{name:"MuiTabs",slot:"Indicator",overridesResolver:(e,t)=>t.indicator})((({ownerState:e,theme:t})=>(0,Jt.A)({position:"absolute",height:2,bottom:0,width:"100%",transition:t.transitions.create()},"primary"===e.indicatorColor&&{backgroundColor:(t.vars||t).palette.primary.main},"secondary"===e.indicatorColor&&{backgroundColor:(t.vars||t).palette.secondary.main},e.vertical&&{height:"100%",width:2,right:0}))),zg=(0,Zn.Ay)((function(e){const{onChange:t}=e,n=(0,qn.A)(e,Sg),r=B.useRef(),o=B.useRef(null),i=()=>{r.current=o.current.offsetHeight-o.current.clientHeight};return(0,xi.A)((()=>{const e=(0,fo.A)((()=>{const e=r.current;i(),e!==r.current&&t(r.current)})),n=(0,po.A)(o.current);return n.addEventListener("resize",e),()=>{e.clear(),n.removeEventListener("resize",e)}}),[t]),B.useEffect((()=>{i(),t(r.current)}),[t]),(0,ir.jsx)("div",(0,Jt.A)({style:xg,ref:o},n))}))({overflowX:"auto",overflowY:"hidden",scrollbarWidth:"none","&::-webkit-scrollbar":{display:"none"}}),$g={},Hg=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTabs"}),r=Jr(),o="rtl"===r.direction,{"aria-label":i,"aria-labelledby":a,action:s,centered:l=!1,children:u,className:c,component:d="div",allowScrollButtonsMobile:f=!1,indicatorColor:p="primary",onChange:h,orientation:m="horizontal",ScrollButtonComponent:g=Rg,scrollButtons:v="auto",selectionFollowsFocus:y,slots:b={},slotProps:w={},TabIndicatorProps:E={},TabScrollButtonProps:S={},textColor:x="primary",value:A,variant:k="standard",visibleScrollbar:_=!1}=n,C=(0,qn.A)(n,jg),O="scrollable"===k,P="vertical"===m,R=P?"scrollTop":"scrollLeft",T=P?"top":"left",F=P?"bottom":"right",j=P?"clientHeight":"clientWidth",N=P?"height":"width",I=(0,Jt.A)({},n,{component:d,allowScrollButtonsMobile:f,indicatorColor:p,orientation:m,vertical:P,scrollButtons:v,textColor:x,variant:k,visibleScrollbar:_,fixed:!O,hideScrollbar:O&&!_,scrollableX:O&&!P,scrollableY:O&&P,centered:l&&!O,scrollButtonsHideMobile:!f}),L=(e=>{const{vertical:t,fixed:n,hideScrollbar:r,scrollableX:o,scrollableY:i,centered:a,scrollButtonsHideMobile:s,classes:l}=e,u={root:["root",t&&"vertical"],scroller:["scroller",n&&"fixed",r&&"hideScrollbar",o&&"scrollableX",i&&"scrollableY"],flexContainer:["flexContainer",t&&"flexContainerVertical",a&&"centered"],indicator:["indicator"],scrollButtons:["scrollButtons",s&&"scrollButtonsHideMobile"],scrollableX:[o&&"scrollableX"],hideScrollbar:[r&&"hideScrollbar"]};return(0,Qn.A)(u,Tg,l)})(I),M=vr({elementType:b.StartScrollButtonIcon,externalSlotProps:w.startScrollButtonIcon,ownerState:I}),D=vr({elementType:b.EndScrollButtonIcon,externalSlotProps:w.endScrollButtonIcon,ownerState:I}),[U,z]=B.useState(!1),[$,H]=B.useState($g),[W,V]=B.useState(!1),[q,K]=B.useState(!1),[G,J]=B.useState(!1),[Y,X]=B.useState({overflow:"hidden",scrollbarWidth:0}),Q=new Map,Z=B.useRef(null),ee=B.useRef(null),te=()=>{const e=Z.current;let t,n;if(e){const n=e.getBoundingClientRect();t={clientWidth:e.clientWidth,scrollLeft:e.scrollLeft,scrollTop:e.scrollTop,scrollLeftNormalized:wg(e,r.direction),scrollWidth:e.scrollWidth,top:n.top,bottom:n.bottom,left:n.left,right:n.right}}if(e&&!1!==A){const e=ee.current.children;if(e.length>0){const t=e[Q.get(A)];n=t?t.getBoundingClientRect():null}}return{tabsMeta:t,tabMeta:n}},ne=(0,Fo.A)((()=>{const{tabsMeta:e,tabMeta:t}=te();let n,r=0;if(P)n="top",t&&e&&(r=t.top-e.top+e.scrollTop);else if(n=o?"right":"left",t&&e){const i=o?e.scrollLeftNormalized+e.clientWidth-e.scrollWidth:e.scrollLeft;r=(o?-1:1)*(t[n]-e[n]+i)}const i={[n]:r,[N]:t?t[N]:0};if(isNaN($[n])||isNaN($[N]))H(i);else{const e=Math.abs($[n]-i[n]),t=Math.abs($[N]-i[N]);(e>=1||t>=1)&&H(i)}})),re=(e,{animation:t=!0}={})=>{t?function(e,t,n,r={},o=()=>{}){const{ease:i=Eg,duration:a=300}=r;let s=null;const l=t[e];let u=!1;const c=r=>{if(u)return void o(new Error("Animation cancelled"));null===s&&(s=r);const d=Math.min(1,(r-s)/a);t[e]=i(d)*(n-l)+l,d>=1?requestAnimationFrame((()=>{o(null)})):requestAnimationFrame(c)};l===n?o(new Error("Element already at target position")):requestAnimationFrame(c)}(R,Z.current,e,{duration:r.transitions.duration.standard}):Z.current[R]=e},oe=e=>{let t=Z.current[R];P?t+=e:(t+=e*(o?-1:1),t*=o&&"reverse"===bg()?-1:1),re(t)},ie=()=>{const e=Z.current[j];let t=0;const n=Array.from(ee.current.children);for(let r=0;r<n.length;r+=1){const o=n[r];if(t+o[j]>e){0===r&&(t=e);break}t+=o[j]}return t},ae=()=>{oe(-1*ie())},se=()=>{oe(ie())},le=B.useCallback((e=>{X({overflow:null,scrollbarWidth:e})}),[]),ue=(0,Fo.A)((e=>{const{tabsMeta:t,tabMeta:n}=te();if(n&&t)if(n[T]<t[T]){const r=t[R]+(n[T]-t[T]);re(r,{animation:e})}else if(n[F]>t[F]){const r=t[R]+(n[F]-t[F]);re(r,{animation:e})}})),ce=(0,Fo.A)((()=>{O&&!1!==v&&J(!G)}));B.useEffect((()=>{const e=(0,fo.A)((()=>{Z.current&&ne()}));let t;const n=(0,po.A)(Z.current);let r;return n.addEventListener("resize",e),"undefined"!=typeof ResizeObserver&&(t=new ResizeObserver(e),Array.from(ee.current.children).forEach((e=>{t.observe(e)}))),"undefined"!=typeof MutationObserver&&(r=new MutationObserver((n=>{n.forEach((e=>{e.removedNodes.forEach((e=>{var n;null==(n=t)||n.unobserve(e)})),e.addedNodes.forEach((e=>{var n;null==(n=t)||n.observe(e)}))})),e(),ce()})),r.observe(ee.current,{childList:!0})),()=>{var o,i;e.clear(),n.removeEventListener("resize",e),null==(o=r)||o.disconnect(),null==(i=t)||i.disconnect()}}),[ne,ce]),B.useEffect((()=>{const e=Array.from(ee.current.children),t=e.length;if("undefined"!=typeof IntersectionObserver&&t>0&&O&&!1!==v){const n=e[0],r=e[t-1],o={root:Z.current,threshold:.99},i=new IntersectionObserver((e=>{V(!e[0].isIntersecting)}),o);i.observe(n);const a=new IntersectionObserver((e=>{K(!e[0].isIntersecting)}),o);return a.observe(r),()=>{i.disconnect(),a.disconnect()}}}),[O,v,G,null==u?void 0:u.length]),B.useEffect((()=>{z(!0)}),[]),B.useEffect((()=>{ne()})),B.useEffect((()=>{ue($g!==$)}),[ue,$]),B.useImperativeHandle(s,(()=>({updateIndicator:ne,updateScrollButtons:ce})),[ne,ce]);const de=(0,ir.jsx)(Ug,(0,Jt.A)({},E,{className:(0,Kn.A)(L.indicator,E.className),ownerState:I,style:(0,Jt.A)({},$,E.style)}));let fe=0;const pe=B.Children.map(u,(e=>{if(!B.isValidElement(e))return null;const t=void 0===e.props.value?fe:e.props.value;Q.set(t,fe);const n=t===A;return fe+=1,B.cloneElement(e,(0,Jt.A)({fullWidth:"fullWidth"===k,indicator:n&&!U&&de,selected:n,selectionFollowsFocus:y,onChange:h,textColor:x,value:t},1!==fe||!1!==A||e.props.tabIndex?{}:{tabIndex:0}))})),he=(()=>{const e={};e.scrollbarSizeListener=O?(0,ir.jsx)(zg,{onChange:le,className:(0,Kn.A)(L.scrollableX,L.hideScrollbar)}):null;const t=O&&("auto"===v&&(W||q)||!0===v);return e.scrollButtonStart=t?(0,ir.jsx)(g,(0,Jt.A)({slots:{StartScrollButtonIcon:b.StartScrollButtonIcon},slotProps:{startScrollButtonIcon:M},orientation:m,direction:o?"right":"left",onClick:ae,disabled:!W},S,{className:(0,Kn.A)(L.scrollButtons,S.className)})):null,e.scrollButtonEnd=t?(0,ir.jsx)(g,(0,Jt.A)({slots:{EndScrollButtonIcon:b.EndScrollButtonIcon},slotProps:{endScrollButtonIcon:D},orientation:m,direction:o?"left":"right",onClick:se,disabled:!q},S,{className:(0,Kn.A)(L.scrollButtons,S.className)})):null,e})();return(0,ir.jsxs)(Mg,(0,Jt.A)({className:(0,Kn.A)(L.root,c),ownerState:I,ref:t,as:d},C,{children:[he.scrollButtonStart,he.scrollbarSizeListener,(0,ir.jsxs)(Dg,{className:L.scroller,ownerState:I,style:{overflow:Y.overflow,[P?"margin"+(o?"Left":"Right"):"marginBottom"]:_?void 0:-Y.scrollbarWidth},ref:Z,children:[(0,ir.jsx)(Bg,{"aria-label":i,"aria-labelledby":a,"aria-orientation":"vertical"===m?"vertical":null,className:L.flexContainer,ownerState:I,onKeyDown:e=>{const t=ee.current,n=(0,Nh.A)(t).activeElement;if("tab"!==n.getAttribute("role"))return;let r="horizontal"===m?"ArrowLeft":"ArrowUp",i="horizontal"===m?"ArrowRight":"ArrowDown";switch("horizontal"===m&&o&&(r="ArrowRight",i="ArrowLeft"),e.key){case r:e.preventDefault(),Lg(t,n,Ig);break;case i:e.preventDefault(),Lg(t,n,Ng);break;case"Home":e.preventDefault(),Lg(t,null,Ng);break;case"End":e.preventDefault(),Lg(t,null,Ig)}},ref:ee,role:"tablist",children:pe}),U&&de]}),he.scrollButtonEnd]}))}));function Wg(e){return(0,rr.A)("MuiTab",e)}const Vg=(0,nr.A)("MuiTab",["root","labelIcon","textColorInherit","textColorPrimary","textColorSecondary","selected","disabled","fullWidth","wrapped","iconWrapper"]),qg=["className","disabled","disableFocusRipple","fullWidth","icon","iconPosition","indicator","label","onChange","onClick","onFocus","selected","selectionFollowsFocus","textColor","value","wrapped"],Kg=(0,Zn.Ay)(di,{name:"MuiTab",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.label&&n.icon&&t.labelIcon,t[`textColor${(0,tr.A)(n.textColor)}`],n.fullWidth&&t.fullWidth,n.wrapped&&t.wrapped]}})((({theme:e,ownerState:t})=>(0,Jt.A)({},e.typography.button,{maxWidth:360,minWidth:90,position:"relative",minHeight:48,flexShrink:0,padding:"12px 16px",overflow:"hidden",whiteSpace:"normal",textAlign:"center"},t.label&&{flexDirection:"top"===t.iconPosition||"bottom"===t.iconPosition?"column":"row"},{lineHeight:1.25},t.icon&&t.label&&{minHeight:72,paddingTop:9,paddingBottom:9,[`& > .${Vg.iconWrapper}`]:(0,Jt.A)({},"top"===t.iconPosition&&{marginBottom:6},"bottom"===t.iconPosition&&{marginTop:6},"start"===t.iconPosition&&{marginRight:e.spacing(1)},"end"===t.iconPosition&&{marginLeft:e.spacing(1)})},"inherit"===t.textColor&&{color:"inherit",opacity:.6,[`&.${Vg.selected}`]:{opacity:1},[`&.${Vg.disabled}`]:{opacity:(e.vars||e).palette.action.disabledOpacity}},"primary"===t.textColor&&{color:(e.vars||e).palette.text.secondary,[`&.${Vg.selected}`]:{color:(e.vars||e).palette.primary.main},[`&.${Vg.disabled}`]:{color:(e.vars||e).palette.text.disabled}},"secondary"===t.textColor&&{color:(e.vars||e).palette.text.secondary,[`&.${Vg.selected}`]:{color:(e.vars||e).palette.secondary.main},[`&.${Vg.disabled}`]:{color:(e.vars||e).palette.text.disabled}},t.fullWidth&&{flexShrink:1,flexGrow:1,flexBasis:0,maxWidth:"none"},t.wrapped&&{fontSize:e.typography.pxToRem(12)}))),Gg=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTab"}),{className:r,disabled:o=!1,disableFocusRipple:i=!1,fullWidth:a,icon:s,iconPosition:l="top",indicator:u,label:c,onChange:d,onClick:f,onFocus:p,selected:h,selectionFollowsFocus:m,textColor:g="inherit",value:v,wrapped:y=!1}=n,b=(0,qn.A)(n,qg),w=(0,Jt.A)({},n,{disabled:o,disableFocusRipple:i,selected:h,icon:!!s,iconPosition:l,label:!!c,fullWidth:a,textColor:g,wrapped:y}),E=(e=>{const{classes:t,textColor:n,fullWidth:r,wrapped:o,icon:i,label:a,selected:s,disabled:l}=e,u={root:["root",i&&a&&"labelIcon",`textColor${(0,tr.A)(n)}`,r&&"fullWidth",o&&"wrapped",s&&"selected",l&&"disabled"],iconWrapper:["iconWrapper"]};return(0,Qn.A)(u,Wg,t)})(w),S=s&&c&&B.isValidElement(s)?B.cloneElement(s,{className:(0,Kn.A)(E.iconWrapper,s.props.className)}):s;return(0,ir.jsxs)(Kg,(0,Jt.A)({focusRipple:!i,className:(0,Kn.A)(E.root,r),ref:t,role:"tab","aria-selected":h,disabled:o,onClick:e=>{!h&&d&&d(e,v),f&&f(e)},onFocus:e=>{m&&!h&&d&&d(e,v),p&&p(e)},ownerState:w,tabIndex:h?0:-1},b,{children:["top"===l||"start"===l?(0,ir.jsxs)(B.Fragment,{children:[S,c]}):(0,ir.jsxs)(B.Fragment,{children:[c,S]}),u]}))}));var Jg=o(926),Yg=o(3571);const Xg=["className","component"];var Qg=o(9071),Zg=o(1574);const ev=(0,nr.A)("MuiBox",["root"]),tv=(0,Zg.A)(),nv=function(e={}){const{themeId:t,defaultTheme:n,defaultClassName:r="MuiBox-root",generateClassName:o}=e,i=(0,Jg.Ay)("div",{shouldForwardProp:e=>"theme"!==e&&"sx"!==e&&"as"!==e})(Yg.A);return B.forwardRef((function(e,a){const s=(0,qr.A)(n),l=Xn(e),{className:u,component:c="div"}=l,d=(0,qn.A)(l,Xg);return(0,ir.jsx)(i,(0,Jt.A)({as:c,ref:a,className:(0,Kn.A)(u,o?o(r):r),theme:t&&s[t]||s},d))}))}({themeId:Gr.A,defaultTheme:tv,defaultClassName:ev.root,generateClassName:Qg.A.generate});function rv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function ov(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?rv(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):rv(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var iv=function(e){return{root:{padding:e.spacing(3,2),marginTop:e.spacing(3)},tabContent:{paddingTop:e.spacing(2)},fab:{marginTop:e.spacing(2)}}},av=[{name:"Empty",disabled:!1},{name:"Catalog",disabled:!1},{name:"Template",disabled:!1}];function sv(e){var t=e.data,n=e.setData,r=la(iv),o=function(e){return function(r){n(ov(ov({},t),{},ue({},e,r)))}};return B.createElement(B.Fragment,null,B.createElement(Hg,{value:t.active,onChange:function(e,t){o("active")(t)},indicatorColor:"primary",textColor:"primary",variant:"scrollable",scrollButtons:"auto"},av.map((function(e,t){return B.createElement(Gg,{key:t,href:"#",label:e.name,disabled:e.disabled})}))),B.createElement(nv,{className:r.tabContent},0===t.active&&B.createElement(tg,{data:t.empty,set:o("empty")}),1===t.active&&B.createElement(pg,{data:t.catalog,set:o("catalog")}),2===t.active&&B.createElement(vg,{data:t.template,set:o("template")})))}var lv=o(6718);function uv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function cv(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?uv(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):uv(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function dv(e){var t=hn().auth,n=ee().t,r=fe((0,B.useState)(!1),2),o=r[0],i=r[1],a=fe((0,B.useState)({active:0,empty:{name:"",namespace:"",kind:"svc"},clone:{src:null,name:"",namespace:""},catalog:{catalog:null,template:null,text:"",data:null,name:"",namespace:"",envs:{}},template:{uri:"",text:"",data:null,name:"",namespace:""}}),2),s=a[0],l=a[1];function u(e){i(!1)}return e.kind&&e.kind!=s.empty.kind&&l(cv(cv({},s),{},{empty:cv(cv({},s.empty),{},{kind:e.kind,kindForced:!0})})),B.createElement(B.Fragment,null,B.createElement(bd,{title:n("Deploy")},B.createElement(gi,{onClick:function(e){e.stopPropagation(),i(!0)},"aria-label":"Deploy","aria-haspopup":"true"},B.createElement(lv.A,null))),B.createElement(Ls,{open:o,onClose:u,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Deploy"),B.createElement(qs,null,B.createElement(sv,{data:s,setData:l})),B.createElement(Us,null,B.createElement(cl,{onClick:u,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(e){var n;0==s.active?(n=[s.empty.namespace,s.empty.kind,s.empty.name].join("/"),fn({namespace:s.empty.namespace,provision:!1,restore:!0,data:ue({},n,{})},(function(e){return dispatchAlerts({ok:"Object "+n+" created",data:e})}),t)):1==s.active?function(){if(Tn()){var e={provision:!0,namespace:s.catalog.namespace,template:s.catalog.template.id,data:s.catalog.envs};if(Object.keys(e).length>1)var n="Objects "+Object.keys(e)+" deployed.";else n="Object "+Object.keys(e)+" deployed."}else{var r=s.catalog.namespace+"/svc/"+s.catalog.name;e={path:r,provision:!0,namespace:s.catalog.namespace,template:s.catalog.template.id,data:s.catalog.envs},n="Object "+r+" deployed."}console.log("submit",e),fn(e,(function(e){return dispatchAlerts({ok:n,data:e})}),t)}():2==s.active&&function(){if(Tn()){var e={provision:!0,namespace:s.template.namespace,data:s.template.data};if(Object.keys(e).length>1)var n="Objects "+Object.keys(e)+" deployed.";else n="Object "+Object.keys(e)+" deployed."}else{var r=s.template.namespace+"/svc/"+s.template.name;e={provision:!0,namespace:s.template.namespace,data:ue({},r,s.template.data)},n="Object "+r+" deployed."}fn(e,(function(e){return dispatchAlerts({ok:n,data:e})}),t)}(),u()},color:"secondary"},"Submit"))))}function fv(e){return(0,rr.A)("MuiCard",e)}(0,nr.A)("MuiCard",["root"]);const pv=["className","raised"],hv=(0,Zn.Ay)(So,{name:"MuiCard",slot:"Root",overridesResolver:(e,t)=>t.root})((()=>({overflow:"hidden"}))),mv=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCard"}),{className:r,raised:o=!1}=n,i=(0,qn.A)(n,pv),a=(0,Jt.A)({},n,{raised:o}),s=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},fv,t)})(a);return(0,ir.jsx)(hv,(0,Jt.A)({className:(0,Kn.A)(s.root,r),elevation:o?8:void 0,ref:t,ownerState:a},i))}));function gv(e){return(0,rr.A)("MuiCardHeader",e)}const vv=(0,nr.A)("MuiCardHeader",["root","avatar","action","content","title","subheader"]),yv=["action","avatar","className","component","disableTypography","subheader","subheaderTypographyProps","title","titleTypographyProps"],bv=(0,Zn.Ay)("div",{name:"MuiCardHeader",slot:"Root",overridesResolver:(e,t)=>(0,Jt.A)({[`& .${vv.title}`]:t.title,[`& .${vv.subheader}`]:t.subheader},t.root)})({display:"flex",alignItems:"center",padding:16}),wv=(0,Zn.Ay)("div",{name:"MuiCardHeader",slot:"Avatar",overridesResolver:(e,t)=>t.avatar})({display:"flex",flex:"0 0 auto",marginRight:16}),Ev=(0,Zn.Ay)("div",{name:"MuiCardHeader",slot:"Action",overridesResolver:(e,t)=>t.action})({flex:"0 0 auto",alignSelf:"flex-start",marginTop:-4,marginRight:-8,marginBottom:-4}),Sv=(0,Zn.Ay)("div",{name:"MuiCardHeader",slot:"Content",overridesResolver:(e,t)=>t.content})({flex:"1 1 auto"}),xv=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCardHeader"}),{action:r,avatar:o,className:i,component:a="div",disableTypography:s=!1,subheader:l,subheaderTypographyProps:u,title:c,titleTypographyProps:d}=n,f=(0,qn.A)(n,yv),p=(0,Jt.A)({},n,{component:a,disableTypography:s}),h=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"],avatar:["avatar"],action:["action"],content:["content"],title:["title"],subheader:["subheader"]},gv,t)})(p);let m=c;null==m||m.type===cr||s||(m=(0,ir.jsx)(cr,(0,Jt.A)({variant:o?"body2":"h5",className:h.title,component:"span",display:"block"},d,{children:m})));let g=l;return null==g||g.type===cr||s||(g=(0,ir.jsx)(cr,(0,Jt.A)({variant:o?"body2":"body1",className:h.subheader,color:"text.secondary",component:"span",display:"block"},u,{children:g}))),(0,ir.jsxs)(bv,(0,Jt.A)({className:(0,Kn.A)(h.root,i),as:a,ref:t,ownerState:p},f,{children:[o&&(0,ir.jsx)(wv,{className:h.avatar,ownerState:p,children:o}),(0,ir.jsxs)(Sv,{className:h.content,ownerState:p,children:[m,g]}),r&&(0,ir.jsx)(Ev,{className:h.action,ownerState:p,children:r})]}))}));function Av(e){return(0,rr.A)("MuiCardContent",e)}(0,nr.A)("MuiCardContent",["root"]);const kv=["className","component"],_v=(0,Zn.Ay)("div",{name:"MuiCardContent",slot:"Root",overridesResolver:(e,t)=>t.root})((()=>({padding:16,"&:last-child":{paddingBottom:24}}))),Cv=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCardContent"}),{className:r,component:o="div"}=n,i=(0,qn.A)(n,kv),a=(0,Jt.A)({},n,{component:o}),s=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},Av,t)})(a);return(0,ir.jsx)(_v,(0,Jt.A)({as:o,className:(0,Kn.A)(s.root,r),ownerState:a,ref:t},i))})),Ov=B.createContext();function Pv(e){return(0,rr.A)("MuiGrid",e)}const Rv=["auto",!0,1,2,3,4,5,6,7,8,9,10,11,12],Tv=(0,nr.A)("MuiGrid",["root","container","item","zeroMinWidth",...[0,1,2,3,4,5,6,7,8,9,10].map((e=>`spacing-xs-${e}`)),...["column-reverse","column","row-reverse","row"].map((e=>`direction-xs-${e}`)),...["nowrap","wrap-reverse","wrap"].map((e=>`wrap-xs-${e}`)),...Rv.map((e=>`grid-xs-${e}`)),...Rv.map((e=>`grid-sm-${e}`)),...Rv.map((e=>`grid-md-${e}`)),...Rv.map((e=>`grid-lg-${e}`)),...Rv.map((e=>`grid-xl-${e}`))]),Fv=["className","columns","columnSpacing","component","container","direction","item","rowSpacing","spacing","wrap","zeroMinWidth"];function jv(e){const t=parseFloat(e);return`${t}${String(e).replace(String(t),"")||"px"}`}function Nv({breakpoints:e,values:t}){let n="";Object.keys(t).forEach((e=>{""===n&&0!==t[e]&&(n=e)}));const r=Object.keys(e).sort(((t,n)=>e[t]-e[n]));return r.slice(0,r.indexOf(n))}const Iv=(0,Zn.Ay)("div",{name:"MuiGrid",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e,{container:r,direction:o,item:i,spacing:a,wrap:s,zeroMinWidth:l,breakpoints:u}=n;let c=[];r&&(c=function(e,t,n={}){if(!e||e<=0)return[];if("string"==typeof e&&!Number.isNaN(Number(e))||"number"==typeof e)return[n[`spacing-xs-${String(e)}`]];const r=[];return t.forEach((t=>{const o=e[t];Number(o)>0&&r.push(n[`spacing-${t}-${String(o)}`])})),r}(a,u,t));const d=[];return u.forEach((e=>{const r=n[e];r&&d.push(t[`grid-${e}-${String(r)}`])})),[t.root,r&&t.container,i&&t.item,l&&t.zeroMinWidth,...c,"row"!==o&&t[`direction-xs-${String(o)}`],"wrap"!==s&&t[`wrap-xs-${String(s)}`],...d]}})((({ownerState:e})=>(0,Jt.A)({boxSizing:"border-box"},e.container&&{display:"flex",flexWrap:"wrap",width:"100%"},e.item&&{margin:0},e.zeroMinWidth&&{minWidth:0},"wrap"!==e.wrap&&{flexWrap:e.wrap})),(function({theme:e,ownerState:t}){const n=(0,Ul.kW)({values:t.direction,breakpoints:e.breakpoints.values});return(0,Ul.NI)({theme:e},n,(e=>{const t={flexDirection:e};return 0===e.indexOf("column")&&(t[`& > .${Tv.item}`]={maxWidth:"none"}),t}))}),(function({theme:e,ownerState:t}){const{container:n,rowSpacing:r}=t;let o={};if(n&&0!==r){const t=(0,Ul.kW)({values:r,breakpoints:e.breakpoints.values});let n;"object"==typeof t&&(n=Nv({breakpoints:e.breakpoints.values,values:t})),o=(0,Ul.NI)({theme:e},t,((t,r)=>{var o;const i=e.spacing(t);return"0px"!==i?{marginTop:`-${jv(i)}`,[`& > .${Tv.item}`]:{paddingTop:jv(i)}}:null!=(o=n)&&o.includes(r)?{}:{marginTop:0,[`& > .${Tv.item}`]:{paddingTop:0}}}))}return o}),(function({theme:e,ownerState:t}){const{container:n,columnSpacing:r}=t;let o={};if(n&&0!==r){const t=(0,Ul.kW)({values:r,breakpoints:e.breakpoints.values});let n;"object"==typeof t&&(n=Nv({breakpoints:e.breakpoints.values,values:t})),o=(0,Ul.NI)({theme:e},t,((t,r)=>{var o;const i=e.spacing(t);return"0px"!==i?{width:`calc(100% + ${jv(i)})`,marginLeft:`-${jv(i)}`,[`& > .${Tv.item}`]:{paddingLeft:jv(i)}}:null!=(o=n)&&o.includes(r)?{}:{width:"100%",marginLeft:0,[`& > .${Tv.item}`]:{paddingLeft:0}}}))}return o}),(function({theme:e,ownerState:t}){let n;return e.breakpoints.keys.reduce(((r,o)=>{let i={};if(t[o]&&(n=t[o]),!n)return r;if(!0===n)i={flexBasis:0,flexGrow:1,maxWidth:"100%"};else if("auto"===n)i={flexBasis:"auto",flexGrow:0,flexShrink:0,maxWidth:"none",width:"auto"};else{const a=(0,Ul.kW)({values:t.columns,breakpoints:e.breakpoints.values}),s="object"==typeof a?a[o]:a;if(null==s)return r;const l=Math.round(n/s*1e8)/1e6+"%";let u={};if(t.container&&t.item&&0!==t.columnSpacing){const n=e.spacing(t.columnSpacing);if("0px"!==n){const e=`calc(${l} + ${jv(n)})`;u={flexBasis:e,maxWidth:e}}}i=(0,Jt.A)({flexBasis:l,flexGrow:0,maxWidth:l},u)}return 0===e.breakpoints.values[o]?Object.assign(r,i):r[e.breakpoints.up(o)]=i,r}),{})})),Lv=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiGrid"}),{breakpoints:r}=Jr(),o=Xn(n),{className:i,columns:a,columnSpacing:s,component:l="div",container:u=!1,direction:c="row",item:d=!1,rowSpacing:f,spacing:p=0,wrap:h="wrap",zeroMinWidth:m=!1}=o,g=(0,qn.A)(o,Fv),v=f||p,y=s||p,b=B.useContext(Ov),w=u?a||12:b,E={},S=(0,Jt.A)({},g);r.keys.forEach((e=>{null!=g[e]&&(E[e]=g[e],delete S[e])}));const x=(0,Jt.A)({},o,{columns:w,container:u,direction:c,item:d,rowSpacing:v,columnSpacing:y,wrap:h,zeroMinWidth:m,spacing:p},E,{breakpoints:r.keys}),A=(e=>{const{classes:t,container:n,direction:r,item:o,spacing:i,wrap:a,zeroMinWidth:s,breakpoints:l}=e;let u=[];n&&(u=function(e,t){if(!e||e<=0)return[];if("string"==typeof e&&!Number.isNaN(Number(e))||"number"==typeof e)return[`spacing-xs-${String(e)}`];const n=[];return t.forEach((t=>{const r=e[t];if(Number(r)>0){const e=`spacing-${t}-${String(r)}`;n.push(e)}})),n}(i,l));const c=[];l.forEach((t=>{const n=e[t];n&&c.push(`grid-${t}-${String(n)}`)}));const d={root:["root",n&&"container",o&&"item",s&&"zeroMinWidth",...u,"row"!==r&&`direction-xs-${String(r)}`,"wrap"!==a&&`wrap-xs-${String(a)}`,...c]};return(0,Qn.A)(d,Pv,t)})(x);return(0,ir.jsx)(Ov.Provider,{value:w,children:(0,ir.jsx)(Iv,(0,Jt.A)({ownerState:x,className:(0,Kn.A)(A.root,i),as:l,ref:t},S))})}));var Mv=o(6325),Dv=o(2543),Bv=function(e){return{root:{height:"100%"},item:{minWidth:"10em"},warn:{color:e.status.warning}}};const Uv=function(e){var t=ee(),n=t.t,r=(t.i18n,bn().cstat),o=la(Bv),i=wl(),a=El(),s=lt(),l={svc:0,vol:0,usr:0,sec:0,cfg:0,ccfg:0},u={},c={memAvail:0,memTotal:0,memAvailMin:null,memAvailMax:null,swapAvail:0,swapTotal:0,swapAvailMin:null,swapAvailMax:null,loadAvg:0,loadAvgMin:null,loadAvgMax:null};if(void 0===r.monitor)return null;for(var d in r.monitor.nodes){var f=r.monitor.nodes[d];if(!(0,Dv.isEmpty)(f)&&void 0!==f.stats){var p=f.stats.mem_avail*f.stats.mem_total/100,h=f.stats.swap_avail*f.stats.swap_total/100;c.memTotal+=f.stats.mem_total,c.memAvail+=p,c.swapTotal+=f.stats.swap_total,c.swapAvail+=h,c.loadAvg+=f.stats.load_15m,c.memAvailMin=null===c.memAvailMin?p:Math.min(p,c.memAvailMin),c.memAvailMax=null===c.memAvailMax?p:Math.max(p,c.memAvailMax),c.swapAvailMin=null===c.swapAvailMin?h:Math.min(h,c.swapAvailMin),c.swapAvailMax=null===c.swapAvailMax?h:Math.max(h,c.swapAvailMax),c.loadAvgMin=null===c.loadAvgMin?f.stats.load_15m:Math.min(f.stats.load_15m,c.loadAvgMin),c.loadAvgMax=null===c.loadAvgMax?f.stats.load_15m:Math.max(f.stats.load_15m,c.loadAvgMax)}}for(var m in r.monitor.services){var g=kn(m);l[g.kind]++,u[g.namespace]=null}return l.nodes=r.cluster.nodes.length,l.namespaces=Object.keys(u).length,l.pools=i?Object.keys(i).length:"-",l.networks=a?Object.keys(a).length:"-",l.heartbeats=Object.keys(r).filter((function(e){return e.match(/^hb#/)})).length/2,c.memUse=100*(c.memTotal-c.memAvail)/c.memTotal,c.swapUse=100*(c.swapTotal-c.swapAvail)/c.swapTotal,B.createElement(mv,{className:o.root},B.createElement(xv,{title:n("Cluster"),subheader:r.cluster.name,action:B.createElement(B.Fragment,null,B.createElement(dv,null),B.createElement(ap,null))}),B.createElement(Cv,null,B.createElement(Lv,{container:!0,spacing:3},B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/stats")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Memory")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},c.memUse.toFixed(0),"%"),B.createElement(cr,{variant:"caption",color:"textSecondary",component:"h3"},B.createElement("div",null,n("Used"),": ",On(c.memTotal-c.memAvail)),B.createElement("div",null,n("Total"),": ",On(c.memTotal)),B.createElement("div",null,n("MinAvail"),": ",On(c.memAvailMin)),B.createElement("div",null,n("MaxAvail"),": ",On(c.memAvailMax)))),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/stats")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Swap")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},c.swapTotal?c.swapUse.toFixed(0)+"%":"-"),B.createElement(cr,{variant:"caption",color:"textSecondary",component:"h3"},B.createElement("div",null,n("Used"),": ",On(c.swapTotal-c.swapAvail)),B.createElement("div",null,n("Total"),": ",On(c.swapTotal)),B.createElement("div",null,n("MinAvail"),": ",On(c.swapAvailMin)),B.createElement("div",null,n("MaxAvail"),": ",On(c.swapAvailMax)))),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/stats")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Load")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},c.loadAvg.toFixed(1)),B.createElement(cr,{variant:"caption",color:"textSecondary",component:"h3"},B.createElement("div",null,n("15min average")),B.createElement("div",null,n("Min"),": ",c.loadAvgMin.toFixed(1)),B.createElement("div",null,n("Max"),": ",c.loadAvgMax.toFixed(1)))),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/nodes")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Nodes")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.nodes,Wn(r)!==xn.OPTIMAL?B.createElement(Mv.A,{className:o.warn}):null)),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/heartbeats")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Heartbeats")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.heartbeats,Bn(r)!==xn.OPTIMAL?B.createElement(Mv.A,{className:o.warn}):null)),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/pools")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Pools")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.pools)),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/networks")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Networks")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.networks)),B.createElement(Lv,{item:!0,xs:!0,className:o.item},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Namespaces")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.namespaces)),B.createElement(Lv,{item:!0,xs:!0,className:o.item,onClick:function(){return s("/objects")}},B.createElement(cr,{variant:"subtitle1",component:"h3"},n("Objects")),B.createElement(cr,{variant:"h4",color:"primary",component:"h3"},l.svc+l.vol+l.cfg+l.sec+l.usr,Hn(r)!=xn.OPTIMAL?B.createElement(Mv.A,{className:o.warn}):null),B.createElement(cr,{variant:"caption",color:"textSecondary",component:"h3"},B.createElement("div",null,"svc: ",l.svc),B.createElement("div",null,"vol: ",l.vol),B.createElement("div",null,"cfg: ",l.cfg),B.createElement("div",null,"sec: ",l.sec),B.createElement("div",null,"usr: ",l.usr))))))};var zv=o(8777);function $v(e){return B.createElement(zv.A,e,B.createElement("path",{d:"M24 24H0V0h24v24z",fill:"none"}),B.createElement("circle",{cx:"12",cy:"12",r:"8"}))}function Hv(e){var t=e.avail,n=e.overall,r=e.className,o=Jr(),i={running:o.status.up,up:o.status.up,"stdby up":o.status.up,down:o.status.danger,"stdby down":o.status.danger,warning:o.status.warning,warn:o.status.warning,"n/a":o.status.notapplicable,undef:o.status.notapplicable};if(!t)return null;if(n)var a=n;else a=t;var s="g-"+t.replace(/\//,"")+"-"+a.replace(/\//,"");return B.createElement($v,{className:r,component:function(e){return B.createElement("svg",e,B.createElement("defs",null,B.createElement("linearGradient",{id:s,gradientTransform:"rotate(45)"},B.createElement("stop",{offset:"50%",stopColor:i[t]}),B.createElement("stop",{offset:"100%",stopColor:i[a]}))),B.cloneElement(e.children[0][0],{}),B.cloneElement(e.children[0][1],{fill:"url(#"+s+")"}))}})}function Wv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Vv(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Wv(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Wv(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var qv=function(e){return{root:{height:"100%"},namespace:{marginBottom:e.spacing(1)},flex:{display:"flex",flexWrap:"wrap"},item:{paddingRight:e.spacing(1),paddingBottom:e.spacing(1)}}};const Kv=function(e){var t=fe(ve(),2),n=t[0].cstat,r=(t[1],lt()),o=la(qv),i=ee(),a=i.t;if(i.i18n,void 0===n.monitor)return null;var s={},l=Object.keys(n.monitor.services);l.sort();for(var u=0,c=l;u<c.length;u++){var d=c[u],f=kn(d);"svc"==f.kind&&(f.namespace in s||(s[f.namespace]=[]),s[f.namespace].push(Vv(Vv({},n.monitor.services[d]),{},{name:f.name,path:d})))}var p=function(e){return function(t){t.stopPropagation(),r({pathname:"/object",search:"?path="+e,state:{kind:"Services"}})}};return B.createElement(mv,{className:o.root},B.createElement(xv,{title:a("Namespaces"),subheader:n.cluster.name}),B.createElement(Cv,null,Object.keys(s).sort().map((function(e,t){return B.createElement("div",{key:t},B.createElement("div",{className:o.namespace},B.createElement(cr,{variant:"h6"},e),B.createElement("div",{className:o.flex},s[e].map((function(e,t){return B.createElement("div",{key:t,className:o.item},B.createElement(ps,{avatar:B.createElement(Hv,{avail:e.avail,overall:e.overall}),label:e.name,component:"a",href:"#",clickable:!0,onClick:p(e.path)}))})))))}))))};var Gv=function(e){return{root:{marginTop:e.spacing(2)},item:{flexGrow:1}}};const Jv=function(e){var t=la(Gv);return B.createElement(Lv,{container:!0,spacing:2,className:t.root},B.createElement(Lv,{item:!0,md:6,className:t.item},B.createElement(Uv,null)),B.createElement(Lv,{item:!0,md:6,className:t.item},B.createElement(Kv,null)))},Yv=B.createContext();function Xv(e){return(0,rr.A)("MuiTable",e)}(0,nr.A)("MuiTable",["root","stickyHeader"]);const Qv=["className","component","padding","size","stickyHeader"],Zv=(0,Zn.Ay)("table",{name:"MuiTable",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.stickyHeader&&t.stickyHeader]}})((({theme:e,ownerState:t})=>(0,Jt.A)({display:"table",width:"100%",borderCollapse:"collapse",borderSpacing:0,"& caption":(0,Jt.A)({},e.typography.body2,{padding:e.spacing(2),color:(e.vars||e).palette.text.secondary,textAlign:"left",captionSide:"bottom"})},t.stickyHeader&&{borderCollapse:"separate"}))),ey="table",ty=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTable"}),{className:r,component:o=ey,padding:i="normal",size:a="medium",stickyHeader:s=!1}=n,l=(0,qn.A)(n,Qv),u=(0,Jt.A)({},n,{component:o,padding:i,size:a,stickyHeader:s}),c=(e=>{const{classes:t,stickyHeader:n}=e,r={root:["root",n&&"stickyHeader"]};return(0,Qn.A)(r,Xv,t)})(u),d=B.useMemo((()=>({padding:i,size:a,stickyHeader:s})),[i,a,s]);return(0,ir.jsx)(Yv.Provider,{value:d,children:(0,ir.jsx)(Zv,(0,Jt.A)({as:o,role:o===ey?null:"table",ref:t,className:(0,Kn.A)(c.root,r),ownerState:u},l))})})),ny=B.createContext();function ry(e){return(0,rr.A)("MuiTableBody",e)}(0,nr.A)("MuiTableBody",["root"]);const oy=["className","component"],iy=(0,Zn.Ay)("tbody",{name:"MuiTableBody",slot:"Root",overridesResolver:(e,t)=>t.root})({display:"table-row-group"}),ay={variant:"body"},sy="tbody",ly=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTableBody"}),{className:r,component:o=sy}=n,i=(0,qn.A)(n,oy),a=(0,Jt.A)({},n,{component:o}),s=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},ry,t)})(a);return(0,ir.jsx)(ny.Provider,{value:ay,children:(0,ir.jsx)(iy,(0,Jt.A)({className:(0,Kn.A)(s.root,r),as:o,ref:t,role:o===sy?null:"rowgroup",ownerState:a},i))})}));function uy(e){return(0,rr.A)("MuiTableCell",e)}const cy=(0,nr.A)("MuiTableCell",["root","head","body","footer","sizeSmall","sizeMedium","paddingCheckbox","paddingNone","alignLeft","alignCenter","alignRight","alignJustify","stickyHeader"]),dy=["align","className","component","padding","scope","size","sortDirection","variant"],fy=(0,Zn.Ay)("td",{name:"MuiTableCell",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],t[`size${(0,tr.A)(n.size)}`],"normal"!==n.padding&&t[`padding${(0,tr.A)(n.padding)}`],"inherit"!==n.align&&t[`align${(0,tr.A)(n.align)}`],n.stickyHeader&&t.stickyHeader]}})((({theme:e,ownerState:t})=>(0,Jt.A)({},e.typography.body2,{display:"table-cell",verticalAlign:"inherit",borderBottom:e.vars?`1px solid ${e.vars.palette.TableCell.border}`:`1px solid\n    ${"light"===e.palette.mode?(0,vo.a)((0,vo.X4)(e.palette.divider,1),.88):(0,vo.e$)((0,vo.X4)(e.palette.divider,1),.68)}`,textAlign:"left",padding:16},"head"===t.variant&&{color:(e.vars||e).palette.text.primary,lineHeight:e.typography.pxToRem(24),fontWeight:e.typography.fontWeightMedium},"body"===t.variant&&{color:(e.vars||e).palette.text.primary},"footer"===t.variant&&{color:(e.vars||e).palette.text.secondary,lineHeight:e.typography.pxToRem(21),fontSize:e.typography.pxToRem(12)},"small"===t.size&&{padding:"6px 16px",[`&.${cy.paddingCheckbox}`]:{width:24,padding:"0 12px 0 16px","& > *":{padding:0}}},"checkbox"===t.padding&&{width:48,padding:"0 0 0 4px"},"none"===t.padding&&{padding:0},"left"===t.align&&{textAlign:"left"},"center"===t.align&&{textAlign:"center"},"right"===t.align&&{textAlign:"right",flexDirection:"row-reverse"},"justify"===t.align&&{textAlign:"justify"},t.stickyHeader&&{position:"sticky",top:0,zIndex:2,backgroundColor:(e.vars||e).palette.background.default}))),py=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTableCell"}),{align:r="inherit",className:o,component:i,padding:a,scope:s,size:l,sortDirection:u,variant:c}=n,d=(0,qn.A)(n,dy),f=B.useContext(Yv),p=B.useContext(ny),h=p&&"head"===p.variant;let m;m=i||(h?"th":"td");let g=s;"td"===m?g=void 0:!g&&h&&(g="col");const v=c||p&&p.variant,y=(0,Jt.A)({},n,{align:r,component:m,padding:a||(f&&f.padding?f.padding:"normal"),size:l||(f&&f.size?f.size:"medium"),sortDirection:u,stickyHeader:"head"===v&&f&&f.stickyHeader,variant:v}),b=(e=>{const{classes:t,variant:n,align:r,padding:o,size:i,stickyHeader:a}=e,s={root:["root",n,a&&"stickyHeader","inherit"!==r&&`align${(0,tr.A)(r)}`,"normal"!==o&&`padding${(0,tr.A)(o)}`,`size${(0,tr.A)(i)}`]};return(0,Qn.A)(s,uy,t)})(y);let w=null;return u&&(w="asc"===u?"ascending":"descending"),(0,ir.jsx)(fy,(0,Jt.A)({as:m,ref:t,className:(0,Kn.A)(b.root,o),"aria-sort":w,scope:g,ownerState:y},d))}));function hy(e){return(0,rr.A)("MuiTableHead",e)}(0,nr.A)("MuiTableHead",["root"]);const my=["className","component"],gy=(0,Zn.Ay)("thead",{name:"MuiTableHead",slot:"Root",overridesResolver:(e,t)=>t.root})({display:"table-header-group"}),vy={variant:"head"},yy="thead",by=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTableHead"}),{className:r,component:o=yy}=n,i=(0,qn.A)(n,my),a=(0,Jt.A)({},n,{component:o}),s=(e=>{const{classes:t}=e;return(0,Qn.A)({root:["root"]},hy,t)})(a);return(0,ir.jsx)(ny.Provider,{value:vy,children:(0,ir.jsx)(gy,(0,Jt.A)({as:o,className:(0,Kn.A)(s.root,r),ref:t,role:o===yy?null:"rowgroup",ownerState:a},i))})}));function wy(e){return(0,rr.A)("MuiTableRow",e)}const Ey=(0,nr.A)("MuiTableRow",["root","selected","hover","head","footer"]),Sy=["className","component","hover","selected"],xy=(0,Zn.Ay)("tr",{name:"MuiTableRow",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.head&&t.head,n.footer&&t.footer]}})((({theme:e})=>({color:"inherit",display:"table-row",verticalAlign:"middle",outline:0,[`&.${Ey.hover}:hover`]:{backgroundColor:(e.vars||e).palette.action.hover},[`&.${Ey.selected}`]:{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / ${e.vars.palette.action.selectedOpacity})`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity),"&:hover":{backgroundColor:e.vars?`rgba(${e.vars.palette.primary.mainChannel} / calc(${e.vars.palette.action.selectedOpacity} + ${e.vars.palette.action.hoverOpacity}))`:(0,vo.X4)(e.palette.primary.main,e.palette.action.selectedOpacity+e.palette.action.hoverOpacity)}}}))),Ay="tr",ky=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiTableRow"}),{className:r,component:o=Ay,hover:i=!1,selected:a=!1}=n,s=(0,qn.A)(n,Sy),l=B.useContext(ny),u=(0,Jt.A)({},n,{component:o,hover:i,selected:a,head:l&&"head"===l.variant,footer:l&&"footer"===l.variant}),c=(e=>{const{classes:t,selected:n,hover:r,head:o,footer:i}=e,a={root:["root",n&&"selected",r&&"hover",o&&"head",i&&"footer"]};return(0,Qn.A)(a,wy,t)})(u);return(0,ir.jsx)(xy,(0,Jt.A)({as:o,ref:t,className:(0,Kn.A)(c.root,r),role:o===Ay?null:"row",ownerState:u},s))}));var _y=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},chip:{margin:e.spacing(1)}}};function Cy(e){var t=ee(),n=t.t,r=(t.i18n,la(_y)),o=bn().cstat;if(!("monitor"in o))return null;var i=["listener","dns","monitor","scheduler"];for(var a in o)a.match(/^hb#/)&&i.push(a);return B.createElement(mv,{id:"threads",className:r.root},B.createElement(xv,{title:n("Threads"),subheader:o.cluster.name}),B.createElement(Cv,null,B.createElement("div",{className:r.wrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,n("Name")),B.createElement(py,null,n("State")))),B.createElement(ly,null,i.map((function(e){return B.createElement(Oy,{key:e,name:e,data:o[e]})})))))))}function Oy(e){var t=la(ma);return e.data?B.createElement(ky,null,B.createElement(py,null,e.name),B.createElement(py,null,B.createElement(cr,{className:t[e.data.state]},e.data.state))):null}var Py=o(5897),Ry=o(483),Ty=o(2702);function Fy(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return jy(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?jy(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function jy(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function Ny(e){var t=hn().auth,n=fe(ve(),2),r=n[0].cstat,o=(n[1],rp().dispatchAlerts);if(void 0===r.monitor)return null;var i=e.selected;return B.createElement(kd,{selected:i,title:e.title,submit:function(e){var n,r=Fy(i);try{for(r.s();!(n=r.n()).done;)on(n.value,e.value,{},(function(e){return o({data:e})}),t)}catch(e){r.e(e)}finally{r.f()}},fab:e.fab},B.createElement(_d,{name:"safe",color:"secondary",confirms:0},B.createElement(Cd,{value:"freeze",text:"Freeze",disabled:function(){var e,t=Fy(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n];if(o&&!o.frozen)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"root"},icon:B.createElement(op.A,null)}),B.createElement(Cd,{value:"thaw",text:"Thaw",disabled:function(){var e,t=Fy(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n];if(o&&o.frozen)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"root"},icon:B.createElement(ip.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"push",color:"secondary",confirms:0},B.createElement(Cd,{value:"pushasset",text:"Asset",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"checks",text:"Checks",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"pushdisks",text:"Disks",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"pushpatch",text:"Patches",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"pushpkg",text:"Packages",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"pushstats",text:"Stats",requires:{role:"root"},icon:B.createElement(Py.A,null)}),B.createElement(Cd,{value:"sysreport",text:"Sysreport",requires:{role:"root"},icon:B.createElement(Py.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"impacting",color:"warning",confirms:3},B.createElement(Cd,{value:"daemon_restart",text:"Daemon Restart",requires:{role:"root"},icon:B.createElement(Ty.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"reboot",text:"Reboot",requires:{role:"root"},icon:B.createElement(Ty.A,null)}),B.createElement(Cd,{value:"shutdown",text:"Shutdown",requires:{role:"root"},icon:B.createElement(Ry.A,null)})))}var Iy=function(e){return{root:{paddingLeft:e.spacing(1),paddingRight:e.spacing(0)},highlight:"light"===e.palette.type?{color:e.palette.secondary.main}:{color:e.palette.text.primary},spacer:{flex:"1 1 100%"},actions:{color:e.palette.text.secondary,display:"flex",flexWrap:"nowrap"},title:{flex:"0 0 auto"}}},Ly=function(e){var t=ee(),n=t.t,r=(t.i18n,la(Iy)),o=e.selected;return B.createElement(Qa,{className:(0,Kn.A)(r.root,ue({},r.highlight,o.length>0))},B.createElement("div",{className:r.title},o.length>0&&B.createElement(cr,{color:"inherit",variant:"subtitle1"},n("{{count}} selected",{count:o.length}))),B.createElement("div",{className:r.spacer}),B.createElement("div",{className:r.actions},e.children))},My=o(5523);function Dy(e){return e.frozen?"thawed"==e.frozen?null:B.createElement(cr,{component:"span",className:e.className},B.createElement(My.A,{color:"primary",title:"Frozen"})):null}function By(e){return e.state?"idle"==e.state?null:B.createElement(cr,{className:e.className,color:"primary",component:"span"},e.state):null}function Uy(e){return e.target?B.createElement(cr,{className:e.className,color:"primary",component:"span"},">"+e.target):null}var zy=o(8157);function $y(e){return 1!=e.speaker?null:B.createElement(cr,{component:"span",className:e.className},B.createElement(zy.A,{title:"Speaker",color:"action"}))}var Hy=function(e){return{root:{color:e.status.warning}}};const Wy=B.forwardRef((function(e,t){var n=la(Hy);return B.createElement(Mv.A,(0,Jt.A)({},e,{ref:t,className:(0,Kn.A)(n.root,e.className)}))})),Vy=function(e){var t=ee(),n=t.t;return t.i18n,Ln(e.data)!=xn.WARNING?null:B.createElement(cr,{component:"span",className:e.className},B.createElement(bd,{title:n("Swap overload")},B.createElement(Wy,null)))},qy=function(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2));return r[0].cstat,r[1],In(e.data)!=xn.WARNING?null:B.createElement(cr,{component:"span",className:e.className},B.createElement(bd,{title:n("Memory overload")},B.createElement(Wy,null)))};var Ky=function(e){return{child:{marginRight:e.spacing(1)}}};function Gy(e){var t,n,r,o=la(Ky),i="unknown",a=[];return(0,Dv.isEmpty)(e.data)?a=[B.createElement(By,{className:o.child,state:"unknown"})]:(void 0!==e.data.monitor&&(i=e.data.monitor.status,t=e.data.monitor.global_expect),n=e.data.frozen,r=e.data.speaker,a=[B.createElement(Dy,{className:o.child,frozen:n}),B.createElement($y,{className:o.child,speaker:r}),B.createElement(By,{className:o.child,state:i}),B.createElement(Uy,{className:o.child,target:t}),B.createElement(qy,{className:o.child,data:e.data}),B.createElement(Vy,{className:o.child,data:e.data})]),B.createElement(Lv,{container:!0,spacing:0},a.map((function(e,t){return e?B.createElement(Lv,{item:!0,key:t},e):null})))}o(412);var Jy=o(7340);function Yy(e,t,n,r,o){const[i,a]=B.useState((()=>o&&n?n(e).matches:r?r(e).matches:t));return(0,xi.A)((()=>{let t=!0;if(!n)return;const r=n(e),o=()=>{t&&a(r.matches)};return o(),r.addListener(o),()=>{t=!1,r.removeListener(o)}}),[e,n]),i}const Xy=U.useSyncExternalStore;function Qy(e,t,n,r,o){const i=B.useCallback((()=>t),[t]),a=B.useMemo((()=>{if(o&&n)return()=>n(e).matches;if(null!==r){const{matches:t}=r(e);return()=>t}return i}),[i,e,r,o,n]),[s,l]=B.useMemo((()=>{if(null===n)return[i,()=>()=>{}];const t=n(e);return[()=>t.matches,e=>(t.addListener(e),()=>{t.removeListener(e)})]}),[i,n,e]);return Xy(l,s,a)}const Zy=["initialWidth","width"],eb=["xs","sm","md","lg","xl"],tb=(e,t,n=!0)=>n?eb.indexOf(e)<=eb.indexOf(t):eb.indexOf(e)<eb.indexOf(t),nb=(e,t,n=!1)=>n?eb.indexOf(t)<=eb.indexOf(e):eb.indexOf(t)<eb.indexOf(e),rb=((e={})=>t=>{const{withTheme:n=!1,noSSR:r=!1,initialWidth:o}=e;return function(e){const i=Jr(),a=e.theme||i,s=(0,Jy.A)({theme:a,name:"MuiWithWidth",props:e}),{initialWidth:l,width:u}=s,c=(0,qn.A)(s,Zy),[d,f]=B.useState(!1);(0,xi.A)((()=>{f(!0)}),[]);const p=a.breakpoints.keys.slice().reverse().reduce(((e,t)=>{const n=function(e,t={}){const n=(0,od.A)(),r="undefined"!=typeof window&&void 0!==window.matchMedia,{defaultMatches:o=!1,matchMedia:i=(r?window.matchMedia:null),ssrMatchMedia:a=null,noSsr:s=!1}=(0,Jy.A)({name:"MuiUseMediaQuery",props:t,theme:n});let l="function"==typeof e?e(n):e;return l=l.replace(/^@media( ?)/m,""),(void 0!==Xy?Qy:Yy)(l,o,i,a,s)}(a.breakpoints.up(t));return!e&&n?t:e}),null),h=(0,Jt.A)({width:u||(d||r?p:void 0)||l||o},n?{theme:a}:{},c);return void 0===h.width?null:(0,ir.jsx)(t,(0,Jt.A)({},h))}})()((function(e){const{children:t,only:n,width:r}=e,o=Jr();let i=!0;if(n)if(Array.isArray(n)){for(let e=0;e<n.length;e+=1)if(r===n[e]){i=!1;break}}else n&&r===n&&(i=!1);if(i)for(let t=0;t<o.breakpoints.keys.length;t+=1){const n=o.breakpoints.keys[t],a=e[`${n}Up`],s=e[`${n}Down`];if(a&&tb(n,r)||s&&nb(n,r)){i=!1;break}}return i?(0,ir.jsx)(B.Fragment,{children:t}):null}));function ob(e){return(0,rr.A)("PrivateHiddenCss",e)}(0,nr.A)("PrivateHiddenCss",["root","xlDown","xlUp","onlyXl","lgDown","lgUp","onlyLg","mdDown","mdUp","onlyMd","smDown","smUp","onlySm","xsDown","xsUp","onlyXs"]);const ib=["children","className","only"],ab=(0,Zn.Ay)("div",{name:"PrivateHiddenCss",slot:"Root"})((({theme:e,ownerState:t})=>{const n={display:"none"};return(0,Jt.A)({},t.breakpoints.map((({breakpoint:t,dir:r})=>"only"===r?{[e.breakpoints.only(t)]:n}:"up"===r?{[e.breakpoints.up(t)]:n}:{[e.breakpoints.down(t)]:n})).reduce(((e,t)=>(Object.keys(t).forEach((n=>{e[n]=t[n]})),e)),{}))})),sb=function(e){const{children:t,className:n,only:r}=e,o=(0,qn.A)(e,ib),i=Jr(),a=[];for(let e=0;e<i.breakpoints.keys.length;e+=1){const t=i.breakpoints.keys[e],n=o[`${t}Up`],r=o[`${t}Down`];n&&a.push({breakpoint:t,dir:"up"}),r&&a.push({breakpoint:t,dir:"down"})}r&&(Array.isArray(r)?r:[r]).forEach((e=>{a.push({breakpoint:e,dir:"only"})}));const s=(0,Jt.A)({},e,{breakpoints:a}),l=(e=>{const{classes:t,breakpoints:n}=e,r={root:["root",...n.map((({breakpoint:e,dir:t})=>"only"===t?`${t}${(0,tr.A)(e)}`:`${e}${(0,tr.A)(t)}`))]};return(0,Qn.A)(r,ob,t)})(s);return(0,ir.jsx)(ab,{className:(0,Kn.A)(l.root,n),ownerState:s,children:t})},lb=["implementation","lgDown","lgUp","mdDown","mdUp","smDown","smUp","xlDown","xlUp","xsDown","xsUp"],ub=function(e){const{implementation:t="js",lgDown:n=!1,lgUp:r=!1,mdDown:o=!1,mdUp:i=!1,smDown:a=!1,smUp:s=!1,xlDown:l=!1,xlUp:u=!1,xsDown:c=!1,xsUp:d=!1}=e,f=(0,qn.A)(e,lb);return"js"===t?(0,ir.jsx)(rb,(0,Jt.A)({lgDown:n,lgUp:r,mdDown:o,mdUp:i,smDown:a,smUp:s,xlDown:l,xlUp:u,xsDown:c,xsUp:d},f)):(0,ir.jsx)(sb,(0,Jt.A)({lgDown:n,lgUp:r,mdDown:o,mdUp:i,smDown:a,smUp:s,xlDown:l,xlUp:u,xsDown:c,xsUp:d},f))};var cb=o(2183),db=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto"},table:{marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function fb(e,t,n){var r=e.monitor.nodes[t];return(0,Dv.isEmpty)(r)||void 0===r.stats?null:r.stats[n]}function pb(e){var t;if(bn().cstat,e.issue==xn.WARNING)var n="error";else n="inherit";return e.refer&&(t=B.createElement(B.Fragment,null,"Â ",B.createElement(cr,{component:"span",variant:"caption",color:"textSecondary"},e.refer))),B.createElement(B.Fragment,null,B.createElement(cr,{component:"span",color:n},e.value,e.unit),t)}function hb(e){var t=bn().cstat;return B.createElement(pb,{label:"Score",value:fb(t,e.node,"score"),unit:""})}function mb(e){var t=bn().cstat;return B.createElement(pb,{label:"Load15m",value:fb(t,e.node,"load_15m"),unit:""})}function gb(e){var t=bn().cstat,n=In(t,e.node);return B.createElement(pb,{label:"Avail Mem",value:fb(t,e.node,"mem_avail"),unit:"%",issue:n,refer:On(fb(t,e.node,"mem_total"))})}function vb(e){var t=bn().cstat,n=Ln(t,e.node);return B.createElement(pb,{label:"Avail Swap",value:fb(t,e.node,"swap_avail"),unit:"%",issue:n,refer:On(fb(t,e.node,"swap_total"))})}function yb(e){var t=e.index,n=e.node,r=e.selected,o=e.setSelected,i=(e.withScalerSlaves,bn().cstat),a=lt();if(void 0===i.monitor)return null;var s=i.monitor.nodes[e.node];if(void 0===s)return null;var l=-1!==r.indexOf(n),u="nodes-checkbox-".concat(t);return B.createElement(ky,{onClick:function(t){a("/node?name="+e.node)},hover:!0,role:"checkbox","aria-checked":l,tabIndex:-1,key:n,selected:l},B.createElement(py,{padding:"checkbox",onClick:function(e){e.stopPropagation();var t=r.indexOf(n),i=[];-1===t?i=i.concat(r,n):0===t?i=i.concat(r.slice(1)):t===r.length-1?i=i.concat(r.slice(0,-1)):t>0&&(i=i.concat(r.slice(0,t),r.slice(t+1))),o(i)}},B.createElement(yu,{checked:l,inputProps:{"aria-labelledby":u}})),B.createElement(py,null,e.node),B.createElement(py,null,B.createElement(Gy,{data:s})),B.createElement(ub,{smDown:!0},B.createElement(py,null,B.createElement(hb,{node:e.node})),B.createElement(py,null,B.createElement(mb,{node:e.node})),B.createElement(py,null,B.createElement(gb,{node:e.node})),B.createElement(py,null,B.createElement(vb,{node:e.node}))),B.createElement(py,null,B.createElement(bb,{data:s,compatIssue:e.compatIssue,versionIssue:e.versionIssue})))}function bb(e){var t=la(ma);return B.createElement(B.Fragment,null,B.createElement(cr,{component:"span",className:t[e.compatIssue.name]},e.data.compat),"Â ",B.createElement(cr,{component:"span",className:t[e.versionIssue.name],variant:"caption"},e.data.agent))}function wb(e){var t=bn().cstat,n=ee(),r=n.t,o=(n.i18n,la(db)),i=fe(B.useState([]),2),a=i[0],s=i[1];if(void 0===t.monitor)return null;var l=Nn(t),u=jn(t),c=Object.keys(t.monitor.nodes).length;return B.createElement(mv,{className:o.root},B.createElement(xv,{title:r("Nodes"),subheader:t.cluster.name}),B.createElement(Cv,null,B.createElement(Ly,{selected:a,className:o.table},a.length>0?B.createElement(Ny,{selected:a,title:""}):B.createElement(bd,{title:"Filter list"},B.createElement(gi,{"aria-label":"Filter list"},B.createElement(cb.A,null)))),B.createElement("div",{className:(0,Kn.A)([o.wrapper,o.table])},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,null,B.createElement(py,{padding:"checkbox"},B.createElement(yu,{indeterminate:a.length>0&&a.length<c,checked:a.length===c,onChange:function(e){if(e.target.checked){var n=Object.keys(t.monitor.nodes);s(n)}else s([])},inputProps:{"aria-label":"Select all"}})),B.createElement(py,null,r("Name")),B.createElement(py,null,r("State")),B.createElement(ub,{smDown:!0},B.createElement(py,null,"Score"),B.createElement(py,null,r("Load15m")),B.createElement(py,null,r("Mem Avail")),B.createElement(py,null,r("Swap Avail"))),B.createElement(py,null,"Version"))),B.createElement(ly,null,t.cluster.nodes.map((function(e,t){return B.createElement(yb,{key:e,index:t,node:e,selected:a,setSelected:s,compatIssue:u,versionIssue:l})})))))))}function Eb(e){return function(e){if(Array.isArray(e))return ce(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||de(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Sb(e){var t=fe((0,B.useState)(null),2),n=t[0],r=t[1],o=fe((0,B.useState)(null),2),i=o[0],a=o[1],s=hn().auth,l=B.useRef(n),u=B.useRef(i),c=e+"/backlogs",d=e+"/logs",f=function(e){console.log(d,"setEs",e),u.current=e,a(e)},p=function(e){l.current=e,r(e)};return(0,B.useEffect)((function(){return null===l.current&&(t=c,n={backlog:"10k"},r=function(e){p(e)},o={headers:rn({Accept:"application/json","Content-Type":"application/json","o-node":"*"},s),method:"GET"},t=tn(t,n),fetch(t,o).then((function(e){return e.json()})).then((function(e){var t=[];for(var n in e.nodes)t=t.concat(e.nodes[n]);t.sort((function(e,t){return e.t-t.t})),r&&r(t)})).catch(console.log)),function(){if(!u.current){console.log("init",d,"logs event source");var t={headers:{}};e&&(t.headers["o-node"]="*"),t.headers=rn(t.headers,s);var n=new(gn())(d,t);n.onmessage=function(e){var t=JSON.parse(e.data);null===l.current?p(t):p([].concat(Eb(l.current),Eb(t)).slice(-100))},f(n)}}(),function(){console.log("stop",d,"logs event source",u.current),u.current&&(u.current.close(),f(null))};var t,n,r,o}),[]),n}function xb(e){return(0,rr.A)("MuiCircularProgress",e)}(0,nr.A)("MuiCircularProgress",["root","determinate","indeterminate","colorPrimary","colorSecondary","svg","circle","circleDeterminate","circleIndeterminate","circleDisableShrink"]);const Ab=["className","color","disableShrink","size","style","thickness","value","variant"];let kb,_b,Cb,Ob,Pb=e=>e;const Rb=Ko(kb||(kb=Pb`
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
`)),Tb=Ko(_b||(_b=Pb`
  0% {
    stroke-dasharray: 1px, 200px;
    stroke-dashoffset: 0;
  }

  50% {
    stroke-dasharray: 100px, 200px;
    stroke-dashoffset: -15px;
  }

  100% {
    stroke-dasharray: 100px, 200px;
    stroke-dashoffset: -125px;
  }
`)),Fb=(0,Zn.Ay)("span",{name:"MuiCircularProgress",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],t[`color${(0,tr.A)(n.color)}`]]}})((({ownerState:e,theme:t})=>(0,Jt.A)({display:"inline-block"},"determinate"===e.variant&&{transition:t.transitions.create("transform")},"inherit"!==e.color&&{color:(t.vars||t).palette[e.color].main})),(({ownerState:e})=>"indeterminate"===e.variant&&qo(Cb||(Cb=Pb`
      animation: ${0} 1.4s linear infinite;
    `),Rb))),jb=(0,Zn.Ay)("svg",{name:"MuiCircularProgress",slot:"Svg",overridesResolver:(e,t)=>t.svg})({display:"block"}),Nb=(0,Zn.Ay)("circle",{name:"MuiCircularProgress",slot:"Circle",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.circle,t[`circle${(0,tr.A)(n.variant)}`],n.disableShrink&&t.circleDisableShrink]}})((({ownerState:e,theme:t})=>(0,Jt.A)({stroke:"currentColor"},"determinate"===e.variant&&{transition:t.transitions.create("stroke-dashoffset")},"indeterminate"===e.variant&&{strokeDasharray:"80px, 200px",strokeDashoffset:0})),(({ownerState:e})=>"indeterminate"===e.variant&&!e.disableShrink&&qo(Ob||(Ob=Pb`
      animation: ${0} 1.4s ease-in-out infinite;
    `),Tb))),Ib=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCircularProgress"}),{className:r,color:o="primary",disableShrink:i=!1,size:a=40,style:s,thickness:l=3.6,value:u=0,variant:c="indeterminate"}=n,d=(0,qn.A)(n,Ab),f=(0,Jt.A)({},n,{color:o,disableShrink:i,size:a,thickness:l,value:u,variant:c}),p=(e=>{const{classes:t,variant:n,color:r,disableShrink:o}=e,i={root:["root",n,`color${(0,tr.A)(r)}`],svg:["svg"],circle:["circle",`circle${(0,tr.A)(n)}`,o&&"circleDisableShrink"]};return(0,Qn.A)(i,xb,t)})(f),h={},m={},g={};if("determinate"===c){const e=2*Math.PI*((44-l)/2);h.strokeDasharray=e.toFixed(3),g["aria-valuenow"]=Math.round(u),h.strokeDashoffset=`${((100-u)/100*e).toFixed(3)}px`,m.transform="rotate(-90deg)"}return(0,ir.jsx)(Fb,(0,Jt.A)({className:(0,Kn.A)(p.root,r),style:(0,Jt.A)({width:a,height:a},m,s),ownerState:f,ref:t,role:"progressbar"},g,d,{children:(0,ir.jsx)(jb,{className:p.svg,ownerState:f,viewBox:"22 22 44 44",children:(0,ir.jsx)(Nb,{className:p.circle,style:h,ownerState:f,cx:44,cy:44,r:(44-l)/2,fill:"none",strokeWidth:l})})}))}));function Lb(e){return(0,rr.A)("MuiSkeleton",e)}(0,nr.A)("MuiSkeleton",["root","text","rectangular","rounded","circular","pulse","wave","withChildren","fitContent","heightAuto"]);const Mb=["animation","className","component","height","style","variant","width"];let Db,Bb,Ub,zb,$b=e=>e;const Hb=Ko(Db||(Db=$b`
  0% {
    opacity: 1;
  }

  50% {
    opacity: 0.4;
  }

  100% {
    opacity: 1;
  }
`)),Wb=Ko(Bb||(Bb=$b`
  0% {
    transform: translateX(-100%);
  }

  50% {
    /* +0.5s of delay between each loop */
    transform: translateX(100%);
  }

  100% {
    transform: translateX(100%);
  }
`)),Vb=(0,Zn.Ay)("span",{name:"MuiSkeleton",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.variant],!1!==n.animation&&t[n.animation],n.hasChildren&&t.withChildren,n.hasChildren&&!n.width&&t.fitContent,n.hasChildren&&!n.height&&t.heightAuto]}})((({theme:e,ownerState:t})=>{const n=(i=e.shape.borderRadius,String(i).match(/[\d.\-+]*\s*(.*)/)[1]||""||"px"),r=(o=e.shape.borderRadius,parseFloat(o));var o,i;return(0,Jt.A)({display:"block",backgroundColor:e.vars?e.vars.palette.Skeleton.bg:(0,vo.X4)(e.palette.text.primary,"light"===e.palette.mode?.11:.13),height:"1.2em"},"text"===t.variant&&{marginTop:0,marginBottom:0,height:"auto",transformOrigin:"0 55%",transform:"scale(1, 0.60)",borderRadius:`${r}${n}/${Math.round(r/.6*10)/10}${n}`,"&:empty:before":{content:'"\\00a0"'}},"circular"===t.variant&&{borderRadius:"50%"},"rounded"===t.variant&&{borderRadius:(e.vars||e).shape.borderRadius},t.hasChildren&&{"& > *":{visibility:"hidden"}},t.hasChildren&&!t.width&&{maxWidth:"fit-content"},t.hasChildren&&!t.height&&{height:"auto"})}),(({ownerState:e})=>"pulse"===e.animation&&qo(Ub||(Ub=$b`
      animation: ${0} 2s ease-in-out 0.5s infinite;
    `),Hb)),(({ownerState:e,theme:t})=>"wave"===e.animation&&qo(zb||(zb=$b`
      position: relative;
      overflow: hidden;

      /* Fix bug in Safari https://bugs.webkit.org/show_bug.cgi?id=68196 */
      -webkit-mask-image: -webkit-radial-gradient(white, black);

      &::after {
        animation: ${0} 2s linear 0.5s infinite;
        background: linear-gradient(
          90deg,
          transparent,
          ${0},
          transparent
        );
        content: '';
        position: absolute;
        transform: translateX(-100%); /* Avoid flash during server-side hydration */
        bottom: 0;
        left: 0;
        right: 0;
        top: 0;
      }
    `),Wb,(t.vars||t).palette.action.hover))),qb=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiSkeleton"}),{animation:r="pulse",className:o,component:i="span",height:a,style:s,variant:l="text",width:u}=n,c=(0,qn.A)(n,Mb),d=(0,Jt.A)({},n,{animation:r,component:i,variant:l,hasChildren:Boolean(c.children)}),f=(e=>{const{classes:t,variant:n,animation:r,hasChildren:o,width:i,height:a}=e,s={root:["root",n,r,o&&"withChildren",o&&!i&&"fitContent",o&&!a&&"heightAuto"]};return(0,Qn.A)(s,Lb,t)})(d);return(0,ir.jsx)(Vb,(0,Jt.A)({as:i,ref:t,className:(0,Kn.A)(f.root,o),ownerState:d},c,{style:(0,Jt.A)({width:u,height:a},s)}))}));var Kb=o(2092),Gb=o(6505);function Jb(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Yb(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Jb(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Jb(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var Xb=function(e){return{content:{paddingTop:0},textField:{width:"100%"},caption:{marginLeft:-e.spacing(1)},context:{paddingBottom:e.spacing(1),paddingTop:e.spacing(1),width:"100%",opacity:"0.7"},date:{fontFamily:"monospace",fontSize:"1em",height:"1em"},logLine:{width:"100%",wordBreak:"break-all",fontFamily:"monospace"},log:{display:"flex",flexDirection:"column-reverse",wordWrap:"break-word",paddingTop:e.spacing(2)},ERROR:{color:e.status.danger},WARNING:{color:e.status.warning},chip:{border:"none"},table:{marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function Qb(e){var t=e.title,n=e.subheader,r=e.hide,o=e.url,i=e.initialContext,a=Sb(o),s=fe((0,B.useState)(!1),2),l=s[0],u=s[1],c=fe((0,B.useState)(""),2),d=c[0],f=c[1],p=fe((0,B.useState)(),2),h=p[0],m=p[1],g=fe((0,B.useState)(i),2),v=g[0],y=g[1],b=la(Xb),w=ee().t;return(0,B.useEffect)((function(){h&&!location.href.match(RegExp("#"+h+"$"))&&(location.href="#"+h)})),B.createElement(mv,{id:"Log"},B.createElement(xv,{title:t,subheader:n,action:B.createElement(Ly,{selected:[],className:b.table},Object.keys(v).length>0&&B.createElement(bd,{title:w("Clear Filters")},B.createElement(gi,{"aria-label":"Filters",disabled:!!d,onClick:function(){y({})}},B.createElement(Kb.A,null))),B.createElement(bd,{title:w("Filters")},B.createElement(gi,{"aria-label":"Filters",disabled:!!d,onClick:function(){!d&&u(!l)}},B.createElement(cb.A,null))))}),B.createElement(Cv,{className:b.content},(l||d)&&B.createElement(Bm,{className:b.textField,id:"search",label:w("Search Regular Expression"),type:"search",margin:"normal",variant:"outlined",onChange:function(e){f(e.target.value),location.href="#",m(null)},value:d}),B.createElement(tw,{setContext:y,context:v}),B.createElement(nw,{setContext:y,context:v}),B.createElement(Zb,{log:a,search:d,setSearch:f,setSkip:m,setContext:y,context:v,hide:r})))}function Zb(e){var t,n=e.log,r=e.search,o=e.setSearch,i=e.setSkip,a=e.setContext,s=e.context,l=e.hide,u=la(Xb);if(!n)return B.createElement(Ib,{color:"primary"});if(r&&r.length>1)try{t=RegExp(r,"i")}catch(e){}return B.createElement("div",{className:u.log},n.map((function(e,r){return B.createElement(ow,{key:r,id:r,data:e,prev:r?n[r-1]:null,re:t,setSearch:o,setSkip:i,setContext:a,context:s,hide:l})})))}function ew(e){var t,n,r,o,i,a=e.k,s=e.v,l=e.context,u=e.setContext,c=e.dense,d=e.hide,f=ee().t,p=la(Xb);if(!a||!s)return null;if(d&&d.indexOf(a)>=0)return null;function h(){var e=Yb({},l);e[a]={value:s},u(e)}if(c){if(a in l&&!l[a].negate)return null;t="sc"===a?f("y"===s?"scheduled":"not scheduled"):"sid"===a?"sid: "+s.slice(0,4):a+": "+s}else t="sc"===a?f("y"===s?"scheduled":"not scheduled"):a+": "+s;return B.createElement(ps,{className:p.chip,variant:"outlined",size:"small",label:t,onClick:function(){var e;a in l?l[a].negate?l[a].value===s?(delete(e=Yb({},l))[a],u(e)):h():function(){var e=Yb({},l);e[a]={value:s,negate:!0},u(e)}():h()},style:{color:"black",backgroundColor:(n=s,r=30,o=80,i=function(e){for(var t=0,n=9;t<e.length;)n=Math.imul(n^e.charCodeAt(t++),Math.pow(9,9));return n^n>>>9}(n)%360,"hsl("+i+", "+r+"%, "+o+"%)"),margin:c?"-8px 1em 0 0":"0 1em 0 0"}})}function tw(e){var t=e.context,n=e.setContext,r=ee().t,o=la(Xb),i={};for(var a in t)t[a].negate||(i[a]=t[a]);return 0===Object.keys(i).length?null:B.createElement("div",{className:o.context},B.createElement(cr,null,r("Show lines with")),"Â ",Object.keys(i).map((function(e){return B.createElement(ew,{key:e,k:e,v:t[e].value,context:t,setContext:n,dense:!1})})))}function nw(e){var t=e.context,n=e.setContext,r=ee().t,o=la(Xb),i={};for(var a in t)t[a].negate&&(i[a]=t[a]);return 0===Object.keys(i).length?null:B.createElement("div",{className:o.context},B.createElement(cr,null,r("Show lines without")),"Â ",Object.keys(i).map((function(e){return B.createElement(ew,{key:e,k:e,v:t[e].value,context:t,setContext:n,dense:!1})})))}function rw(e){var t=e.data,n=e.context,r=e.setContext,o=e.hide,i=la(Xb);return 0===Object.keys(t).length?null:B.createElement("div",{className:i.context},function(){for(var e in t)if((!(e in n)||n[e].negate)&&o.indexOf(e)<0)return!0;return!1}()&&B.createElement(Gb.A,null),Object.keys(t).map((function(e){return B.createElement(ew,{key:e,k:e,v:t[e],context:n,setContext:r,dense:!0,hide:o})})))}function ow(e){var t=e.re,n=e.data,r=e.prev,o=e.context,i=e.setContext,a=e.setSearch,s=e.setSkip,l=e.id,u=e.hide,c=la(Xb);if(!n||void 0===n.m)return B.createElement(qb,null);var d=n.l,f=n.m.join("\n"),p=new Date(1e3*n.t);for(var h in o){var m=o[h];if(m.negate){if(n.x[h]===m.value)return null}else if(n.x[h]!==m.value)return null}return t&&!f.match(t)?null:B.createElement(B.Fragment,null,(!r||JSON.stringify(r.x)!==JSON.stringify(n.x))&&B.createElement(rw,{data:n.x,setContext:i,context:o,hide:u}),B.createElement("div",{className:c.logLine,id:l},B.createElement("div",{className:c[d]},B.createElement(ps,{className:(0,Kn.A)(c.chip,c.caption,c.date),color:"primary",variant:"outlined",size:"small",label:p.toLocaleString(),onClick:function(){a(""),s(l)}}),n.m.map((function(e,t){return B.createElement("span",{key:t},e)})))))}function iw(e){return(0,rr.A)("MuiCardActions",e)}(0,nr.A)("MuiCardActions",["root","spacing"]);const aw=["disableSpacing","className"],sw=(0,Zn.Ay)("div",{name:"MuiCardActions",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,!n.disableSpacing&&t.spacing]}})((({ownerState:e})=>(0,Jt.A)({display:"flex",alignItems:"center",padding:8},!e.disableSpacing&&{"& > :not(style) ~ :not(style)":{marginLeft:8}}))),lw=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCardActions"}),{disableSpacing:r=!1,className:o}=n,i=(0,qn.A)(n,aw),a=(0,Jt.A)({},n,{disableSpacing:r}),s=(e=>{const{classes:t,disableSpacing:n}=e,r={root:["root",!n&&"spacing"]};return(0,Qn.A)(r,iw,t)})(a);return(0,ir.jsx)(sw,(0,Jt.A)({className:(0,Kn.A)(s.root,o),ownerState:a,ref:t},i))}));var uw=o(7091);function cw(e){return(0,rr.A)("MuiCollapse",e)}(0,nr.A)("MuiCollapse",["root","horizontal","vertical","entered","hidden","wrapper","wrapperInner"]);const dw=["addEndListener","children","className","collapsedSize","component","easing","in","onEnter","onEntered","onEntering","onExit","onExited","onExiting","orientation","style","timeout","TransitionComponent"],fw=(0,Zn.Ay)("div",{name:"MuiCollapse",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[n.orientation],"entered"===n.state&&t.entered,"exited"===n.state&&!n.in&&"0px"===n.collapsedSize&&t.hidden]}})((({theme:e,ownerState:t})=>(0,Jt.A)({height:0,overflow:"hidden",transition:e.transitions.create("height")},"horizontal"===t.orientation&&{height:"auto",width:0,transition:e.transitions.create("width")},"entered"===t.state&&(0,Jt.A)({height:"auto",overflow:"visible"},"horizontal"===t.orientation&&{width:"auto"}),"exited"===t.state&&!t.in&&"0px"===t.collapsedSize&&{visibility:"hidden"}))),pw=(0,Zn.Ay)("div",{name:"MuiCollapse",slot:"Wrapper",overridesResolver:(e,t)=>t.wrapper})((({ownerState:e})=>(0,Jt.A)({display:"flex",width:"100%"},"horizontal"===e.orientation&&{width:"auto",height:"100%"}))),hw=(0,Zn.Ay)("div",{name:"MuiCollapse",slot:"WrapperInner",overridesResolver:(e,t)=>t.wrapperInner})((({ownerState:e})=>(0,Jt.A)({width:"100%"},"horizontal"===e.orientation&&{width:"auto",height:"100%"}))),mw=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiCollapse"}),{addEndListener:r,children:o,className:i,collapsedSize:a="0px",component:s,easing:l,in:u,onEnter:c,onEntered:d,onEntering:f,onExit:p,onExited:h,onExiting:m,orientation:g="vertical",style:v,timeout:y=uw.p0.standard,TransitionComponent:b=Vr}=n,w=(0,qn.A)(n,dw),E=(0,Jt.A)({},n,{orientation:g,collapsedSize:a}),S=(e=>{const{orientation:t,classes:n}=e,r={root:["root",`${t}`],entered:["entered"],hidden:["hidden"],wrapper:["wrapper",`${t}`],wrapperInner:["wrapperInner",`${t}`]};return(0,Qn.A)(r,cw,n)})(E),x=Jr(),A=B.useRef(),k=B.useRef(null),_=B.useRef(),C="number"==typeof a?`${a}px`:a,O="horizontal"===g,P=O?"width":"height";B.useEffect((()=>()=>{clearTimeout(A.current)}),[]);const R=B.useRef(null),T=(0,Qr.A)(t,R),F=e=>t=>{if(e){const n=R.current;void 0===t?e(n):e(n,t)}},j=()=>k.current?k.current[O?"clientWidth":"clientHeight"]:0,N=F(((e,t)=>{k.current&&O&&(k.current.style.position="absolute"),e.style[P]=C,c&&c(e,t)})),I=F(((e,t)=>{const n=j();k.current&&O&&(k.current.style.position="");const{duration:r,easing:o}=Xr({style:v,timeout:y,easing:l},{mode:"enter"});if("auto"===y){const t=x.transitions.getAutoHeightDuration(n);e.style.transitionDuration=`${t}ms`,_.current=t}else e.style.transitionDuration="string"==typeof r?r:`${r}ms`;e.style[P]=`${n}px`,e.style.transitionTimingFunction=o,f&&f(e,t)})),L=F(((e,t)=>{e.style[P]="auto",d&&d(e,t)})),M=F((e=>{e.style[P]=`${j()}px`,p&&p(e)})),D=F(h),U=F((e=>{const t=j(),{duration:n,easing:r}=Xr({style:v,timeout:y,easing:l},{mode:"exit"});if("auto"===y){const n=x.transitions.getAutoHeightDuration(t);e.style.transitionDuration=`${n}ms`,_.current=n}else e.style.transitionDuration="string"==typeof n?n:`${n}ms`;e.style[P]=C,e.style.transitionTimingFunction=r,m&&m(e)}));return(0,ir.jsx)(b,(0,Jt.A)({in:u,onEnter:N,onEntered:L,onEntering:I,onExit:M,onExited:D,onExiting:U,addEndListener:e=>{"auto"===y&&(A.current=setTimeout(e,_.current||0)),r&&r(R.current,e)},nodeRef:R,timeout:"auto"===y?null:y},w,{children:(e,t)=>(0,ir.jsx)(fw,(0,Jt.A)({as:s,className:(0,Kn.A)(S.root,i,{entered:S.entered,exited:!u&&"0px"===C&&S.hidden}[e]),style:(0,Jt.A)({[O?"minWidth":"minHeight"]:C},v),ownerState:(0,Jt.A)({},E,{state:e}),ref:T},t,{children:(0,ir.jsx)(pw,{ownerState:(0,Jt.A)({},E,{state:e}),className:S.wrapper,ref:k,children:(0,ir.jsx)(hw,{ownerState:(0,Jt.A)({},E,{state:e}),className:S.wrapperInner,children:o})})}))}))}));mw.muiSupportAuto=!0;const gw=mw;var vw=o(2048);function yw(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return bw(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?bw(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function bw(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var ww=function(e){return{tableWrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},content:{height:"100%"},expand:{transform:"rotate(0deg)",marginLeft:"auto",transition:e.transitions.create("transform",{duration:e.transitions.duration.shortest})},expandOpen:{transform:"rotate(180deg)"}}};function Ew(e){return e.data.map((function(t,n){return B.createElement(Sw,{key:e.mac+"-"+n,mac:e.mac,data:t})}))}function Sw(e){return B.createElement(ky,null,B.createElement(py,{"data-title":"Interface",style:{flexBasis:"11rem"}},e.data.intf),B.createElement(py,{"data-title":"L2 Address",style:{flexBasis:"11rem"}},e.mac),B.createElement(py,{"data-title":"L3 Address",style:{flexBasis:"11rem"}},e.data.addr),B.createElement(py,{"data-title":"L3 Mask",style:{flexBasis:"11rem"}},e.data.mask))}function xw(e){var t=ee(),n=t.t,r=(t.i18n,la(ww)),o=fe(B.useState(!1),2),i=o[0],a=o[1];if(i)var s={xs:12};else s={xs:12,sm:6,md:4};return B.createElement(Lv,(0,Jt.A)({item:!0},s),B.createElement(mv,{id:"Network",className:r.content},B.createElement(xv,{title:n("Network")}),B.createElement(Cv,null,B.createElement(Aw,e)),B.createElement(lw,null,B.createElement(gi,{className:(0,Kn.A)(r.expand,ue({},r.expandOpen,i)),onClick:function(){a(!i)},"aria-expanded":i,"aria-label":"show more"},B.createElement(vw.A,null))),B.createElement(gw,{in:i,timeout:"auto",unmountOnExit:!0},B.createElement(Cv,null,B.createElement(kw,e)))))}function Aw(e){var t=ee(),n=t.t;if(t.i18n,void 0===e.nodeData)return B.createElement(qb,null);if(void 0===e.nodeData.lan)return B.createElement(qb,null);var r={mac:0,ipv4:0,ipv6:0};for(var o in e.nodeData.lan){r.mac+=1;var i,a=yw(e.nodeData.lan[o]);try{for(a.s();!(i=a.n()).done;)r[i.value.type]+=1}catch(e){a.e(e)}finally{a.f()}}return B.createElement(cr,{component:"span"},r.mac," ",n("interfaces"),",Â ",r.ipv4," ipv4,Â ",r.ipv6," ipv6.")}function kw(e){var t=la(ww);return void 0===e.nodeData||void 0===e.nodeData.lan?null:B.createElement(nv,{className:t.tableWrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,"Interface"),B.createElement(py,null,"L2 Address"),B.createElement(py,null,"L3 Address"),B.createElement(py,null,"L3 Mask"))),B.createElement(ly,null,Object.keys(e.nodeData.lan).map((function(t){return B.createElement(Ew,{key:t,mac:t,data:e.nodeData.lan[t]})})))))}function _w(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var Cw=function(e){return{tableWrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},content:{height:"100%"},expand:{transform:"rotate(0deg)",marginLeft:"auto",transition:e.transitions.create("transform",{duration:e.transitions.duration.shortest})},expandOpen:{transform:"rotate(180deg)"}}};function Ow(e){return B.createElement(ky,null,B.createElement(py,{"data-title":"Type"},e.data.type),B.createElement(py,{"data-title":"Path"},e.data.path),B.createElement(py,{"data-title":"Class"},e.data.class),B.createElement(py,{"data-title":"Driver"},e.data.driver),B.createElement(py,{"data-title":"Description",style:{flexBasis:"100%"}},e.data.description))}function Pw(e){var t=la(Cw),n=ee(),r=n.t,o=(n.i18n,fe(B.useState(!1),2)),i=o[0],a=o[1];if(i)var s={xs:12};else s={xs:12,sm:6,md:4};return B.createElement(Lv,(0,Jt.A)({item:!0},s),B.createElement(mv,{id:"Hardware",className:t.content},B.createElement(xv,{title:r("Hardware")}),B.createElement(Cv,null,B.createElement(Rw,e)),B.createElement(lw,null,B.createElement(gi,{className:(0,Kn.A)(t.expand,ue({},t.expandOpen,i)),onClick:function(){a(!i)},"aria-expanded":i,"aria-label":"show more"},B.createElement(vw.A,null))),B.createElement(gw,{in:i,timeout:"auto",unmountOnExit:!0},B.createElement(Cv,null,B.createElement(Tw,e)))))}function Rw(e){var t=ee(),n=t.t;if(t.i18n,void 0===e.nodeData)return B.createElement(qb,null);if(void 0===e.nodeData.hardware)return B.createElement(qb,null);var r,o={components:0},i=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return _w(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?_w(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(e.nodeData.hardware);try{for(i.s();!(r=i.n()).done;)r.value,o.components+=1}catch(e){i.e(e)}finally{i.f()}return B.createElement(cr,{component:"span"},o.components," ",n("components"),".")}function Tw(e){var t=la(Cw),n=ee();return n.t,n.i18n,void 0===e.nodeData||void 0===e.nodeData.hardware?null:B.createElement(nv,{className:t.tableWrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,"Type"),B.createElement(py,null,"Path"),B.createElement(py,null,"Class"),B.createElement(py,null,"Driver"),B.createElement(py,null,"Description"))),B.createElement(ly,null,e.nodeData.hardware.map((function(e,t){return B.createElement(Ow,{key:t,data:e})})))))}function Fw(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var jw=function(e){return{tableWrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},content:{height:"100%"},expand:{transform:"rotate(0deg)",marginLeft:"auto",transition:e.transitions.create("transform",{duration:e.transitions.duration.shortest})},expandOpen:{transform:"rotate(180deg)"}}};function Nw(e){return B.createElement(ky,null,B.createElement(py,{"data-title":"Id"},e.data.hba_id),B.createElement(py,{"data-title":"Type"},e.data.hba_type),B.createElement(py,{"data-title":"Host"},e.data.host))}function Iw(e){var t=ee(),n=t.t,r=(t.i18n,la(jw)),o=fe(B.useState(!1),2),i=o[0],a=o[1];if(i)var s={xs:12};else s={xs:12,sm:6,md:4};return B.createElement(Lv,(0,Jt.A)({item:!0},s),B.createElement(mv,{id:"Initiators",className:r.content},B.createElement(xv,{title:n("Initiators")}),B.createElement(Cv,null,B.createElement(Lw,e)),B.createElement(lw,null,B.createElement(gi,{className:(0,Kn.A)(r.expand,ue({},r.expandOpen,i)),onClick:function(){a(!i)},"aria-expanded":i,"aria-label":"show more"},B.createElement(vw.A,null))),B.createElement(gw,{in:i,timeout:"auto",unmountOnExit:!0},B.createElement(Cv,null,B.createElement(Mw,e)))))}function Lw(e){var t=ee(),n=t.t;if(t.i18n,void 0===e.nodeData)return B.createElement(qb,null);if(void 0===e.nodeData.hba)return B.createElement(qb,null);var r,o={initiator:0,iscsi:0,fc:0},i=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Fw(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Fw(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(e.nodeData.hba);try{for(i.s();!(r=i.n()).done;){var a=r.value;o.initiator+=1;try{o[a.type]+=1}catch(e){}}}catch(e){i.e(e)}finally{i.f()}return B.createElement(cr,{component:"span"},o.initiator," ",n("initiators"),",Â ",o.iscsi," iscsi,Â ",o.fc," fc.")}function Mw(e){var t=la(jw);return void 0===e.nodeData||void 0===e.nodeData.hba?null:B.createElement(nv,{className:t.tableWrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,"Id"),B.createElement(py,null,"Type"),B.createElement(py,null,"Host"))),B.createElement(ly,null,e.nodeData.hba.map((function(e,t){return B.createElement(Nw,{key:t,data:e})})))))}function Dw(e){var t=ee(),n=t.t;return t.i18n,e.frozen?"thawed"==e.frozen?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",className:e.className},B.createElement(My.A,{color:"primary",title:"Frozen"}))),B.createElement(qi,null,n("Frozen. Orchestration is disabled."))):null}function Bw(e){if(!e.state)return null;if("idle"==e.state)return null;if(e.state.match(/failed/))var t="error";else t="primary";return B.createElement(Li,null,B.createElement(zi,null,B.createElement(Nu.A,{color:t})),B.createElement(qi,null,e.state))}var Uw=o(4967);function zw(e){var t=ee(),n=t.t;return t.i18n,e.target?B.createElement(Li,null,B.createElement(zi,null,B.createElement(Uw.A,{color:"primary"})),B.createElement(qi,null,n("Target state is {{state}}.",{state:e.target}))):null}function $w(e){var t=ee(),n=t.t;return t.i18n,e.speaker?B.createElement(Li,null,B.createElement(zi,null,B.createElement(zy.A,{color:"action"})),B.createElement(qi,null,n("This node feeds the collector."))):null}var Hw=function(e){return{root:{color:e.status.warning}}};function Ww(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2)),o=r[0].cstat,i=(r[1],la(Hw));return Nn(o)==xn.OPTIMAL?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(Mv.A,{className:i.root})),B.createElement(qi,null,n("Nodes run different versions.")))}var Vw=o(1447),qw=function(e){return{root:{color:e.status.error}}};const Kw=B.forwardRef((function(e,t){var n=la(qw);return B.createElement(Vw.A,(0,Jt.A)({},e,{ref:t,className:(0,Kn.A)(n.root,e.className)}))}));function Gw(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2)),o=r[0].cstat;return r[1],jn(o)==xn.OPTIMAL?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(Kw,null)),B.createElement(qi,null,n("Nodes run incompatible versions. Orchestration is disabled.")))}const Jw=function(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2));return r[0].cstat,r[1],In(e.data)!=xn.WARNING?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(Wy,null)),B.createElement(qi,null,n("Memory overload. Orchestration is disabled.")))},Yw=function(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2));return r[0].cstat,r[1],Ln(e.data)!=xn.WARNING?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(Wy,null)),B.createElement(qi,null,n("Swap overload. Orchestration is disabled.")))};function Xw(e){var t=fe(ve(),2),n=t[0].cstat;if(t[1],void 0===n.monitor)return null;var r=n.monitor.nodes[e.name];return void 0===r||void 0===r.monitor?B.createElement(Ei,{dense:!0},B.createElement(Bw,{state:"unknown"})):B.createElement(Ei,{dense:!0},B.createElement(Dw,{frozen:r.frozen}),B.createElement($w,{speaker:r.speaker}),B.createElement(Bw,{state:r.monitor.status}),B.createElement(zw,{target:r.monitor.global_expect}),B.createElement(Jw,{data:r}),B.createElement(Yw,{data:r}),B.createElement(Gw,null),B.createElement(Ww,null))}var Qw=function(e){return{content:{height:"100%"},divider:{marginBottom:e.spacing(2)}}};function Zw(e){var t=la(Qw),n=e.name,r=(e.nodeData,ee()),o=r.t,i=(r.i18n,fe(ve(),2));return i[0].user,i[1],B.createElement(mv,{className:t.content},B.createElement(xv,{title:o("Node"),subheader:n,action:B.createElement(Ny,{selected:n})}),B.createElement(Cv,null,B.createElement(Xw,{name:n}),B.createElement(Nl,{className:t.divider}),B.createElement(Lv,{container:!0,spacing:1},["Labels","Server","Processor","Memory","System","Agent","Network","Initiators","Hardware","Log"].map((function(e){return B.createElement(Lv,{item:!0,key:e},B.createElement(ps,{label:o(e),component:"a",href:"#"+e,clickable:!0}))})))))}var eE=function(e){return{content:{height:"100%"}}};const tE=function(e){var t=la(eE),n=ee(),r=(n.i18n,n.t);return B.createElement(Lv,{item:!0,xs:12,sm:6,md:4},B.createElement(mv,{id:e.title,className:t.content},B.createElement(xv,{title:r(e.title),action:e.action}),B.createElement(Cv,null,e.children)))};var nE=function(e){return{prop:{paddingTop:e.spacing(1)},edited:{paddingTop:e.spacing(2),paddingBottom:e.spacing(2)},actions:{"&:first-child":{marginRight:-e.spacing(1)}}}};const rE=function(e){var t=la(nE),n=ee(),r=(n.i18n,n.t);if(e.remove||e.change)var o={title:4,value:4,action:4},i=t.edited;else o={title:8,value:4,action:0},i=null;return B.createElement(Lv,{container:!0,className:t.prop},B.createElement(Lv,{item:!0,xs:o.title,className:i},B.createElement(cr,{color:"textSecondary"},r(e.title))),B.createElement(Lv,{item:!0,xs:o.value,className:i},B.createElement(cr,null,e.value)),(e.remove||e.change)&&B.createElement(Lv,{item:!0,xs:o.action},B.createElement(Lv,{container:!0,direction:"row-reverse",wrap:"nowrap"},e.change&&B.createElement(Lv,{item:!0,className:t.actions},e.change),e.remove&&B.createElement(Lv,{item:!0,className:t.actions},e.remove))))};var oE=function(e){return{formcontrol:{margin:e.spacing(2,0)}}};const iE=function(e){var t=e.node,n=hn().auth,r=fe((0,B.useState)(!1),2),o=r[0],i=r[1],a=fe((0,B.useState)(e.key?e.key:""),2),s=a[0],l=a[1],u=fe((0,B.useState)(e.val?e.val:""),2),c=u[0],d=u[1],f=fe(ve(),2),p=f[0].user,h=f[1],m=(rp().dispatchAlerts,ee()),g=m.t,v=(m.i18n,la(oE));if(!p.grant)return null;if(!("root"in p.grant))return null;function y(e){i(!1)}return B.createElement(B.Fragment,null,B.createElement(gi,{"aria-label":"Add Node Label","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),i(!0)}},B.createElement(lv.A,null)),B.createElement(Ls,{open:o,onClose:y,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},g("Add Node Label")),B.createElement(qs,null,B.createElement(Ys,null,g("add_node_label")),B.createElement(Au,{className:v.formcontrol,fullWidth:!0},B.createElement(Bm,{label:g("Key"),id:"key",value:s,onChange:function(e){return l(e.target.value)},autoFocus:!0})),B.createElement(Au,{className:v.formcontrol,fullWidth:!0},B.createElement(Bm,{label:g("Value"),id:"val",value:c,onChange:function(e){return d(e.target.value)}}))),B.createElement(Us,null,B.createElement(cl,{onClick:y,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(e){var r=[];if(s&&c){var o="labels."+s+"="+c;r.push(o),on(t,"set",{kw:r},(function(e){return h({data:e})}),n),l(""),d(""),y()}},color:"secondary"},"Submit"))))};var aE=o(8597),sE=function(e){return{formcontrol:{margin:e.spacing(2,0)}}};const lE=function(e){var t=e.node,n=(e.labelKey,e.labelCurrent,hn().auth),r=fe(B.useState(!1),2),o=r[0],i=r[1],a=fe((0,B.useState)(e.val?e.val:""),2),s=a[0],l=a[1],u=fe(ve(),2),c=u[0].user,d=(u[1],rp().dispatchAlerts),f=ee(),p=f.t,h=(f.i18n,la(sE));if(!c.grant)return null;if(!("root"in c.grant))return null;function m(e){i(!1)}return B.createElement(B.Fragment,null,B.createElement(gi,{"aria-label":"Change Node Label","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),i(!0)}},B.createElement(aE.A,null)),B.createElement(Ls,{open:o,onClose:m,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},p("Change Node Label")),B.createElement(qs,null,B.createElement(Ys,null,p("add_node_label")),B.createElement(Au,{className:h.formcontrol,fullWidth:!0},B.createElement(Bm,{label:p("New value"),id:"val",placeholder:e.labelCurrent,value:s,onChange:function(e){return l(e.target.value)},autoFocus:!0}))),B.createElement(Us,null,B.createElement(cl,{onClick:m,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(r){var o=[];if(e.labelKey&&s){var i="labels."+e.labelKey+"="+s;o.push(i),on(t,"set",{kw:o},(function(e){return d({data:e})}),n),m()}},color:"secondary"},"Submit"))))};var uE=o(2987);const cE=function(e){var t=e.node,n=e.labelKey,r=rp().dispatchAlerts,o=hn().auth;return B.createElement(gi,{"aria-label":"Remove","aria-haspopup":!0,onClick:function(){console.log("unset",t,"label",n);var e=[];if(n){var i="labels."+n;e.push(i),on(t,"unset",{kw:e},(function(e){return r({data:e})}),o)}}},B.createElement(uE.A,null))};function dE(e){var t=hn().user;return t.grant&&"root"in t.grant?B.createElement(gi,{"aria-label":"Edit Labels","aria-haspopup":!0,onClick:e.toggle},B.createElement(aE.A,null)):null}const fE=function(e){var t,n=e.name,r=bn().cstat,o=fe((0,B.useState)(!1),2),i=o[0],a=o[1];try{if(void 0===(t=r.monitor.nodes[n].labels))return null}catch(e){return null}return B.createElement(tE,{title:"Labels",action:B.createElement(B.Fragment,null,B.createElement(dE,{toggle:function(){a(!i)}}),B.createElement(iE,{node:n}))},Object.keys(t).sort().map((function(e){return B.createElement(rE,{key:e,title:e,value:t[e],change:i?B.createElement(lE,{node:n,labelKey:e,labelCurrent:t[e]}):null,remove:i?B.createElement(cE,{node:n,labelKey:e}):null})})))};var pE=function(e){return{root:{marginTop:e.spacing(3),flexGrow:1}}};function hE(e){var t=la(pE),n=it(),r=hn().auth,o=fe((0,B.useState)(),2),i=o[0],a=o[1],s=fe((0,B.useState)(!1),2),l=s[0],u=s[1],c=new URLSearchParams(n.search).get("name");return(0,B.useEffect)((function(){l||i||(u(!0),un("GET",c,"/node",{},(function(e){a(e),u(!1)}),r))})),B.createElement(Lv,{container:!0,spacing:2,className:t.root},B.createElement(Lv,{item:!0,xs:12,sm:6,md:4},B.createElement(Zw,{name:c,nodeData:i})),B.createElement(fE,{name:c}),B.createElement(mE,{nodeData:i}),B.createElement(xw,{nodeData:i}),B.createElement(Iw,{nodeData:i}),B.createElement(Pw,{nodeData:i}),B.createElement(Lv,{item:!0,xs:12},B.createElement(gE,{node:c})))}function mE(e){return e.nodeData&&e.nodeData.manufacturer?B.createElement(B.Fragment,null,B.createElement(tE,{title:"Server"},B.createElement(rE,{title:"Manufacturer",value:e.nodeData.manufacturer.value}),B.createElement(rE,{title:"Model",value:e.nodeData.model.value}),B.createElement(rE,{title:"Serial",value:e.nodeData.serial.value}),B.createElement(rE,{title:"SP Version",value:e.nodeData.sp_version.value}),B.createElement(rE,{title:"Bios Version",value:e.nodeData.bios_version.value}),B.createElement(rE,{title:"Enclosure",value:e.nodeData.enclosure.value}),B.createElement(rE,{title:"Timezone",value:e.nodeData.tz.value})),B.createElement(tE,{title:"Processor"},B.createElement(rE,{title:"Dies",value:e.nodeData.cpu_dies.value}),B.createElement(rE,{title:"Cores",value:e.nodeData.cpu_cores.value}),B.createElement(rE,{title:"Threads",value:e.nodeData.cpu_threads.value}),B.createElement(rE,{title:"Frequency",value:e.nodeData.cpu_freq.value}),B.createElement(rE,{title:"Model",value:e.nodeData.cpu_model.value})),B.createElement(tE,{title:"Memory"},B.createElement(rE,{title:"Size",value:e.nodeData.mem_bytes.formatted_value}),B.createElement(rE,{title:"Banks",value:e.nodeData.mem_banks.value}),B.createElement(rE,{title:"Slots",value:e.nodeData.mem_slots.value})),B.createElement(tE,{title:"System"},B.createElement(rE,{title:"Name",value:e.nodeData.os_name.value}),B.createElement(rE,{title:"Vendor",value:e.nodeData.os_vendor.value}),B.createElement(rE,{title:"Release",value:e.nodeData.os_release.value}),B.createElement(rE,{title:"Arch",value:e.nodeData.os_arch.value}),B.createElement(rE,{title:"Kernel",value:e.nodeData.os_kernel.value}),B.createElement(rE,{title:"Last Boot",value:e.nodeData.last_boot.value})),B.createElement(tE,{title:"Agent"},B.createElement(rE,{title:"Version",value:e.nodeData.version.value}),B.createElement(rE,{title:"Env",value:e.nodeData.node_env.value}))):null}function gE(e){var t=ee(),n=(t.i18n,t.t),r=fe(ve(),2),o=r[0].user;return r[1],o.grant&&"root"in o.grant?B.createElement(Qb,{title:n("Log"),url:"/node/"+e.node,hide:["n"],initialContext:{}}):null}var vE=o(8142),yE=o.n(vE),bE=o(2299),wE=o(9257),EE=o(1730),SE=o(908),xE=o(8707);function AE(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return kE(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?kE(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function kE(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function _E(e){var t=hn().auth,n=fe(ve(),2),r=n[0].cstat,o=(n[1],rp().dispatchAlerts);if(void 0===r.monitor)return null;if(E=e.path)var i=[e.path];else 1==(i=e.selected).length&&(E=i[0]);if(E){var a=r.monitor.services[E],s=kn(E),l=[s.namespace];if(void 0===a)return null;var u=function(e){sn(E,e.value,(function(e){return o({data:e})}),t)},c=function(){var e=0;for(var t in r.monitor.nodes)if(0!==Object.entries(r.monitor.nodes[t]).length){var n=r.monitor.nodes[t].services;void 0!==n&&E in n.status&&e++}return e}(),d=function(){return"frozen"==a.frozen},f=function(){return"thawed"==a.frozen},p=function(){return a.avail in{up:null,"n/a":null}},h=function(){return"n/a"==a.avail||1==c},m=function(){return"n/a"==a.avail||1==c},g=function(){return a.avail in{down:null,"stdby down":null,"n/a":null}},v=function(){return"mixed"!=a.provisioned&&!!a.provisioned},y=function(){return"mixed"!=a.provisioned&&!a.provisioned},b=[s.kind]}else{var w,E=i.join(","),S=(b=["svc"],l=[],AE(i));try{for(S.s();!(w=S.n()).done;){var x=kn(w.value);l.indexOf(x.namespace)<0&&l.push(x.namespace)}}catch(e){S.e(e)}finally{S.f()}u=function(e){var n,r=AE(i);try{for(r.s();!(n=r.n()).done;)sn(n.value,e.value,(function(e){return o({data:e})}),t)}catch(e){r.e(e)}finally{r.f()}},d=function(){return!1},f=function(){return!1},p=function(){return!1},h=function(){return!1},m=function(){return!1},g=function(){return!1},v=function(){return!1},y=function(){return!1}}return b.indexOf("svc")>-1||b.indexOf("vol")>-1?B.createElement(kd,{path:E,node:e.node,title:e.title,submit:u,fab:e.fab},B.createElement(_d,{name:"safe",color:"secondary",confirms:0},B.createElement(Cd,{value:"started",text:"Start",disabled:p(),requires:{role:"operator",namespace:l},icon:B.createElement(Nu.A,null)}),B.createElement(Cd,{value:"thawed",text:"Thaw",disabled:f(),requires:{role:"operator",namespace:l},icon:B.createElement(ip.A,null)}),B.createElement(Cd,{value:"placed",text:"Giveback",disabled:h(),requires:{role:"operator",namespace:l},icon:B.createElement(SE.A,null),confirmations:[_l]}),B.createElement(Cd,{value:"placed@<peer>",text:"Switch",disabled:m(),requires:{role:"operator",namespace:l},icon:B.createElement(SE.A,null),confirmations:[_l]}),B.createElement(Cd,{value:"aborted",text:"Abort",disabled:function(){var e,t=AE(i);try{for(t.s();!(e=t.n()).done;){var n=e.value;for(var o in r.monitor.nodes){var a=void 0;try{if(!(a=r.monitor.nodes[o].services.status[n]))continue}catch(e){continue}if(a.monitor.status&&a.monitor.status.indexOf("failed")<0)return!1}}}catch(e){t.e(e)}finally{t.f()}return!1}(),requires:{role:"operator",namespace:l},icon:B.createElement(bE.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"impacting",color:"warning"},B.createElement(Cd,{value:"stopped",text:"Stop",disabled:g(),requires:{role:"operator",namespace:l},icon:B.createElement(wE.A,null),confirmations:[Sl,Cl]}),B.createElement(Cd,{value:"frozen",text:"Freeze",disabled:d(),requires:{role:"operator",namespace:l},icon:B.createElement(op.A,null),confirmations:["I understand the selected services orchestration will be paused."]}),B.createElement(Cd,{value:"provisioned",text:"Provision",disabled:v(),requires:{role:"admin",namespace:l},icon:B.createElement(xE.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"purged",text:"Purge",requires:{role:"admin",namespace:l},icon:B.createElement(uE.A,null),confirmations:[Al,xl,Cl,Sl]}),B.createElement(Cd,{value:"unprovisioned",text:"Unprovision",disabled:y(),requires:{role:"admin",namespace:l},icon:B.createElement(ta.A,null),confirmations:[Al,Cl,Sl]}),B.createElement(Cd,{value:"deleted",text:"Delete",requires:{role:"admin",namespace:l},icon:B.createElement(EE.A,null),confirmations:[xl,Cl]}))):b.indexOf("ccfg")>-1?null:b.indexOf("cfg")>-1||b.indexOf("sec")>-1||b.indexOf("usr")>-1?B.createElement(kd,{path:E,node:e.node,title:e.title,submit:u,fab:e.fab},B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"deleted",text:"Delete",requires:{role:"admin",namespace:l},icon:B.createElement(EE.A,null),confirmations:[xl,Cl]}))):void 0}function CE(e){var t=fe(ve(),2),n=t[0].cstat;if(t[1],void 0===n.monitor)return null;for(var r in n.monitor.nodes)if(0!==Object.entries(n.monitor.nodes[r]).length){var o=n.monitor.nodes[r].services;if(void 0!==o){var i=o.status[e.path];if(void 0!==i&&void 0!==i.monitor){if(i.monitor.status&&i.monitor.status.indexOf("failed")>-1)return B.createElement(Nu.A,{color:"error"});if(i.monitor.global_expect||"idle"!==i.monitor.status)return B.createElement(cr,{className:e.className,component:"span"},B.createElement(Nu.A,{color:"primary"}))}}}return null}var OE=function(e){return{root:{color:e.status.warning}}};function PE(e){var t=la(OE);return"warn"==e.overall?B.createElement(cr,{component:"span",className:e.className},B.createElement(Mv.A,{className:t.root})):null}var RE=o(6298);function TE(e){var t=la(ma);return"optimal"==e.placement||"n/a"==e.placement?null:e.placement?B.createElement(cr,{component:"span",className:e.className},B.createElement(RE.A,{className:t.warning,title:"Placement "+e.placement})):null}function FE(e){return 0==e.provisioned?B.createElement(cr,{component:"span",className:e.className},B.createElement(Mv.A,{color:"error",title:"Not Provisionned"})):(e.provisioned,null)}function jE(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function NE(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?jE(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):jE(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function IE(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var LE=function(e){return{root:{marginTop:e.spacing(3)},tools:{alignItems:"flex-end"},row:{"&:hover":{cursor:"pointer"}},table:{marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},content:{paddingTop:0},list:{paddingLeft:0,paddingRight:0}}};function ME(e){var t=e.search,n=e.setSearch,r=ee(),o=r.t;return r.i18n,B.createElement(Bm,{id:"obj-filter",label:o("Filter"),value:t,onChange:function(e){n(e.target.value)},margin:"none",variant:"outlined",type:"search",fullWidth:!0,autoFocus:!0})}function DE(e){var t=la(LE),n=fe(ve(),2),r=n[0].cstat,o=(n[1],fe((0,B.useState)(""),2)),i=o[0],a=o[1],s=function(e){var t=fe((0,B.useState)(e),2),n=t[0],r=t[1];return(0,B.useEffect)((function(){var t=setTimeout((function(){return r(e)}),400);return function(){return clearTimeout(t)}}),[e]),n}(i),l=fe((0,B.useState)(!1),2),u=l[0],c=l[1],d=(e.kind,e.withScalerSlaves,ee()),f=d.t,p=(d.i18n,lt()),h={svc:"Services",vol:"Volumes",cfg:"Configs",sec:"Secrets",usr:"Users"}[e.kind]||"Objects",m=fe((0,B.useReducer)((function(e,t){try{return t.has("foo"),t}catch(e){}var n=new Set(e);return n.has(t)?n.delete(t):n.add(t),n}),new Set),2),g=m[0],v=m[1];if(!r.monitor)return null;console.log("getlines");var y=function(e){var t=e.kind,n=e.search,r=e.withScalerSlaves,o=e.cstat,i=e.selected,a=[];if(!o.monitor)return a;if(n)var s=RegExp(n,"i");else s=null;var l,u=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return IE(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?IE(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(Object.keys(o.monitor.services).sort());try{for(u.s();!(l=u.n()).done;){var c=l.value,d=kn(c);if(!t||d.kind==t){try{if(s&&!s.test(c))continue}catch(e){}!r&&d.name.match(/^[0-9]+\./)||a.push(NE(NE({},o.monitor.services[c]),{},{path:c,selected:i.has(c)}))}}}catch(e){u.e(e)}finally{u.f()}return a}({kind:e.kind,search:s,cstat:r,selected:g}),b=y.length;return console.log("table"),B.createElement(mv,{className:t.root},B.createElement(xv,{title:f(h),subheader:r.cluster.name,action:B.createElement(Ly,{selected:Array.from(g),className:t.table},g.size>0&&B.createElement(_E,{selected:Array.from(g),title:""}),B.createElement(dv,{kind:e.kind}),B.createElement(bd,{title:f("Filters")},B.createElement(gi,{"aria-label":"Filters",disabled:!!i,onClick:function(){!i&&c(!u)}},B.createElement(cb.A,null))),B.createElement(yu,{indeterminate:g.size>0&&g.size<b,checked:g.size===b,disableRipple:!0,onChange:function(e){e.target.checked?v(new Set(y.map((function(e){return e.path})))):v(new Set)},inputProps:{"aria-label":f("Select all")}}))}),B.createElement(Cv,{className:t.content},(u||i)&&B.createElement(ME,{search:i,setSearch:a}),B.createElement(BE,{lines:y,setSelected:v,handleLineClick:function(e){p({pathname:"/object",search:"?path="+e,state:{kind:h}})}})))}function BE(e){var t=la(LE),n=e.lines,r=e.setSelected,o=e.handleLineClick;return B.createElement(Ei,{className:t.list},n.map((function(e,t){return B.createElement(UE,{key:t,line:e,setSelected:r,handleLineClick:o})})))}var UE=B.memo((function(e){var t=e.line,n=e.setSelected,r=e.handleLineClick,o={paddingTop:"6px"};return console.log("line"),B.createElement(Li,{button:!0,disableRipple:!0,onClick:function(e){return r(t.path)}},B.createElement(zi,null,B.createElement(Sa,{path:t.path,avail:t.avail})),B.createElement(qi,{primary:t.path}),B.createElement(Ti,null,B.createElement(Lv,{container:!0,alignItems:"center",alignContent:"center",spacing:0},B.createElement(Lv,{item:!0,style:o},B.createElement(CE,{path:t.path})),B.createElement(Lv,{item:!0,style:o},B.createElement(PE,{overall:t.overall})),B.createElement(Lv,{item:!0,style:o},B.createElement(TE,{placement:t.placement})),B.createElement(Lv,{item:!0,style:o},B.createElement(Dy,{frozen:t.frozen})),B.createElement(Lv,{item:!0,style:o},B.createElement(FE,{provisioned:t.provisioned})),B.createElement(Lv,{item:!0,style:o}),B.createElement(Lv,{item:!0},B.createElement(yu,{checked:t.selected,onChange:function(e){return n(t.path)},edge:"end",disableRipple:!0})))))}),(function(e,t){return yE()(e.line,t.line)}));function zE(e){var t=ee(),n=t.t,r=(t.i18n,la(ma)),o={up:"Available",down:"Unavailable","stdby up":"Standby","stdby down":"Standby Down",undef:"Availability is undefined",warn:"Warning"}[e.avail];if(!o)return null;var i=n(o);return B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",className:(0,Kn.A)(e.className,r[e.avail])},B.createElement(ga.A,null))),B.createElement(qi,null,i))}function $E(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2)),o=r[0].cstat;if(r[1],void 0===o.monitor)return null;for(var i in o.monitor.nodes)if(!(0,Dv.isEmpty)(o.monitor.nodes[i])){var a=o.monitor.nodes[i].services;if(void 0!==a){var s=a.status[e.path];if(void 0!==s&&void 0!==s.monitor){if(s.monitor.status&&s.monitor.status.indexOf("failed")>-1)return B.createElement(Li,null,B.createElement(zi,null,B.createElement(Nu.A,{color:"error"})),B.createElement(qi,null,n("Last action failed")+" ("+s.monitor.status.replace(/ failed/,"")+")"));if(s.monitor.global_expect||"idle"!=s.monitor.status)return B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{className:e.className,component:"span"},B.createElement(Nu.A,{color:"primary"}))),B.createElement(qi,null,n("Action in progress.")))}}}return null}function HE(e){var t=ee(),n=t.t;return t.i18n,"warn"!=e.overall?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",className:e.className},B.createElement(Wy,null))),B.createElement(qi,null,n("Overall state is {{state}}.",{state:e.overall})))}function WE(e){var t=ee(),n=t.t,r=(t.i18n,la(ma));return"optimal"==e.placement||"n/a"==e.placement?null:e.placement?B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",className:e.className},B.createElement(RE.A,{className:r.warning,title:"Placement "+e.placement}))),B.createElement(qi,null,n("Placement is not optimal."))):null}function VE(e){var t=ee(),n=t.t;return t.i18n,0==e.provisioned?B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",className:e.className},B.createElement(Mv.A,{color:"error"}))),B.createElement(qi,null,n("Not provisioned. Orchestration is disabled."))):(e.provisioned,null)}function qE(e){var t=fe(ve(),2),n=t[0].cstat;return t[1],void 0===n.monitor?null:B.createElement(Ei,{dense:!0},B.createElement(zE,{avail:n.monitor.services[e.path].avail}),B.createElement($E,{path:e.path}),B.createElement(HE,{overall:n.monitor.services[e.path].overall}),B.createElement(WE,{placement:n.monitor.services[e.path].placement}),B.createElement(Dw,{frozen:n.monitor.services[e.path].frozen}),B.createElement(VE,{provisioned:n.monitor.services[e.path].provisioned}))}var KE=o(5932),GE=function(e){return{formcontrol:{margin:e.spacing(2,0)}}};function JE(e){var t=e.path,n=hn().auth,r=fe(ve(),2),o=r[0].cstat,i=(r[1],rp().dispatchAlerts);if(void 0===o.monitor)return null;if(void 0===o.monitor.services[t])return null;if(!("scale"in o.monitor.services[t]))return null;var a=fe(B.useState(!1),2),s=a[0],l=a[1],u=fe((0,B.useState)(o.monitor.services[t].scale),2),c=u[0],d=u[1],f=la(GE);function p(e){l(!1)}return B.createElement(B.Fragment,null,B.createElement(gi,{"aria-label":"Scale","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),l(!0)}},B.createElement(KE.A,null)),B.createElement(Ls,{open:s,onClose:p,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Scale ",t),B.createElement(qs,null,B.createElement(Ys,null,"Change the target number instances for this service."),B.createElement(Au,{className:f.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"Number of instances",id:"scale",value:c,onChange:function(e){e.target.value<0?d(0):d(e.target.value)},type:"number"}))),B.createElement(Us,null,B.createElement(cl,{onClick:p,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(){an("ANY",t,"scale",{to:c},(function(e){return i({data:e})}),n),p()},color:"secondary"},"Submit"))))}var YE=function(e){return{card:{height:"100%"}}};function XE(e){var t=lt(),n=it(),r=ee().t,o=e.path;return"/object"==n.pathname?null:B.createElement(bd,{title:r("Go to object")},B.createElement(gi,{"aria-label":"Go to object","aria-haspopup":!0,onClick:function(){t("/object?path="+o)}},B.createElement(Gb.A,null)))}function QE(e){var t=e.path,n=ee(),r=n.t,o=(n.i18n,fe(ve(),2)),i=o[0].cstat,a=(o[1],la(YE));return i.monitor?void 0===i.monitor.services[t]?null:B.createElement(mv,{className:a.card},B.createElement(xv,{title:r("Object"),subheader:t,action:B.createElement(B.Fragment,null,B.createElement(XE,{path:t}),B.createElement(JE,{path:t,title:""}),B.createElement(_E,{selected:[t],title:""}))}),B.createElement(Cv,null,B.createElement(qE,{path:t}))):null}var ZE=o(9118);function eS(e){return la(ma),"leader"!=e.placement?null:B.createElement(bd,{title:"Leader"},B.createElement(cr,{component:"span",color:"primary"},B.createElement(ZE.A,null)))}var tS=function(e){return{child:{marginRight:e.spacing(1)}}};function nS(e){var t=fe(ve(),2),n=t[0].cstat,r=(t[1],la(tS));return void 0===n.monitor?B.createElement(Lv,{container:!0,spacing:1},B.createElement(By,{className:r.child,state:"unkown"})):B.createElement(Lv,{container:!0,spacing:1},B.createElement(Hv,{className:r.child,avail:n.monitor.nodes[e.node].services.status[e.path].avail}),B.createElement(PE,{className:r.child,overall:n.monitor.nodes[e.node].services.status[e.path].overall}),B.createElement(Dy,{className:r.child,frozen:n.monitor.nodes[e.node].services.status[e.path].frozen}),B.createElement(FE,{className:r.child,provisioned:n.monitor.nodes[e.node].services.status[e.path].provisioned}),B.createElement(eS,{className:r.child,placement:n.monitor.nodes[e.node].services.status[e.path].monitor.placement}),B.createElement(By,{className:r.child,state:n.monitor.nodes[e.node].services.status[e.path].monitor.status}),B.createElement(Uy,{className:r.child,target:n.monitor.nodes[e.node].services.status[e.path].monitor.global_expect}))}var rS=o(7926),oS=o(4634),iS=o(4606);function aS(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return sS(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?sS(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function sS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function lS(e){var t=hn().auth,n=fe(ve(),2),r=n[0].cstat,o=(n[1],rp().dispatchAlerts),i=e.selected;if(!r.monitor)return null;if(void 0===i)return null;if(!i.length)return null;var a=kn(i[0].path);function s(e){var n,r=aS(i);try{for(r.s();!(n=r.n()).done;){var a=n.value;an(a.node,a.path,e.value,{},(function(e){return o({data:e})}),t)}}catch(e){r.e(e)}finally{r.f()}}function l(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if("down"!=o.avail)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}function u(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(!o.provisioned)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}return"svc"==a.kind||"vol"==a.kind?B.createElement(kd,{path:e.path,node:e.node,title:e.title,submit:s,fab:e.fab},B.createElement(_d,{name:"safe",color:"secondary",confirms:0},B.createElement(Cd,{value:"start",text:"Start",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if("up"!=o.avail)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(Nu.A,null)}),B.createElement(Cd,{value:"restart",text:"Restart",disabled:!1,requires:{role:"operator",namespace:a.namespace},icon:B.createElement(oS.A,null)}),B.createElement(Cd,{value:"thaw",text:"Thaw",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(o.frozen)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(ip.A,null)}),B.createElement(Cd,{value:"enable",text:"Enable",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(o.disable)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(ip.A,null)}),B.createElement(Cd,{value:"clear",text:"Clear",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(o.monitor.status&&o.monitor.status.match(/failed/))return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(rS.A,null)}),B.createElement(Cd,{value:"status",text:"Refresh",requires:{role:"operator",namespace:a.namespace},icon:B.createElement(Ty.A,null)}),B.createElement(Cd,{value:"sync all",text:"Sync All",disabled:l(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(iS.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"impacting",color:"warning",confirms:3},B.createElement(Cd,{value:"stop",text:"Stop",disabled:l(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(wE.A,null),confirmations:[kl]}),B.createElement(Cd,{value:"freeze",text:"Freeze",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(!o.frozen)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(op.A,null),confirmations:["I understand the selected instances will no longer be a failover candidate.","I understand the selected resources will no longer be monitored."]}),B.createElement(Cd,{value:"provision",text:"Provision",disabled:u(),requires:{role:"admin",namespace:a.namespace},icon:B.createElement(xE.A,null)}),B.createElement(Cd,{value:"disable",text:"Disable",disabled:u(),requires:{role:"operator",namespace:a.namespace},icon:B.createElement(op.A,null),confirmations:["I understand the selected instances will no longer report any status, but the resources will stay in they current state."]})),B.createElement(xd,null),B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"purge",text:"Purge",requires:{role:"admin",namespace:a.namespace},icon:B.createElement(uE.A,null),confirmations:[Al,xl,kl]}),B.createElement(Cd,{value:"delete",text:"Delete",requires:{role:"admin",namespace:a.namespace},icon:B.createElement(ta.A,null),confirmations:[xl]}),B.createElement(Cd,{value:"unprovision",text:"Unprovision",disabled:function(){var e,t=aS(i);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[n.node].services.status[n.path];if(!o)return!0;if(o.provisioned)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"admin",namespace:a.namespace},icon:B.createElement(EE.A,null),confirmations:[Al,kl]}))):"ccfg"==a.kind?null:"cfg"==a.kind||"sec"==a.kind||"usr"==a.kind?B.createElement(kd,{path:e.path,node:e.node,title:e.title,submit:s,fab:e.fab},B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"delete",text:"Delete",requires:{role:"admin",namespace:a.namespace},icon:B.createElement(ta.A,null),confirmations:[xl]}))):void 0}function uS(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return cS(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?cS(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function cS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function dS(e,t){var n=0;for(var r in t.monitor.nodes){var o=t.monitor.nodes[r].services.status[e];o&&o.scaler_slave&&"up"===o.avail&&(n+=1)}return n}const fS={ObjInstanceCounts:function(e){var t=fe(ve(),2),n=t[0].cstat;if(t[1],void 0===n.monitor)return null;var r=0,o=0,i=kn(e.path);for(var a in n.monitor.nodes){var s=n.monitor.nodes[a];if(!(0,Dv.isEmpty)(s)){var l=void 0;try{l=s.services.status[e.path]}catch(e){continue}if(l&&!l.scaler_slave){if("scale"in l){o=l.scale;var u,c=uS(l.scaler_slaves);try{for(c.s();!(u=c.n()).done;){var d=u.value;r+=dS(d=_n(d,i.namespace,i.kind),n)}}catch(e){c.e(e)}finally{c.f()}return B.createElement("span",null,r,"/",o)}"failover"===l.topology?o=1:"flex"===l.topology&&(o=l.flex_target),"up"===l.avail&&(r+=1)}}}return 0===o?B.createElement("span",null):B.createElement(cr,{component:"span"},r,"/",o)}};function pS(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return hS(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?hS(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function hS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var mS=function(e){return{tableWrapper:{overflowX:"auto"},card:{height:"100%"},content:{margin:-e.spacing(2)},row:{"&:hover":{cursor:"pointer"}}}};function gS(e,t,n){var r=[],o=kn(e);if(void 0===t.monitor)return r;if(n)var i=o.name.replace(/\..*$/,"");else i=null;var a,s=pS(t.cluster.nodes);try{for(s.s();!(a=s.n()).done;){var l=a.value;try{var u=t.monitor.nodes[l].services.status[e]}catch(e){continue}if(u)if("scaler_slaves"in u){var c=[],d=RegExp("^"+_n("[0-9]+."+o.name,o.namespace,o.kind)+"$");for(var e in t.monitor.services)e.match(d)&&c.push(e);var f,p=pS(c.sort());try{for(p.s();!(f=p.n()).done;){var h=f.value;r=r.concat(gS(h,t,!0))}}catch(e){p.e(e)}finally{p.f()}}else r.push({node:l,path:e,slice:i,instance:u})}}catch(e){s.e(e)}finally{s.f()}return r}function vS(e){var t=la(mS),n=bn().cstat,r=fe(B.useState([]),2),o=r[0],i=r[1],a=ee(),s=a.t;if(a.i18n,void 0===n.monitor)return null;if(void 0===n.monitor.services[e.path])return null;var l=gS(e.path,n),u=l.length;if("scale"in n.monitor.services[e.path])var c=B.createElement(py,null,"Slice");return B.createElement(mv,{className:t.card},B.createElement(xv,{title:s("Instances"),subheaderTypographyProps:B.createElement(fS,{path:e.path}),action:B.createElement(Ly,{selected:o},o.length>0&&B.createElement(lS,{selected:o,title:""}))}),B.createElement(Cv,{className:t.content},B.createElement("div",{className:t.tableWrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,{padding:"checkbox"},B.createElement(yu,{indeterminate:o.length>0&&o.length<u,checked:o.length===u,onChange:function(e){e.target.checked?i(l):i([])},inputProps:{"aria-label":"Select all"}})),c,B.createElement(py,null,s("Node")),B.createElement(py,null,s("State")))),B.createElement(ly,null,l.map((function(t,n){return B.createElement(yS,{key:n,index:n,path:e.path,instance:t,selected:o,setSelected:i})})))))))}function yS(e){var t=it(),n=la(mS),r=bn().cstat,o=e.index,i=e.path,a=e.instance,s=e.selected,l=e.setSelected,u=lt();if(void 0===r.monitor)return null;if(!a)return null;var c=s.some((function(e){return e.path==a.path&&e.node==a.node})),d="nodes-checkbox-".concat(o);return B.createElement(ky,{onClick:function(e){u({pathname:"/instance",search:"?path="+a.path+"&node="+a.node,state:t.state})},className:n.row},B.createElement(py,{padding:"checkbox",onClick:function(e){e.stopPropagation();for(var t={node:a.node,path:a.path},n=-1,r=0;r<s.length;r++){var o=s[r];if(o.path==a.path&&o.node==a.node){n=r;break}}var i=[];-1===n?i=i.concat(s,t):0===n?i=i.concat(s.slice(1)):n===s.length-1?i=i.concat(s.slice(0,-1)):n>0&&(i=i.concat(s.slice(0,n),s.slice(n+1))),l(i)}},B.createElement(yu,{checked:c,inputProps:{"aria-labelledby":d}})),null!==a.slice&&B.createElement(py,null,a.slice),B.createElement(py,null,a.node),B.createElement(py,null,B.createElement(nS,{node:a.node,path:i})))}function bS(e){var t=fe((0,B.useState)(null),2),n=t[0],r=t[1],o=bn().cstat,i=hn().auth,a=Fn(o,e);return(0,B.useEffect)((function(){a&&function(e,t,n){var r={Accept:"application/json","Content-Type":"application/json","o-node":"ANY"},o={headers:r=rn(r,n),method:"GET"},i=tn("/object_config",e);fetch(i,o).then((function(e){return e.json()})).then((function(e){var n=null;for(var r in e.nodes){n=e.nodes[r];break}t&&t(n)})).catch(console.log)}({path:e},(function(t){console.log("load new",e,"config, csum",a),r(t)}),i)}),[a]),n}function wS(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function ES(e){var t=hn().auth,n=fe(ve(),2),r=(n[0].cstat,n[1],rp().dispatchAlerts),o=e.path,i=e.selected,a=e.title,s=e.fab,l=kn(o);return B.createElement(kd,{path:o,title:a,submit:function(e){if("delete"==e.value){var n,a=[],s=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return wS(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?wS(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(i);try{for(s.s();!(n=s.n()).done;){var l=n.value;a.push("data."+l)}}catch(e){s.e(e)}finally{s.f()}an("ANY",o,"unset",{kw:a},(function(e){return r({data:e})}),t)}},fab:s},B.createElement(_d,{name:"dangerous",color:"danger",confirms:6},B.createElement(Cd,{value:"delete",text:"Delete",requires:{role:"admin",namespace:l.namespace},icon:B.createElement(ta.A,null)})))}function SS(e){var t=e.path,n=e.keyName,r=fe((0,B.useState)(null),2),o=r[0],i=r[1],a=hn().auth;return(0,B.useEffect)((function(){null===o&&dn("/key",{path:t,key:n},(function(e){i(e.data)}),a)}),[]),[o,i]}function xS(e){var t=function(e){return SS(e)[0]}({path:e.path,keyName:e.keyName});return B.createElement(cr,{component:"div",color:"textSecondary"},B.createElement("pre",null,t))}function AS(e){var t=e.path,n=e.keyName,r=fe(B.useState(!1),2),o=r[0],i=r[1];function a(e){i(!1)}return B.createElement(B.Fragment,null,B.createElement(cl,{onClick:function(e){e.stopPropagation(),i(!0)}},"Decode"),B.createElement(Ls,{open:o,onClose:a,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},n),B.createElement(qs,null,B.createElement(xS,{path:t,keyName:n})),B.createElement(Us,null,B.createElement(cl,{onClick:a,color:"primary"},"Dismiss"))))}var kS=function(e){return{formcontrol:{margin:e.spacing(2,0)},textarea:{fontFamily:"monospace",width:"100%",whiteSpace:"nowrap",overflow:"auto !important"}}};function _S(e){var t=e.value,n=e.setValue,r=la(kS);return B.createElement(jp,{className:r.textarea,rowsMax:30,rowsMin:15,id:"data",onChange:function(e){n(e.target.value),console.log(e)},value:t})}function CS(e){var t=e.path,n=e.keyName,r=fe((0,B.useState)(!1),2),o=r[0],i=r[1];return B.createElement(B.Fragment,null,B.createElement(cl,{onClick:function(e){e.stopPropagation(),i(!0)},color:"secondary"},"Edit"),o&&B.createElement(OS,{path:t,keyName:n,open:o,handleClose:function(e){i(!1)}}))}function OS(e){var t=e.path,n=e.keyName,r=e.open,o=e.handleClose,i=hn().auth,a=fe(SS({path:t,keyName:n}),2),s=a[0],l=a[1],u=fe((0,B.useState)(""),2),c=u[0],d=u[1],f=fe((0,B.useState)(""),2),p=f[0],h=f[1],m=rp().dispatchAlerts,g=la(kS),v="User Input",y="Local File",b="Remote Location",w=fe((0,B.useState)(v),2),E=w[0],S=w[1];return B.createElement(Ls,{open:r,onClose:o,"aria-labelledby":"form-dialog-title",maxWidth:"md",fullWidth:!0},B.createElement(Zs,{id:"form-dialog-title"},n),B.createElement(qs,null,B.createElement(_S,{value:s,setValue:l}),E==b&&B.createElement(Au,{className:g.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"Remote Location",id:"url",type:"url",value:c,onChange:function(e){return d(e.target.value)}})),E==y&&B.createElement(Au,{className:g.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"File",id:"file",type:"file",onChange:function(e){return h(e.target.files)}}))),B.createElement(Us,null,B.createElement(cl,{onClick:function(e){E==b?fetch(c).then((function(e){return e.text()})).then((function(e){l(e),S(v)})):S(b)},color:E==b?"secondary":"primary"},"Load URL"),B.createElement(cl,{onClick:function(e){if(E==y){console.log("fileValue",p);var t=new File(p,"foo",{type:"text/plain"}),n=new FileReader;n.onload=function(){l(n.result),S(v)},n.readAsText(t)}else S(y)},color:E==y?"secondary":"primary"},"Load File"),B.createElement(cl,{onClick:o,color:"primary"},"Dismiss"),B.createElement(cl,{onClick:function(e){cn("/key",{path:t,key:n,data:s},(function(e){console.log(e),m({data:e})}),i)},color:"secondary"},"Save")))}var PS=function(e){return{formcontrol:{margin:e.spacing(2,0)},textarea:{fontFamily:"monospace",width:"100%",whiteSpace:"nowrap",overflow:"auto !important"}}};function RS(e){var t=e.path,n=kn(t);if(["cfg","sec","usr"].indexOf(n.kind)<0)return null;var r=hn().auth,o=fe(B.useState(!1),2),i=o[0],a=o[1],s="User Input",l="Local File",u="Remote Location",c=fe((0,B.useState)(s),2),d=c[0],f=c[1],p=fe((0,B.useState)(""),2),h=p[0],m=p[1],g=fe((0,B.useState)(""),2),v=g[0],y=g[1],b=fe((0,B.useState)(""),2),w=b[0],E=b[1],S=fe((0,B.useState)(""),2),x=S[0],A=S[1],k=la(PS);function _(e){a(!1)}return B.createElement(B.Fragment,null,B.createElement(gi,{"aria-label":"Add Key","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),a(!0)}},B.createElement(lv.A,null)),B.createElement(Ls,{open:i,onClose:_,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Add Key"),B.createElement(qs,null,B.createElement(Ys,null,"The key name can be a path expression. When using the key in a volume projection, the key name is the path of the file relative to the volume root, and the content is the key value."),B.createElement(Au,{className:k.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"Key Name",id:"name",value:x,onChange:function(e){return A(e.target.value)}})),B.createElement(Au,{className:k.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Key Value"),B.createElement(jp,{className:k.textarea,rowsMax:25,id:"value",onChange:function(e){return m(e.target.value)},value:h})),d==u&&B.createElement(Au,{className:k.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"Remote Location",id:"url",type:"url",value:v,onChange:function(e){return y(e.target.value)}})),d==l&&B.createElement(Au,{className:k.formcontrol,fullWidth:!0},B.createElement(Bm,{label:"File",id:"file",type:"file",onChange:function(e){return E(e.target.files)}}))),B.createElement(Us,null,B.createElement(cl,{onClick:function(e){d==u?fetch(v).then((function(e){return e.text()})).then((function(e){m(e),f(s)})):f(u)},color:d==u?"secondary":"primary"},"Load URL"),B.createElement(cl,{onClick:function(e){if(d==l){console.log("fileValue",w);var t=new File(w,"foo",{type:"text/plain"}),n=new FileReader;n.onload=function(){m(n.result),f(s)},n.readAsText(t)}else f(l)},color:d==l?"secondary":"primary"},"Load File"),B.createElement(cl,{onClick:_,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(e){cn("/key",{path:t,key:x,data:h},(function(e){console.log(e)}),r),_()},color:"secondary"},"Submit"))))}var TS=function(e){return{tableWrapper:{overflowX:"auto"},wrapper:{marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function FS(e){var t=la(TS),n=ee(),r=n.t,o=(n.i18n,fe(B.useState([]),2)),i=o[0],a=o[1],s=bS(e.path);if(!s||!s.data)return null;var l=Cn(s.data);void 0===l.data&&(l.data={});var u=Object.keys(l.data).length;return B.createElement(mv,null,B.createElement(xv,{title:r("Keys"),subheader:e.path,action:B.createElement(RS,{path:e.path})}),B.createElement(Cv,null,B.createElement("div",{className:t.wrapper},B.createElement(Ly,{selected:i},i.length>0?B.createElement(ES,{path:e.path,selected:i,title:""}):B.createElement(bd,{title:"Filter list"},B.createElement(gi,{"aria-label":"Filter list"},B.createElement(cb.A,null)))),B.createElement("div",{className:t.tableWrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,null,B.createElement(py,{padding:"checkbox"},B.createElement(yu,{indeterminate:i.length>0&&i.length<u,checked:i.length===u,onChange:function(e){if(e.target.checked){var t=Object.keys(l.data);a(t)}else a([])},inputProps:{"aria-label":"Select all"}})),B.createElement(py,null,r("Key")),B.createElement(py,null,"Type"),B.createElement(py,null,r("Value")),B.createElement(py,null))),B.createElement(ly,null,Object.keys(l.data).sort().map((function(t,n){return B.createElement(jS,{key:t,index:n,path:e.path,keyValue:l.data[t],keyName:t,selected:i,setSelected:a})}))))))))}function jS(e){var t=e.index,n=e.path,r=e.keyName,o=e.keyValue,i=e.selected,a=e.setSelected,s=o.indexOf(":"),l=o.slice(0,s);if("literal"==l)var u=B.createElement(B.Fragment,null,o.slice(s+1));else u=B.createElement(AS,{path:n,keyName:r});var c=i.indexOf(r)>-1,d="nodes-checkbox-".concat(t);return B.createElement(ky,null,B.createElement(py,{padding:"checkbox",onClick:function(e){e.stopPropagation();var t=i.indexOf(r),n=[];-1===t?n=n.concat(i,r):0===t?n=n.concat(i.slice(1)):t===i.length-1?n=n.concat(i.slice(0,-1)):t>0&&(n=n.concat(i.slice(0,t),i.slice(t+1))),a(n)}},B.createElement(yu,{checked:c,inputProps:{"aria-labelledby":d}})),B.createElement(py,null,r),B.createElement(py,null,B.createElement("span",{className:"badge badge-secondary mr-2"},l)),B.createElement(py,null,u),B.createElement(py,null,B.createElement(CS,{path:n,keyName:r})))}function NS(e){var t=fe((0,B.useState)(null),2),n=t[0],r=t[1],o=hn().auth;return(0,B.useEffect)((function(){console.log(e,"keywords:",n),null===n&&dn("/keywords",{kind:e},(function(e){r(e)}),o)}),[]),n}const IS=function(e){var t=e.val,n=e.setVal,r=e.defaultValue,o=e.keyword,i=e.requiredError,a=r||"",s=o||"nodeSelector",l=void 0!==i&&i;return B.createElement(Bm,{autoComplete:"off",placeholder:a,id:s,error:l,type:"search",onChange:function(e){n(e.target.value)},value:t})},LS=function(e){var t=e.setVal,n=e.val,r=e.requiredError;return B.createElement(Bm,{value:n,error:function(e){var t,n=["","k","m","g","t","p","e","b","kb","mb","gb","tb","pb","eb","ki","mi","gi","ti","pi","ei","kib","mib","gib","tib","pib","eib"];if(void 0===e||""==e||null==e)return r;var o=e.split(/([0-9\.]+)/);return 3!=o.length||""!=o[0].trim()||(t=""==o[2]?n[0]:o[2].trim(),!(n.indexOf(t)>-1))}(n),onChange:function(e){t(e.target.value)}})},MS=function(e){var t=e.setVal,n=e.val,r=(e.requiredError,fe((0,B.useState)(n),2)),o=r[0],i=r[1];return B.createElement(Bm,{multiline:!0,rows:1,rowsMax:12,value:o,error:function(){try{return JSON.parse(o),!1}catch(e){return!0}}(),onChange:function(e){i(e.target.value);try{var n=JSON.parse(e.target.value);t(n)}catch(e){}}})};function DS(e){return(0,rr.A)("MuiSwitch",e)}const BS=(0,nr.A)("MuiSwitch",["root","edgeStart","edgeEnd","switchBase","colorPrimary","colorSecondary","sizeSmall","sizeMedium","checked","disabled","input","thumb","track"]),US=["className","color","edge","size","sx"],zS=(0,Zn.Ay)("span",{name:"MuiSwitch",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,n.edge&&t[`edge${(0,tr.A)(n.edge)}`],t[`size${(0,tr.A)(n.size)}`]]}})((({ownerState:e})=>(0,Jt.A)({display:"inline-flex",width:58,height:38,overflow:"hidden",padding:12,boxSizing:"border-box",position:"relative",flexShrink:0,zIndex:0,verticalAlign:"middle","@media print":{colorAdjust:"exact"}},"start"===e.edge&&{marginLeft:-8},"end"===e.edge&&{marginRight:-8},"small"===e.size&&{width:40,height:24,padding:7,[`& .${BS.thumb}`]:{width:16,height:16},[`& .${BS.switchBase}`]:{padding:4,[`&.${BS.checked}`]:{transform:"translateX(16px)"}}}))),$S=(0,Zn.Ay)(su,{name:"MuiSwitch",slot:"SwitchBase",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.switchBase,{[`& .${BS.input}`]:t.input},"default"!==n.color&&t[`color${(0,tr.A)(n.color)}`]]}})((({theme:e})=>({position:"absolute",top:0,left:0,zIndex:1,color:e.vars?e.vars.palette.Switch.defaultColor:`${"light"===e.palette.mode?e.palette.common.white:e.palette.grey[300]}`,transition:e.transitions.create(["left","transform"],{duration:e.transitions.duration.shortest}),[`&.${BS.checked}`]:{transform:"translateX(20px)"},[`&.${BS.disabled}`]:{color:e.vars?e.vars.palette.Switch.defaultDisabledColor:`${"light"===e.palette.mode?e.palette.grey[100]:e.palette.grey[600]}`},[`&.${BS.checked} + .${BS.track}`]:{opacity:.5},[`&.${BS.disabled} + .${BS.track}`]:{opacity:e.vars?e.vars.opacity.switchTrackDisabled:""+("light"===e.palette.mode?.12:.2)},[`& .${BS.input}`]:{left:"-100%",width:"300%"}})),(({theme:e,ownerState:t})=>(0,Jt.A)({"&:hover":{backgroundColor:e.vars?`rgba(${e.vars.palette.action.activeChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette.action.active,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}}},"default"!==t.color&&{[`&.${BS.checked}`]:{color:(e.vars||e).palette[t.color].main,"&:hover":{backgroundColor:e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / ${e.vars.palette.action.hoverOpacity})`:(0,vo.X4)(e.palette[t.color].main,e.palette.action.hoverOpacity),"@media (hover: none)":{backgroundColor:"transparent"}},[`&.${BS.disabled}`]:{color:e.vars?e.vars.palette.Switch[`${t.color}DisabledColor`]:`${"light"===e.palette.mode?(0,vo.a)(e.palette[t.color].main,.62):(0,vo.e$)(e.palette[t.color].main,.55)}`}},[`&.${BS.checked} + .${BS.track}`]:{backgroundColor:(e.vars||e).palette[t.color].main}}))),HS=(0,Zn.Ay)("span",{name:"MuiSwitch",slot:"Track",overridesResolver:(e,t)=>t.track})((({theme:e})=>({height:"100%",width:"100%",borderRadius:7,zIndex:-1,transition:e.transitions.create(["opacity","background-color"],{duration:e.transitions.duration.shortest}),backgroundColor:e.vars?e.vars.palette.common.onBackground:`${"light"===e.palette.mode?e.palette.common.black:e.palette.common.white}`,opacity:e.vars?e.vars.opacity.switchTrack:""+("light"===e.palette.mode?.38:.3)}))),WS=(0,Zn.Ay)("span",{name:"MuiSwitch",slot:"Thumb",overridesResolver:(e,t)=>t.thumb})((({theme:e})=>({boxShadow:(e.vars||e).shadows[1],backgroundColor:"currentColor",width:20,height:20,borderRadius:"50%"}))),VS=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiSwitch"}),{className:r,color:o="primary",edge:i=!1,size:a="medium",sx:s}=n,l=(0,qn.A)(n,US),u=(0,Jt.A)({},n,{color:o,edge:i,size:a}),c=(e=>{const{classes:t,edge:n,size:r,color:o,checked:i,disabled:a}=e,s={root:["root",n&&`edge${(0,tr.A)(n)}`,`size${(0,tr.A)(r)}`],switchBase:["switchBase",`color${(0,tr.A)(o)}`,i&&"checked",a&&"disabled"],thumb:["thumb"],track:["track"],input:["input"]},l=(0,Qn.A)(s,DS,t);return(0,Jt.A)({},t,l)})(u),d=(0,ir.jsx)(WS,{className:c.thumb,ownerState:u});return(0,ir.jsxs)(zS,{className:(0,Kn.A)(c.root,r),sx:s,ownerState:u,children:[(0,ir.jsx)($S,(0,Jt.A)({type:"checkbox",icon:d,checkedIcon:d,ref:t,ownerState:u},l,{classes:(0,Jt.A)({},c,{root:c.switchBase})})),(0,ir.jsx)(HS,{className:c.track,ownerState:u})]})}));var qS=o(2501),KS=o(7151);const GS={border:0,clip:"rect(0 0 0 0)",height:"1px",margin:-1,overflow:"hidden",padding:0,position:"absolute",whiteSpace:"nowrap",width:"1px"};function JS(e,t){return e-t}function YS(e,t,n){return null==e?t:Math.min(Math.max(t,e),n)}function XS(e,t){var n;const{index:r}=null!=(n=e.reduce(((e,n,r)=>{const o=Math.abs(t-n);return null===e||o<e.distance||o===e.distance?{distance:o,index:r}:e}),null))?n:{};return r}function QS(e,t){if(void 0!==t.current&&e.changedTouches){const n=e;for(let e=0;e<n.changedTouches.length;e+=1){const r=n.changedTouches[e];if(r.identifier===t.current)return{x:r.clientX,y:r.clientY}}return!1}return{x:e.clientX,y:e.clientY}}function ZS(e,t,n){return 100*(e-t)/(n-t)}function ex({values:e,newValue:t,index:n}){const r=e.slice();return r[n]=t,r.sort(JS)}function tx({sliderRef:e,activeIndex:t,setActive:n}){var r,o;const i=(0,yr.A)(e.current);var a;null!=(r=e.current)&&r.contains(i.activeElement)&&Number(null==i||null==(o=i.activeElement)?void 0:o.getAttribute("data-index"))===t||null==(a=e.current)||a.querySelector(`[type="range"][data-index="${t}"]`).focus(),n&&n(t)}function nx(e,t){return"number"==typeof e&&"number"==typeof t?e===t:"object"==typeof e&&"object"==typeof t&&function(e,t,n=(e,t)=>e===t){return e.length===t.length&&e.every(((e,r)=>n(e,t[r])))}(e,t)}const rx={horizontal:{offset:e=>({left:`${e}%`}),leap:e=>({width:`${e}%`})},"horizontal-reverse":{offset:e=>({right:`${e}%`}),leap:e=>({width:`${e}%`})},vertical:{offset:e=>({bottom:`${e}%`}),leap:e=>({height:`${e}%`})}},ox=e=>e;let ix;function ax(){return void 0===ix&&(ix="undefined"==typeof CSS||"function"!=typeof CSS.supports||CSS.supports("touch-action","none")),ix}function sx(e){return(0,rr.A)("MuiSlider",e)}const lx=(0,nr.A)("MuiSlider",["root","active","colorPrimary","colorSecondary","colorError","colorInfo","colorSuccess","colorWarning","disabled","dragging","focusVisible","mark","markActive","marked","markLabel","markLabelActive","rail","sizeSmall","thumb","thumbColorPrimary","thumbColorSecondary","thumbColorError","thumbColorSuccess","thumbColorInfo","thumbColorWarning","track","trackInverted","trackFalse","thumbSizeSmall","valueLabel","valueLabelOpen","valueLabelCircle","valueLabelLabel","vertical"]),ux=["aria-label","aria-valuetext","aria-labelledby","component","components","componentsProps","color","classes","className","disableSwap","disabled","getAriaLabel","getAriaValueText","marks","max","min","name","onChange","onChangeCommitted","orientation","size","step","scale","slotProps","slots","tabIndex","track","value","valueLabelDisplay","valueLabelFormat"];function cx(e){return e}const dx=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`color${(0,tr.A)(n.color)}`],"medium"!==n.size&&t[`size${(0,tr.A)(n.size)}`],n.marked&&t.marked,"vertical"===n.orientation&&t.vertical,"inverted"===n.track&&t.trackInverted,!1===n.track&&t.trackFalse]}})((({theme:e,ownerState:t})=>(0,Jt.A)({borderRadius:12,boxSizing:"content-box",display:"inline-block",position:"relative",cursor:"pointer",touchAction:"none",color:(e.vars||e).palette[t.color].main,WebkitTapHighlightColor:"transparent"},"horizontal"===t.orientation&&(0,Jt.A)({height:4,width:"100%",padding:"13px 0","@media (pointer: coarse)":{padding:"20px 0"}},"small"===t.size&&{height:2},t.marked&&{marginBottom:20}),"vertical"===t.orientation&&(0,Jt.A)({height:"100%",width:4,padding:"0 13px","@media (pointer: coarse)":{padding:"0 20px"}},"small"===t.size&&{width:2},t.marked&&{marginRight:44}),{"@media print":{colorAdjust:"exact"},[`&.${lx.disabled}`]:{pointerEvents:"none",cursor:"default",color:(e.vars||e).palette.grey[400]},[`&.${lx.dragging}`]:{[`& .${lx.thumb}, & .${lx.track}`]:{transition:"none"}}}))),fx=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"Rail",overridesResolver:(e,t)=>t.rail})((({ownerState:e})=>(0,Jt.A)({display:"block",position:"absolute",borderRadius:"inherit",backgroundColor:"currentColor",opacity:.38},"horizontal"===e.orientation&&{width:"100%",height:"inherit",top:"50%",transform:"translateY(-50%)"},"vertical"===e.orientation&&{height:"100%",width:"inherit",left:"50%",transform:"translateX(-50%)"},"inverted"===e.track&&{opacity:1}))),px=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"Track",overridesResolver:(e,t)=>t.track})((({theme:e,ownerState:t})=>{const n="light"===e.palette.mode?(0,vo.a)(e.palette[t.color].main,.62):(0,vo.e$)(e.palette[t.color].main,.5);return(0,Jt.A)({display:"block",position:"absolute",borderRadius:"inherit",border:"1px solid currentColor",backgroundColor:"currentColor",transition:e.transitions.create(["left","width","bottom","height"],{duration:e.transitions.duration.shortest})},"small"===t.size&&{border:"none"},"horizontal"===t.orientation&&{height:"inherit",top:"50%",transform:"translateY(-50%)"},"vertical"===t.orientation&&{width:"inherit",left:"50%",transform:"translateX(-50%)"},!1===t.track&&{display:"none"},"inverted"===t.track&&{backgroundColor:e.vars?e.vars.palette.Slider[`${t.color}Track`]:n,borderColor:e.vars?e.vars.palette.Slider[`${t.color}Track`]:n})})),hx=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"Thumb",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.thumb,t[`thumbColor${(0,tr.A)(n.color)}`],"medium"!==n.size&&t[`thumbSize${(0,tr.A)(n.size)}`]]}})((({theme:e,ownerState:t})=>(0,Jt.A)({position:"absolute",width:20,height:20,boxSizing:"border-box",borderRadius:"50%",outline:0,backgroundColor:"currentColor",display:"flex",alignItems:"center",justifyContent:"center",transition:e.transitions.create(["box-shadow","left","bottom"],{duration:e.transitions.duration.shortest})},"small"===t.size&&{width:12,height:12},"horizontal"===t.orientation&&{top:"50%",transform:"translate(-50%, -50%)"},"vertical"===t.orientation&&{left:"50%",transform:"translate(-50%, 50%)"},{"&::before":(0,Jt.A)({position:"absolute",content:'""',borderRadius:"inherit",width:"100%",height:"100%",boxShadow:(e.vars||e).shadows[2]},"small"===t.size&&{boxShadow:"none"}),"&::after":{position:"absolute",content:'""',borderRadius:"50%",width:42,height:42,top:"50%",left:"50%",transform:"translate(-50%, -50%)"},[`&:hover, &.${lx.focusVisible}`]:{boxShadow:`0px 0px 0px 8px ${e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / 0.16)`:(0,vo.X4)(e.palette[t.color].main,.16)}`,"@media (hover: none)":{boxShadow:"none"}},[`&.${lx.active}`]:{boxShadow:`0px 0px 0px 14px ${e.vars?`rgba(${e.vars.palette[t.color].mainChannel} / 0.16)`:(0,vo.X4)(e.palette[t.color].main,.16)}`},[`&.${lx.disabled}`]:{"&:hover":{boxShadow:"none"}}}))),mx=(0,Zn.Ay)((function(e){const{children:t,className:n,value:r}=e,o=(e=>{const{open:t}=e;return{offset:(0,Kn.A)(t&&lx.valueLabelOpen),circle:lx.valueLabelCircle,label:lx.valueLabelLabel}})(e);return t?B.cloneElement(t,{className:(0,Kn.A)(t.props.className)},(0,ir.jsxs)(B.Fragment,{children:[t.props.children,(0,ir.jsx)("span",{className:(0,Kn.A)(o.offset,n),"aria-hidden":!0,children:(0,ir.jsx)("span",{className:o.circle,children:(0,ir.jsx)("span",{className:o.label,children:r})})})]})):null}),{name:"MuiSlider",slot:"ValueLabel",overridesResolver:(e,t)=>t.valueLabel})((({theme:e,ownerState:t})=>(0,Jt.A)({[`&.${lx.valueLabelOpen}`]:{transform:("vertical"===t.orientation?"translateY(-50%)":"translateY(-100%)")+" scale(1)"},zIndex:1,whiteSpace:"nowrap"},e.typography.body2,{fontWeight:500,transition:e.transitions.create(["transform"],{duration:e.transitions.duration.shortest}),transform:("vertical"===t.orientation?"translateY(-50%)":"translateY(-100%)")+" scale(0)",position:"absolute",backgroundColor:(e.vars||e).palette.grey[600],borderRadius:2,color:(e.vars||e).palette.common.white,display:"flex",alignItems:"center",justifyContent:"center",padding:"0.25rem 0.75rem"},"horizontal"===t.orientation&&{top:"-10px",transformOrigin:"bottom center","&::before":{position:"absolute",content:'""',width:8,height:8,transform:"translate(-50%, 50%) rotate(45deg)",backgroundColor:"inherit",bottom:0,left:"50%"}},"vertical"===t.orientation&&{right:"small"===t.size?"20px":"30px",top:"50%",transformOrigin:"right center","&::before":{position:"absolute",content:'""',width:8,height:8,transform:"translate(-50%, -50%) rotate(45deg)",backgroundColor:"inherit",right:-8,top:"50%"}},"small"===t.size&&{fontSize:e.typography.pxToRem(12),padding:"0.25rem 0.5rem"}))),gx=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"Mark",shouldForwardProp:e=>(0,Zn._n)(e)&&"markActive"!==e,overridesResolver:(e,t)=>{const{markActive:n}=e;return[t.mark,n&&t.markActive]}})((({theme:e,ownerState:t,markActive:n})=>(0,Jt.A)({position:"absolute",width:2,height:2,borderRadius:1,backgroundColor:"currentColor"},"horizontal"===t.orientation&&{top:"50%",transform:"translate(-1px, -50%)"},"vertical"===t.orientation&&{left:"50%",transform:"translate(-50%, 1px)"},n&&{backgroundColor:(e.vars||e).palette.background.paper,opacity:.8}))),vx=(0,Zn.Ay)("span",{name:"MuiSlider",slot:"MarkLabel",shouldForwardProp:e=>(0,Zn._n)(e)&&"markLabelActive"!==e,overridesResolver:(e,t)=>t.markLabel})((({theme:e,ownerState:t,markLabelActive:n})=>(0,Jt.A)({},e.typography.body2,{color:(e.vars||e).palette.text.secondary,position:"absolute",whiteSpace:"nowrap"},"horizontal"===t.orientation&&{top:30,transform:"translateX(-50%)","@media (pointer: coarse)":{top:40}},"vertical"===t.orientation&&{left:36,transform:"translateY(50%)","@media (pointer: coarse)":{left:44}},n&&{color:(e.vars||e).palette.text.primary}))),yx=({children:e})=>e,bx=B.forwardRef((function(e,t){var n,r,o,i,a,s,l,u,c,d,f,p,h,m,g,v,y,b,w,E,S,x,A,k;const _=(0,er.A)({props:e,name:"MuiSlider"}),C="rtl"===Jr().direction,{"aria-label":O,"aria-valuetext":P,"aria-labelledby":R,component:T="span",components:F={},componentsProps:j={},color:N="primary",classes:I,className:L,disableSwap:M=!1,disabled:D=!1,getAriaLabel:U,getAriaValueText:z,marks:$=!1,max:H=100,min:W=0,orientation:V="horizontal",size:q="medium",step:K=1,scale:G=cx,slotProps:J,slots:Y,track:X="normal",valueLabelDisplay:Q="off",valueLabelFormat:Z=cx}=_,ee=(0,qn.A)(_,ux),te=(0,Jt.A)({},_,{isRtl:C,max:H,min:W,classes:I,disabled:D,disableSwap:M,orientation:V,marks:$,color:N,size:q,step:K,scale:G,track:X,valueLabelDisplay:Q,valueLabelFormat:Z}),{axisProps:ne,getRootProps:re,getHiddenInputProps:oe,getThumbProps:ie,open:ae,active:se,axis:le,focusedThumbIndex:ue,range:ce,dragging:de,marks:fe,values:pe,trackOffset:he,trackLeap:me,getThumbStyle:ge}=function(e){const{"aria-labelledby":t,defaultValue:n,disabled:r=!1,disableSwap:o=!1,isRtl:i=!1,marks:a=!1,max:s=100,min:l=0,name:u,onChange:c,onChangeCommitted:d,orientation:f="horizontal",rootRef:p,scale:h=ox,step:m=1,tabIndex:g,value:v}=e,y=B.useRef(),[b,w]=B.useState(-1),[E,S]=B.useState(-1),[x,A]=B.useState(!1),k=B.useRef(0),[_,C]=(0,qS.A)({controlled:v,default:null!=n?n:l,name:"Slider"}),O=c&&((e,t,n)=>{const r=e.nativeEvent||e,o=new r.constructor(r.type,r);Object.defineProperty(o,"target",{writable:!0,value:{value:t,name:u}}),c(o,t,n)}),P=Array.isArray(_);let R=P?_.slice().sort(JS):[_];R=R.map((e=>YS(e,l,s)));const T=!0===a&&null!==m?[...Array(Math.floor((s-l)/m)+1)].map(((e,t)=>({value:l+m*t}))):a||[],F=T.map((e=>e.value)),{isFocusVisibleRef:j,onBlur:N,onFocus:I,ref:L}=(0,KS.A)(),[M,D]=B.useState(-1),U=B.useRef(),z=(0,dr.A)(L,U),$=(0,dr.A)(p,z),H=e=>t=>{var n;const r=Number(t.currentTarget.getAttribute("data-index"));I(t),!0===j.current&&D(r),S(r),null==e||null==(n=e.onFocus)||n.call(e,t)},W=e=>t=>{var n;N(t),!1===j.current&&D(-1),S(-1),null==e||null==(n=e.onBlur)||n.call(e,t)};(0,jr.A)((()=>{var e;r&&U.current.contains(document.activeElement)&&(null==(e=document.activeElement)||e.blur())}),[r]),r&&-1!==b&&w(-1),r&&-1!==M&&D(-1);const V=B.useRef();let q=f;i&&"horizontal"===f&&(q+="-reverse");const K=({finger:e,move:t=!1})=>{const{current:n}=U,{width:r,height:i,bottom:a,left:u}=n.getBoundingClientRect();let c,d;if(c=0===q.indexOf("vertical")?(a-e.y)/i:(e.x-u)/r,-1!==q.indexOf("-reverse")&&(c=1-c),d=function(e,t,n){return(n-t)*e+t}(c,l,s),m)d=function(e,t,n){const r=Math.round((e-n)/t)*t+n;return Number(r.toFixed(function(e){if(Math.abs(e)<1){const t=e.toExponential().split("e-"),n=t[0].split(".")[1];return(n?n.length:0)+parseInt(t[1],10)}const t=e.toString().split(".")[1];return t?t.length:0}(t)))}(d,m,l);else{const e=XS(F,d);d=F[e]}d=YS(d,l,s);let f=0;if(P){f=t?V.current:XS(R,d),o&&(d=YS(d,R[f-1]||-1/0,R[f+1]||1/0));const e=d;d=ex({values:R,newValue:d,index:f}),o&&t||(f=d.indexOf(e),V.current=f)}return{newValue:d,activeIndex:f}},G=(0,br.A)((e=>{const t=QS(e,y);if(!t)return;if(k.current+=1,"mousemove"===e.type&&0===e.buttons)return void J(e);const{newValue:n,activeIndex:r}=K({finger:t,move:!0});tx({sliderRef:U,activeIndex:r,setActive:w}),C(n),!x&&k.current>2&&A(!0),O&&!nx(n,_)&&O(e,n,r)})),J=(0,br.A)((e=>{const t=QS(e,y);if(A(!1),!t)return;const{newValue:n}=K({finger:t,move:!0});w(-1),"touchend"===e.type&&S(-1),d&&d(e,n),y.current=void 0,X()})),Y=(0,br.A)((e=>{if(r)return;ax()||e.preventDefault();const t=e.changedTouches[0];null!=t&&(y.current=t.identifier);const n=QS(e,y);if(!1!==n){const{newValue:t,activeIndex:r}=K({finger:n});tx({sliderRef:U,activeIndex:r,setActive:w}),C(t),O&&!nx(t,_)&&O(e,t,r)}k.current=0;const o=(0,yr.A)(U.current);o.addEventListener("touchmove",G,{passive:!0}),o.addEventListener("touchend",J,{passive:!0})})),X=B.useCallback((()=>{const e=(0,yr.A)(U.current);e.removeEventListener("mousemove",G),e.removeEventListener("mouseup",J),e.removeEventListener("touchmove",G),e.removeEventListener("touchend",J)}),[J,G]);B.useEffect((()=>{const{current:e}=U;return e.addEventListener("touchstart",Y,{passive:ax()}),()=>{e.removeEventListener("touchstart",Y),X()}}),[X,Y]),B.useEffect((()=>{r&&X()}),[r,X]);const Q=ZS(P?R[0]:l,l,s),Z=ZS(R[R.length-1],l,s)-Q,ee=e=>t=>{var n;null==(n=e.onMouseLeave)||n.call(e,t),S(-1)};return{active:b,axis:q,axisProps:rx,dragging:x,focusedThumbIndex:M,getHiddenInputProps:(n={})=>{var a;const c=hr(n),p={onChange:(v=c||{},e=>{var t;null==(t=v.onChange)||t.call(v,e);const n=Number(e.currentTarget.getAttribute("data-index")),r=R[n],i=F.indexOf(r);let a=e.target.valueAsNumber;if(T&&null==m){const e=F[F.length-1];a=a>e?e:a<F[0]?F[0]:a<r?F[i-1]:F[i+1]}if(a=YS(a,l,s),P){o&&(a=YS(a,R[n-1]||-1/0,R[n+1]||1/0));const e=a;a=ex({values:R,newValue:a,index:n});let t=n;o||(t=a.indexOf(e)),tx({sliderRef:U,activeIndex:t})}C(a),D(n),O&&!nx(a,_)&&O(e,a,n),d&&d(e,a)}),onFocus:H(c||{}),onBlur:W(c||{})};var v;const y=(0,Jt.A)({},c,p);return(0,Jt.A)({tabIndex:g,"aria-labelledby":t,"aria-orientation":f,"aria-valuemax":h(s),"aria-valuemin":h(l),name:u,type:"range",min:e.min,max:e.max,step:null===e.step&&e.marks?"any":null!=(a=e.step)?a:void 0,disabled:r},n,y,{style:(0,Jt.A)({},GS,{direction:i?"rtl":"ltr",width:"100%",height:"100%"})})},getRootProps:(e={})=>{const t=hr(e),n={onMouseDown:(o=t||{},e=>{var t;if(null==(t=o.onMouseDown)||t.call(o,e),r)return;if(e.defaultPrevented)return;if(0!==e.button)return;e.preventDefault();const n=QS(e,y);if(!1!==n){const{newValue:t,activeIndex:r}=K({finger:n});tx({sliderRef:U,activeIndex:r,setActive:w}),C(t),O&&!nx(t,_)&&O(e,t,r)}k.current=0;const i=(0,yr.A)(U.current);i.addEventListener("mousemove",G,{passive:!0}),i.addEventListener("mouseup",J)})};var o;const i=(0,Jt.A)({},t,n);return(0,Jt.A)({},e,{ref:$},i)},getThumbProps:(e={})=>{const t=hr(e),n={onMouseOver:(r=t||{},e=>{var t;null==(t=r.onMouseOver)||t.call(r,e);const n=Number(e.currentTarget.getAttribute("data-index"));S(n)}),onMouseLeave:ee(t||{})};var r;return(0,Jt.A)({},e,t,n)},marks:T,open:E,range:P,rootRef:$,trackLeap:Z,trackOffset:Q,values:R,getThumbStyle:e=>({pointerEvents:-1!==b&&b!==e?"none":void 0})}}((0,Jt.A)({},te,{rootRef:t}));te.marked=fe.length>0&&fe.some((e=>e.label)),te.dragging=de,te.focusedThumbIndex=ue;const ve=(e=>{const{disabled:t,dragging:n,marked:r,orientation:o,track:i,classes:a,color:s,size:l}=e,u={root:["root",t&&"disabled",n&&"dragging",r&&"marked","vertical"===o&&"vertical","inverted"===i&&"trackInverted",!1===i&&"trackFalse",s&&`color${(0,tr.A)(s)}`,l&&`size${(0,tr.A)(l)}`],rail:["rail"],track:["track"],mark:["mark"],markActive:["markActive"],markLabel:["markLabel"],markLabelActive:["markLabelActive"],valueLabel:["valueLabel"],thumb:["thumb",t&&"disabled",l&&`thumbSize${(0,tr.A)(l)}`,s&&`thumbColor${(0,tr.A)(s)}`],active:["active"],disabled:["disabled"],focusVisible:["focusVisible"]};return(0,Qn.A)(u,sx,a)})(te),ye=null!=(n=null!=(r=null==Y?void 0:Y.root)?r:F.Root)?n:dx,be=null!=(o=null!=(i=null==Y?void 0:Y.rail)?i:F.Rail)?o:fx,we=null!=(a=null!=(s=null==Y?void 0:Y.track)?s:F.Track)?a:px,Ee=null!=(l=null!=(u=null==Y?void 0:Y.thumb)?u:F.Thumb)?l:hx,Se=null!=(c=null!=(d=null==Y?void 0:Y.valueLabel)?d:F.ValueLabel)?c:mx,xe=null!=(f=null!=(p=null==Y?void 0:Y.mark)?p:F.Mark)?f:gx,Ae=null!=(h=null!=(m=null==Y?void 0:Y.markLabel)?m:F.MarkLabel)?h:vx,ke=null!=(g=null!=(v=null==Y?void 0:Y.input)?v:F.Input)?g:"input",_e=null!=(y=null==J?void 0:J.root)?y:j.root,Ce=null!=(b=null==J?void 0:J.rail)?b:j.rail,Oe=null!=(w=null==J?void 0:J.track)?w:j.track,Pe=null!=(E=null==J?void 0:J.thumb)?E:j.thumb,Re=null!=(S=null==J?void 0:J.valueLabel)?S:j.valueLabel,Te=null!=(x=null==J?void 0:J.mark)?x:j.mark,Fe=null!=(A=null==J?void 0:J.markLabel)?A:j.markLabel,je=null!=(k=null==J?void 0:J.input)?k:j.input,Ne=vr({elementType:ye,getSlotProps:re,externalSlotProps:_e,externalForwardedProps:ee,additionalProps:(0,Jt.A)({},($e=ye,(!$e||!fr($e))&&{as:T})),ownerState:(0,Jt.A)({},te,null==_e?void 0:_e.ownerState),className:[ve.root,L]}),Ie=vr({elementType:be,externalSlotProps:Ce,ownerState:te,className:ve.rail}),Le=vr({elementType:we,externalSlotProps:Oe,additionalProps:{style:(0,Jt.A)({},ne[le].offset(he),ne[le].leap(me))},ownerState:(0,Jt.A)({},te,null==Oe?void 0:Oe.ownerState),className:ve.track}),Me=vr({elementType:Ee,getSlotProps:ie,externalSlotProps:Pe,ownerState:(0,Jt.A)({},te,null==Pe?void 0:Pe.ownerState),className:ve.thumb}),De=vr({elementType:Se,externalSlotProps:Re,ownerState:(0,Jt.A)({},te,null==Re?void 0:Re.ownerState),className:ve.valueLabel}),Be=vr({elementType:xe,externalSlotProps:Te,ownerState:te,className:ve.mark}),Ue=vr({elementType:Ae,externalSlotProps:Fe,ownerState:te,className:ve.markLabel}),ze=vr({elementType:ke,getSlotProps:oe,externalSlotProps:je,ownerState:te});var $e;return(0,ir.jsxs)(ye,(0,Jt.A)({},Ne,{children:[(0,ir.jsx)(be,(0,Jt.A)({},Ie)),(0,ir.jsx)(we,(0,Jt.A)({},Le)),fe.filter((e=>e.value>=W&&e.value<=H)).map(((e,t)=>{const n=ZS(e.value,W,H),r=ne[le].offset(n);let o;return o=!1===X?-1!==pe.indexOf(e.value):"normal"===X&&(ce?e.value>=pe[0]&&e.value<=pe[pe.length-1]:e.value<=pe[0])||"inverted"===X&&(ce?e.value<=pe[0]||e.value>=pe[pe.length-1]:e.value>=pe[0]),(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)(xe,(0,Jt.A)({"data-index":t},Be,!fr(xe)&&{markActive:o},{style:(0,Jt.A)({},r,Be.style),className:(0,Kn.A)(Be.className,o&&ve.markActive)})),null!=e.label?(0,ir.jsx)(Ae,(0,Jt.A)({"aria-hidden":!0,"data-index":t},Ue,!fr(Ae)&&{markLabelActive:o},{style:(0,Jt.A)({},r,Ue.style),className:(0,Kn.A)(ve.markLabel,Ue.className,o&&ve.markLabelActive),children:e.label})):null]},t)})),pe.map(((e,t)=>{const n=ZS(e,W,H),r=ne[le].offset(n),o="off"===Q?yx:Se;return(0,ir.jsx)(o,(0,Jt.A)({},!fr(o)&&{valueLabelFormat:Z,valueLabelDisplay:Q,value:"function"==typeof Z?Z(G(e),t):Z,index:t,open:ae===t||se===t||"on"===Q,disabled:D},De,{children:(0,ir.jsx)(Ee,(0,Jt.A)({"data-index":t},Me,{className:(0,Kn.A)(ve.thumb,Me.className,se===t&&ve.active,ue===t&&ve.focusVisible),style:(0,Jt.A)({},r,ge(t),Me.style),children:(0,ir.jsx)(ke,(0,Jt.A)({"data-index":t,"aria-label":U?U(t):O,"aria-valuenow":G(e),"aria-labelledby":R,"aria-valuetext":z?z(G(e),t):P,value:pe[t]},ze))}))}),t)}))]}))}));function wx(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Ex(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?wx(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):wx(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function Sx(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var xx=function(e){return{formcontrol:{margin:e.spacing(2,0)},kw:{fontFamily:"monospace",color:e.palette.primary.main},cmd:{fontFamily:"monospace",color:e.palette.primary.main},code:{fontFamily:"monospace",fontWeight:"bold"},helper:{wordBreak:"break-word"},convert:{marginLeft:"auto"},expand:{transform:"rotate(0deg)",marginLeft:"auto",transition:e.transitions.create("transform",{duration:e.transitions.duration.shortest})},expandOpen:{transform:"rotate(180deg)"}}};function Ax(e){var t,n,r=e.kind,o=e.kws,i=e.data,a=e.setData,s=e.optionalTitle,l=e.requiredTitle,u=e.optionalExpanded,c=la(xx),d=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Sx(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Sx(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(o);try{for(d.s();!(n=d.n()).done;){var f=n.value;"type"==f.keyword&&(t=f)}}catch(e){d.e(e)}finally{d.f()}return B.createElement(B.Fragment,null,["DEFAULT","data","env"].indexOf(r)<0&&B.createElement(Au,{className:c.formcontrol,fullWidth:!0},B.createElement(Bm,{label:r+" Name",id:"sectionName",value:i.sectionName?i.sectionName:"",onChange:function(e){return a(Ex(Ex({},i),{},{sectionName:e.target.value}))}})),t&&B.createElement(Au,{className:c.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Type"),B.createElement(Nm,{value:i.type?i.type:"",onChange:function(e){return a(Ex(Ex({},i),{},{type:e.target.value}))},inputProps:{id:"source"}},t.candidates.map((function(e,t){return B.createElement(Wm,{key:t,value:e},e)})))),B.createElement(kx,{title:l,kws:o,data:i,setData:a,typeKw:t}),B.createElement(_x,{title:s,kws:o,data:i,setData:a,typeKw:t,optionalExpanded:u}))}function kx(e){var t=ee(),n=t.t,r=(t.i18n,e.title),o=e.kws,i=e.data,a=e.setData;if(e.typeKw&&!i.type)return null;var s=o.filter((function(e){return e.required})),l=r||n("Required");return 0==s.length?null:B.createElement(B.Fragment,null,B.createElement(cr,{component:"p",variant:"h5"},l," (",s.length,")"),s.map((function(e,t){return B.createElement(Cx,{key:t,kwData:e,data:i,setData:a})})))}function _x(e){var t=e.title,n=e.kws,r=e.data,o=e.setData,i=e.typeKw,a=e.optionalExpanded,s=ee(),l=s.t,u=(s.i18n,fe((0,B.useState)(void 0!==a&&a),2)),c=u[0],d=u[1],f=la(xx);if(i&&!r.type)return null;var p=t||l("Optional"),h=n.filter((function(e){return!e.required}));return 0==h.length?null:B.createElement(B.Fragment,null,B.createElement(Lv,{container:!0},B.createElement(Lv,{item:!0},B.createElement(cr,{component:"p",variant:"h5"},p," (",h.length,")")),B.createElement(Lv,{item:!0,className:f.convert},B.createElement(gi,{className:(0,Kn.A)(f.expand,ue({},f.expandOpen,c)),onClick:function(e){d(!c)},"aria-expanded":c,"aria-label":"show more"},B.createElement(vw.A,null)))),B.createElement(gw,{in:c,timeout:"auto",unmountOnExit:!0},h.map((function(e,t){return B.createElement(Cx,{key:t,kwData:e,data:r,setData:o})}))))}function Cx(e){var t=e.kwData,n=e.data,r=e.setData,o=la(xx);if("type"==t.keyword)return null;if(t.type&&t.type.indexOf(n.type)<0)return null;var i,a,s,l,u=t.required&&!n;if("boolean"==t.convert)var c=B.createElement(VS,{checked:void 0!==n[t.keyword]?(s=n[t.keyword],l=+s,isNaN(l)?!!String(s).toLowerCase().replace(!1,""):!!l):!!t.default&&t.default,onChange:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e.target.checked)))},value:t.keyword,color:"primary",inputProps:{"aria-label":"primary checkbox"}});else if("dict"==t.convert)c=B.createElement(MS,{setVal:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e)))},val:n[t.keyword]});else if("node_selector"==t.convert)c=B.createElement(IS,{setVal:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e)))},val:n[t.keyword],defaultValue:t.default?t.default:"",keyword:t.keyword,requiredError:u});else if("size"==t.convert)c=B.createElement(LS,{setVal:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e)))},val:n[t.keyword]});else if("tristate"==t.convert){var d=function(e){var t=f.findIndex((function(t){return t.value===e}));return t<0?f[1]:f[t]},f=[{value:0,label:"False",realValue:!1},{value:1,label:"Ignore",realValue:null},{value:2,label:"True",realValue:!0}];c=B.createElement(bx,{defaultValue:(i=t.default,a=f.findIndex((function(e){return e.realValue===i})),a<0?f[1]:f[a]).value,valueLabelFormat:function(e){return d(e).label},"aria-labelledby":"discrete-slider-restrict",step:1,min:0,max:2,valueLabelDisplay:"off",marks:f,onChange:function(e,o){return r(Ex(Ex({},n),{},ue({},t.keyword,d(o).realValue)))}})}else c=t.candidates?B.createElement(Nm,{inputProps:{id:t.keyword},value:n[t.keyword]?n[t.keyword]:t.default?t.default:"",onChange:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e.target.value)))},error:u},t.candidates.map((function(e,t){return B.createElement(Wm,{key:t,value:e},e||"None")}))):B.createElement(Bm,{autoComplete:"off",placeholder:t.default?t.default.toString():"",id:t.keyword,value:n[t.keyword]?n[t.keyword]:t.default?t.default:"",onChange:function(e){return r(Ex(Ex({},n),{},ue({},t.keyword,e.target.value)))},error:u,type:"integer"==t.convert?"number":"text"});return B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"h6"},B.createElement(Lv,{container:!0},B.createElement(Lv,{item:!0},t.keyword,t.required&&B.createElement(cr,{variant:"h6",component:"span",color:"primary"},"Â *")),B.createElement(Lv,{item:!0,className:o.convert},B.createElement(ps,{size:"small",label:t.convert})))),c,B.createElement(jh,{className:o.helper},function(e){var t=la(xx),n=RegExp(":cmd:`(.*?)`|:kw:`(.*?)`|:opt:`(.*?)`|:c-.*:`(.*?)`|``(.*?)``");if(void 0===e)return e;for(var r=e.split(n),o=1;o<r.length;o+=4)r[o]?r[o]=B.createElement("span",{className:t.cmd,key:o},r[o]):r[o+1]?r[o+1]=B.createElement("span",{className:t.kw,key:o+1},r[o+1]):r[o+2]&&(r[o+2]=B.createElement("span",{className:t.code,key:o+2},r[o+2]));return B.createElement(B.Fragment,null,r)}(t.text)),u&&B.createElement(jh,{className:o.helper},"This keyword is required."))}function Ox(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Px(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?Ox(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):Ox(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var Rx=function(e){return{root:{padding:e.spacing(3,2),marginTop:e.spacing(3)},tabContent:{paddingTop:e.spacing(2)},fab:{marginTop:e.spacing(2)},formcontrol:{margin:e.spacing(2,0)}}};function Tx(e){var t=e.path,n=e.data,r=e.setData,o=la(Rx),i=NS(kn(t).kind);if(!i)return B.createElement(Ib,null);var a=Object.keys(i);return B.createElement(B.Fragment,null,B.createElement(Au,{className:o.formcontrol,fullWidth:!0},B.createElement(cr,{variant:"caption",color:"textSecondary"},"Resource Kind"),B.createElement(Nm,{value:n.kind?n.kind:"",onChange:function(e){return r(Px(Px({},n),{},{kind:e.target.value}))},inputProps:{id:"resourceKind"}},a.map((function(e,t){return B.createElement(Wm,{key:t,value:e},e)})))),n.kind&&B.createElement(Ax,{kind:n.kind,kws:i[n.kind],data:n.keywords,setData:function(e){r(Px(Px({},n),{},{keywords:e}))}}))}function Fx(e){var t=kn(e.path),n=hn().auth,r=ee().t,o=fe(ve(),2),i=o[0].user,a=(o[1],rp().dispatchAlerts),s=fe((0,B.useState)(!1),2),l=s[0],u=s[1],c=fe((0,B.useState)({kind:"",keywords:{}}),2),d=c[0],f=c[1];if(["vol","svc"].indexOf(t.kind)<0)return null;if(!i.grant)return null;if(!("root"in i.grant)&&i.grant.admin.indexOf(t.namespace)<0)return null;function p(e){u(!1)}return B.createElement(B.Fragment,null,B.createElement(bd,{title:r("Add Resource")},B.createElement(gi,{"aria-label":"Add Resource","aria-haspopup":"true",onClick:function(e){e.stopPropagation(),u(!0)}},B.createElement(lv.A,null))),B.createElement(Ls,{open:l,onClose:p,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Add Resource to ",e.path),B.createElement(qs,null,B.createElement(Tx,{path:e.path,data:d,setData:f})),B.createElement(Us,null,B.createElement(cl,{onClick:p,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(){var t=[];if(d.keywords.sectionName)var r=d.kind+"#"+d.keywords.sectionName;else r=d.kind;for(var o in d.keywords)"sectionName"!=o&&d.keywords[o]&&t.push(r+"."+o+"="+d.keywords[o]);var i={kw:t},s="Resource "+d.keywords.sectionName+" added.";an("ANY",e.path,"set",i,(function(e){return a({ok:s,data:e})}),n),f({kind:"",keywords:{}}),p()},color:"secondary"},"Submit"))))}var jx=function(e){return{icon:{marginLeft:e.spacing(1)}}};function Nx(e){var t=e.path,n=e.rid,r=e.conf,o=kn(t);if(["vol","svc"].indexOf(o.kind)<0)return null;var i=hn().auth,a=fe((0,B.useState)(!1),2),s=a[0],l=a[1],u=la(jx),c=rp().dispatchAlerts,d=fe((0,B.useState)(null),2),f=d[0],p=d[1],h=r[n],m=n.split("#")[0],g=n.split("#")[1];if(!h)return null;if(h.sectionName=g,!f)return p({kind:m,keywords:h}),null;function v(e){p(!1),l(!1)}return B.createElement(B.Fragment,null,B.createElement(aE.A,{color:"action",className:u.icon,onClick:function(e){e.stopPropagation(),p(!1),l(!0)}}),B.createElement(Ls,{open:s,onClose:v,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Edit Resource ",n," of ",t),B.createElement(qs,null,B.createElement(Tx,{path:t,data:f,setData:p})),B.createElement(Us,null,B.createElement(cl,{onClick:v,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(){var e=[];if(["DEFAULT","env","data"].indexOf(f.kind)<0)var n=f.kind+"#"+f.keywords.sectionName;else n=f.kind;for(var r in f.keywords)"sectionName"!=r&&void 0!==f.keywords[r]&&e.push(n+"."+r+"="+f.keywords[r]);var o={kw:e},a="Resource "+f.keywords.sectionName+" added.";an("ANY",t,"set",o,(function(e){return c({ok:a,data:e})}),i),v()},color:"secondary"},"Submit"))))}function Ix(e){var t=e.path,n=e.rid,r=kn(t),o=rp().dispatchAlerts;if(["vol","svc"].indexOf(r.kind)<0)return null;var i=hn().auth,a=fe((0,B.useState)(!1),2),s=a[0],l=a[1],u=ee(),c=u.t,d=(u.i18n,fe(ve(),2));function f(e){l(!1)}return ne(d[0]),d[1],B.createElement(B.Fragment,null,B.createElement(uE.A,{color:"action",onClick:function(e){e.stopPropagation(),l(!0)}}),B.createElement(Ls,{open:s,onClose:f,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},c("Delete section {{rid}} of {{path}}",{rid:n,path:t})),B.createElement(qs,null,c("delete_resource")),B.createElement(Us,null,B.createElement(cl,{onClick:f,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(){var e="Resource "+n+" deleted.";an("ANY",t,"delete",{rid:n},(function(t){return o({ok:e,data:t})}),i),f()},color:"secondary"},"Submit"))))}var Lx=function(e){return{card:{height:"100%"},expand:{transform:"rotate(0deg)",marginLeft:"auto",transition:e.transitions.create("transform",{duration:e.transitions.duration.shortest})},expandOpen:{transform:"rotate(180deg)"}}};function Mx(e){var t=kn(e.path),n=fe(ve(),2),r=n[0].user,o=(n[1],ee().t);return r.grant?!("root"in r.grant)&&r.grant.admin.indexOf(t.namespace)<0?null:B.createElement(bd,{title:o("Edit")},B.createElement(gi,{"aria-label":"Edit Labels","aria-haspopup":!0,onClick:e.toggle},B.createElement(aE.A,null))):null}function Dx(e){var t=e.path,n=e.expanded,r=e.setExpanded,o=fe(ve(),2),i=o[0].user,a=(o[1],ee().t),s=la(Lx),l=kn(t);return i.grant?!("root"in i.grant)&&i.grant.admin.indexOf(l.namespace)<0?null:B.createElement(bd,{title:a("Raw")},B.createElement(gi,{className:(0,Kn.A)(s.expand,ue({},s.expandOpen,n)),onClick:function(e){r(!n)},"aria-expanded":n,"aria-label":"show more"},B.createElement(vw.A,null))):null}function Bx(e){var t=e.path,n=e.edit,r=fe(ve(),2),o=r[0],i=o.cstat,a=o.user,s=(r[1],bS(t));if(void 0===i.monitor)return null;if(void 0===a.grant)return null;if(!s)return null;var l=Cn(s.data),u=null;for(var c in i.monitor.nodes)if(u=i.monitor.nodes[c].services.status[t])break;if(!u)return null;var d=[];for(var f in l){try{var p=u.resources[f].label}catch(e){p=""}d.push({section:f,label:p})}return B.createElement(Ei,{dense:!0},d.map((function(e,r){return B.createElement(Li,{key:r},B.createElement(qi,{primary:e.section,secondary:e.label,secondaryTypographyProps:{component:"div"}}),B.createElement(Ti,null,n&&B.createElement(Ix,{path:t,rid:e.section}),n&&B.createElement(Nx,{path:t,rid:e.section,conf:l})))})))}function Ux(e){var t=bS(e.path);if(t){var n=new Date(1e3*t.mtime);r=B.createElement(B.Fragment,null,B.createElement(cr,{variant:"caption",color:"textSecondary"},"Last Modified ",n.toLocaleString()),B.createElement("pre",{style:{overflowX:"auto"}},t.data))}else var r=B.createElement(Ib,null);return B.createElement(B.Fragment,null,r)}const zx=function(e){var t=e.path,n=la(Lx),r=ee(),o=r.t,i=(r.i18n,fe((0,B.useState)(!1),2)),a=i[0],s=i[1],l=fe((0,B.useState)(!1),2),u=l[0],c=l[1];return B.createElement(mv,{className:n.card},B.createElement(xv,{title:o("Configuration"),subheader:t,action:B.createElement(B.Fragment,null,B.createElement(Mx,{path:t,toggle:function(){c(!u)}}),B.createElement(Fx,{path:t}))}),B.createElement(Cv,null,B.createElement(Bx,{path:t,edit:u})),B.createElement(lw,{disableSpacing:!0},B.createElement(Dx,{path:t,expanded:a,setExpanded:s})),B.createElement(gw,{in:a,timeout:"auto",unmountOnExit:!0},B.createElement(Cv,null,B.createElement(Ux,{path:t}))))};var $x=function(e){return{root:{flewGrow:1},section:{padding:e.spacing(1)},tabContent:{paddingTop:e.spacing(2)},tabSection:{marginBottom:e.spacing(3)}}};function Hx(e){var t=it(),n=new URLSearchParams(t.search).get("path"),r=la($x),o=fe((0,B.useState)(),2),i=(o[0],o[1],fe((0,B.useState)(0),2)),a=i[0],s=(i[1],fe(ve(),2));return s[0].user,s[1],B.createElement(Lv,{container:!0,className:r.root},B.createElement(Wx,{active:a,path:n}),B.createElement(Lv,{item:!0,xs:12,lg:6,className:r.section},B.createElement(zx,{active:a,path:n})),B.createElement(Lv,{item:!0,xs:12,lg:6,className:r.section},B.createElement(Gx,{active:a,path:n})))}function Wx(e){var t=kn(e.path);return"svc"==t.kind||"vol"==t.kind?B.createElement(Kx,{path:e.path}):"cfg"==t.kind||"sec"==t.kind||"ccfg"==t.kind?B.createElement(Vx,{path:e.path}):"usr"==t.kind||"ccfg"==t.kind?B.createElement(qx,{path:e.path}):null}function Vx(e){var t=la($x);return kn(e.path),B.createElement(Lv,{item:!0,xs:12,className:t.section},B.createElement(FS,{path:e.path}))}function qx(e){var t=la($x);return kn(e.path),B.createElement(Lv,{item:!0,xs:12,className:t.section},B.createElement(FS,{path:e.path}))}function Kx(e){kn(e.path);var t=fe(ve(),2),n=t[0].cstat,r=(t[1],la($x));return void 0===n.monitor||void 0===n.monitor.services[e.path]?null:("scale"in n.monitor.services[e.path]||n.monitor.services[e.path].scaler_slave,B.createElement(B.Fragment,null,B.createElement(Lv,{item:!0,xs:12,md:6,className:r.section},B.createElement(QE,{path:e.path})),B.createElement(Lv,{item:!0,xs:12,md:6,className:r.section},B.createElement(vS,{path:e.path}))))}function Gx(e){var t=ee(),n=t.t;return t.i18n,B.createElement(Qb,{title:n("Log"),subheader:e.path,url:"/object/"+e.path,hide:["o"],initialContext:{sc:{value:"n"}}})}function Jx(e){la(ma);var t=ee(),n=t.t;return t.i18n,"leader"!=e.placement?null:B.createElement(Li,null,B.createElement(zi,null,B.createElement(cr,{component:"span",color:"primary"},B.createElement(ZE.A,null))),B.createElement(qi,null,n("Placement leader.")))}function Yx(e){var t=e.path,n=e.node,r=fe(ve(),2),o=r[0].cstat;if(r[1],void 0===o.monitor)return null;var i=o.monitor.nodes[n];if(void 0===i)return null;var a=i.services.status[t];return void 0===a?null:B.createElement(Ei,{dense:!0},B.createElement(zE,{avail:a.avail}),B.createElement(Bw,{state:a.monitor.status}),B.createElement(zw,{target:a.monitor.global_expect}),B.createElement(HE,{overall:a.overall}),B.createElement(Jx,{placement:a.monitor.placement}),B.createElement(Dw,{frozen:a.frozen}),B.createElement(VE,{provisioned:a.provisioned}))}var Xx=function(e){return{card:{height:"100%"}}};function Qx(e){var t=ee(),n=t.t,r=(t.i18n,fe(ve(),2)),o=(r[0].cstat,r[1],la(Xx)),i=e.path+"@"+e.node;return B.createElement(mv,{className:o.card},B.createElement(xv,{title:n("Instance"),subheader:i,action:B.createElement(lS,{selected:[{node:e.node,path:e.path}],title:""})}),B.createElement(Cv,null,B.createElement(Yx,{node:e.node,path:e.path})))}var Zx=function(e){return{flags:{fontFamily:"monospace",whiteSpace:"pre"}}};function eA(e){return B.createElement(bd,{title:e.title},B.createElement("span",null,e.value))}const tA=function(e){var t,n=e.rid,r=e.data,o=e.idata,i=ee().t,a=la(Zx);r.restart&&(t=r.restart-function(e,t){if(void 0===t)return 0;if(!(e in t))return 0;var n=t[e].retries;return void 0===n?t[e]:n}(n,o.monitor.restart))<0&&(t=0);var s=null;return r.provisioned&&(s=r.provisioned.state),B.createElement("div",{className:a.flags},r.running?B.createElement(eA,{value:"R",title:i("Running")}):B.createElement(eA,{value:".",title:i("Idle")}),r.monitor?B.createElement(eA,{value:"M",title:i("Monitored")}):B.createElement(eA,{value:".",title:i("Not Monitored")}),r.disable?B.createElement(eA,{value:"D",title:i("Disabled")}):B.createElement(eA,{value:".",title:i("Enabled")}),r.optional?B.createElement(eA,{value:"O",title:i("Optional")}):B.createElement(eA,{value:".",title:i("Mandatory")}),r.encap?B.createElement(eA,{value:"E",title:i("Encapsulated")}):B.createElement(eA,{value:".",title:i("Global")}),s?B.createElement(eA,{value:".",title:i("Provisioned")}):null==s?B.createElement(eA,{value:"/",title:i("Provisioned State Not Applicable")}):B.createElement(eA,{value:"P",title:i("Not Provisioned")}),r.standby?B.createElement(eA,{value:"S",title:i("Standby")}):B.createElement(eA,{value:".",title:i("Not Standby")}),r.restart?t<10?B.createElement(eA,{value:t,title:i("Number of Restarts Left")}):B.createElement(eA,{value:"+",title:i("{{count}} Restarts Left",{count:t})}):B.createElement(eA,{value:".",title:i("No Restart")}))};function nA(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return rA(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?rA(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function rA(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function oA(e){var t=hn().auth,n=fe(ve(),2),r=n[0].cstat,o=(n[1],rp().dispatchAlerts),i=kn(e.path),a=e.node,s=e.path,l=e.rids,u=e.title,c=e.fab,d=ee().t,f=function(e){var t=fe((0,B.useState)([]),2),n=t[0],r=t[1],o=hn().auth,i=Fn(bn().cstat,e);return(0,B.useEffect)((function(){i&&(n.length>0?console.log("useObjConfirmations, already loaded"):e?dn("/object_confirmations",{path:e},(function(e){console.log("useObjConfirmations, set data",e.data),r(e.data)}),o):console.log("useObjConfirmations, no path"))}),[i]),n}(e.path);function p(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[a].services.status[s].resources[n];if(!o.provisioned||!o.provisioned.state)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}return B.createElement(kd,{rid:l,path:s,node:a,title:u,submit:function(e){var n={rid:l.join(",")};"run"==e.value&&(n.confirm=!0),an(a,s,e.value,n,(function(e){return o({data:e})}),t)},fab:c},B.createElement(_d,{name:"safe",color:"secondary",confirms:0},B.createElement(Cd,{value:"start",text:"Start",disabled:function(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[a].services.status[s].resources[n];if("n/a"!=o.status&&"up"!=o.status)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:i.namespace},icon:B.createElement(Nu.A,null)}),B.createElement(Cd,{value:"restart",text:"Restart",disabled:!1,requires:{role:"operator",namespace:i.namespace},icon:B.createElement(oS.A,null)}),B.createElement(Cd,{value:"run",text:"Run",disabled:function(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;)if(e.value.match(/^task#/))return!1}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:i.namespace},icon:B.createElement(Uw.A,null),confirmations:function(){var e,t=[],n=nA(f);try{for(n.s();!(e=n.n()).done;){var r=e.value;l.indexOf(r)<0||t.push(d("I confirm {{rid}} action",{rid:r}))}}catch(e){n.e(e)}finally{n.f()}return t}()}),B.createElement(Cd,{value:"enable",text:"Enable",disabled:function(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;){var n=e.value;if(r.monitor.nodes[a].services.status[s].resources[n].disable)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:i.namespace},icon:B.createElement(ip.A,null)})),B.createElement(xd,null),B.createElement(_d,{name:"impacting",color:"warning",confirms:3},B.createElement(Cd,{value:"stop",text:"Stop",disabled:function(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[a].services.status[s].resources[n];if("n/a"!=o.status&&"stdby down"!=o.status&&"down"!=o.status)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"operator",namespace:i.namespace},icon:B.createElement(wE.A,null),confirmations:[kl]}),B.createElement(Cd,{value:"provision",text:"Provision",disabled:p(),requires:{role:"admin",namespace:i.namespace},icon:B.createElement(xE.A,null)}),B.createElement(Cd,{value:"disable",text:"Disable",disabled:p(),requires:{role:"operator",namespace:i.namespace},icon:B.createElement(op.A,null),confirmations:["I understand the selected resources will no longer report any status, but they will stay in they current state."]})),B.createElement(xd,null),B.createElement(_d,{name:"dangerous",color:"danger"},B.createElement(Cd,{value:"delete",text:"Delete",requires:{role:"admin",namespace:i.namespace},icon:B.createElement(ta.A,null),confirmations:[xl]}),B.createElement(Cd,{value:"unprovision",text:"Unprovision",disabled:function(){var e,t=nA(l);try{for(t.s();!(e=t.n()).done;){var n=e.value,o=r.monitor.nodes[a].services.status[s].resources[n];if(o.provisioned&&o.provisioned.state)return!1}}catch(e){t.e(e)}finally{t.f()}return!0}(),requires:{role:"admin",namespace:i.namespace},icon:B.createElement(EE.A,null),confirmations:[Al]})))}var iA=o(9592),aA=function(e){return{tableWrapper:{overflowX:"auto"},iconText:{display:"flex"},card:{height:"100%"},content:{margin:-e.spacing(2)},grow:{flexGrow:"1"}}};function sA(e){var t=e.path,n=e.rid,r=hn().auth,o=ee().t;return n.match(/^container#/)?B.createElement(bd,{title:o("Enter container")},B.createElement(gi,{onClick:function(e){dn("/object_enter",{path:t,rid:n},(function(e){window.open(e.data.url,"_blank")}),r)}},B.createElement(iA.A,null))):null}function lA(e){var t=bn().cstat,n=ee(),r=n.t,o=(n.i18n,la(aA)),i=fe(B.useState([]),2),a=i[0],s=i[1];if(void 0===t.monitor)return null;var l=kn(e.path);try{var u=t.monitor.nodes[e.node].services.status[e.path].resources}catch(e){u={}}var c=Object.keys(u).length;return B.createElement(mv,{className:o.card},B.createElement(xv,{title:r("Resources"),subheader:e.path+"@"+e.node,action:B.createElement(Ly,{selected:a},a.length>0&&B.createElement(oA,{path:e.path,node:e.node,rids:a,title:""}))}),B.createElement(Cv,{className:o.content},B.createElement("div",{style:{overflowX:"auto"}},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,{padding:"checkbox"},B.createElement(yu,{indeterminate:a.length>0&&a.length<c,checked:a.length===c,onChange:function(e){if(e.target.checked){var t=Object.keys(u);s(t)}else s([])},inputProps:{"aria-label":"Select all"}})),B.createElement(py,null,"Id"),B.createElement(py,null,r("Availability")),B.createElement(py,null,"Flags"),B.createElement(py,null,"Desc"))),B.createElement(ly,null,Object.keys(u).sort().map((function(t,n){return B.createElement(uA,{key:n,index:n,rid:t,node:e.node,path:e.path,selected:a,setSelected:s,sp:l})})))))))}function uA(e){var t=bn().cstat,n=(hn().user,e.index),r=e.node,o=e.path,i=e.rid,a=e.selected,s=e.setSelected,l=(e.sp,la(aA));if(void 0===t.monitor)return null;var u=t.monitor.nodes[r].services.status[o],c=u.resources[i];if(!c.status)return null;var d=a.indexOf(i)>-1,f="rid-checkbox-".concat(n);return B.createElement(ky,null,B.createElement(py,{padding:"checkbox",onClick:function(e){e.stopPropagation();var t=a.indexOf(i),n=[];-1===t?n=n.concat(a,i):0===t?n=n.concat(a.slice(1)):t===a.length-1?n=n.concat(a.slice(0,-1)):t>0&&(n=n.concat(a.slice(0,t),a.slice(t+1))),s(n)}},B.createElement(yu,{checked:d,inputProps:{"aria-labelledby":f}})),B.createElement(py,null,B.createElement(Lv,{container:!0,direction:"row",alignItems:"center"},B.createElement(Lv,{item:!0,className:l.grow},B.createElement(cr,{component:"div",noWrap:!0,className:l.iconText},i)),B.createElement(Lv,{item:!0},B.createElement(sA,{path:o,rid:i})))),B.createElement(py,null,B.createElement(Hv,{avail:c.status})),B.createElement(py,null,B.createElement(tA,{rid:i,data:c,idata:u})),B.createElement(py,null,B.createElement(cA,{data:c})))}function cA(e){var t=e.data,n=la(ma),r=[];if(t.log)for(var o=0;o<t.log.length;o++){var i=t.log[o];if(i.match(/^warn:/))var a="warn";else a=i.match(/^error:/)?"error":"n/a";r.push(B.createElement(cr,{key:o,component:"div",variant:"caption",className:(0,Kn.A)(e.className,n[a])},i))}return B.createElement(B.Fragment,null,t.label,r)}var dA=function(e){return{root:{flexGrow:1},section:{padding:e.spacing(1),overflowX:"auto"}}};function fA(e){var t=fe((0,B.useState)(0),2),n=t[0],r=(t[1],it()),o=new URLSearchParams(r.search),i=o.get("path"),a=o.get("node"),s=la(dA);return kn(i),B.createElement(Lv,{container:!0,className:s.root},B.createElement(Lv,{item:!0,xs:12,md:6,className:s.section},B.createElement(QE,{path:i})),B.createElement(Lv,{item:!0,xs:12,md:6,className:s.section},B.createElement(Qx,{path:i,node:a})),B.createElement(Lv,{item:!0,xs:12,className:s.section},B.createElement(lA,{path:i,node:a})),B.createElement(Lv,{item:!0,xs:12,className:s.section},B.createElement(pA,{active:n,path:i,node:a})))}function pA(e){var t=ee(),n=t.t;return t.i18n,B.createElement(Qb,{title:n("Log"),subheader:e.path+"@"+e.node,url:"/object/"+e.path,hide:["o"],initialContext:{sc:{value:"n"},n:{value:e.node}}})}var hA=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function mA(e){var t=la(hA),n=bn().cstat,r=ee(),o=r.t;if(r.i18n,void 0===n.monitor)return null;var i=[];for(var a in n)/^hb#/.test(a)&&a.match(/rx$/)&&i.push(a.slice(0,-3));return Object.keys(n.monitor.nodes),B.createElement(mv,{id:"heartbeats",className:t.root},B.createElement(xv,{title:o("Heartbeats"),subheader:n.cluster.name}),B.createElement(Cv,null,B.createElement("div",{className:t.wrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,o("Nodes")),i.map((function(e,t){return B.createElement(py,{key:t},e)})))),B.createElement(ly,null,n.cluster.nodes.map((function(e,t){return B.createElement(gA,{key:t,node:e,hbNames:i})})))))))}function gA(e){return B.createElement(ky,null,B.createElement(py,{"data-title":"Node"},e.node),e.hbNames.map((function(t,n){return B.createElement(vA,{key:n,node:e.node,hbName:t})})))}function vA(e){var t=la(ma),n=bn().cstat;if(void 0===n.monitor)return null;function r(e){var t="undef";return 0==e?t="down":1==e&&(t="up"),t}return B.createElement(py,null,B.createElement(cr,{component:"span",className:t[r(n[e.hbName+".rx"].peers[e.node].beating)]},"rx"),"Â /Â ",B.createElement(cr,{component:"span",className:t[r(n[e.hbName+".tx"].peers[e.node].beating)]},"tx"))}var yA=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function bA(e){var t,n=la(ma),r=[];for(t in e.arbitrators){var o=e.arbitrators[t];o.an=t,r.push(o)}return B.createElement(ky,null,B.createElement(py,{"data-title":"Node"},e.node),r.map((function(e,t){return B.createElement(py,{key:t,"data-title":e.name},B.createElement(cr,{component:"span",className:n[e.status?"up":"down"]},e.status))})))}const wA=function(e){var t=la(yA),n=ee(),r=n.t,o=(n.i18n,bn().cstat),i={},a=[],s={};if(void 0===o.monitor)return null;for(var l in o.monitor.nodes){var u=o.monitor.nodes[l];if(u.arbitrators)for(var c in l in i||(i[l]={}),u.arbitrators){var d=u.arbitrators[c];a.indexOf(c)<0&&(a.push(c),s[c]=d.name),i[l][c]=d}}return a.length?(console.log("rerender Arb"),B.createElement(mv,{className:t.root},B.createElement(xv,{title:r("Arbitrators"),subheader:o.cluster.name}),B.createElement(Cv,null,B.createElement("div",{className:t.wrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,{className:"text-secondary"},B.createElement(py,null,"Nodes"),a.map((function(e,t){return B.createElement(py,{key:t,title:e},s[e])})))),B.createElement(ly,null,Object.keys(i).map((function(e){return B.createElement(bA,{key:e,node:e,arbitrators:i[e]})})))))))):null};function EA(e){return(0,rr.A)("MuiLink",e)}const SA=(0,nr.A)("MuiLink",["root","underlineNone","underlineHover","underlineAlways","button","focusVisible"]);var xA=o(6481);const AA={primary:"primary.main",textPrimary:"text.primary",secondary:"secondary.main",textSecondary:"text.secondary",error:"error.main"},kA=({theme:e,ownerState:t})=>{const n=(e=>AA[e]||e)(t.color),r=(0,xA.Yn)(e,`palette.${n}`,!1)||t.color,o=(0,xA.Yn)(e,`palette.${n}Channel`);return"vars"in e&&o?`rgba(${o} / 0.4)`:(0,vo.X4)(r,.4)},_A=["className","color","component","onBlur","onFocus","TypographyClasses","underline","variant","sx"],CA=(0,Zn.Ay)(cr,{name:"MuiLink",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`underline${(0,tr.A)(n.underline)}`],"button"===n.component&&t.button]}})((({theme:e,ownerState:t})=>(0,Jt.A)({},"none"===t.underline&&{textDecoration:"none"},"hover"===t.underline&&{textDecoration:"none","&:hover":{textDecoration:"underline"}},"always"===t.underline&&(0,Jt.A)({textDecoration:"underline"},"inherit"!==t.color&&{textDecorationColor:kA({theme:e,ownerState:t})},{"&:hover":{textDecorationColor:"inherit"}}),"button"===t.component&&{position:"relative",WebkitTapHighlightColor:"transparent",backgroundColor:"transparent",outline:0,border:0,margin:0,borderRadius:0,padding:0,cursor:"pointer",userSelect:"none",verticalAlign:"middle",MozAppearance:"none",WebkitAppearance:"none","&::-moz-focus-inner":{borderStyle:"none"},[`&.${SA.focusVisible}`]:{outline:"auto"}}))),OA=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiLink"}),{className:r,color:o="primary",component:i="a",onBlur:a,onFocus:s,TypographyClasses:l,underline:u="always",variant:c="inherit",sx:d}=n,f=(0,qn.A)(n,_A),{isFocusVisibleRef:p,onBlur:h,onFocus:m,ref:g}=(0,jo.A)(),[v,y]=B.useState(!1),b=(0,Qr.A)(t,g),w=(0,Jt.A)({},n,{color:o,component:i,focusVisible:v,underline:u,variant:c}),E=(e=>{const{classes:t,component:n,focusVisible:r,underline:o}=e,i={root:["root",`underline${(0,tr.A)(o)}`,"button"===n&&"button",r&&"focusVisible"]};return(0,Qn.A)(i,EA,t)})(w);return(0,ir.jsx)(CA,(0,Jt.A)({color:o,className:(0,Kn.A)(E.root,r),classes:l,component:i,onBlur:e=>{h(e),!1===p.current&&y(!1),a&&a(e)},onFocus:e=>{m(e),!0===p.current&&y(!0),s&&s(e)},ref:b,ownerState:w,variant:c,sx:[...Object.keys(AA).includes(o)?[]:[{color:o}],...Array.isArray(d)?d:[d]]},f))}));var PA=o(9483),RA=[{title:"en",code:"en"},{title:"fr",code:"fr"}],TA=function(e){return{formControl:{margin:e.spacing(1)}}};const FA=function(e){la(TA);var t=fe(B.useState(null),2),n=t[0],r=t[1],o=ee(),i=(o.t,o.i18n);return B.createElement(B.Fragment,null,B.createElement(cl,{"aria-controls":"language","aria-haspopup":"true",color:"primary",onClick:function(e){r(e.currentTarget)}},i.languages[0],B.createElement(PA.A,null)),B.createElement(sm,{id:"language",anchorEl:n,keepMounted:!0,open:Boolean(n),onClose:function(e){r(null)}},RA.map((function(e,t){return B.createElement(Wm,{key:t,onClick:function(){return t=e.code,i.changeLanguage(t),void r(null);var t}},e.title)}))))};var jA=["dark","light"],NA=function(e){return{formControl:{margin:e.spacing(1)}}};const IA=function(e){la(NA);var t=fe(B.useState(null),2),n=t[0],r=t[1],o=ee(),i=o.t,a=(o.i18n,fe(ve(),2)),s=a[0].theme,l=a[1];return B.createElement(B.Fragment,null,B.createElement(cl,{"aria-controls":"language","aria-haspopup":"true",color:"primary",onClick:function(e){r(e.currentTarget)}},s,B.createElement(PA.A,null)),B.createElement(sm,{id:"language",anchorEl:n,keepMounted:!0,open:Boolean(n),onClose:function(e){r(null)}},jA.map((function(e){return B.createElement(Wm,{key:e,onClick:function(){return function(e){l({type:"setTheme",data:e}),r(null)}(e)}},i(e))}))))};var LA=function(e){return{root:{marginTop:e.spacing(3)},inline:{display:"inline-block",margin:0},pre:{whiteSpace:"normal",fontFamily:"monospace",wordBreak:"break-all"}}};function MA(e){return B.createElement(B.Fragment,null,B.createElement(DA,null),B.createElement(HA,null),B.createElement($A,null))}function DA(e){var t=(0,he.km)(),n=(t.oidcUser,t.logout),r=ee(),o=(r.i18n,r.t),i=bn().close,a=hn().unloadUser,s=fe(ve(),2),l=s[0].user,u=(s[1],la(LA));return B.createElement(mv,{className:u.root},B.createElement(xv,{title:o("User"),subheader:l.name?l.name:B.createElement(qb,{width:"5rem"})}),B.createElement(Cv,null,B.createElement(cr,null,B.createElement(BA,{user:l})),B.createElement("br",null),B.createElement(cr,{component:"div"},B.createElement(zA,null))),B.createElement(lw,null,B.createElement(cl,{onClick:function(e){n(),a(),i()},color:"primary"},o("Logout")),B.createElement(IA,null),B.createElement(FA,null)))}function BA(e){var t=e.user,n=ee(),r=(n.i18n,n.t);return la(LA),t.auth?B.createElement(B.Fragment,null,r("Authenticated via"),"Â ",B.createElement(OA,{href:"#"+t.auth},t.auth),"."):r("Not authenticated")}function UA(e){var t=e.href,n=new URL(t);return B.createElement(OA,{href:t},n.hostname)}function zA(e){var t=(0,he.km)().oidcUser,n=ee(),r=(n.i18n,n.t),o=wn();return la(LA),o?void 0===o.openid?null:t?B.createElement(B.Fragment,null,r("Token provided by openid server"),"Â ",B.createElement(UA,{href:o.openid.well_known_uri}),"."):null:B.createElement(qb,null)}function $A(e){var t=(0,he.km)().oidcUser,n=ee(),r=(n.i18n,n.t),o=la(LA);if(!t)return null;var i=new Date(1e3*t.expires_at);return B.createElement(mv,{id:"jwt",className:o.root},B.createElement(xv,{title:r("Access Token"),subheader:r("Expires at {{date}}",{date:i.toLocaleString()})}),B.createElement(Cv,null,B.createElement(cr,{variant:"body1",component:"pre",className:o.pre},t.access_token)))}function HA(e){var t=fe(ve(),2),n=t[0].user,r=(t[1],ee()),o=(r.i18n,r.t),i=la(LA);if(void 0===n.grant)return B.createElement(qb,{variant:"rect",width:"100%",height:"8rem"});var a=function(e){var t=[];if("root"in e)return t.push({role:"root"}),t;for(var n in e)t.push({role:n,namespaces:e[n]});return t}(n.grant);return a.length?B.createElement(mv,{className:i.root},B.createElement(xv,{title:o("Grants"),subheader:n.raw_grant}),B.createElement(Cv,null,B.createElement(Ei,null,a.map((function(e,t){return B.createElement(WA,{key:t,namespaces:e.namespaces,role:e.role})}))))):null}function WA(e){var t=ee(),n=(t.i18n,t.t);if(e.namespaces)r=0==e.namespaces.length?n("on no existing namespaces"):n("on namespaces {{ns}}",{ns:e.namespaces.join(", ")});else{if(!(["heartbeat","blacklistadmin","squatter","prioritizer","root"].indexOf(e.role)>-1))return null;var r=""}return B.createElement(Li,{disableGutters:!0},B.createElement(qi,{primary:e.role,secondary:r}))}function VA(e){e.path;var t=hn().auth,n=ee().t,r=fe(B.useState(!1),2),o=r[0],i=r[1],a=fe((0,B.useState)({}),2),s=a[0],l=a[1],u=NS("ccfg"),c=fe(ve(),2),d=c[0].user,f=(c[1],rp().dispatchAlerts);if(!d.grant)return null;if(!("root"in d.grant))return null;if(!u)return null;function p(e){i(!1)}return B.createElement(B.Fragment,null,B.createElement(bd,{title:n("Add Pool")},B.createElement(gi,{"aria-label":"Add Pool","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),i(!0)}},B.createElement(lv.A,null))),B.createElement(Ls,{open:o,onClose:p,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Create New Pool"),B.createElement(qs,null,B.createElement(Ys,null,"A pool hosts data volumes abstracted from nodes hardware."),B.createElement(Ax,{kind:"Pool",kws:u.pool,data:s,setData:l})),B.createElement(Us,null,B.createElement(cl,{onClick:p,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(e){var n=[];for(var r in s)if("sectionName"!=r&&s[r]){var o="pool#"+s.sectionName+"."+r+"="+s[r];n.push(o)}console.log("SUBMIT",s.sectionName,s.type,s,"=>",n),an("ANY","cluster","set",{kw:n},(function(e){return f({data:e})}),t),p()},color:"secondary"},"Submit"))))}var qA=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function KA(e){var t=la(qA),n=ee(),r=n.t,o=(n.i18n,function(e){if(!e)return[];var t=[];for(var n in e){var r=e[n];t.push(r)}return t}(wl()));return B.createElement(mv,{id:"pools",className:t.root},B.createElement(xv,{title:r("Pools"),action:B.createElement(VA,null)}),B.createElement(Cv,null,B.createElement("div",{className:t.wrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,null,B.createElement(py,null,r("Name")),B.createElement(py,null,"Type"),B.createElement(py,null,"Volumes"),B.createElement(py,null,r("Usage")),B.createElement(ub,{smDown:!0},B.createElement(py,null,"Head")))),B.createElement(ly,null,o.map((function(e,t){return B.createElement(GA,{key:t,index:t,data:e})})))))))}function GA(e){e.index;var t=e.data,n=(100*t.used/t.size).toFixed(2);return B.createElement(ky,null,B.createElement(py,null,t.name),B.createElement(py,null,t.type),B.createElement(py,null,t.volumes.length),B.createElement(py,null,n,"%"),B.createElement(ub,{smDown:!0},B.createElement(py,null,t.head)))}function JA(e){e.path;var t=hn().auth,n=ee().t,r=fe(B.useState(!1),2),o=r[0],i=r[1],a=fe((0,B.useState)({}),2),s=a[0],l=a[1],u=NS("ccfg"),c=fe(ve(),2),d=c[0].user;if(c[1],!d.grant)return null;if(!("root"in d.grant))return null;if(!u)return null;function f(e){i(!1)}return B.createElement(B.Fragment,null,B.createElement(bd,{title:n("Add Network")},B.createElement(gi,{"aria-label":"Add Network","aria-haspopup":!0,onClick:function(e){e.stopPropagation(),i(!0)}},B.createElement(lv.A,null))),B.createElement(Ls,{open:o,onClose:f,"aria-labelledby":"form-dialog-title"},B.createElement(Zs,{id:"form-dialog-title"},"Create New Network"),B.createElement(qs,null,B.createElement(Ys,null,"A network hosts dynamically allocated ip addresses for ip.cni resources. The ",B.createElement("code",null,"bridge")," types is node-local, the ",B.createElement("code",null,"routed_bridge"),"and ",B.createElement("code",null,"weave")," types are cluster-wide."),B.createElement(Ax,{kind:"Network",kws:u.network,data:s,setData:l})),B.createElement(Us,null,B.createElement(cl,{onClick:f,color:"primary"},"Cancel"),B.createElement(cl,{onClick:function(e){var n=[];for(var r in s)if("sectionName"!=r&&s[r]){var o="network#"+s.sectionName+"."+r+"="+s[r];n.push(o)}console.log("SUBMIT",s.sectionName,s.type,s,"=>",n),an("ANY","cluster","set",{kw:n},(function(e){return dispatchAlerts({data:e})}),t),f()},color:"secondary"},"Submit"))))}var YA=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},row:{"&:hover":{cursor:"pointer"}}}};function XA(e){var t=la(YA),n=ee(),r=n.t,o=(n.i18n,El()),i=fe(ve(),2);ne(i[0]),i[1];var a=function(e){if(!e)return[];var t=[];for(var n in e){var r=e[n];r.name=n,t.push(r)}return t}(o);return B.createElement(mv,{id:"networks",className:t.root},B.createElement(xv,{title:r("Networks"),action:B.createElement(JA,null)}),B.createElement(Cv,null,B.createElement("div",{className:t.wrapper},B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,null,B.createElement(py,null,r("Name")),B.createElement(py,null,"Type"),B.createElement(py,null,r("Network")),B.createElement(py,null,r("Addresses")),B.createElement(py,null,r("Usage")))),B.createElement(ly,null,a.map((function(e,t){return B.createElement(QA,{key:t,index:t,data:e})})))))))}function QA(e){e.index;var t=e.data,n=fe(ve(),2);ne(n[0]),n[1];var r=la(YA),o=lt();return B.createElement(ky,{onClick:function(e){e.stopPropagation(),o("/network?name="+t.name)},className:r.row},B.createElement(py,null,t.name),B.createElement(py,null,t.type),B.createElement(py,null,t.network),B.createElement(py,null,t.used),B.createElement(py,null,t.pct.toFixed(2),"%"))}var ZA=function(e){return{root:{marginTop:e.spacing(3)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)}}};function ek(e){var t=it(),n=new URLSearchParams(t.search).get("name"),r=ee(),o=r.t,i=(r.i18n,la(ZA));return B.createElement(mv,{className:i.root},B.createElement(xv,{title:o("Network Addresses"),subheader:n}),B.createElement(Cv,null,B.createElement("div",{className:i.wrapper},B.createElement(tk,{name:n}))))}function tk(e){var t=e.name,n=El(),r=fe(ve(),2);if(ne(r[0]),r[1],!n)return null;var o=n[t].ips;return B.createElement(ty,null,B.createElement(by,null,B.createElement(ky,null,B.createElement(py,null,"Address"),B.createElement(py,null,"Node"),B.createElement(py,null,"Service"),B.createElement(py,null,"Resource"))),B.createElement(ly,null,o.map((function(e,t){return B.createElement(nk,{key:t,index:t,data:e})}))))}function nk(e){e.index;var t=e.data,n=fe(ve(),2);return ne(n[0]),n[1],B.createElement(ky,null,B.createElement(py,null,t.ip),B.createElement(py,null,t.node),B.createElement(py,null,t.path),B.createElement(py,null,t.rid))}function rk(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var ok=function(e){return{root:{marginTop:e.spacing(3)},param:{paddingTop:e.spacing(1),paddingBottom:e.spacing(1)},wrapper:{overflowX:"auto",marginLeft:-e.spacing(2),marginRight:-e.spacing(2)},method:{width:"7em",marginRight:e.spacing(2)},cardAction:{marginLeft:"auto"},section:{paddingBottom:e.spacing(3)},tt:{fontFamily:"monospace"},pre:{overflowX:"auto",whiteSpace:"pre",fontFamily:"monospace"}}};function ik(e){var t=e.data,n=(e.index,hn().auth),r=la(ok),o=fe((0,B.useState)({node:""}),2),i=o[0],a=o[1],s=fe((0,B.useState)({}),2),l=s[0],u=s[1],c=fe((0,B.useState)(""),2),d=c[0],f=c[1];return B.createElement(mv,{className:r.root},B.createElement(xv,{title:B.createElement(pk,{data:t})}),B.createElement(Cv,null,B.createElement(nv,{className:r.section},B.createElement(cr,null,t.desc)),B.createElement(sk,{data:t.access}),B.createElement(ck,{method:t.routes[0].method,data:t.prototype,formData:l,setFormData:u}),B.createElement(uk,{data:t,formData:i,setFormData:a}),B.createElement(ak,{data:t,formData:l,node:i})),B.createElement(lw,null,B.createElement(cl,{className:r.cardAction,onClick:function(e){f(""),ln(t.routes[0].method,i.node,t.routes[0].path,l,(function(e){f(e)}),n)},color:"GET"==t.routes[0].method?"primary":"secondary"},t.routes[0].method)),d&&B.createElement(Cv,null,B.createElement(lk,{data:d})))}function ak(e){var t=ee(),n=t.t,r=(t.i18n,hn().auth),o=la(ok),i=e.data,a=e.formData,s=e.node,l="curl -s --http2 -X "+i.routes[0].method;return s&&s.node&&(l+=" -H 'o-node: "+s.node+"'"),i.stream?l+=" -N -H 'Accept: text/event-stream'":l+=" -H 'Content-Type: application/json'",r&&r.access_token?l+=" -H 'Authorization: Bearer <token>'":r&&r.username?l+=" -u "+r.username:l+=" --cert-type P12 -E <p12file>:<pass>","POST"==i.routes[0].method&&(l+=" --data '"+JSON.stringify(a)+"'"),l+=" https://"+window.location.host+"/"+i.routes[0].path,"GET"==i.routes[0].method&&(l+=tn("",a)),B.createElement(nv,{className:o.section},B.createElement(cr,{variant:"h5"},n("Example")),B.createElement(cr,{component:"div"},B.createElement(nv,{className:o.tt},l)))}function sk(e){var t=ee(),n=t.t,r=(t.i18n,la(ok)),o=e.data,i="";if("custom"==o)i=n("Custom access policy.");else if(o.roles){var a=o.roles.join(", ");if(o.namespaces)if("FROM:path"==o.namespaces){var s=n("extracted from the 'path' request parameter or data key");i=n("Usable with {{roles}} privileges on namespaces {{namespaces}}.",{roles:a,namespaces:s})}else"ANY"==o.namespaces?(s=n("any"),i=n("Usable with {{roles}} privileges on any namespace.",{roles:a})):i=n("Usable with {{roles}} privileges on namespaces {{namespaces}}.",{roles:a,namespaces:s=o.namespaces.join(", ")});else i=n("Usable with {{roles}} privileges.",{roles:a})}else i=n("World-usable.");return B.createElement(nv,{className:r.section},B.createElement(cr,{variant:"h5"},n("Access")),B.createElement(cr,{component:"div"},i))}function lk(e){var t=ee(),n=t.t,r=(t.i18n,la(ok)),o=e.data;return o?B.createElement(nv,{className:r.section},B.createElement(cr,{variant:"h5"},n("Response")),B.createElement(cr,{component:"div"},B.createElement(nv,{className:r.pre},JSON.stringify(o,null,4)))):null}function uk(e){var t=e.data,n=e.formData,r=e.setFormData,o=la(ok),i=ee();return i.t,i.i18n,"never"==!t.multiplex?null:B.createElement(nv,{className:o.section},B.createElement(Ax,{kind:"data",kws:[{keyword:"node",default:"",convert:"node_selector",text:"Route request to nodes"}],data:n,setData:r,requiredTitle:"",optionalTitle:"Routing"}))}function ck(e){var t=e.data,n=e.method,r=e.formData,o=e.setFormData,i=la(ok),a=ee(),s=a.t;if(a.i18n,!t.length)return null;var l,u=[],c=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return rk(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?rk(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(t);try{for(c.s();!(l=c.n()).done;){var d=l.value;if(!d.deprecated){var f={keyword:d.name,default:d.default,convert:d.format,text:d.desc,candidates:d.candidates,strict_candidates:d.strict_candidates,required:d.required};u.push(f)}}}catch(e){c.e(e)}finally{c.f()}var p=s("GET"==n?"Parameters":"Data");return B.createElement(nv,{className:i.section},B.createElement(Ax,{kind:"data",kws:u,data:r,setData:o,requiredTitle:s("Required {{what}}",{what:p}),optionalTitle:s("Optional {{what}}",{what:p})}))}function dk(e){var t=e.data;return t?B.createElement(Ei,null,t.map((function(e,t){return B.createElement(hk,{data:e,index:t,key:t})}))):B.createElement(qb,{variant:"rect",width:"100%",height:"8rem"})}function fk(e){var t=e.method,n=la(ok);return B.createElement(ps,{color:"GET"==t?"primary":"secondary",label:t,className:n.method})}function pk(e){var t=e.data;return t?B.createElement(B.Fragment,null,B.createElement(fk,{method:t.routes[0].method}),"/",t.routes[0].path):B.createElement(qb,null)}function hk(e){var t=e.data,n=e.index,r=lt();return B.createElement(Li,{button:!0,component:"a",onClick:function(e){r("/api?index="+n)}},B.createElement(qi,null,B.createElement(pk,{data:t})))}const mk=function(e){var t=function(){var e=fe((0,B.useState)(),2),t=e[0],n=e[1],r=hn().auth;return(0,B.useEffect)((function(){void 0===t&&dn("/api",{},(function(e){n(e)}),r)}),[]),t}(),n=la(ok),r=it(),o=ee(),i=o.t,a=(o.i18n,new URLSearchParams(r.search).get("index"));if(!Array.isArray(t))return null;var s=t.sort((function(e,t){return e.routes[0].path.localeCompare(t.routes[0].path)||e.routes[0].method.localeCompare(t.routes[0].method)}));return s&&null!==a?B.createElement(ik,{data:s[a],index:a}):B.createElement(mv,{id:"api",className:n.root},B.createElement(xv,{title:i("Api")}),B.createElement(Cv,null,B.createElement(dk,{data:s})))},gk=function(e){var t=fe(ve(),2),n=(t[0].user,t[1],hn().auth),r=fe((0,B.useState)({last:null,prev:null}),2),o=r[0],i=r[1],a=fe((0,B.useState)(!0),2),s=a[0],l=a[1],u=(0,B.useRef)(s),c=(0,B.useRef)(o),d=(0,B.useRef)(null),f=e&&e.period?e.period:3e3,p=e&&e.node?e.node:"*",h=!1;function m(){if(u.current&&!h){var e={headers:{Accept:"application/json","Content-Type":"application/json","o-node":p}};e.headers=rn(e.headers,n);try{h=!0,fetch("/daemon_stats",e).then((function(e){return e.json()})).then((function(e){e.error||(i({prev:c.current.last,last:e}),c.current={prev:c.current.last,last:e},h=!1)})).catch((function(e){h=!1,console.log(e)}))}catch(e){console.log(e),h=!1}}}return(0,B.useEffect)((function(){return d.current||(console.log("start daemon stats loop"),d.current=setInterval(m,f)),function(){d.current&&(console.log("stop daemon stats loop"),clearTimeout(d.current),d.current=null)}}),[n.access_token]),{last:o.last,prev:o.prev,pause:function(){u.current=!1,l(!1)},play:function(){u.current=!0,l(!0)},playing:s}};var vk=function(e){return{itemGrid:{flexWrap:"nowrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function yk(e){var t=e.data,n=e.node,r=e.name,o=e.agg,i=la(vk);try{if("ns"==o)var a=100*(s=t.nodes[n].namespaces[r])/t.sum.namespaces[r];else a=100*(s=t.nodes[n].services[r])/t.sum.services[r]}catch(e){var s=void 0}if(void 0===s)var l="1px";else l=a.toFixed(0)+"%";var u={width:"0.3em",marginRight:"3px",height:l};return B.createElement(Lv,{item:!0,style:u,className:i.bar},"Â ")}function bk(e){var t=la(vk),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(yk,{key:e,node:e,name:r,data:n,agg:o})})))}function wk(e){var t=e.value,n=la(vk),r=(100*t).toFixed(2);return B.createElement(cr,{component:"div",className:n.value},r,"%")}const Ek=function(e){var t=e.last,n=e.prev,r=e.sortKey,o=e.agg,i=e.setAgg,a=e.search,s=e.setSearch,l=la(vk),u=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;if(!(0,Dv.isEmpty)(i)){var a=i.node.cpu.time,s=1,l=void 0;try{s=a-(l=t.nodes[o].data).node.cpu.time}catch(e){continue}for(var u in i.services)if(!n||u.match(n)){var c=kn(u),d=i.services[u],f=0,p=0;try{f=d.cpu.time,p=l.services[u].cpu.time}catch(e){continue}var h=(f-p)/s;o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[u]=h,c.namespace in r.nodes[o].namespaces?r.nodes[o].namespaces[c.namespace]+=h:r.nodes[o].namespaces[c.namespace]=h,u in r.sum.services?r.sum.services[u]+=h:r.sum.services[u]=h,c.namespace in r.sum.namespaces?r.sum.namespaces[c.namespace]+=h:r.sum.namespaces[c.namespace]=h}}}return r}(t,n,a);if("ns"==o)var c=u.sum.namespaces;else c=u.sum.services;var d=Object.keys(c);"value"==r?d.sort((function(e,t){return c[t]-c[e]})):d.sort();var f=function(e){return function(t){s("ns"==o?"^"+e+"/":e),i("path")}};return B.createElement(Ei,null,d.map((function(e){return B.createElement(Li,{key:e,onClick:f(e)},B.createElement(Lv,{container:!0,className:l.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:4,className:l.itemTitle},e),B.createElement(Lv,{item:!0,xs:4},B.createElement(bk,{data:u,agg:o,name:e})),B.createElement(Lv,{item:!0,xs:4},B.createElement(wk,{value:c[e]}))))})))};var Sk=function(e){return{itemGrid:{flexWrap:"nowrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function xk(e){var t,n,r=e.data,o=e.node,i=e.name,a=e.agg,s=la(Sk);try{t="ns"===a?100*(n=r.nodes[o].namespaces[i])/r.sum.namespaces[i]:100*(n=r.nodes[o].services[i])/r.sum.services[i]}catch(e){n=void 0}var l={width:"0.3em",marginRight:"3px",height:void 0===n?"1px":t.toFixed(0)+"%"};return B.createElement(Lv,{item:!0,style:l,className:s.bar},"Â ")}function Ak(e){var t=la(Sk),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(xk,{key:e,node:e,name:r,data:n,agg:o})})))}function kk(e){var t=e.value,n=la(Sk);return B.createElement(cr,{component:"div",className:n.value},On(t/1048576))}const _k=function(e){var t,n=e.last,r=(e.prev,e.sortKey),o=e.agg,i=e.setAgg,a=e.search,s=e.setSearch,l=la(Sk),u=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;if(!(0,Dv.isEmpty)(i))for(var a in i.services)if(!n||a.match(n)){var s=kn(a),l=i.services[a],u=void 0;try{u=l.mem.total}catch(e){continue}o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[a]=u,s.namespace in r.nodes[o].namespaces?r.nodes[o].namespaces[s.namespace]+=u:r.nodes[o].namespaces[s.namespace]=u,a in r.sum.services?r.sum.services[a]+=u:r.sum.services[a]=u,s.namespace in r.sum.namespaces?r.sum.namespaces[s.namespace]+=u:r.sum.namespaces[s.namespace]=u}}return r}(n,0,a);t="ns"===o?u.sum.namespaces:u.sum.services;var c=Object.keys(t);"value"===r?c.sort((function(e,n){return t[n]-t[e]})):c.sort();var d=function(e){return function(t){s("ns"===o?"^"+e+"/":e),i("path")}};return B.createElement(Ei,null,c.map((function(e){return B.createElement(Li,{key:e,onClick:d(e)},B.createElement(Lv,{container:!0,className:l.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:4,className:l.itemTitle},e),B.createElement(Lv,{item:!0,xs:4},B.createElement(Ak,{data:u,agg:o,name:e})),B.createElement(Lv,{item:!0,xs:4},B.createElement(kk,{value:t[e]}))))})))};var Ck=function(e){return{itemGrid:{flexWrap:"nowrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function Ok(e){var t=e.data,n=e.node,r=e.name,o=e.agg,i=la(Ck);try{if("ns"==o)var a=100*(s=t.nodes[n].namespaces[r])/t.sum.namespaces[r];else a=100*(s=t.nodes[n].services[r])/t.sum.services[r]}catch(e){var s=void 0}if(void 0===s)var l="1px";else l=a.toFixed(0)+"%";var u={width:"0.3em",marginRight:"3px",height:l};return B.createElement(Lv,{item:!0,style:u,className:i.bar},"Â ")}function Pk(e){var t=la(Ck),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(Ok,{key:e,node:e,name:r,data:n,agg:o})})))}function Rk(e){var t=e.value,n=la(Ck);return B.createElement(cr,{component:"div",className:n.value},t)}const Tk=function(e){var t=e.last,n=(e.prev,e.sortKey),r=e.agg,o=e.setAgg,i=e.search,a=e.setSearch,s=la(Ck),l=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;if(!(0,Dv.isEmpty)(i))for(var a in i.services)if(!n||a.match(n)){var s=kn(a),l=i.services[a].tasks;void 0!==l&&(o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[a]=l,s.namespace in r.nodes[o].namespaces?r.nodes[o].namespaces[s.namespace]+=l:r.nodes[o].namespaces[s.namespace]=l,a in r.sum.services?r.sum.services[a]+=l:r.sum.services[a]=l,s.namespace in r.sum.namespaces?r.sum.namespaces[s.namespace]+=l:r.sum.namespaces[s.namespace]=l)}}return r}(t,0,i);if("ns"==r)var u=l.sum.namespaces;else u=l.sum.services;var c=Object.keys(u);"value"==n?c.sort((function(e,t){return u[t]-u[e]})):c.sort();var d=function(e){return function(t){a("ns"==r?"^"+e+"/":e),o("path")}};return B.createElement(Ei,null,c.map((function(e){return B.createElement(Li,{key:e,onClick:d(e)},B.createElement(Lv,{container:!0,className:s.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:4,className:s.itemTitle},e),B.createElement(Lv,{item:!0,xs:4},B.createElement(Pk,{data:l,agg:r,name:e})),B.createElement(Lv,{item:!0,xs:4},B.createElement(Rk,{value:u[e]}))))})))};function Fk(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}var jk=function(e){return{biasContainer:{textAlign:"center"},biasContent:{width:"4em",display:"inline"},hline:{height:"0.6em",lineHeight:"0.6em"},hlabel:{display:"inline-block",width:"1.3em",lineHeight:"0.6em",color:e.palette.text.secondary,fontSize:"0.6em",verticalAlign:"middle"},hbarContainer:{display:"inline-block",width:"2.7em",verticalAlign:"middle"},hbar:{height:"0.3em",background:e.palette.primary.main,borderLeftWidth:"1px",borderLeftStyle:"solid",borderLeftColor:e.palette.primary.main,transition:"width 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};const Nk=function(e){var t,n=e.values,r=la(jk),o=0,i=function(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return Fk(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?Fk(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}(n);try{for(i.s();!(t=i.n()).done;){var a=t.value;o+=a.value}}catch(e){i.e(e)}finally{i.f()}return B.createElement("div",{className:r.biasContainer},B.createElement("div",{className:r.biasContent},n.map((function(e,t){return B.createElement("div",{key:t,className:r.hline},B.createElement("div",{className:r.hlabel},e.label),B.createElement("div",{className:r.hbarContainer},B.createElement("div",{className:r.hbar,style:{width:(n=e.value,o?100*n/o+"%":"1px")}},"Â ")));var n}))))};var Ik=function(e){return{itemGrid:{flexWrap:"wrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function Lk(e){var t=e.data,n=e.node,r=e.name,o=e.agg,i=la(Ik);try{if("ns"==o)var a=100*(s=t.nodes[n].namespaces[r].rw)/t.sum.namespaces[r].rw;else a=100*(s=t.nodes[n].services[r].rw)/t.sum.services[r].rw}catch(e){var s=void 0}if(void 0===s)var l="1px";else l=a.toFixed(0)+"%";var u={width:"0.3em",marginRight:"3px",height:l};return B.createElement(Lv,{item:!0,style:u,className:i.bar},"Â ")}function Mk(e){var t=la(Ik),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(Lk,{key:e,node:e,name:r,data:n,agg:o})})))}function Dk(e){var t=e.value,n=[{label:"r",value:t.r},{label:"w",value:t.w}];return B.createElement(Nk,{values:n})}function Bk(e){var t=e.value,n=la(Ik);return B.createElement(cr,{component:"div",className:n.value},On(t.rw/1048576),"rw/s")}const Uk=function(e){var t=e.last,n=e.prev,r=e.sortKey,o=e.agg,i=e.setAgg,a=e.search,s=e.setSearch,l=la(Ik),u=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;try{var a=t.nodes[o].data}catch(e){continue}var s=i.timestamp-a.timestamp;for(var l in i.services)if(!n||l.match(n)){var u=kn(l),c=i.services[l];try{var d=a.services[l],f={};f.r=(c.blk.r-d.blk.r)/s,f.w=(c.blk.w-d.blk.w)/s,f.rw=f.r+f.w}catch(e){continue}o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[l]=f,u.namespace in r.nodes[o].namespaces?(r.nodes[o].namespaces[u.namespace].r+=f.r,r.nodes[o].namespaces[u.namespace].w+=f.w,r.nodes[o].namespaces[u.namespace].rw+=f.rw):r.nodes[o].namespaces[u.namespace]=f,l in r.sum.services?(r.sum.services[l].r+=f.r,r.sum.services[l].w+=f.w,r.sum.services[l].rw+=f.rw):r.sum.services[l]=f,u.namespace in r.sum.namespaces?(r.sum.namespaces[u.namespace].r+=f.r,r.sum.namespaces[u.namespace].w+=f.w,r.sum.namespaces[u.namespace].rw+=f.rw):r.sum.namespaces[u.namespace]=f}}return r}(t,n,a);if("ns"==o)var c=u.sum.namespaces;else c=u.sum.services;var d=Object.keys(c);"value"==r?d.sort((function(e,t){return c[t].rw-c[e].rw})):d.sort();var f=function(e){return function(t){s("ns"==o?"^"+e+"/":e),i("path")}};return B.createElement(Ei,null,d.map((function(e){return B.createElement(Li,{key:e,onClick:f(e)},B.createElement(Lv,{container:!0,className:l.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:12,sm:6,className:l.itemTitle},e),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Mk,{data:u,agg:o,name:e})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Dk,{value:c[e]})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Bk,{value:c[e]}))))})))};var zk=function(e){return{itemGrid:{flexWrap:"wrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{width:"0.3em",marginRight:"3px",background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function $k(e){var t,n,r=e.data,o=e.node,i=e.name,a=e.agg,s=la(zk);try{n="ns"===a?100*(t=r.nodes[o].namespaces[i].rwb)/r.sum.namespaces[i].rwb:100*(t=r.nodes[o].services[i].rwb)/r.sum.services[i].rwb}catch(e){t=void 0}var l={height:void 0===t?"1px":n.toFixed(0)+"%"};return B.createElement(Lv,{item:!0,style:l,className:s.bar},"Â ")}function Hk(e){var t=la(zk),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement($k,{key:e,node:e,name:r,data:n,agg:o})})))}function Wk(e){var t=e.value,n=la(zk);return B.createElement(cr,{component:"div",className:n.value},On(t.rwb/1048576),"b/s")}function Vk(e){var t=e.value,n=[{label:"r",value:t.rb},{label:"w",value:t.wb}];return B.createElement(Nk,{values:n})}const qk=function(e){var t,n=e.last,r=e.prev,o=e.sortKey,i=e.agg,a=e.setAgg,s=e.search,l=e.setSearch,u=la(zk),c=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;if(!(0,Dv.isEmpty)(i)){var a=void 0,s=void 0;try{a=t.nodes[o].data,s=i.timestamp-a.timestamp}catch(e){continue}for(var l in i.services)if(!n||l.match(n)){var u=kn(l),c=i.services[l],d={};try{var f=a.services[l];d.rb=(c.blk.rb-f.blk.rb)/s,d.wb=(c.blk.wb-f.blk.wb)/s,d.rwb=d.rb+d.wb}catch(e){continue}o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[l]=d,u.namespace in r.nodes[o].namespaces?(r.nodes[o].namespaces[u.namespace].rb+=d.rb,r.nodes[o].namespaces[u.namespace].wb+=d.wb,r.nodes[o].namespaces[u.namespace].rwb+=d.rwb):r.nodes[o].namespaces[u.namespace]=d,l in r.sum.services?(r.sum.services[l].rb+=d.rb,r.sum.services[l].wb+=d.wb,r.sum.services[l].rwb+=d.rwb):r.sum.services[l]=d,u.namespace in r.sum.namespaces?(r.sum.namespaces[u.namespace].rb+=d.rb,r.sum.namespaces[u.namespace].wb+=d.wb,r.sum.namespaces[u.namespace].rwb+=d.rwb):r.sum.namespaces[u.namespace]=d}}}return r}(n,r,s);t="ns"===i?c.sum.namespaces:c.sum.services;var d=Object.keys(t);"value"===o?d.sort((function(e,n){return t[n].rwb-t[e].rwb})):d.sort();var f=function(e){return function(t){l("ns"===i?"^"+e+"/":e),a("path")}};return B.createElement(Ei,null,d.map((function(e){return B.createElement(Li,{key:e,onClick:f(e)},B.createElement(Lv,{container:!0,alignItems:"center",className:u.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:12,sm:6,className:u.itemTitle},e),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Hk,{data:c,agg:i,name:e})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Vk,{value:t[e]})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Wk,{value:t[e]}))))})))};var Kk=function(e){return{itemGrid:{flexWrap:"wrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function Gk(e){var t=e.data,n=e.node,r=e.name,o=e.agg,i=la(Kk);try{if("ns"==o)var a=100*(s=t.nodes[n].namespaces[r].rw)/t.sum.namespaces[r].rw;else a=100*(s=t.nodes[n].services[r].rw)/t.sum.services[r].rw}catch(e){var s=void 0}if(void 0===s)var l="1px";else l=a.toFixed(0)+"%";var u={width:"0.3em",marginRight:"3px",height:l};return B.createElement(Lv,{item:!0,style:u,className:i.bar},"Â ")}function Jk(e){var t=la(Kk),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(Gk,{key:e,node:e,name:r,data:n,agg:o})})))}function Yk(e){var t=e.value,n=[{label:"r",value:t.r},{label:"w",value:t.w}];return B.createElement(Nk,{values:n})}function Xk(e){var t=e.value,n=la(Kk);return B.createElement(cr,{component:"div",className:n.value},On(t.rw/1048576),"rw/s")}const Qk=function(e){var t=e.last,n=e.prev,r=e.sortKey,o=e.agg,i=e.setAgg,a=e.search,s=e.setSearch,l=la(Kk),u=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;try{var a=t.nodes[o].data}catch(e){continue}var s=i.timestamp-a.timestamp;for(var l in i.services)if(!n||l.match(n)){var u=kn(l),c=i.services[l];try{var d=a.services[l],f={};f.r=(c.net.r-d.net.r)/s,f.w=(c.net.w-d.net.w)/s,f.rw=f.r+f.w}catch(e){continue}o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[l]=f,u.namespace in r.nodes[o].namespaces?(r.nodes[o].namespaces[u.namespace].r+=f.r,r.nodes[o].namespaces[u.namespace].w+=f.w,r.nodes[o].namespaces[u.namespace].rw+=f.rw):r.nodes[o].namespaces[u.namespace]=f,l in r.sum.services?(r.sum.services[l].r+=f.r,r.sum.services[l].w+=f.w,r.sum.services[l].rw+=f.rw):r.sum.services[l]=f,u.namespace in r.sum.namespaces?(r.sum.namespaces[u.namespace].r+=f.r,r.sum.namespaces[u.namespace].w+=f.w,r.sum.namespaces[u.namespace].rw+=f.rw):r.sum.namespaces[u.namespace]=f}}return r}(t,n,a);if("ns"==o)var c=u.sum.namespaces;else c=u.sum.services;var d=Object.keys(c);"value"==r?d.sort((function(e,t){return c[t].rw-c[e].rw})):d.sort();var f=function(e){return function(t){s("ns"==o?"^"+e+"/":e),i("path")}};return B.createElement(Ei,null,d.map((function(e){return B.createElement(Li,{key:e,onClick:f(e)},B.createElement(Lv,{container:!0,className:l.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:12,sm:6,className:l.itemTitle},e),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Jk,{data:u,agg:o,name:e})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Yk,{value:c[e]})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(Xk,{value:c[e]}))))})))};var Zk=function(e){return{itemGrid:{flexWrap:"wrap"},itemTitle:{whiteSpace:"nowrap"},mapGrid:{flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-end",height:"1.5em"},value:{textAlign:"right"},bar:{width:"0.3em",marginRight:"3px",background:e.palette.primary.main,borderBottomWidth:"1px",borderBottomStyle:"solid",borderBottomColor:e.palette.primary.main,transition:"height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms"}}};function e_(e){var t=e.data,n=e.node,r=e.name,o=e.agg,i=la(Zk);try{if("ns"==o)var a=100*(s=t.nodes[n].namespaces[r].rwb)/t.sum.namespaces[r].rwb;else a=100*(s=t.nodes[n].services[r].rwb)/t.sum.services[r].rwb}catch(e){var s=void 0}if(void 0===s)var l="1px";else l=a.toFixed(0)+"%";var u={height:l};return B.createElement(Lv,{item:!0,style:u,className:i.bar},"Â ")}function t_(e){var t=la(Zk),n=e.data,r=e.name,o=e.agg,i=Object.keys(n.nodes).sort();return B.createElement(Lv,{container:!0,className:t.mapGrid,spacing:0},i.map((function(e){return B.createElement(e_,{key:e,node:e,name:r,data:n,agg:o})})))}function n_(e){var t=e.value,n=la(Zk);return B.createElement(cr,{component:"div",className:n.value},On(t.rwb/1048576),"b/s")}function r_(e){var t=e.value,n=[{label:"r",value:t.rb},{label:"w",value:t.wb}];return B.createElement(Nk,{values:n})}const o_=function(e){var t=e.last,n=e.prev,r=e.sortKey,o=e.agg,i=e.setAgg,a=e.search,s=e.setSearch,l=la(Zk),u=function(e,t,n){var r={nodes:{},sum:{namespaces:{},services:{}}};if(!e||void 0===e.nodes)return r;for(var o in e.nodes){var i=e.nodes[o].data;try{var a=t.nodes[o].data}catch(e){continue}var s=i.timestamp-a.timestamp;for(var l in i.services)if(!n||l.match(n)){var u=kn(l),c=i.services[l];try{var d=a.services[l],f={};f.rb=(c.net.rb-d.net.rb)/s,f.wb=(c.net.wb-d.net.wb)/s,f.rwb=f.rb+f.wb}catch(e){continue}o in r.nodes||(r.nodes[o]={namespaces:{},services:{}}),r.nodes[o].services[l]=f,u.namespace in r.nodes[o].namespaces?(r.nodes[o].namespaces[u.namespace].rb+=f.rb,r.nodes[o].namespaces[u.namespace].wb+=f.wb,r.nodes[o].namespaces[u.namespace].rwb+=f.rwb):r.nodes[o].namespaces[u.namespace]=f,l in r.sum.services?(r.sum.services[l].rb+=f.rb,r.sum.services[l].wb+=f.wb,r.sum.services[l].rwb+=f.rwb):r.sum.services[l]=f,u.namespace in r.sum.namespaces?(r.sum.namespaces[u.namespace].rb+=f.rb,r.sum.namespaces[u.namespace].wb+=f.wb,r.sum.namespaces[u.namespace].rwb+=f.rwb):r.sum.namespaces[u.namespace]=f}}return r}(t,n,a);if("ns"==o)var c=u.sum.namespaces;else c=u.sum.services;var d=Object.keys(c);"value"==r?d.sort((function(e,t){return c[t].rwb-c[e].rwb})):d.sort();var f=function(e){return function(t){s("ns"==o?"^"+e+"/":e),i("path")}};return B.createElement(Ei,null,d.map((function(e){return B.createElement(Li,{key:e,onClick:f(e)},B.createElement(Lv,{container:!0,alignItems:"center",className:l.itemGrid,spacing:1},B.createElement(Lv,{item:!0,xs:12,sm:6,className:l.itemTitle},e),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(t_,{data:u,agg:o,name:e})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(r_,{value:c[e]})),B.createElement(Lv,{item:!0,xs:4,sm:2},B.createElement(n_,{value:c[e]}))))})))};var i_=o(6327),a_=o(5697),s_=o(1706),l_=o(884),u_=o(1260),c_=["children","value","index"],d_=function(e){return{root:{marginTop:e.spacing(3)},tabs:{marginBottom:e.spacing(2)}}};function f_(e){var t=e.search,n=e.searchOpen,r=e.setSearchOpen,o=ee().t;return B.createElement(bd,{title:o("Filter")},B.createElement(gi,{onClick:function(e){t||r(!n)}},B.createElement(cb.A,null)))}function p_(e){var t=e.agg,n=e.setAgg,r=ee().t;return B.createElement(bd,{title:r("ns"==t?"Toggle object aggregation":"Toggle namespace aggregation")},B.createElement(gi,{onClick:function(){n("ns"==t?"path":"ns")}},"ns"==t?B.createElement(l_.A,null):B.createElement(u_.A,null)))}function h_(e){var t=e.playing,n=e.play,r=e.pause,o=ee().t;return B.createElement(bd,{title:o(t?"Pause feed":"Resume feed")},B.createElement(gi,{onClick:function(){t?r():n()}},t?B.createElement(i_.A,null):B.createElement(Nu.A,null)))}function m_(e){var t=e.sortKey,n=e.setSortKey,r=ee().t;return B.createElement(bd,{title:r("value"==t?"Toggle sort by name":"Toggle sort by metric")},B.createElement(gi,{onClick:function(e){n("value"==t?"alpha":"value")}},"value"==t?B.createElement(a_.A,null):B.createElement(s_.A,null)))}function g_(e){return{id:"simple-tab-".concat(e),"aria-controls":"simple-tabpanel-".concat(e)}}function v_(e){var t=e.children,n=e.value,r=e.index,o=wp(e,c_);return n!==r?null:B.createElement(cr,(0,Jt.A)({component:"div",role:"tabpanel",id:"simple-tabpanel-".concat(r),"aria-labelledby":"simple-tab-".concat(r)},o),B.createElement(nv,{p:0},t))}function y_(e){var t=e.search,n=e.setSearch,r=ee(),o=r.t;return r.i18n,B.createElement(Bm,{id:"filter",label:o("Filter"),value:t,onChange:function(e){n(e.target.value)},margin:"none",variant:"outlined",type:"search",fullWidth:!0,autoFocus:!0})}const b_=function(e){var t=gk(),n=t.last,r=t.prev,o=t.pause,i=t.play,a=t.playing,s=fe((0,B.useState)("value"),2),l=s[0],u=s[1],c=fe((0,B.useState)("ns"),2),d=c[0],f=c[1],p=fe((0,B.useState)(0),2),h=p[0],m=p[1],g=fe((0,B.useState)(""),2),v=g[0],y=g[1],b=fe((0,B.useState)(!1),2),w=b[0],E=b[1],S=ee(),x=(S.i18n,S.t),A=la(d_);return B.createElement(mv,{className:A.root},B.createElement(xv,{title:x("Statistics"),subheader:x("Aggregation by {{agg}}",{agg:d}),action:B.createElement(B.Fragment,null,B.createElement(f_,{search:v,searchOpen:w,setSearchOpen:E}),B.createElement(p_,{agg:d,setAgg:f}),B.createElement(m_,{sortKey:l,setSortKey:u}),B.createElement(h_,{play:i,pause:o,playing:a}))}),B.createElement(Cv,null,(v||w)&&B.createElement(y_,{search:v,setSearch:y}),B.createElement(Hg,{value:h,onChange:function(e,t){m(t)},"aria-label":"Statistic selector",indicatorColor:"primary",textColor:"primary",className:A.tabs,variant:"scrollable"},B.createElement(Gg,(0,Jt.A)({label:"Mem"},g_(0))),B.createElement(Gg,(0,Jt.A)({label:"Cpu"},g_(1))),B.createElement(Gg,(0,Jt.A)({label:"Tasks"},g_(2))),B.createElement(Gg,(0,Jt.A)({label:"Disk I/O"},g_(3))),B.createElement(Gg,(0,Jt.A)({label:"Disk B/W"},g_(4))),B.createElement(Gg,(0,Jt.A)({label:"Net I/O"},g_(5))),B.createElement(Gg,(0,Jt.A)({label:"Net B/W"},g_(6)))),B.createElement(v_,{value:h,index:0},B.createElement(_k,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:1},B.createElement(Ek,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:2},B.createElement(Tk,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:3},B.createElement(Uk,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:4},B.createElement(qk,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:5},B.createElement(Qk,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y})),B.createElement(v_,{value:h,index:6},B.createElement(o_,{prev:r,last:n,sortKey:l,agg:d,setAgg:f,search:v,setSearch:y}))))};var w_=function(e){return{root:{marginTop:e.spacing(3),padding:e.spacing(3)}}};function E_(e){var t=ee(),n=t.t,r=(t.i18n,la(w_));return B.createElement(So,{className:r.root},B.createElement(cr,{component:"div",variant:"h2",align:"center"},B.createElement(Vw.A,{fontSize:"large",color:"secondary"})),B.createElement(cr,{component:"div",variant:"subtitle1",align:"center"},n("Page Not Found")))}const S_=(0,he.dZ)((function(e){var t=fe(ve(),2);return t[0].nav,t[1],B.createElement(Et,null,B.createElement(bt,{path:"/",element:B.createElement(Jv,null)}),B.createElement(bt,{path:"/authentication/callback",element:B.createElement(Jv,null)}),B.createElement(bt,{path:"/threads",element:B.createElement(Cy,null)}),B.createElement(bt,{path:"/deploy",element:B.createElement(sv,null)}),B.createElement(bt,{path:"/heartbeats",element:B.createElement(mA,null)}),B.createElement(bt,{path:"/arbitrators",element:B.createElement(wA,null)}),B.createElement(bt,{path:"/nodes",element:B.createElement(wb,null)}),B.createElement(bt,{path:"/networks",element:B.createElement(XA,null)}),B.createElement(bt,{path:"/pools",element:B.createElement(KA,null)}),B.createElement(bt,{path:"/objects",element:B.createElement(DE,null)}),B.createElement(bt,{path:"/services",element:B.createElement(DE,{kind:"svc"})}),B.createElement(bt,{path:"/volumes",element:B.createElement(DE,{kind:"vol"})}),B.createElement(bt,{path:"/configs",element:B.createElement(DE,{kind:"cfg"})}),B.createElement(bt,{path:"/secrets",element:B.createElement(DE,{kind:"sec"})}),B.createElement(bt,{path:"/users",element:B.createElement(DE,{kind:"usr"})}),B.createElement(bt,{path:"/network",element:B.createElement(ek,null)}),B.createElement(bt,{path:"/node",element:B.createElement(hE,null)}),B.createElement(bt,{path:"/object",element:B.createElement(Hx,null)}),B.createElement(bt,{path:"/instance",element:B.createElement(fA,null)}),B.createElement(bt,{path:"/user",element:B.createElement(MA,null)}),B.createElement(bt,{path:"/stats",element:B.createElement(b_,null)}),B.createElement(bt,{path:"/api",element:B.createElement(mk,null)}),B.createElement(bt,{element:B.createElement(E_,null)}))})),x_=function(e){console.log("auth completed, returning to application")};var A_=function(e){return{control:{marginBottom:e.spacing(2)},root:{marginTop:e.spacing(4)}}};const k_=function(e){var t=fe((0,B.useState)(""),2),n=t[0],r=t[1],o=fe((0,B.useState)(""),2),i=o[0],a=o[1],s=fe(ve(),2),l=(s[0].basicLogin,s[1]),u=ee(),c=u.t,d=(u.i18n,la(A_));function f(e){l({type:"setBasicLogin",data:{username:n,password:i}})}return B.createElement(Ls,{open:!0,"aria-labelledby":"dialog-title"},B.createElement(Zs,{id:"dialog-title"},c("Login")),B.createElement(qs,null,B.createElement(Au,{className:d.control,fullWidth:!0},B.createElement(Bm,{placeholder:c("Username"),autoFocus:!0,onChange:function(e){return r(e.target.value)}})),B.createElement(Au,{className:d.control,fullWidth:!0},B.createElement(Bm,{className:d.input,type:"password",placeholder:c("password"),onChange:function(e){return a(e.target.value)},onKeyDown:function(e){13==e.keyCode&&f()}}))),B.createElement(Us,null,B.createElement(cl,{onClick:f,disabled:!n||!i,color:"primary"},c("Submit")),B.createElement(cl,{onClick:function(e){l({type:"setAuthChoice",data:""})}},c("Change Method"))))};var __=o(5556),C_=o.n(__);function O_(e){return(0,rr.A)("MuiAppBar",e)}(0,nr.A)("MuiAppBar",["root","positionFixed","positionAbsolute","positionSticky","positionStatic","positionRelative","colorDefault","colorPrimary","colorSecondary","colorInherit","colorTransparent","colorError","colorInfo","colorSuccess","colorWarning"]);const P_=["className","color","enableColorOnDark","position"],R_=(e,t)=>e?`${null==e?void 0:e.replace(")","")}, ${t})`:t,T_=(0,Zn.Ay)(So,{name:"MuiAppBar",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`position${(0,tr.A)(n.position)}`],t[`color${(0,tr.A)(n.color)}`]]}})((({theme:e,ownerState:t})=>{const n="light"===e.palette.mode?e.palette.grey[100]:e.palette.grey[900];return(0,Jt.A)({display:"flex",flexDirection:"column",width:"100%",boxSizing:"border-box",flexShrink:0},"fixed"===t.position&&{position:"fixed",zIndex:(e.vars||e).zIndex.appBar,top:0,left:"auto",right:0,"@media print":{position:"absolute"}},"absolute"===t.position&&{position:"absolute",zIndex:(e.vars||e).zIndex.appBar,top:0,left:"auto",right:0},"sticky"===t.position&&{position:"sticky",zIndex:(e.vars||e).zIndex.appBar,top:0,left:"auto",right:0},"static"===t.position&&{position:"static"},"relative"===t.position&&{position:"relative"},!e.vars&&(0,Jt.A)({},"default"===t.color&&{backgroundColor:n,color:e.palette.getContrastText(n)},t.color&&"default"!==t.color&&"inherit"!==t.color&&"transparent"!==t.color&&{backgroundColor:e.palette[t.color].main,color:e.palette[t.color].contrastText},"inherit"===t.color&&{color:"inherit"},"dark"===e.palette.mode&&!t.enableColorOnDark&&{backgroundColor:null,color:null},"transparent"===t.color&&(0,Jt.A)({backgroundColor:"transparent",color:"inherit"},"dark"===e.palette.mode&&{backgroundImage:"none"})),e.vars&&(0,Jt.A)({},"default"===t.color&&{"--AppBar-background":t.enableColorOnDark?e.vars.palette.AppBar.defaultBg:R_(e.vars.palette.AppBar.darkBg,e.vars.palette.AppBar.defaultBg),"--AppBar-color":t.enableColorOnDark?e.vars.palette.text.primary:R_(e.vars.palette.AppBar.darkColor,e.vars.palette.text.primary)},t.color&&!t.color.match(/^(default|inherit|transparent)$/)&&{"--AppBar-background":t.enableColorOnDark?e.vars.palette[t.color].main:R_(e.vars.palette.AppBar.darkBg,e.vars.palette[t.color].main),"--AppBar-color":t.enableColorOnDark?e.vars.palette[t.color].contrastText:R_(e.vars.palette.AppBar.darkColor,e.vars.palette[t.color].contrastText)},{backgroundColor:"var(--AppBar-background)",color:"inherit"===t.color?"inherit":"var(--AppBar-color)"},"transparent"===t.color&&{backgroundImage:"none",backgroundColor:"transparent",color:"inherit"}))})),F_=B.forwardRef((function(e,t){const n=(0,er.A)({props:e,name:"MuiAppBar"}),{className:r,color:o="primary",enableColorOnDark:i=!1,position:a="fixed"}=n,s=(0,qn.A)(n,P_),l=(0,Jt.A)({},n,{color:o,position:a,enableColorOnDark:i}),u=(e=>{const{color:t,position:n,classes:r}=e,o={root:["root",`color${(0,tr.A)(t)}`,`position${(0,tr.A)(n)}`]};return(0,Qn.A)(o,O_,r)})(l);return(0,ir.jsx)(T_,(0,Jt.A)({square:!0,component:"header",ownerState:l,elevation:4,className:(0,Kn.A)(u.root,r,"fixed"===a&&"mui-fixed"),ref:t},s))})),j_=(e,t)=>(0,Jt.A)({WebkitFontSmoothing:"antialiased",MozOsxFontSmoothing:"grayscale",boxSizing:"border-box",WebkitTextSizeAdjust:"100%"},t&&!e.vars&&{colorScheme:e.palette.mode}),N_=e=>(0,Jt.A)({color:(e.vars||e).palette.text.primary},e.typography.body1,{backgroundColor:(e.vars||e).palette.background.default,"@media print":{backgroundColor:(e.vars||e).palette.common.white}}),I_=function(e){const t=(0,er.A)({props:e,name:"MuiCssBaseline"}),{children:n,enableColorScheme:r=!1}=t;return(0,ir.jsxs)(B.Fragment,{children:[(0,ir.jsx)(Lp,{styles:e=>((e,t=!1)=>{var n;const r={};t&&e.colorSchemes&&Object.entries(e.colorSchemes).forEach((([t,n])=>{var o;r[e.getColorSchemeSelector(t).replace(/\s*&/,"")]={colorScheme:null==(o=n.palette)?void 0:o.mode}}));let o=(0,Jt.A)({html:j_(e,t),"*, *::before, *::after":{boxSizing:"inherit"},"strong, b":{fontWeight:e.typography.fontWeightBold},body:(0,Jt.A)({margin:0},N_(e),{"&::backdrop":{backgroundColor:(e.vars||e).palette.background.default}})},r);const i=null==(n=e.components)||null==(n=n.MuiCssBaseline)?void 0:n.styleOverrides;return i&&(o=[o,i]),o})(e,r)}),n]})},L_=["getTrigger","target"];function M_(e,t){const{disableHysteresis:n=!1,threshold:r=100,target:o}=t,i=e.current;return o&&(e.current=void 0!==o.pageYOffset?o.pageYOffset:o.scrollTop),!(!n&&void 0!==i&&e.current<i)&&e.current>r}const D_="undefined"!=typeof window?window:null;var B_=o(3967);const U_=["className","component","disableGutters","fixed","maxWidth","classes"],z_=(0,Bl.A)(),$_=Ml("div",{name:"MuiContainer",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`maxWidth${(0,B_.A)(String(n.maxWidth))}`],n.fixed&&t.fixed,n.disableGutters&&t.disableGutters]}}),H_=e=>(0,Dl.A)({props:e,name:"MuiContainer",defaultTheme:z_}),W_=function(e={}){const{createStyledComponent:t=$_,useThemeProps:n=H_,componentName:r="MuiContainer"}=e,o=t((({theme:e,ownerState:t})=>(0,Jt.A)({width:"100%",marginLeft:"auto",boxSizing:"border-box",marginRight:"auto",display:"block"},!t.disableGutters&&{paddingLeft:e.spacing(2),paddingRight:e.spacing(2),[e.breakpoints.up("sm")]:{paddingLeft:e.spacing(3),paddingRight:e.spacing(3)}})),(({theme:e,ownerState:t})=>t.fixed&&Object.keys(e.breakpoints.values).reduce(((t,n)=>{const r=n,o=e.breakpoints.values[r];return 0!==o&&(t[e.breakpoints.up(r)]={maxWidth:`${o}${e.breakpoints.unit}`}),t}),{})),(({theme:e,ownerState:t})=>(0,Jt.A)({},"xs"===t.maxWidth&&{[e.breakpoints.up("xs")]:{maxWidth:Math.max(e.breakpoints.values.xs,444)}},t.maxWidth&&"xs"!==t.maxWidth&&{[e.breakpoints.up(t.maxWidth)]:{maxWidth:`${e.breakpoints.values[t.maxWidth]}${e.breakpoints.unit}`}})));return B.forwardRef((function(e,t){const i=n(e),{className:a,component:s="div",disableGutters:l=!1,fixed:u=!1,maxWidth:c="lg"}=i,d=(0,qn.A)(i,U_),f=(0,Jt.A)({},i,{component:s,disableGutters:l,fixed:u,maxWidth:c}),p=((e,t)=>{const{classes:n,fixed:r,disableGutters:o,maxWidth:i}=e,a={root:["root",i&&`maxWidth${(0,B_.A)(String(i))}`,r&&"fixed",o&&"disableGutters"]};return(0,Qn.A)(a,(e=>(0,rr.A)(t,e)),n)})(f,r);return(0,ir.jsx)(o,(0,Jt.A)({as:s,ownerState:f,className:(0,Kn.A)(p.root,a),ref:t},d))}))}({createStyledComponent:(0,Zn.Ay)("div",{name:"MuiContainer",slot:"Root",overridesResolver:(e,t)=>{const{ownerState:n}=e;return[t.root,t[`maxWidth${(0,tr.A)(String(n.maxWidth))}`],n.fixed&&t.fixed,n.disableGutters&&t.disableGutters]}}),useThemeProps:e=>(0,er.A)({props:e,name:"MuiContainer"})}),V_="#ffa000";var q_=o(1338),K_=o(3542),G_=o(5878),J_=o(9577);const Y_=B.createContext(null);function X_(){return B.useContext(Y_)}const Q_="function"==typeof Symbol&&Symbol.for?Symbol.for("mui.nested"):"__THEME_NESTED__",Z_=function(e){const{children:t,theme:n}=e,r=X_(),o=B.useMemo((()=>{const e=null===r?n:function(e,t){return"function"==typeof t?t(e):(0,Jt.A)({},e,t)}(r,n);return null!=e&&(e[Q_]=null!==r),e}),[n,r]);return(0,ir.jsx)(Y_.Provider,{value:o,children:t})},eC={};function tC(e,t,n,r=!1){return B.useMemo((()=>{const o=e&&t[e]||t;if("function"==typeof n){const i=n(o),a=e?(0,Jt.A)({},t,{[e]:i}):i;return r?()=>a:a}return e?(0,Jt.A)({},t,{[e]:n}):(0,Jt.A)({},t,n)}),[e,t,n,r])}const nC=function(e){const{children:t,theme:n,themeId:r}=e,o=(0,od.A)(eC),i=X_()||eC,a=tC(r,o,n),s=tC(r,i,n,!0);return(0,ir.jsx)(Z_,{theme:s,children:(0,ir.jsx)(Uo.T.Provider,{value:a,children:t})})},rC=["theme"];function oC(e){let{theme:t}=e,n=(0,qn.A)(e,rC);const r=t[Gr.A];return(0,ir.jsx)(nC,(0,Jt.A)({},n,{themeId:r?Gr.A:void 0,theme:r||t}))}function iC(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){})))}catch(e){}return(iC=function(){return!!e})()}function aC(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function sC(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?aC(Object(n),!0).forEach((function(t){ue(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):aC(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}var lC=function(e){return(0,Zg.A)({palette:{mode:e.theme?e.theme:"light",primary:{main:"light"==e.theme?J_.A[900]:J_.A[100]},secondary:{main:"#ff392b"}},status:{up:K_.A[500],danger:q_.A[500],error:q_.A[500],warning:V_,notapplicable:G_.A[500]},overrides:{MuiAppBar:{colorPrimary:J_.A[900]}},typography:{fontWeight:300,fontWeightLight:200,fontWeightRegular:300,fontWeightMedium:400}})};function uC(e){var t=function(e={}){const{getTrigger:t=M_,target:n=D_}=e,r=(0,qn.A)(e,L_),o=B.useRef(),[i,a]=B.useState((()=>t(o,r)));return B.useEffect((()=>{const e=()=>{a(t(o,(0,Jt.A)({target:n},r)))};return e(),n.addEventListener("scroll",e,{passive:!0}),()=>{n.removeEventListener("scroll",e,{passive:!0})}}),[n,t,JSON.stringify(r)]),i}({target:window});return B.createElement(go,{appear:!1,direction:"down",in:!t},e.children)}uC.propTypes={children:C_().node.isRequired};var cC=function(e){return{root:{padding:e.spacing(3,2),marginTop:e.spacing(3)}}},dC=function(){return B.createElement(hC,null,B.createElement(fC,null,B.createElement(pC,null,B.createElement(nv,{fontWeight:300},B.createElement(Ht,null,B.createElement(mC,null,B.createElement(gC,null)))))))},fC=function(e){var t=fe(ve(),2),n=t[0].theme;return t[1],B.createElement(oC,{theme:lC({theme:n})},e.children)},pC=function(e){var t=ee().t,n=B.createRef(),r=function(e){return function(){n.current.closeSnackbar(e)}};return B.createElement(tp,{maxSnack:2,ref:n,action:function(e){return B.createElement(cl,{onClick:r(e),color:"inherit"},t("Dismiss"))}},e.children)},hC=function(e){var t={theme:localStorage.getItem("opensvc.theme")||"light",authChoice:localStorage.getItem("opensvc.authChoice"),cstat:{},user:{},basicLogin:{},alerts:[],eventSourceAlive:!1,authenticated:!1};return B.createElement(ge,{initialState:t,reducer:function(e,t){switch(t.type){case"loadUser":return sC(sC({},e),{},{user:t.data});case"setAuthenticated":return sC(sC({},e),{},{authenticated:t.data});case"setEventSourceAlive":return t.data==e.eventSourceAlive?e:sC(sC({},e),{},{eventSourceAlive:t.data});case"setBasicLogin":return sC(sC({},e),{},{basicLogin:t.data});case"setAuthChoice":return localStorage.setItem("opensvc.authChoice",t.data),sC(sC({},e),{},{authChoice:t.data});case"setTheme":return localStorage.setItem("opensvc.theme",t.data),sC(sC({},e),{},{theme:t.data});case"loadCstat":return void 0===t.data.cluster?e:(document.title!=t.data.cluster.name&&(document.title=t.data.cluster.name),sC(sC({},e),{},{cstat:t.data}));case"pushAlerts":var n=e.alerts.concat(t.data);return sC(sC({},e),{},{alerts:n});case"pushAlert":return n=e.alerts,t.data.date=new Date,n.push(t.data),sC(sC({},e),{},{alerts:n});case"closeAlert":return(n=e.alerts).splice(t.i,1),sC(sC({},e),{},{alerts:n});default:return e}}},e.children)};function mC(e){var t=wn(),n=(lt(),fe(ve(),2)),r=n[0],o=r.authChoice,i=r.user,a=r.basicLogin;n[1];try{(0,he.km)().oidcUser}catch(e){var s=null}if(!t)return null;if(!("basic"!=o||a.username&&a.password))return B.createElement(k_,null);if(!o&&!s&&"/authentication/callback"!=location.pathname)return B.createElement(dl,null);if(!s&&"/authentication/callback"!=location.pathname&&i&&401==i.status)return B.createElement(pl,null);try{var l="openid"==o}catch(e){l=!1}return B.createElement(he.sH,{configuration:bl(t),loggerLevel:he.cU.DEBUG,notAuthenticated:fl,notAuthorized:pl,authenticating:hl,callbackComponentOverride:x_,isEnabled:l},B.createElement(he.zR,null,e.children))}function gC(e){return B.createElement(B.Fragment,null,B.createElement(I_,null),B.createElement(uC,null,B.createElement(F_,{color:"default"},B.createElement(Ss,null))),B.createElement(Qa,null),B.createElement(W_,null,B.createElement(vC,null,B.createElement(S_,null))))}var vC=function(e){function t(e){var n;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,t),ue(ae(n=function(e,t,n){return t=se(t),function(e,t){if(t&&("object"==re(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return ae(e)}(e,iC()?Reflect.construct(t,n||[],se(e).constructor):t.apply(e,n))}(this,t,[e])),"handleResetButtonClick",(function(){var e=fe(n.context,2);ne(e[0]),e[1],n.setState((function(e){return{error:null,errorInfo:null}}))})),n.state={error:null,errorInfo:null},n}var n,r;return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&le(e,t)}(t,e),n=t,(r=[{key:"componentDidCatch",value:function(e,t){this.setState({error:e,errorInfo:t})}},{key:"render",value:function(){return this.state.error?B.createElement(yC,{error:this.state.error,info:this.state.errorInfo,clear:this.handleResetButtonClick}):this.props.children}}])&&ie(n.prototype,r),Object.defineProperty(n,"prototype",{writable:!1}),t}(B.Component);function yC(e){var t=la(cC),n=lt();return B.createElement(So,{className:t.root},B.createElement(cr,{variant:"h5",component:"h3"},"Something went wrong."),B.createElement(cl,{color:"secondary",onClick:function(){n("/"),e.clear()}},"Clear"),B.createElement("br",null),B.createElement("details",{style:{whiteSpace:"pre-wrap"}},e.error&&e.error.toString(),B.createElement("br",null),e.info.componentStack))}ue(vC,"contextType",me);var bC=document.querySelector("#app");(0,pe.H)(bC).render(B.createElement(dC,null)),console.log("running app version ",vs)})()})(); 0707010001f13c000081a400000000000000000000000168ac987800000c61000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/html/index.js.LICENSE.txt  /*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <http://feross.org>
 * @license  MIT
 */

/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */

/*!
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.9.0
*/

/*! (c) Stefan Thomas | https://github.com/bitcoinjs/bitcoinjs-lib
 */

/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
 */

/*! CryptoJS v3.1.2 core-fix.js
 * code.google.com/p/crypto-js
 * (c) 2009-2013 by Jeff Mott. All rights reserved.
 * code.google.com/p/crypto-js/wiki/License
 * THIS IS FIX of 'core.js' to fix Hmac issue.
 * https://code.google.com/p/crypto-js/issues/detail?id=84
 * https://crypto-js.googlecode.com/svn-history/r667/branches/3.x/src/core.js
 */

/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval
 */

/*! https://mths.be/punycode v1.4.1 by @mathias */

/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */

/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */

/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */

/**
 * @license
 * Lodash <https://lodash.com/>
 * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
 * Released under MIT license <https://lodash.com/license>
 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
 */

/**
 * @license React
 * react-dom.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * @license React
 * react-is.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * @license React
 * react-jsx-runtime.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * @license React
 * react.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 * @license React
 * scheduler.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/** @license React v16.13.1
 * react-is.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
   0707010001f13d000041ed0000000000000000000000086a102a9300000000000000e600010003ffffffffffffffff0000001f00000000root/usr/share/opensvc/opensvc    0707010001f1b0000041ed0000000000000000000000046a102a9200000000000000e600010003ffffffffffffffff0000002600000000root/usr/share/opensvc/opensvc/daemon 0707010001f279000081a40000000000000000000000016a100daf0002c71b000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/daemon/monitor.py  """
Monitor Thread
"""
import codecs
import json
import logging
import os
import re
import shutil
import sys
import tempfile
import threading
import time
import uuid

import daemon.shared as shared
from core.configfile import move_config_file
from core.comm import DEFAULT_DAEMON_TIMEOUT
from core.freezer import Freezer
from env import Env
# noinspection PyUnresolvedReferences
from foreign.six.moves import queue
from utilities.naming import (factory, fmt_path, list_services,
                              resolve_path, split_path, svc_pathcf,
                              svc_pathvar)
from utilities.files import makedirs, fsum
from utilities.storage import Storage

STARTED_STATES = [
    "n/a",
    "up",
]
STOPPED_STATES = [
    "n/a",
    "down",
    "stdby up",
    "stdby down",
    "unknown",     # slavers with deleted slaves
    None,          # base-kind services
]
SHUTDOWN_STATES = [
    "n/a",
    "down",
    "stdby down",
    "unknown",     # slavers with deleted slaves
    None,          # base-kind services
]
ORCHESTRATE_STATES = (
    "ready",
    "idle",
    "wait children",
    "wait parents",
    "wait priors",
    "wait sync",
    "wait leader",
    "wait non-leader",
)
ABORT_STATES = (
    ("thaw failed", "thawed"),
    ("freeze failed", "frozen"),
    ("start failed", "started"),
    ("stop failed", "stopped"),
    ("stop failed", "purged"),
    ("stop failed", "unprovisioned"),
    ("shutdown failed", "shutdown"),
    ("delete failed", "deleted"),
    ("unprovision failed", "unprovisioned"),
    ("provision failed", "provisioned"),
    ("place failed", "placed"),
    ("purge failed", "purged"),
)
NON_LEADER_ABORT_STATES = (
    ("stop failed", "placed@"),
    ("stop failed", "placed"),
)
LEADER_ABORT_STATES = (
    ("start failed", "placed"),
    ("start failed", "placed@"),
)

ETC_NS_SKIP = len(os.path.join(Env.paths.pathetcns, ""))

# delay time before clear draining (1s)
DELAY_CLEAR_DRAINING = 1

# import cProfile
# import pstats
# pr = cProfile.Profile()

# def start_profile():
#    pr.enable()

# def stop_profile():
#    pr.disable()
#    ps = pstats.Stats(pr).sort_stats(2)
#    ps.print_stats()


class Defer(Exception):
    """
    Raised from orchestration routines to signal the condition to
    start the next step are not satisfied yet.
    """
    pass


class MonitorObjectOrchestratorManualMixin(object):
    def object_orchestrator_manual(self, svc, smon, status):
        """
        Take actions to meet global expect target, set by user or by
        object_orchestrator_auto()
        """
        if smon.global_expect is None:
            return
        instance = self.get_service_instance(svc.path, Env.nodename)
        kwargs = dict(svc=svc, smon=smon, status=status, instance=instance)
        try:
            base_global_expect, target = smon.global_expect.split("@", 1)
            fnname = "_oom_{ge}_at".format(ge=base_global_expect)
            kwargs["target"] = target
        except Exception:
            fnname = "_oom_{ge}".format(ge=smon.global_expect)
        try:
            fn = getattr(self, fnname)
        except AttributeError:
            self.log.warning("unsupported global expect on %s: %s",
                             svc.path, smon.global_expect)
            self.set_smon(svc.path, global_expect="unset")
            return
        try:
            fn(**kwargs)
        except Defer as exc:
            self.log.debug("%s %s defer %s", svc.path, fnname, exc.args[0])

    def _oom_frozen(self, svc=None, smon=None, status=None, instance=None):
        if not self.instance_frozen(svc.path):
            self.event("instance_freeze", {
                "reason": "target",
                "path": svc.path,
                "monitor": smon,
            })
            self.service_freeze(svc.path)

    def _oom_thawed(self, svc=None, smon=None, status=None, instance=None):
        if self.instance_frozen(svc.path):
            self.event("instance_thaw", {
                "reason": "target",
                "path": svc.path,
                "monitor": smon,
            })
            self.service_thaw(svc.path)

    def _oom_shutdown(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_children():
            if not self.children_down(svc):
                self.set_smon(svc.path, status="wait children")
                raise Defer("wait: children are not stopped yet")
            elif smon.status == "wait children":
                self.set_smon(svc.path, status="idle")
        
        def step_freeze():
            if self.instance_frozen(svc.path):
                return
            self.event("instance_freeze", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_freeze(svc.path)
            raise Defer("freeze: action started")

        def step_shutdown():
            if self.is_instance_shutdown(instance):
                return
            thawed_on = self.service_instances_thawed(svc.path)
            if thawed_on:
                self.duplog("info", "service %(path)s still has thawed "
                            "instances on nodes %(thawed_on)s, delay "
                            "shutdown",
                            path=svc.path,
                            thawed_on=", ".join(thawed_on))
            else:
                self.service_shutdown(svc.path)

        step_wait_children()
        step_freeze()
        step_shutdown()

    def _oom_stopped(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_children():
            if not self.children_down(svc):
                self.set_smon(svc.path, status="wait children")
                raise Defer("wait: children are not stopped yet")
            elif smon.status == "wait children":
                self.set_smon(svc.path, status="idle")
        
        def step_freeze():
            if self.instance_frozen(svc.path):
                return
            self.log.info("freeze service %s", svc.path)
            self.event("instance_freeze", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_freeze(svc.path)
            raise Defer("freeze: action started")

        def step_stop():
            if instance.avail in STOPPED_STATES:
                return
            thawed_on = self.service_instances_thawed(svc.path)
            if thawed_on:
                self.duplog("info", "service %(path)s still has thawed instances "
                            "on nodes %(thawed_on)s, delay stop",
                            path=svc.path,
                            thawed_on=", ".join(thawed_on))
            else:
                self.event("instance_stop", {
                    "reason": "target",
                    "path": svc.path,
                })
                self.service_stop(svc.path)

        step_wait_children()
        step_freeze()
        step_stop()

    def _oom_started(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_parents():
            if not self.parents_available(svc):
                self.set_smon(svc.path, status="wait parents")
                raise Defer("wait: parents are not started yet")
            elif smon.status == "wait parents":
                self.set_smon(svc.path, status="idle")

        def step_thaw():
            if not self.instance_frozen(svc.path):
                return
            self.event("instance_thaw", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_thaw(svc.path)
            raise Defer("thaw: action started")

        def step_start():
            if status in STARTED_STATES:
                return
            agg = self.get_service_agg(svc.path)
            if agg.frozen != "thawed":
                raise Defer("start: instance is not thawed yet")
            self.object_orchestrator_auto(svc, smon, status)
            raise Defer("start: action started")

        step_thaw()
        step_wait_parents()
        step_start()

    def _oom_unprovisioned(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_status():
            if smon.status not in ("unprovisioning", "stopping"):
                return
            raise Defer("wait: incompatible monitor status: %s" % smon.status)

        def step_wait_done():
            if svc.path in shared.SERVICES and not self.instance_unprovisioned(instance):
                return
            if smon.status != "idle":
                self.set_smon(svc.path, status="idle")
            raise Defer("wait: instance does not exist or already unprovisioned")

        def step_set_wait_children():
            if self.children_unprovisioned(svc):
                return
            self.set_smon(svc.path, status="wait children")
            raise Defer("wait: set wait children")

        def step_stop():
            if instance.avail in STOPPED_STATES:
                return
            self.event("instance_stop", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_stop(svc.path, force=True)
            raise Defer("stop: action started")

        def step_wait_stopped():
            agg = self.get_service_agg(svc.path)
            if agg.avail in STOPPED_STATES:
                return
            raise Defer("wait: local instance not stopped yet")

        def step_wait_children():
            if smon.status != "wait children":
                return
            if self.children_unprovisioned(svc):
                return
            raise Defer("wait: children are not unprovisioned yet")

        def step_wait_non_leader():
            if smon.status != "wait non-leader":
                return
            if self.leader_last(svc, provisioned=False, silent=True):
                return
            self.log.info("service %s still waiting non leaders", svc.path)
            raise Defer("wait: non-leader instances are not unprovisioned yet")

        def step_set_wait_non_leader():
            leader = self.leader_last(svc, provisioned=False)
            if leader:
                return leader
            self.set_smon(svc.path, status="wait non-leader")
            raise Defer("wait: set wait non-leader")

        def step_unprovision():
            self.event("instance_unprovision", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_unprovision(svc, leader)

        step_wait_status()
        step_wait_done()
        step_set_wait_children()
        step_stop()
        step_wait_stopped()
        step_wait_children()
        leader = step_wait_non_leader()  # pylint: disable=assignment-from-none
        step_set_wait_non_leader()
        step_unprovision()

    def _oom_provisioned(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_parents():
            if smon.status == "wait parents":
                if not self.parents_available(svc):
                    raise Defer("wait: parents not available")

        def step_wait_leader():
            if smon.status == "wait leader":
                if not self.leader_first(svc, provisioned=True, silent=True):
                    raise Defer("wait: leader not provisioned yet")

        def step_wait_sync():
            if smon.status == "wait sync":
                if not self.min_instances_reached(svc):
                    raise Defer("wait: the object configuration is not synced yet")

        def step_provision():
            if self.instance_provisioned(instance):
                self.set_smon(svc.path, status="idle")
                raise Defer("provision: instance already provisioned")
            if not self.min_instances_reached(svc):
                self.set_smon(svc.path, status="wait sync")
                raise Defer("provision: set wait sync")
            if self.is_natural_leader(svc) and not self.parents_available(svc):
                self.set_smon(svc.path, status="wait parents")
                raise Defer("provision: set wait parents")
            if not self.leader_first(svc, provisioned=True):
                self.set_smon(svc.path, status="wait leader")
                raise Defer("provision: set wait leader")
            self.event("instance_provision", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_provision(svc)
            raise Defer("provision: action started")

        step_wait_parents()
        step_wait_leader()
        step_wait_sync()
        step_provision()

    def _oom_deleted(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_children():
            if self.children_down(svc):
                if smon.status == "wait children":
                    self.set_smon(svc.path, status="idle")
                return
            self.set_smon(svc.path, status="wait children")
            raise Defer("wait: children are not down yet")

        def step_delete():
            if svc.path not in shared.SERVICES:
                raise Defer("delete: object does not exist")
            self.event("instance_delete", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_delete(svc.path)
            raise Defer("delete: action started")

        step_wait_children()
        step_delete()

    def _oom_purged(self, svc=None, smon=None, status=None, instance=None):
        def step_wait_status():
            if smon.status not in ("purging", "deleting", "stopping"):
                return
            raise Defer("wait: incompatible monitor status: %s" % smon.status)

        def step_wait_children():
            if self.children_unprovisioned(svc):
                return
            self.set_smon(svc.path, status="wait children")
            raise Defer("wait: children are not unprovisioned yet")

        def step_stop():
            if instance.avail in STOPPED_STATES:
                return
            self.event("instance_stop", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_stop(svc.path, force=True)
            raise Defer("stop: action started")

        def step_purge():
            agg = self.get_service_agg(svc.path)
            if agg.avail not in STOPPED_STATES:
                raise Defer("purge: object is not stopped")
            if smon.status == "wait children":
                if not self.children_unprovisioned(svc):
                    raise Defer("purge: waiting children")
            elif smon.status == "wait non-leader":
                if not self.leader_last(svc, provisioned=False, silent=True, deleted=True):
                    raise Defer("purge: waiting non-leader")
            leader = self.leader_last(svc, provisioned=False, deleted=True)
            if not leader:
                self.set_smon(svc.path, status="wait non-leader")
                raise Defer("purge: wait non-leader")
            if svc.path in shared.SERVICES and svc.kind not in ("vol", "svc"):
                # base services do not implement the purge action
                self.event("instance_delete", {
                    "reason": "target",
                    "path": svc.path,
                })
                self.service_delete(svc.path)
                raise Defer("purge: delete action started")
            if svc.path not in shared.SERVICES or not instance:
                if smon.status != "idle":
                    self.set_smon(svc.path, status="idle")
                raise Defer("purge: object or instance does not exist")
            self.event("instance_purge", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_purge(svc, leader)
            raise Defer("purge: action started")

        step_wait_status()
        step_wait_children()
        step_stop()
        step_purge()

    def _oom_aborted(self, svc=None, smon=None, status=None, instance=None):
        if smon.local_expect in (None, "started"):
            return
        self.event("instance_abort", {
            "reason": "target",
            "path": svc.path,
        })
        self.set_smon(svc.path, local_expect="unset")

    def _oom_placed(self, svc=None, smon=None, status=None, instance=None):
        """
        Flex start new instances before stopping the old.
        Failover stop old instance before stopping the new.
        """
        # refresh smon for placement attr change caused by a clear
        smon = self.get_service_monitor(svc.path)

        def step_thaw():
            if not self.instance_frozen(svc.path):
                return
            self.event("instance_thaw", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_thaw(svc.path)
            raise Defer("thaw: action started")

        def step_stop():
            if smon.placement == "leader":
                return
            if not self.has_leader(svc):
                raise Defer("stop: no peer node can takeover")
            if instance.avail in STOPPED_STATES:
                raise Defer("stop: local instance already stopped")
            if svc.topology == "flex" and not self.leaders_started(svc.path):
                raise Defer("start: flex leader instances not started")
            self.event("instance_stop", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_stop(svc.path)
            raise Defer("stop: action started")

        def step_start():
            if instance.avail in STARTED_STATES:
                return
            if svc.topology == "failover" and not self.non_leaders_stopped(svc.path):
                raise Defer("start: failover non-leader instances not stopped")
            agg = self.get_service_agg(svc.path)
            if agg.placement in ("optimal", "n/a") and agg.avail == "up":
                raise Defer("start: aggregate placement is optimal and avail up")
            self.object_orchestrator_auto(svc, smon, status)
            raise Defer("start: action started")

        step_thaw()
        step_stop()
        step_start()

    def _oom_placed_at(self, svc=None, smon=None, status=None, instance=None, target=None):
        """
        Flex start new instances before stopping the old.
        Failover stop old instance before stopping the new.
        """
        target = target.split(",")
        candidates = self.placement_candidates(
            svc, discard_frozen=False,
            discard_overloaded=False,
            discard_unprovisioned=False,
            discard_constraints_violation=False,
            discard_start_failed=False,
            discard_affinities=False,
        )

        def step_thaw():
            if not self.instance_frozen(svc.path):
                return
            self.event("instance_thaw", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_thaw(svc.path)
            raise Defer("thaw: action started")

        def step_stop():
            if Env.nodename in target:
                return
            if instance.avail in STOPPED_STATES:
                return
            if svc.topology == "flex" and not self.instances_started(svc.path, set(svc.peers) & set(target)):
                raise Defer("stop: flex destination instances not started")
            if smon.status == "stop failed":
                raise Defer("stop: local instance blocking mon status (%s)" % smon.status)
            if not (set(target) & set(candidates)):
                raise Defer("stop: no candidate to takeover")
            if not self.children_down(svc):
                self.set_smon(svc.path, status="wait children")
                raise Defer("wait: children are not stopped yet")
            elif smon.status == "wait children":
                self.set_smon(svc.path, status="idle")
            self.event("instance_stop", {
                "reason": "target",
                "path": svc.path,
            })
            self.service_stop(svc.path)
            raise Defer("stop: action started")

        def step_wait_parents():
            pavail = self.parents_available(svc)
            ptrans = self.parent_transitioning(svc)
            dep = pavail and not ptrans
            if not dep and instance.avail not in STARTED_STATES:
                self.set_smon(svc.path, status="wait parents")
                raise Defer("wait: parents are not started yet")
            if smon.status == "wait parents":
                if dep or instance.avail in STARTED_STATES:
                    self.set_smon(svc.path, status="idle")

        def step_start():
            if Env.nodename not in target:
                return
            if instance.avail in STARTED_STATES:
                return
            if svc.topology == "failover" and not self.instances_stopped(svc.path, set(svc.peers) - set(target)):
                raise Defer("start: failover source instances not stopped")
            if Env.nodename not in target:
                raise Defer("start: not a target")
            if instance.avail not in STOPPED_STATES + ["warn"]:
                raise Defer("start: local instance not stopped or warn")
            if smon.status in ("start failed", "place failed"):
                raise Defer("start: local instance blocking mon status (%s)" % smon.status)
            self.event("instance_start", {
                "reason": "target",
                "path": svc.path,
            })
            err_status = "place failed" if smon.global_expect == "placed" else "start failed"
            self.service_start(svc.path, err_status=err_status)
            raise Defer("start: action started")

        step_thaw()
        step_stop()
        step_wait_parents()
        step_start()

    def _oom_restarted_at(self, svc=None, smon=None, status=None, instance=None, target=None):
        def step_mark():
            if not smon.global_expect.startswith("restarted@"):
                return
            if smon.status != "idle":
                return
            if smon.local_expect != "started":
                if time.time() > ts + 2:
                    self.set_smon(svc.path, global_expect="unset")
                return
            if smon.local_expect_updated and ts < smon.local_expect_updated:
                return
            self.set_smon(svc.path, status="wait priors")

        def step_restart():
            if smon.status != "wait priors":
                return
            if not self.prior_instances_restarted(svc):
                return
            self.service_restart(svc.path)

        ts = float(target)
        step_mark()
        step_restart()


class Monitor(shared.OsvcThread, MonitorObjectOrchestratorManualMixin):
    """
    The monitoring thread collecting local service states and taking decisions.
    """
    name = "monitor"
    monitor_period = 0.5
    arbitrators_check_period = 60
    max_shortloops = 30
    default_stdby_nb_restart = 2
    arbitrators_data = None
    last_arbitrator_ping = 0

    def __init__(self):
        shared.OsvcThread.__init__(self)
        self._shutdown = False
        self.compat = True
        self.last_node_data = None
        self.init_steps = set()
        self.transitions = set([])

    def init(self):
        self.set_tid()
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.monitor"),
                                         {"node": Env.nodename, "component": self.name})
        self.event("monitor_started")
        self.startup = time.time()
        self.rejoin_grace_period_expired = False
        self.shortloops = 0
        self.unfreeze_when_all_nodes_joined = False
        self.node_frozen = self.freezer.node_frozen()
        self.init_data()

        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            if not self.node_frozen:
                self.event("node_freeze", data={"reason": "upgrade"})
                self.unfreeze_when_all_nodes_joined = True
                self.freezer.node_freeze()
                self.node_frozen = self.freezer.node_frozen()

        last_boot_id = shared.NODE.last_boot_id()
        boot_id = shared.NODE.asset.get_boot_id()
        try:
            # align float precision (py2/3 use different precision for mtime)
            last_boot_id = "%.02f" % float(last_boot_id)
            boot_id = "%.02f" % float(boot_id)
        except (TypeError, ValueError):
            pass
        self.log.info("boot id %s, last %s", boot_id, last_boot_id)
        self.wait_listener()
        if last_boot_id in (None, boot_id):
            self.services_init_status()
        else:
            self.kern_freeze()
            self.services_init_boot()
        shared.NODE.write_boot_id()
        self.dump_nodes_info()

        # send a first message without service status, so the peers know
        # we are in init state.
        self.update_hb_data()

    def wait_listener(self):
        while True:
            lsnr = shared.THREADS.get("listener")
            if lsnr and lsnr.stage == "ready":
                break
            time.sleep(0.2)

    def init_data(self):
        self._update_cluster_data()
        if shared.GEN > 1:
            self.log.warning("monitor init data after thread crashed at gen %s", shared.GEN)
            # inc GEN to ensure out of sequence patch detection on peer
            shared.GEN = shared.GEN + 2
            with shared.GEN_MERGED_LOCK:
                # Protect from LOCAL_GEN_MERGED_ON_PEER, PEER_GEN_MERGED delete items
                # during hb::delete_peer_data(...)
                #
                # Ensure ask full from peers
                shared.PEER_GEN_MERGED = {peer: 0 for peer in shared.PEER_GEN_MERGED}
                # no diff until one peer node receive our full
                shared.LOCAL_GEN_MERGED_ON_PEER = {peer: 0 for peer in shared.LOCAL_GEN_MERGED_ON_PEER}
            # reset GEN_DIFF to ensure fresh patch sequence
            shared.GEN_DIFF = {}
            self.transitions = set([])
        initial_data = {
            "compat": shared.COMPAT_VERSION,
            "api": shared.API_VERSION,
            "agent": shared.NODE.agent_version,
            "monitor": {
                "status": "init",
                "status_updated": time.time(),
            },
            "labels": shared.NODE.labels,
            "targets": shared.NODE.targets,
            "services": {
                "status": {},
                "config": {},
            },
            "gen": {},
            "config": {
                "csum": shared.NODE.nodeconf_csum(),
            },
            "hb": {},
        }
        self.node_data.set([], initial_data)
        for nodename in self.cluster_nodes:
            self.nodes_data.setnx([nodename], {})

    def run(self):
        try:
            self.init()
        except Exception as exc:
            self.log.exception(exc)
            raise
        try:
            while True:
                self.do()
                if self.stopped():
                    self.join_threads()
                    self.kill_procs()
                    sys.exit(0)
        except Exception as exc:
            self.log.exception(exc)

    def transition_count(self):
        return len(self.transitions)

    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 add_service(self, path):
        name, namespace, kind = split_path(path)
        svc = factory(kind)(name, namespace, node=shared.NODE, log_handlers=[])
        svc.configure_scheduler()
        shared.SERVICES[path] = svc
        return svc

    def reconfigure(self):
        """
        The node config references may have changed, update the services objects.
        """
        self._update_cluster_data()
        shared.NODE.unset_lazy("labels")
        self.node_data.set(["labels"], shared.NODE.labels)
        self.node_data.set(["config"], {"csum": shared.NODE.nodeconf_csum()})
        self.on_nodes_info_change()
        for path in [p for p in shared.SERVICES]:
            try:
                self.add_service(path)
            except Exception as exc:
                continue

    def do(self):
        terminated = self.janitor_procs() + self.janitor_threads()
        changed = self.merge_rx()
        changed |= self.mon_changed()
        if self.get_node_monitor().status == "init" and self.services_have_init_status():
            self.set_nmon(status="rejoin")
            self.rejoin_grace_period_expired = False
        if terminated == 0 and not changed and self.shortloops < self.max_shortloops:
            self.shortloops += 1
            if self.shortloops == self.max_shortloops:
                # we're very idle, take time to ...
                self.update_completions()
            with shared.MON_TICKER:
                shared.MON_TICKER.wait(self.monitor_period)
            return
        self.node_frozen = self.freezer.node_frozen()
        if 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()
        if self._shutdown:
            if len(self.procs) == 0:
                self.stop()
        else:
            self.update_cluster_data()
            self.orchestrator()
        self.update_hb_data()
        shared.wake_collector()

    #########################################################################
    #
    # 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 path, data in confs.items():
            try:
                shared.SERVICES[path]
                new_service = False
            except KeyError:
                new_service = True
            if self.has_instance_with(path, global_expect=["purged", "deleted"]):
                continue
            if Env.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 = Env.nodename
            else:
                ref_conf = data[Env.nodename]
                ref_nodename = Env.nodename
            for nodename, conf in data.items():
                if Env.nodename == nodename:
                    continue
                if new_service and Env.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 not new_service and ref_conf.scope and Env.nodename not in ref_conf.scope:
                smon = self.get_service_monitor(path)
                if not smon or smon.status == "deleting":
                    continue
                remote_nmon = self.get_node_monitor(ref_nodename)
                if remote_nmon.status in ("init", "rejoin", "upgrade", "maintenance"):
                    # scope may be incomplete during unstable remote node status
                    continue
                self.log.info("node %s has the most recent %s config, "
                              "which no longer defines %s as a node.",
                              ref_nodename, path, Env.nodename)
                # self.event("instance_stop", {
                #     "reason": "relayout",
                #     "path": svc.path,
                # })
                # self.service_stop(path, force=True)
                self.service_delete(path)
                continue
            if ref_nodename == Env.nodename:
                # we already have the most recent version
                continue
            try:
                svc = shared.SERVICES[path]
                if Env.nodename in svc.nodes and ref_nodename in svc.drpnodes:
                    # don't fetch drp config from prd nodes
                    continue
            except KeyError:
                pass
            self.log.info("node %s has the most recent %s config",
                          ref_nodename, path)
            self.fetch_service_config(path, ref_nodename)
            if new_service:
                self.init_new_service(path)
            else:
                self.service_status_fallback(path)

    def init_new_service(self, path):
        try:
            self.add_service(path)
        except Exception as exc:
            self.log.error("unbuildable service %s fetched: %s", path, exc)
            return

        try:
            svc = shared.SERVICES[path]
            if svc.kind == "svc":
                self.event("instance_freeze", {
                    "reason": "install",
                    "path": svc.path,
                })
                Freezer(path).freeze()
            if not os.path.exists(svc.paths.cf):
                return
            self.service_status_fallback(svc.path)
        except Exception:
            # can happen when deleting the service
            pass

    def fetch_service_config(self, path, nodename):
        """
        Fetch and install the most recent service configuration file, using
        the remote node listener.
        """
        req = {
            "action": "object_config",
            "options": {
                "path": path,
            },
        }
        resp = self.daemon_get(req, server=nodename, timeout=DEFAULT_DAEMON_TIMEOUT)
        if resp is None:
            self.log.error("unable to fetch service %s config from node %s: "
                           "received %s", path, nodename, resp)
            return
        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", path, nodename, resp)
            return
        with tempfile.NamedTemporaryFile(dir=Env.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 path in shared.SERVICES:
                    svc = shared.SERVICES[path]
                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", path, exc)
                    return
            else:
                results = {"errors": 0}
            if results["errors"] == 0:
                dst = svc_pathcf(path)
                makedirs(os.path.dirname(dst))
                mtime = resp.get("mtime")
                if mtime:
                    # tmpfpath updated time with be preserved by move_config_file
                    os.utime(tmpfpath, (mtime, mtime))
                move_config_file(tmpfpath, dst)
            else:
                self.log.error("the service %s config fetched from node %s is "
                               "not valid", path, nodename)
                return
            try:
                svc = self.add_service(path)
                svc.postinstall()
            except Exception as exc:
                self.log.error("service %s postinstall failed: %s", path, exc)
        finally:
            try:
                os.unlink(tmpfpath)
            except Exception:
                pass

        self.event("service_config_installed", {
            "path": path,
            "from": nodename
        })

    #########################################################################
    #
    # Node and Service Commands
    #
    #########################################################################
    def generic_callback(self, path, **kwargs):
        self.set_smon(path, **kwargs)
        self.update_hb_data()

    def node_stonith(self, node):
        proc = self.node_command(["stonith", "--node", node])

        # make sure we won't redo the stonith for another service
        for path, smon in self.iter_local_services_monitors():
            if smon.stonith == node:
                self.set_smon(path, stonith="unset")

        # wait for 10sec before giving up
        for step in range(10):
            ret = proc.poll()
            if ret is not None:
                return ret
            time.sleep(1)

        # timeout, free the caller
        self.push_proc(proc=proc)

    def service_startstandby_resources(self, path, rids, slave=None):
        self.set_smon(path, "restarting")
        cmd = ["startstandby", "--rid", ",".join(rids)]
        if slave:
            cmd += ["--slave", slave]
        proc = self.service_command(path, cmd)
        self.push_proc(
            proc=proc,
            on_success="service_start_resources_on_success",
            on_success_args=[path, rids],
            on_success_kwargs={"slave": slave},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "idle"},
        )

    def service_start_resources(self, path, rids, slave=None):
        self.set_smon(path, "restarting")
        cmd = ["start", "--rid", ",".join(rids)]
        if slave:
            cmd += ["--slave", slave]
        proc = self.service_command(path, cmd)
        self.push_proc(
            proc=proc,
            on_success="service_start_resources_on_success",
            on_success_args=[path, rids],
            on_success_kwargs={"slave": slave},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "idle"},
        )

    def service_start_resources_on_success(self, path, rids, slave=None):
        self.set_smon(path, status="idle")
        self.update_hb_data()
        changed = False
        for rid in rids:
            instance = self.get_service_instance(path, Env.nodename)
            if instance is None:
                self.reset_smon_retries(path, rid)
                changed = True
                continue
            if slave is None:
                res = instance.get("resources", {}).get(rid, {})
                if res.get("status") not in ("up", "stdby up"):
                    self.log.error("%s start returned success but resource is "
                                   "still not up", rid)
                    continue
            else:
                res = instance.get("encap", {}).get(slave, {}).get("resources", {}).get(rid, {})
                if res.get("status") not in ("up", "stdby up"):
                    self.log.error("%s start in container %s returned success "
                                   "but resource is still not up", rid, slave)
                    continue
            changed = True
            self.reset_smon_retries(path, rid)
        if changed:
            self.update_hb_data()

    def service_status(self, path):
        if Env.nodename not in shared.SERVICES[path].peers:
            self.log.info("skip status refresh on %s (foreign)", path)
            return
        smon = self.get_service_monitor(path)
        if smon.status and smon.status.endswith("ing"):
            # no need to run status, the running action will refresh the status earlier
            return
        cmd = ["status", "--refresh", "--waitlock=0"]
        if self.has_proc([path] +  cmd):
            # no need to run status twice
            return
        proc = self.service_command(path, cmd, local=False)
        self.push_proc(
            proc=proc,
            cmd=[path] + cmd,
        )

    def service_toc(self, path):
        self.set_smon(path, "tocing")
        try:
            # Do our best to have most recent log sync on file system, node is going to crash of fast reboot
            if hasattr(os, "fsync"):
                with open(os.path.join(Env.paths.pathlog, "node.log"), "a") as f:
                    f.flush()
                    os.fsync(f.fileno())
        except:
            pass
        proc = self.service_command(path, ["toc"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle", "local_expect": "unset", "expected_status": "tocing"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "toc failed"},
        )

    def service_restart(self, path, err_status="restart failed"):
        self.set_smon(path, "restarting", local_expect="unset")
        proc = self.service_command(path, ["restart"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle", "local_expect": "started", "global_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": err_status},
        )

    def service_start(self, path, err_status="start failed"):
        self.set_smon(path, "starting")
        proc = self.service_command(path, ["start"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle", "local_expect": "started"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": err_status},
        )

    def service_stop(self, path, force=False):
        self.set_smon(path, "stopping")
        cmd = ["stop"]
        if force:
            cmd.append("--force")
        proc = self.service_command(path, cmd)
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "stop failed"},
        )

    def service_shutdown(self, path):
        self.set_smon(path, "shutdown")
        proc = self.service_command(path, ["shutdown"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "shutdown failed", "local_expect": "unset"},
        )

    def service_delete(self, path):
        self.set_smon(path, "deleting", local_expect="unset")
        proc = self.service_command(path, ["delete", "--purge-collector"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "delete failed"},
        )

    def service_purge(self, svc, leader=None):
        self.set_smon(svc.path, "unprovisioning")
        if leader is None:
            leader = self.is_natural_leader(svc)
        else:
            leader = Env.nodename == leader
        cmd = ["unprovision"]
        if leader:
            cmd += ["--leader"]
        proc = self.service_command(svc.path, cmd)
        self.push_proc(
            proc=proc,
            on_success="service_purge_on_success",
            on_success_args=[svc.path],
            on_error="generic_callback",
            on_error_args=[svc.path],
            on_error_kwargs={"status": "purge failed"},
        )

    def service_purge_on_success(self, path):
        self.set_smon(path, "deleting", local_expect="unset")
        proc = self.service_command(path, ["delete", "--purge-collector"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "purge failed"},
        )

    def service_provision(self, svc):
        self.set_smon(svc.path, "provisioning")
        leader = self.is_natural_leader(svc)
        cmd = ["provision"]
        if leader:
            cmd += ["--leader", "--disable-rollback"]
        proc = self.service_command(svc.path, cmd)
        self.push_proc(
            proc=proc,
            on_success="service_thaw",
            on_success_args=[svc.path],
            on_success_kwargs={"slaves": True},
            on_error="generic_callback",
            on_error_args=[svc.path],
            on_error_kwargs={"status": "provision failed"},
        )

    def is_natural_leader(self, svc):
        candidates = self.placement_candidates(
            svc,
            discard_frozen=False,
            discard_na=False,
            discard_overloaded=False,
            discard_unprovisioned=False,
            discard_affinities=False,
            discard_start_failed=False,
            discard_constraints_violation=False
        )
        return self.placement_leader(svc, candidates)

    def service_unprovision(self, svc, leader=None):
        self.set_smon(svc.path, "unprovisioning", local_expect="unset")
        if leader is None:
            leader = self.is_natural_leader(svc)
        else:
            leader = Env.nodename == leader
        cmd = ["unprovision"]
        if leader:
            cmd += ["--leader"]
        proc = self.service_command(svc.path, cmd)
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[svc.path],
            on_success_kwargs={"status": "idle", "local_expect": "unset"},
            on_error="generic_callback",
            on_error_args=[svc.path],
            on_error_kwargs={"status": "unprovision failed"},
        )

    def wait_global_expect_change(self, path, ref, timeout):
        for step in range(timeout):
            try:
                global_expect = self.get_service_monitor(path).global_expect
            except (TypeError, KeyError):
                global_expect = None
            if global_expect != ref:
                return True
            time.sleep(1)
        return False

    def service_set_flex_instances(self, path, instances):
        svc = self.get_service(path)
        try:
            svc.set_multi([
                "flex_min=%d" % instances,
                "flex_max=%d" % instances,
                "flex_target=%d" % instances,
            ])
            return 0
        except Exception:
            return 1

    def service_create_scaler_slave(self, path, svc, data, instances=None):
        data["DEFAULT"]["scaler_slave"] = "true"
        if svc.topology == "flex" and instances is not None:
            data["DEFAULT"]["flex_target"] = instances
        try:
            del data["metadata"]
        except KeyError:
            pass
        for kw in ("scale", "id"):
            try:
                del data["DEFAULT"][kw]
            except KeyError:
                pass
        try:
            newsvc = factory("svc")(path, svc.namespace, node=shared.NODE, cd=data)
            newsvc.commit()
            del newsvc
            self.service_status_fallback(path)
        except Exception as exc:
            self.log.error("create %s failed: %s", path, exc)
            self.set_smon(path, "create failed")
            return

        try:
            ret = self.wait_service_config_consensus(path, svc.peers)
        except Exception as exc:
            self.log.exception(exc)
            return

        self.set_smon(path, global_expect="thawed")
        self.wait_global_expect_change(path, "thawed", 600)

        self.set_smon(path, global_expect="provisioned")
        self.wait_global_expect_change(path, "provisioned", 600)

    def service_freeze(self, path):
        self.set_smon(path, "freezing")
        proc = self.service_command(path, ["freeze"])
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "idle"},
        )

    def service_thaw(self, path, slaves=False):
        self.set_smon(path, "thawing")
        cmd = ["thaw"]
        if slaves:
            cmd += ["--master", "--slaves"]
        proc = self.service_command(path, cmd)
        self.push_proc(
            proc=proc,
            on_success="generic_callback",
            on_success_args=[path],
            on_success_kwargs={"status": "idle"},
            on_error="generic_callback",
            on_error_args=[path],
            on_error_kwargs={"status": "idle"},
        )

    def services_have_init_status(self):
        need_log = (time.time() - self.startup) > 60
        for path in list_services():
            try:
                svc = shared.SERVICES[path]
            except KeyError:
                if need_log:
                    self.duplog("info", "init waiting for %(path)s daemon object allocation", path=path)
                return False
            if Env.nodename not in svc.peers:
                # a configuration file is present, but foreign: don't wait for a status.json
                continue
            fpath = os.path.join(svc.var_d, "status.json")
            try:
                mtime = os.path.getmtime(fpath)
            except Exception:
                if need_log:
                    self.duplog("info", "init waiting for %(path)s status to exist", path=path)
                return False
            if self.startup > mtime:
                if need_log:
                    self.duplog("info", "init waiting for %(path)s status refresh", path=path)
                return False
        return True

    def services_purge_status(self, paths=None):
        paths = paths or list_services()
        for path in paths:
            fpath = svc_pathvar(path, "status.json")
            try:
                os.unlink(fpath)
            except Exception as exc:
                pass

    def init_steps_done(self):
        """
        Return true if both boot and status commands are finished.
        Used to determine if we can run service_status() from the monitor loop.
        """
        return len(self.init_steps) == 2

    def add_init_step(self, step):
        """
        Used as a callback of initial boot and status commands.
        """
        self.init_steps.add(step)

    def services_init_status(self):
        svcs = list_services()
        if not svcs:
            self.log.info("no objects to get an initial status from")
            return
        self.services_purge_status(paths=svcs)
        proc = self.service_command(",".join(svcs), ["status", "--parallel", "--refresh"], local=False)
        self.add_init_step("boot")
        self.push_proc(
            proc=proc,
            cmd="init status",
            on_success="add_init_step",
            on_success_args=["status"],
            on_error="add_init_step",
            on_error_args=["status"],
        )

    def services_init_boot(self):
        self.services_purge_status()
        proc1 = self.service_command(",".join(list_services(kinds=["vol", "svc"])), ["boot", "--parallel"])
        self.push_proc(
            proc=proc1,
            on_success="add_init_step",
            on_success_args=["boot"],
            on_error="add_init_step",
            on_error_args=["boot"],
        )
        proc2 = self.service_command(",".join(list_services(kinds=["usr", "cfg", "sec", "ccfg"])), ["status", "--parallel", "--refresh"], local=False)
        self.push_proc(
            proc=proc2,
            on_success="add_init_step",
            on_success_args=["status"],
            on_error="add_init_step",
            on_error_args=["status"],
        )

    #########################################################################
    #
    # Orchestration
    #
    #########################################################################
    def orchestrator(self):
        if self.get_node_monitor().status == "init":
            return

        if self.missing_beating_peer_data():
            # just after a split+rejoin, we don't have the peers full dataset
            # even if all hb rx are reporting beating. Avoid taking decisions
            # during this transient period.
            return

        # node
        self.node_orchestrator()

        # services (iterate over deleting services too)
        paths = self.prioritized_paths()
        for path in paths:
            self.clear_start_failed(path)
            if self.transitions_maxed():
                break
            if self.status_older_than_cf(path):
                # self.log.info("%s status dump is older than its config file",
                #               path)
                instance = self.get_service_instance(path, Env.nodename)
                if instance:
                    self.service_status(path)
                continue
            svc = self.get_service(path)
            self.resources_orchestrator(path, svc)
            self.object_orchestrator(path, svc)
        self.sync_services_conf()

    def prioritized_paths(self):

        def prio(path):
            try:
                priority = self.instances_status_data.get([path, "priority"])
                if priority is None:
                    return Env.default_priority
                else:
                    return priority
            except KeyError:
                return Env.default_priority

        paths = self.instances_status_data.keys()
        data = [(path, prio(path)) for path in paths]
        return [d[0] for d in sorted(data, key=lambda x: x[1])]

    def transitions_maxed(self):
        transitions = self.transition_count()
        if transitions < shared.NODE.max_parallel:
            return False
        self.duplog("info", "delay services orchestration: "
                    "%(transitions)d/%(max)d transitions already "
                    "in progress", transitions=transitions,
                    max=shared.NODE.max_parallel)
        return True

    def resources_orchestrator(self, path, svc):
        if self.get_node_monitor().status == "shutting":
            return
        if svc is None:
            return
        if self.node_frozen or self.instance_frozen(path):
            # self.log.info("resource %s orchestrator out (frozen)", svc.path)
            return
        if svc.disabled:
            # self.log.info("resource %s orchestrator out (disabled)", svc.path)
            return

        def is_delayed_restart(rid):
            try:
                restart_delay = svc.get_resource(rid, with_encap=True).restart_delay
            except AttributeError:
                return False
            if not restart_delay:
                return False
            if time.time() - self.get_smon_retries_updated(svc.path, rid) < restart_delay:
                return True
            return False

        def monitored_resource(svc, rid, resource, smon):
            if resource.get("disable"):
                return False
            if smon.local_expect != "started":
                return False
            if resource.get("stopped") is True:
                return False
            try:
                nb_restart = svc.get_resource(rid, with_encap=True).nb_restart
            except AttributeError:
                nb_restart = 0
            retries = self.get_smon_retries(svc.path, rid)

            if retries > nb_restart:
                return False
            elif retries == nb_restart:
                if nb_restart > 0:
                    self.event("max_resource_restart", {
                        "path": svc.path,
                        "rid": rid,
                        "resource": resource,
                        "restart": nb_restart,
                    })
                self.inc_smon_retries(svc.path, rid, retries=retries)
                if resource.get("monitor"):
                    candidates = self.placement_candidates(svc)
                    if candidates != [Env.nodename] and len(candidates) > 0:
                        self.event("resource_toc", {
                            "path": svc.path,
                            "rid": rid,
                            "resource": resource,
                        })
                        try:
                            smon = self.get_service_monitor(path)
                        except KeyError:
                            smon = Storage()
                        if smon.status != "tocing":
                            self.service_toc(svc.path)
                    else:
                        self.event("resource_would_toc", {
                            "reason": "no_candidate",
                            "path": svc.path,
                            "rid": rid,
                            "resource": resource,
                        })
                else:
                    self.event("resource_degraded", {
                        "path": svc.path,
                        "rid": rid,
                        "resource": resource,
                    })
                    return False
            elif is_delayed_restart(rid):
                return False
            else:
                self.inc_smon_retries(svc.path, rid, retries=retries)
                self.event("resource_restart", {
                    "path": svc.path,
                    "rid": rid,
                    "resource": resource,
                    "restart": nb_restart,
                    "try": retries+1,
                })
                return True

        def stdby_resource(svc, rid, resource):
            if resource.get("standby") is not True:
                return False
            if resource.get("stopped") is True:
                return False
            try:
                nb_restart = svc.get_resource(rid, with_encap=True).nb_restart
                if nb_restart < self.default_stdby_nb_restart:
                    nb_restart = self.default_stdby_nb_restart
            except AttributeError:
                nb_restart = self.default_stdby_nb_restart

            retries = self.get_smon_retries(svc.path, rid)
            if retries > nb_restart:
                return False
            elif retries >= nb_restart:
                self.inc_smon_retries(svc.path, rid, retries=retries)
                self.event("max_stdby_resource_restart", {
                    "path": svc.path,
                    "rid": rid,
                    "resource": resource,
                    "restart": nb_restart,
                })
                return False
            elif is_delayed_restart(rid):
                return

            self.inc_smon_retries(svc.path, rid, retries=retries)
            self.event("stdby_resource_restart", {
                "path": svc.path,
                "rid": rid,
                "resource": resource,
                "restart": nb_restart,
                "try": retries+1,
            })
            return True

        smon = self.get_service_monitor(svc.path)
        if smon.status != "idle":
            return
        if smon.global_expect in ("unprovisioned", "purged", "deleted", "frozen"):
            return

        instance = self.get_service_instance(svc.path, Env.nodename)
        if not instance:
            return
        if instance.encap is True:
            return
        resources = instance.get("resources", {})

        mon_rids = []
        stdby_rids = []
        for rid, resource in resources.items():
            if resource["status"] not in ("warn", "down", "stdby down"):
                self.reset_smon_retries(svc.path, rid)
                continue
            if resource.get("provisioned", {}).get("state") is False:
                continue
            if monitored_resource(svc, rid, resource, smon):
                mon_rids.append(rid)
            elif stdby_resource(svc, rid, resource):
                stdby_rids.append(rid)

        if len(mon_rids) > 0:
            self.service_start_resources(svc.path, mon_rids)
        if len(stdby_rids) > 0:
            self.service_startstandby_resources(svc.path, stdby_rids)

        # same for encap resources
        for crid, cdata in instance.get("encap", {}).items():
            if cdata.get("frozen"):
                continue
            resources = cdata.get("resources", {})
            mon_rids = []
            stdby_rids = []
            for rid, resource in resources.items():
                if resource["status"] not in ("warn", "down", "stdby down"):
                    self.reset_smon_retries(svc.path, rid)
                    continue
                if resource.get("provisioned", {}).get("state") is False:
                    continue
                if monitored_resource(svc, rid, resource, smon):
                    mon_rids.append(rid)
                elif stdby_resource(svc, rid, resource):
                    stdby_rids.append(rid)
            if len(mon_rids) > 0:
                self.service_start_resources(svc.path, mon_rids, slave=crid)
            if len(stdby_rids) > 0:
                self.service_startstandby_resources(svc.path, stdby_rids, slave=crid)

    def node_orchestrator(self):
        nmon = self.get_node_monitor()
        if nmon.status == "shutting":
            return
        if nmon.status == "draining":
            self.node_orchestrator_clear_draining(nmon.status_updated)
        self.orchestrator_auto_grace()
        nmon = self.get_node_monitor()
        if self.unfreeze_when_all_nodes_joined \
                and self.node_frozen \
                and len(self.cluster_nodes) == len(self.thread_data.keys(["nodes"])):
            self.event("node_thaw", data={"reason": "upgrade"})
            self.freezer.node_thaw()
            self.unfreeze_when_all_nodes_joined = False
            self.node_frozen = 0
        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 self.node_frozen:
                self.event("node_freeze", {"reason": "target"})
                self.freezer.node_freeze()
                self.node_frozen = self.freezer.node_frozen()
        elif nmon.global_expect == "thawed":
            self.unfreeze_when_all_nodes_joined = False
            if self.node_frozen:
                self.event("node_thaw", {"reason": "target"})
                self.freezer.node_thaw()
                self.node_frozen = 0

    def object_orchestrator(self, path, svc):
        smon = self.get_service_monitor(path)
        nmon = self.get_node_monitor()
        if svc is None:
            if smon and path in self.list_cluster_paths():
                # deleting service: unset global expect if done cluster-wide
                agg = self.get_service_agg(path)
                self.set_smon_g_expect_from_status(path, smon, agg.avail)
            return
        if self.peer_init(svc):
            return
        if smon.global_expect and smon.global_expect != "aborted":
            if "failed" in smon.status:
                if self.abort_state(smon.status, smon.global_expect, smon.placement):
                    self.set_smon(path, global_expect="unset")
                    return
            elif smon.status not in ORCHESTRATE_STATES:
                # self.log.info("service %s orchestrator out (mon status %s)", svc.path, smon.status)
                return
        agg = self.get_service_agg(path)
        self.set_smon_g_expect_from_status(svc.path, smon, agg.avail)
        if nmon.status in ("shutting", "draining"):
            self.object_orchestrator_shutting(svc, smon, agg.avail)
        elif smon.global_expect:
            self.object_orchestrator_manual(svc, smon, agg.avail)
        else:
            self.object_orchestrator_auto(svc, smon, agg.avail)

    def abort_state(self, status, global_expect, placement):
        states = (status, global_expect)
        if states in ABORT_STATES:
            return True
        if placement == "leader" and states in LEADER_ABORT_STATES:
            return True
        if placement != "leader" and states in NON_LEADER_ABORT_STATES:
            return True
        return False

    @staticmethod
    def scale_path(path, idx):
        name, namespace, kind = split_path(path)
        return fmt_path(str(idx)+"."+name, namespace, kind)

    def object_orchestrator_auto(self, svc, smon, status):
        """
        Automatic instance start decision.
        Verifies hard affinity and anti-affinity, then routes to
        failover and flex specific policies.
        """
        if status == "unknown":
            return
        if svc.topology == "span":
            return
        if svc.disabled:
            # self.log.info("%s orchestrator out (disabled)", svc.path)
            return
        if not self.compat:
            return
        if svc.topology == "failover" and smon.local_expect == "started":
            # decide if local_expect=started should be reset
            svc_instance = self.get_service_instance(svc.path, Env.nodename)
            if svc_instance is None or len(smon.restart or {}) > 0:
                return
            if status == "up" and svc_instance.avail != "up":
                self.log.info("%s is globally up but the local instance is "
                              "not and is in 'started' local expect. reset",
                              svc.path)
                self.set_smon(svc.path, local_expect="unset")
            elif self.service_started_instances_count(svc.path) > 1 \
                    and svc_instance.avail != "up" \
                    and not self.placement_leader(svc):
                self.log.info("%s has multiple instance in 'started' "
                              "local expect and we are not leader. reset",
                              svc.path)
                self.set_smon(svc.path, local_expect="unset")
            elif status != "up"\
                    and svc_instance.avail in ("down", "stdby down", "undef", "n/a") \
                    and not self.resources_orchestrator_will_handle(svc):
                self.log.info("%s is not up and no resource monitor "
                              "action will be attempted, but "
                              "is in 'started' local expect. reset",
                              svc.path)
                self.set_smon(svc.path, local_expect="unset")
            else:
                return
        if self.node_frozen or self.instance_frozen(svc.path):
            # self.log.info("%s orchestrator out (frozen)", svc.path)
            return
        if not self.rejoin_grace_period_expired:
            return
        if svc.scale_target is not None and smon.global_expect is None:
            self.object_orchestrator_scaler(svc)
            return
        if status in (None, "undef", "n/a"):
            # self.log.info("%s orchestrator out (agg avail status %s)",
            #               svc.path, status)
            return

        candidates = self.placement_candidates(svc)

        if svc.topology == "failover":
            self.object_orchestrator_auto_failover(svc, smon, status, candidates)
        elif svc.topology == "flex":
            self.object_orchestrator_auto_flex(svc, smon, status, candidates)

    def object_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 != Env.nodename:
                # after loosing the placement leader status, the smon state
                # may need a reset
                if smon.status in ("ready", "wait parents"):
                    self.set_smon(svc.path, "idle")
                # not natural leader, skip orchestration
                return
            # natural leader, let orchestration unroll
        instance = self.get_service_instance(svc.path, Env.nodename)
        if smon.global_expect in ("started", "placed"):
            allowed_status = ("down", "stdby down", "stdby up", "warn")
        else:
            allowed_status = ("down", "stdby down", "stdby up")
        if smon.status in ("ready", "wait parents"):
            if instance.avail == "up":
                self.log.info("abort '%s' because the local instance "
                              "has started", smon.status)
                self.set_smon(svc.path, "idle")
                return
            if status not in allowed_status or \
               self.peer_warn(svc.path):
                self.log.info("abort '%s' because the aggregated status has "
                              "gone %s", smon.status, status)
                self.set_smon(svc.path, "idle")
                return
            peer = self.better_peer_ready(svc, candidates)
            if peer:
                self.log.info("abort '%s' because node %s has a better "
                              "placement score for service %s and is also "
                              "ready", smon.status, peer, svc.path)
                self.set_smon(svc.path, "idle")
                return
            peer = self.peer_transitioning(svc.path)
            if peer:
                self.log.info("abort '%s' because node %s is already "
                              "acting on service %s", smon.status, peer,
                              svc.path)
                self.set_smon(svc.path, "idle")
                return
        if smon.status == "wait parents":
            if self.parents_available(svc):
                self.set_smon(svc.path, status="idle")
                return
        elif smon.status == "ready":
            if self.parent_transitioning(svc):
                self.log.info("abort 'ready' because a parent is transitioning")
                self.set_smon(svc.path, "idle")
                return
            now = time.time()
            if smon.status_updated < (now - self.ready_period):
                self.event("instance_start", {
                    "reason": "from_ready",
                    "path": svc.path,
                    "since": int(now-smon.status_updated),
                })
                if smon.stonith and smon.stonith not in self.thread_data.keys(["nodes"]):
                    # stale peer which previously ran the service
                    self.node_stonith(smon.stonith)
                self.service_start(svc.path,
                                   err_status="place failed" if smon.global_expect == "placed" else "start failed")
                return
            tmo = int(smon.status_updated + self.ready_period - now) + 1
            self.log.info("service %s will start in %d seconds",
                          svc.path, tmo)
            self.set_next(tmo)
        elif smon.status == "idle":
            if svc.orchestrate == "no" and smon.global_expect not in ("started", "placed"):
                return
            if status not in allowed_status:
                return
            if self.peer_warn(svc.path):
                return
            if svc.disable_rollback and self.peer_start_failed(svc.path):
                return
            peer = self.peer_transitioning(svc.path)
            if peer:
                return
            if not self.placement_leader(svc, candidates):
                return
            if not self.parents_available(svc) or self.parent_transitioning(svc):
                self.set_smon(svc.path, status="wait parents")
                return
            if len(svc.peers) == 1:
                self.event("instance_start", {
                    "reason": "single_node",
                    "path": svc.path,
                })
                self.service_start(svc.path)
                return
            if instance.avail == "up":
                # avoid: smon idle->ready->idle when avail is warn, but instance is up
                return
            self.log.info("failover service %s status %s", svc.path,
                          status)
            self.set_smon(svc.path, "ready")

    def object_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(Env.nodename)
            except ValueError:
                return
            if Env.nodename not in ranks[:svc.flex_target]:
                # after loosing the placement leader status, the smon state
                # may need a reset
                if smon.status in ("ready", "wait parents"):
                    self.set_smon(svc.path, "idle")
                # natural not a leader, skip orchestration
                return
            # natural leader, let orchestration unroll
        instance = self.get_service_instance(svc.path, Env.nodename)
        up_nodes = self.up_service_instances(svc.path)
        n_up = len(up_nodes)
        n_missing = svc.flex_target - n_up

        if smon.status in ("ready", "wait parents"):
            if n_up > svc.flex_target:
                self.log.info("flex service %s instance count reached "
                              "required minimum while we were ready",
                              svc.path)
                self.set_smon(svc.path, "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.path)
                self.set_smon(svc.path, "idle")
                return
        if smon.status == "wait parents":
            if self.parents_available(svc):
                self.set_smon(svc.path, status="idle")
                return
        if smon.status == "ready":
            now = time.time()
            if smon.status_updated < (now - self.ready_period):
                self.event("instance_start", {
                    "reason": "from_ready",
                    "path": svc.path,
                    "since": now-smon.status_updated,
                })
                self.service_start(svc.path)
            else:
                tmo = int(smon.status_updated + self.ready_period - now) + 1
                self.log.info("%s will start in %d seconds",
                              svc.path, tmo)
                self.set_next(tmo)
        elif smon.status == "idle":
            if svc.orchestrate == "no" and smon.global_expect not in ("started", "placed"):
                return
            if n_up == svc.flex_target and smon.global_expect != "placed":
                return
            if n_up <= svc.flex_target:
                if smon.global_expect in ("started", "placed"):
                    allowed_avail = STOPPED_STATES + ["warn"]
                else:
                    allowed_avail = STOPPED_STATES
                if instance.avail not in allowed_avail:
                    return
                if not self.placement_leader(svc, candidates):
                    return
                if not self.parents_available(svc):
                    self.set_smon(svc.path, status="wait parents")
                    return
                self.log.info("flex %s started, starting or ready to "
                              "start instances: %d/%d. local status %s",
                              svc.path, n_up, svc.flex_target,
                              instance.avail)
                self.set_smon(svc.path, "ready")
            elif n_up > svc.flex_target:
                if instance is None:
                    return
                if instance.avail not in STARTED_STATES:
                    return
                try:
                    peer = self.place_in_progress(svc.path, discard_local=False)
                    if peer:
                        # don't auto-stop instance while a place is in progress
                        # to allow for temporary excess of running instances and
                        # thus avoid outages/performance degradation during moves.
                        return
                except ValueError:
                    return
                n_to_stop = n_up - svc.flex_target
                overloaded_up_nodes = self.overloaded_up_service_instances(svc.path)
                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 %s "
                              "flex_target=%d. choose %s",
                              n_to_stop, svc.path, svc.flex_target,
                              ", ".join(to_stop))
                if Env.nodename not in to_stop:
                    return
                self.event("instance_stop", {
                    "reason": "flex_threshold",
                    "path": svc.path,
                    "up": n_up,
                })
                self.service_stop(svc.path)

    def node_orchestrator_clear_draining(self, nmon_status_updated):
        # Don't clear draining too early, we need delay before check services local_expect
        # local_expect are set using defer_set_smon(path, local_expect="shutdown" ... has been applied
        if (time.time() - nmon_status_updated) < DELAY_CLEAR_DRAINING:
            return
        for path, smon in self.iter_local_services_monitors():
            if not smon:
                continue
            if smon.status == "shutdown failed":
                continue
            if smon.local_expect == "shutdown":
                return
        self.set_nmon("idle")

    def object_orchestrator_shutting(self, svc, smon, status):
        """
        Take actions to shutdown all local services instances marked with
        local_expect == "shutdown", even if frozen.

        Honor parents/children sequencing.
        """
        instance = self.get_service_instance(svc.path, Env.nodename)
        if smon.local_expect == "shutdown":
            if smon.status in ("shutdown", "shutdown failed"):
                return
            if self.is_instance_shutdown(instance):
                if smon.status == "wait children":
                    # volumes may be in "wait children" but stopped by their
                    # last consumer => clear their "wait children" state.
                    new_status = "idle"
                else:
                    new_status = None
                self.set_smon(svc.path, local_expect="unset", status=new_status)
                return
            if not self.local_children_down(svc):
                self.set_smon(svc.path, status="wait children")
                return
            elif smon.status == "wait children":
                self.set_smon(svc.path, status="idle")
            self.service_shutdown(svc.path)

    def scaler_current_slaves(self, path):
        name, namespace, kind = split_path(path)
        pattern = r"[0-9]+\." + name + "$"
        if namespace:
            pattern = "^%s/%s/%s" % (namespace, kind, pattern)
        else:
            pattern = "^%s" % pattern
        return [slave for slave in shared.SERVICES if re.match(pattern, slave)]

    def object_orchestrator_scaler(self, svc):
        smon = self.get_service_monitor(svc.path)
        if smon.status != "idle":
            return
        peer = self.peer_transitioning(svc.path)
        if peer:
            return
        candidates = self.placement_candidates(
            svc, discard_frozen=False,
            discard_na=False,
            discard_overloaded=False,
            discard_unprovisioned=False,
            discard_constraints_violation=False,
            discard_start_failed=False
        )
        ranks = self.placement_ranks(svc, candidates)
        if not ranks:
            return
        if ranks[0] != Env.nodename:
            # not natural leader
            return
        current_slaves = self.scaler_current_slaves(svc.path)
        n_slots = self.scaler_slots(current_slaves)
        if n_slots == svc.scale_target:
            return
        missing = svc.scale_target - n_slots
        if missing > 0:
            self.event("scale_up", {
               "path": svc.path,
               "delta": missing,
            })
            self.object_orchestrator_scaler_up(svc, missing, current_slaves)
        else:
            self.event("scale_down", {
               "path": svc.path,
               "delta": -missing,
            })
            self.object_orchestrator_scaler_down(svc, missing, current_slaves)

    def object_orchestrator_scaler_up(self, svc, missing, current_slaves):
        if svc.topology == "flex":
            self.object_orchestrator_scaler_up_flex(svc, missing, current_slaves)
        else:
            self.object_orchestrator_scaler_up_failover(svc, missing, current_slaves)

    def object_orchestrator_scaler_down(self, svc, missing, current_slaves):
        if svc.topology == "flex":
            self.object_orchestrator_scaler_down_flex(svc, missing, current_slaves)
        else:
            self.object_orchestrator_scaler_down_failover(svc, missing, current_slaves)

    def object_orchestrator_scaler_up_flex(self, svc, missing, current_slaves):
        candidates = self.placement_candidates(svc, discard_na=False, 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 = self.sort_scaler_slaves(current_slaves, reverse=True)
        for slavename in current_slaves:
            slave = shared.SERVICES[slavename]
            if slave.flex_max >= width:
                continue
            remain = width - slave.flex_max
            if remain > missing:
                pad = remain - missing
                new_width = slave.flex_max + 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 [self.scale_path(svc.path, idx) 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 += [[self.scale_path(svc.path, n_current_slaves+idx), 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 %s: %s", svc.path, delta)
        self.set_smon(svc.path, status="scaling")
        try:
            tname = "scaler:%s" % svc.path
            thr = threading.Thread(target=self.scaling_worker, name=tname, args=(svc, to_add, []))
            thr.start()
            self.threads.append(thr)
        except RuntimeError as exc:
            self.log.warning("failed to start a scaling thread for %s: %s", svc.path, exc)

    def object_orchestrator_scaler_down_flex(self, svc, missing, current_slaves):
        to_remove = []
        excess = -missing
        for slavename in self.sort_scaler_slaves(current_slaves, reverse=True):
            slave = shared.SERVICES[slavename]
            n_slots = slave.flex_target
            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 %s: %s", svc.path, delta)
        self.set_smon(svc.path, status="scaling")
        try:
            tname = "scaler:%s" % svc.path
            thr = threading.Thread(target=self.scaling_worker, name=tname, args=(svc, [], to_remove))
            thr.start()
            self.threads.append(thr)
        except RuntimeError as exc:
            self.log.warning("failed to start a scaling thread for %s: %s", svc.path, exc)

    @staticmethod
    def sort_scaler_slaves(slaves, reverse=False):
        return sorted(slaves, key=lambda x: int(x.split("/")[-1].split(".")[0]), reverse=reverse)

    def object_orchestrator_scaler_up_failover(self, svc, missing, current_slaves):
        slaves_count = missing
        n_current_slaves = len(current_slaves)
        new_slaves_list = [self.scale_path(svc.path, n_current_slaves+idx) for idx in range(slaves_count)]

        to_add = self.sort_scaler_slaves(new_slaves_list)
        to_add = [[path, None] for path in to_add]
        delta = "add " + ",".join([elem[0] for elem in to_add])
        self.log.info("scale %s: %s", svc.path, delta)
        self.set_smon(svc.path, status="scaling")
        try:
            tname = "scaler:%s" % svc.path
            thr = threading.Thread(target=self.scaling_worker, name=tname, args=(svc, to_add, []))
            thr.start()
            self.threads.append(thr)
        except RuntimeError as exc:
            self.log.warning("failed to start a scaling thread for %s: %s", svc.path, exc)

    def object_orchestrator_scaler_down_failover(self, svc, missing, current_slaves):
        slaves_count = -missing
        n_current_slaves = len(current_slaves)
        slaves_list = [self.scale_path(svc.path, n_current_slaves-1-idx) for idx in range(slaves_count)]

        to_remove = self.sort_scaler_slaves(slaves_list)
        to_remove = [path for path in to_remove]
        delta = "delete " + ",".join([elem[0] for elem in to_remove])
        self.log.info("scale %s: %s", svc.path, delta)
        self.set_smon(svc.path, status="scaling")
        try:
            tname = "scaler:%s" % svc.path
            thr = threading.Thread(target=self.scaling_worker, name=tname, args=(svc, [], to_remove))
            thr.start()
            self.threads.append(thr)
        except RuntimeError as exc:
            self.log.warning("failed to start a scaling thread for %s: %s", svc.path, exc)

    def scaling_worker(self, svc, to_add, to_remove):
        threads = []
        for path, instances in to_add:
            if path in shared.SERVICES:
                continue
            data = svc.print_config_data()
            try:
                thr = threading.Thread(
                    target=self.service_create_scaler_slave,
                    name="scaler:%s" % svc.path,
                    args=(path, svc, data, instances)
                )
                thr.start()
                threads.append(thr)
            except RuntimeError as exc:
                self.log.warning("failed to start a scaling thread for %s: %s", svc.path, exc)
        for path in to_remove:
            if path not in shared.SERVICES:
                continue
            self.set_smon(path, global_expect="purged")
        for path in to_remove:
            self.wait_global_expect_change(path, "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.path, global_expect="unset", status="idle")

        return True

    def end_rejoin_grace_period(self, reason=""):
        self.rejoin_grace_period_expired = True
        self.duplog("info", "end of rejoin grace period: %s" % reason,
                    nodename="")
        nmon = self.get_node_monitor()
        if nmon.status == "rejoin":
            self.set_nmon(status="idle")
        self.merge_frozen()
        try:
            del os.environ["OPENSVC_AGENT_UPGRADE"]
        except Exception:
            pass

    def idle_node_count(self):
        n = 0
        for node in self.thread_data.keys(["nodes"]):
            nmon = self.get_node_monitor(node)
            if nmon.status not in ("idle", "rejoin"):
                continue
            try:
                paths = self.thread_data.keys(["nodes", node, "services"])
            except KeyError:
                continue
            n += 1
        return n

    def orchestrator_auto_grace(self):
        """
        After daemon startup, wait for <rejoin_grace_period_expired> seconds
        before allowing object_orchestrator_auto() to proceed.
        """
        if self.rejoin_grace_period_expired:
            return False
        if len(self.cluster_nodes) == 1:
            self.end_rejoin_grace_period("single node cluster")
            return False
        n_idle = self.idle_node_count()
        if n_idle >= len(self.cluster_nodes):
            self.end_rejoin_grace_period("now rejoined")
            return False
        now = time.time()
        if now > self.startup + self.rejoin_grace_period:
            self.end_rejoin_grace_period("expired, but some nodes are still "
                                         "unreacheable. freeze node.")
            self.event("node_freeze", data={"reason": "rejoin_expire"})
            self.freezer.node_freeze()
            self.node_frozen = self.freezer.node_frozen()
            return False
        self.duplog("info", "in rejoin grace period", nodename="")
        return True

    def clear_start_failed(self, path):
        try:
            agg = self.get_service_agg(path)
            avail = agg.avail
        except KeyError:
            avail = "unknown"
        if avail != "up":
            return
        smon = self.get_service_monitor(path)
        if not smon:
            return
        if smon.status not in ("start failed", "place failed"):
            return
        n_up = 0
        flex_target = ""
        for nodename, instance in self.get_service_instances(path).items():
            if instance["monitor"].get("global_expect") is not None:
                return
            flex_target = instance.get("flex_target")
            if instance.get("avail") == "up":
                n_up += 1
        if flex_target and flex_target > n_up:
            return
        self.log.info("clear %s %s: the service is up", path, smon.status)
        self.set_smon(path, status="idle")

    def local_children_down(self, svc):
        missing = []
        if len(svc.children_and_slaves) == 0:
            return True
        for child in svc.children_and_slaves:
            if child == svc.path:
                continue
            instance = self.get_service_instance(child, Env.nodename)
            if not instance:
                continue
            avail = instance.get("avail", "unknown")
            if avail in STOPPED_STATES + ["unknown"]:
                continue
            missing.append(child)
        if len(missing) == 0:
            self.duplog("info", "%(path)s local children all avail down",
                        path=svc.path)
            return True
        self.duplog("info", "%(path)s local children still available:"
                    " %(missing)s", path=svc.path,
                    missing=" ".join(missing))
        return False

    def children_unprovisioned(self, svc):
        return self.children_down(svc, unprovisioned=True)

    def children_down(self, svc, unprovisioned=None):
        missing = []
        if len(svc.children_and_slaves) == 0:
            return True
        for child in svc.children_and_slaves:
            child = resolve_path(child, svc.namespace)
            if child == svc.path:
                continue
            agg = self.get_service_agg(child) or Storage()
            avail = agg.avail or "unknown"
            if avail not in STOPPED_STATES + ["unknown"]:
                missing.append(child)
                continue
            if unprovisioned:
                prov = agg.provisioned or "unknown"
                if prov not in [False, "unknown"]:
                    # mixed or true
                    missing.append(child)
        if len(missing) == 0:
            state = "avail down"
            if unprovisioned:
                state += " and unprovisioned"
            self.duplog("info", "%(path)s children all %(state)s:"
                        " %(children)s", path=svc.path, state=state,
                        children=" ".join(svc.children_and_slaves))
            return True
        state = "available"
        if unprovisioned:
            state += " or provisioned"
        self.duplog("info", "%(path)s children still %(state)s:"
                    " %(missing)s", path=svc.path, state=state,
                    missing=" ".join(missing))
        return False

    def parents_available(self, svc):
        if len(svc.parents) == 0:
            return True
        missing = []
        for parent in svc.parents:
            try:
                parent, nodename = parent.split("@")
            except ValueError:
                nodename = None
            parent = resolve_path(parent, svc.namespace)
            if parent == svc.path:
                continue
            if nodename:
                instance = self.get_service_instance(parent, nodename)
                if instance:
                    avail = instance["avail"]
                else:
                    missing.append(parent)
                    continue
            else:
                agg = self.get_service_agg(parent) or Storage()
                avail = agg.avail or "unknown"
            if avail in STARTED_STATES + ["unknown"]:
                continue
            missing.append(parent)
        if len(missing) == 0:
            self.duplog("info", "%(path)s parents all avail up",
                        path=svc.path)
            return True
        self.duplog("info", "%(path)s parents not available:"
                    " %(missing)s", path=svc.path,
                    missing=" ".join(missing))
        return False

    def min_instances_reached(self, svc):
        instances = self.get_service_instances(svc.path, discard_empty=False)
        live_nodes = self.list_nodes()
        min_instances = set(svc.peers) & set(live_nodes)
        return len(instances) >= len(min_instances)

    def instances_started_or_start_failed(self, path, nodes):
        return self.instances_started(path, nodes, accept_start_failed=True)

    def instances_started(self, path, nodes, accept_start_failed=False):
        for nodename in nodes:
            instance = self.get_service_instance(path, nodename)
            if instance is None:
                continue
            if instance.get("avail") in STOPPED_STATES:
                if accept_start_failed:
                    if instance["monitor"].get("status") != "start failed":
                        return False
                else:
                    return False
        self.log.info("%s instances on nodes '%s' are stopped",
                      path, ", ".join(nodes))
        return True

    def instances_stopped(self, path, nodes):
        for nodename in nodes:
            instance = self.get_service_instance(path, nodename)
            if instance is None:
                continue
            if instance.get("avail") not in STOPPED_STATES:
                self.log.info("%s instance node '%s' is not stopped yet",
                              path, nodename)
                return False
        return True

    def has_leader(self, svc):
        for nodename, instance in self.get_service_instances(svc.path).items():
            if instance["monitor"].get("placement") == "leader":
                return True
        return False

    def prior_instances_restarted(self, svc):
        if svc is None:
            return False
        candidates = self.placement_candidates(
            svc, discard_frozen=False,
            discard_unprovisioned=True,
            discard_start_failed=True,
            discard_overloaded=False,
        )
        ranks = self.placement_ranks(svc, candidates=candidates)
        for nodename in ranks:
            if nodename == Env.nodename:
                return True
            instance = self.get_service_instance(svc.path, nodename)
            if not instance:
                return False
            status = instance.get("monitor", {}).get("status")
            if status in ("restarting", "wait priors", "restart failed"):
                return False
        return False

    def leaders_started(self, path, exclude_status=None):
        svc = self.get_service(path)
        exclude_status = exclude_status or []
        if svc is None:
            return True
        for nodename in svc.peers:
            if nodename == Env.nodename:
                continue
            instance = self.get_service_instance(svc.path, nodename)
            if instance is None:
                continue
            if instance.get("monitor", {}).get("placement") != "leader":
                continue
            avail = instance.get("avail")
            smon_status = instance.get("monitor", {}).get("status")
            if avail not in STARTED_STATES and smon_status not in exclude_status:
                if exclude_status:
                    extra = "(%s/%s)" % (avail, smon_status)
                else:
                    extra = "(%s)" % avail
                self.log.info("%s leader instance on node %s "
                              "is not started yet %s",
                              svc.path, nodename, extra)
                return False
        return True

    def non_leaders_stopped(self, path, exclude_status=None):
        svc = self.get_service(path)
        exclude_status = exclude_status or []
        if svc is None:
            return True
        for nodename in svc.peers:
            if nodename == Env.nodename:
                continue
            instance = self.get_service_instance(svc.path, nodename)
            if instance is None:
                continue
            if instance.get("monitor", {}).get("placement") == "leader":
                continue
            avail = instance.get("avail")
            smon_status = instance.get("monitor", {}).get("status")
            if avail not in STOPPED_STATES and smon_status not in exclude_status:
                if exclude_status:
                    extra = "(%s/%s)" % (avail, smon_status)
                else:
                    extra = "(%s)" % avail
                self.log.info("%s non leader instance on node %s "
                              "is not stopped yet %s",
                              svc.path, nodename, extra)
                return False
        return True

    def leader_last(self, svc, provisioned=False, deleted=False, silent=False):
        """
        Return the leader nodename if the peers not selected for anteriority are
        found to have reached the target status, or if the local node is not 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.
        """
        agg = self.get_service_agg(svc.path) or Storage()
        if agg.avail is None:
            # base services can be unprovisioned and purged in parallel
            return Env.nodename
        candidates = self.placement_candidates(
            svc, discard_frozen=False,
            discard_unprovisioned=True,
            discard_start_failed=False,
            discard_overloaded=False,
        )
        try:
            ranks = self.placement_ranks(svc, candidates=candidates)
            top = ranks[0]
            if not silent:
                self.log.info("elected %s as the last node to take action on "
                              "%s", top, svc.path)
        except IndexError:
            if not silent:
                self.log.info("unblock %s leader last action (placement ranks empty)", svc.path)
            return Env.nodename
        if top != Env.nodename:
            if not silent:
                self.log.info("unblock %s leader last action (not leader)",
                              svc.path)
            return top
        for node in svc.peers:
            if node == Env.nodename:
                continue
            instance = self.get_service_instance(svc.path, node)
            if instance is None:
                continue
            elif deleted:
                if not silent:
                    self.log.info("delay leader-last action on %s: "
                                  "node %s is still not deleted", svc.path, node)
                return
            if instance.get("provisioned", False) is not provisioned:
                if not silent:
                    self.log.info("delay leader-last action on %s: "
                                  "node %s is still %s", svc.path, node,
                                  "unprovisioned" if provisioned else "provisioned")
                return
        self.log.info("unblock %s leader last action (leader)",
                      svc.path)
        return Env.nodename

    def leader_first(self, svc, provisioned=False, deleted=None, silent=False):
        """
        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.
        """
        instances = self.get_service_instances(svc.path, discard_empty=True)
        candidates = [nodename for (nodename, data) in instances.items()
                      if data.get("avail") in ("up", "warn")]
        if len(candidates) == 0:
            if not silent:
                self.log.info("%s has no up instance, relax candidates "
                              "constraints", svc.path)
            candidates = self.placement_candidates(
                svc, discard_frozen=False,
                discard_unprovisioned=False,
                discard_overloaded=False,
            )
        try:
            top = self.placement_ranks(svc, candidates=candidates)[0]
            if not silent:
                self.log.info("elected %s as the first node to take action on "
                              "%s", top, svc.path)
        except IndexError:
            if not silent:
                self.log.error("%s placement ranks list is empty", svc.path)
            return True
        if top == Env.nodename:
            return True
        instance = self.get_service_instance(svc.path, top)
        if instance is None and deleted:
            return True
        if instance.get("provisioned", True) is provisioned:
            return True
        if not silent:
            self.log.info("delay leader-first action on %s", svc.path)
        return False

    def overloaded_up_service_instances(self, path):
        return [nodename for nodename in self.up_service_instances(path) if self.node_overloaded(nodename)]

    def scaler_slots(self, paths):
        count = 0
        for path in paths:
            svc = shared.SERVICES[path]
            if svc.topology == "flex":
                width = len([1 for nodename in svc.peers if nodename in self.list_nodes()])
                count += min(width, svc.flex_target)
            else:
                count += 1
        return count

    def resources_orchestrator_will_handle(self, svc):
        """
        Return True if the resource orchestrator will try something to restore
        service to its optimal state.
        """
        for res in svc.get_resources():
            if res.disabled:
                continue
            try:
                status = self.thread_data.get(["nodes", Env.nodename, "services", "status", svc.path, "resources",
                                               res.rid, "status"])
            except KeyError:
                continue
            if status in ("up", "stdby up", "n/a", "undef"):
                continue
            if res.nb_restart and self.get_smon_retries(svc.path, res.rid) < res.nb_restart:
                return True
            if res.monitor:
                return True
        return False

    def service_started_instances_count(self, path):
        """
        Count the number of instances in 'started' local expect state.
        """
        count = 0
        try:
            for node, ndata in self.iter_nodes():
                try:
                    local_expect = ndata["services"]["status"][path]["monitor"]["local_expect"]
                except Exception:
                    continue
                if local_expect == "started":
                    count += 1
            return count
        except Exception as exc:
            return 0

    def parent_transitioning(self, svc):
        if len(svc.parents) == 0:
            return False
        for parent in svc.parents:
            if parent == svc.path:
                continue
            if self.peer_transitioning(parent, discard_local=False, with_waiters=True):
                return True
        return False

    def peer_init(self, svc):
        """
        Return True if a peer node is in init nmon status.
        Else return False.
        """
        for nodename in svc.peers:
            nmon = self.get_node_monitor(nodename=nodename)
            if nmon is None:
                continue
            if nmon.status == "init":
                return True
        return False

    def peer_warn(self, path, with_self=False):
        """
        For failover services, return the nodename of the first peer with the
        service in warn avail status.
        """
        try:
            if shared.SERVICES[path].topology != "failover":
                return
        except:
            return
        for nodename, instance in self.get_service_instances(path).items():
            if not with_self and nodename == Env.nodename:
                continue
            if instance["avail"] == "warn" and not instance["monitor"].get("status").endswith("ing"):
                return nodename

    def peers_options(self, path, candidates, status):
        """
        Return the nodes in <candidates> that are a viable start/place
        orchestration option.

        This method is used to determine if the global expect should be
        reset if no options are left.
        """
        nodenames = []
        for nodename in candidates:
            instance = self.get_service_instance(path, nodename)
            if instance is None:
                continue
            smon_status = instance["monitor"].get("status", "")
            if smon_status in status:
                continue
            avail = instance["avail"]
            if (avail == "warn" and not smon_status.endswith("ing")) or \
               avail in STOPPED_STATES + STARTED_STATES:
                nodenames.append(nodename)
        return nodenames

    def place_in_progress(self, path, discard_local=True):
        """
        Return the nodename of any object instance that has its global expect
        set to any kind of placed variants.
        """
        for nodename, instance in self.get_service_instances(path).items():
            if discard_local and nodename == Env.nodename:
                continue
            ge_time = instance["monitor"].get("global_expect_updated") or 0
            status_time = instance.get("updated") or 0
            if ge_time > status_time:
                # we don't have the lastest instance status. in doubt, avoid
                # taking decision
                raise ValueError
            ge = instance["monitor"].get("global_expect") or ""
            if ge.startswith("placed"):
                return nodename

    def peer_transitioning(self, path, discard_local=True, with_waiters=False):
        """
        Return the nodename of the first peer with the service in a transition
        state.
        """
        for nodename, instance in self.get_service_instances(path).items():
            if discard_local and nodename == Env.nodename:
                continue
            status = instance["monitor"].get("status", "")
            if status.endswith("ing"):
                return nodename
            if with_waiters and status.startswith("wait"):
                return nodename

    def instance_in_state(self, path, state):
        """
        Return the nodename of the first instance in the specified state.
        """
        for nodename, instance in self.get_service_instances(path).items():
            if instance["monitor"].get("status") == state:
                return nodename

    def peer_start_failed(self, path):
        """
        Return the nodename of the first peer with the service in a start failed
        state.
        """
        for nodename, instance in self.get_service_instances(path).items():
            if nodename == Env.nodename:
                continue
            if instance["monitor"].get("status") == "start failed":
                return nodename

    def better_peers_ready(self, svc):
        ranks = self.placement_ranks(svc, candidates=svc.peers)
        peers = []
        for nodename in ranks:
            if nodename == Env.nodename:
                return peers
            instance = self.get_service_instance(svc.path, 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.path).items():
            if nodename == Env.nodename:
                continue
            if instance["monitor"].get("status") == "ready":
                return nodename

    #########################################################################
    #
    # Cluster nodes aggregations
    #
    #########################################################################
    def get_clu_agg_frozen(self):
        fstatus = "undef"
        fstatus_l = []
        n_instances = 0
        for nodename, ndata in self.iter_nodes():
            try:
                fstatus_l.append(ndata["frozen"])
            except KeyError:
                # sender daemon outdated
                continue
            n_instances += 1
        n_frozen = len([True for froz in fstatus_l if froz])
        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, path):
        try:
            instance = self.get_any_service_instance(path)
        except IndexError:
            instance = None
        if instance is None:
            # during init for example
            return "unknown"
        topology = instance.get("topology")
        if topology == "failover":
            avail = self.get_agg_avail_failover(path)
        elif topology == "flex":
            avail = self.get_agg_avail_flex(path)
        else:
            avail = "unknown"

        if instance.get("scale") is not None:
            n_up = 0
            for slave in self.scaler_current_slaves(path):
                n_up += len(self.up_service_instances(slave))
            if n_up == 0:
                return "n/a"
            if n_up > 0 and n_up < instance.get("scale"):
                return "warn"

        slaves = instance.get("slaves", [])
        slaves += instance.get("scaler_slaves", [])
        if slaves:
            _, namespace, _ = split_path(path)
            avails = set([avail])
            for child in slaves:
                child = resolve_path(child, namespace)
                agg = self.get_service_agg(child) or Storage()
                child_avail = agg.avail or "unknown"
                avails.add(child_avail)
            if avails == set(["n/a"]):
                return "n/a"
            avails -= set(["n/a", "undef", "unknown"])
            n_avails = len(avails)
            if n_avails == 0:
                return "n/a"
            elif n_avails == 1:
                return list(avails)[0]
            else:
                return "warn"
        elif instance.get("scale") is not None:
            # scaler without slaves
            return "n/a"
        return avail

    def get_agg_overall(self, path):
        ostatus = 'undef'
        ostatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(path).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"
        try:
            instance = self.get_any_service_instance(path)
        except IndexError:
            instance = Storage()
        if instance is None:
            # during init for example
            return "unknown"
        slaves = instance.get("slaves", [])
        slaves += instance.get("scaler_slaves", [])
        if slaves:
            _, namespace, _ = split_path(path)
            avails = set([ostatus])
            for child in slaves:
                child = resolve_path(child, namespace)
                agg = self.get_service_agg(child) or Storage()
                child_status = agg.overall or "unknown"
                avails.add(child_status)
            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 ostatus

    def get_agg_frozen(self, path):
        frozen = 0
        total = 0
        for instance in self.get_service_instances(path).values():
            if "avail" 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.get("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, path):
        for instance in self.get_service_instances(path).values():
            if not self.is_instance_shutdown(instance):
                return False
        return True

    def get_agg_avail_failover(self, path):
        astatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(path).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_up == 1:
            return 'up'
        elif n_instances == 0:
            return 'n/a'
        elif astatus_s == set(['n/a']):
            return 'n/a'
        elif n_up > 1:
            return 'warn'
        elif 'warn' in astatus_l:
            return 'warn'
        else:
            return 'down'

    def get_agg_avail_flex(self, path):
        astatus_l = []
        n_instances = 0
        for instance in self.get_service_instances(path).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:
            if "warn" in astatus_l:
                return "warn"
            else:
                return 'down'
        elif n_up > instance.get("flex_max", n_instances):
            return 'warn'
        elif n_up < instance.get("flex_min", 1) and not instance.get("scaler_slave"):
            # scaler slaves are allowed to go under-target: the scaler will pop more slices
            # to reach the target. This is what happens when a node goes does.
            return 'warn'
        else:
            return 'up'

    def get_agg_placement(self, path):
        try:
            if shared.SERVICES[path].placement == "none":
                return "n/a"
            if shared.SERVICES[path].topology == "flex" and shared.SERVICES[path].flex_min == 0:
                return "n/a"
        except KeyError:
            pass
        instances = [instance for instance in self.get_service_instances(path).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 in ("up", "warn"):
                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, path):
        provisioned = 0
        total = 0
        instance_count = 0
        for instance in self.get_service_instances(path).values():
            instance_count += 1
            instance_provisioned = instance.get("provisioned")
            if instance_provisioned is None:
                # all resources [un]provision=true or disable, no resources
                continue
            total += 1
            if instance_provisioned is True:
                provisioned += 1
            elif instance_provisioned == "mixed":
                return "mixed"
        if instance_count == 0:
            # no instances found yet, consider provisioned False until instances found
            # False provisioned status will prevent scheduler from creating schedules
            return False
        elif total == 0:
            return "n/a"
        elif provisioned == total:
            return True
        elif provisioned == 0:
            return False
        return "mixed"

    def get_agg_aborted(self, path):
        for inst in self.get_service_instances(path).values():
            try:
                global_expect = inst["monitor"].get("global_expect")
            except KeyError:
                global_expect = None
            if global_expect not in (None, "aborted"):
                return False
            try:
                local_expect = inst["monitor"].get("local_expect")
            except KeyError:
                local_expect = None
            if local_expect not in (None, "started"):
                return False
        return True

    def get_agg_conf(self, path):
        data = Storage()
        for inst in self.get_service_instances(path).values():
            scale = inst.get("scale")
            if scale is not None:
                data.scale = scale
            scaler_slave = inst.get("scaler_slave")
            if scaler_slave:
                data.scaler_slave = True
            break
        return data

    def get_agg_deleted(self, path):
        if len([True for inst in self.get_service_instances(path).values() if "updated" in inst]) > 0:
            return False
        return True

    def get_agg_purged(self, provisioned, deleted):
        if deleted is False:
            return False
        if provisioned in (False, None, "mixed"):
            return False
        return True

    #########################################################################
    #
    # Convenience methods
    #
    #########################################################################
    def status_older_than_cf(self, path):
        """
        Return True if the instance status data is older than its config data
        or if one of the age is not a timestamp float.

        Returning True skips orchestration of the instance.
        """
        status_age = self.node_data.get(["services", "status", path, "updated"], default=0)
        config_age = self.node_data.get(["services", "config", path, "updated"], default=0)
        try:
            return status_age < config_age
        except TypeError:
            return True

    def service_instances_frozen(self, path):
        """
        Return the nodenames with a frozen instance of the specified service.
        """
        return [nodename for (nodename, instance) in self.get_service_instances(path).items()
                if instance.get("frozen")]

    def service_instances_thawed(self, path):
        """
        Return the nodenames with a frozen instance of the specified service.
        """
        return [nodename for (nodename, instance) in self.get_service_instances(path).items()
                if not instance.get("frozen")]

    def has_instance_with(self, path, global_expect=None):
        """
        Return True if an instance of the specified service is in the
        specified state.
        """
        for nodename, smon in self.iter_service_monitors(path):
            if global_expect and smon.global_expect in global_expect:
                return True
        return False

    def get_local_paths(self):
        """
        Extract service instance names from the locally maintained hb data.
        """
        paths = []
        for path in self.node_data.keys(["services", "status"]):
            try:
                status = self.node_data.get(["services", "status", path, "avail"])
            except KeyError:
                continue
            if status == "up":
                paths.append(path)
        return paths

    def get_services_configs(self):
        """
        Return a hash indexed by path and nodename, containing the services
        configuration mtime and checksum.
        """
        data = {}
        for path, nodename, config in self.iter_services_configs():
            if path not in data:
                data[path] = {}
            data[path][nodename] = config
        return data

    def get_any_service_instance(self, path):
        """
        Return the specified service status structure on any node.
        """
        for nodename, data in self.iter_service_instances(path):
            if data in (None, ""):
                continue
            return data

    def wait_service_config_consensus(self, path, peers, timeout=60):
        if len(peers) < 2:
            return True
        self.log.info("wait for service %s consensus on config amongst peers %s",
                      path, ",".join(peers))
        for _ in range(timeout):
            if self.service_config_consensus(path, peers):
                return True
            time.sleep(1)
        self.log.error("service %s couldn't reach config consensus in %d seconds",
                       path, timeout)
        return False

    def has_service_config_consensus(self, path, peers):
        if len(peers) < 2:
            self.log.debug("%s auto consensus. peers: %s", path, peers)
            return True
        ref_csum = None
        for peer in peers:
            if peer not in self.list_nodes():
                # discard unreachable nodes from the consensus
                continue
            try:
                csum = self.get_service_config(path, peer).csum
            except (TypeError, KeyError, AttributeError):
                # self.log.debug("service %s peer %s has no config cksum yet", path, 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", path, peer)
                return False
        return True

    def service_config_consensus(self, path, peers):
        if not self.has_service_config_consensus(path, peers):
            return False
        self.log.info("service %s config consensus reached", path)
        return True

    def update_status(self):
        data = self.status()
        data.update({
            "compat": self.compat,
            "transitions": self.transition_count(),
            "frozen": self.get_clu_agg_frozen(),
        })
        self.thread_data.merge([], data)

    def update_services_config(self):
        config = {}
        for path in list_services():
            cfg = svc_pathcf(path)
            try:
                config_mtime = os.path.getmtime(cfg)
            except Exception as exc:
                self.log.warning("failed to get %s mtime: %s", cfg, str(exc))
                config_mtime = 0
            last_config = self.get_service_config(path, Env.nodename)
            if last_config is None or config_mtime > last_config["updated"]:
                # self.log.debug("compute service %s config checksum", path)
                try:
                    csum = fsum(cfg)
                except (OSError, IOError) as exc:
                    self.log.warning("service %s config checksum error: %s", path, exc)
                    continue
                try:
                    self.add_service(path)
                except Exception as exc:
                    self.log.error("%s build error: %s", path, str(exc))
                    continue
            else:
                csum = last_config["csum"]
            if last_config is None or last_config["csum"] != csum:
                if last_config is not None:
                    self.log.info("service %s configuration change" % path)
                try:
                    status_mtime = os.path.getmtime(shared.SERVICES[path].status_data_dump)
                    if config_mtime > status_mtime:
                        self.log.info("service %s refresh instance status older than config", path)
                        self.service_status(path)
                except OSError:
                    pass
                shared.reconfigure_scheduler()
            with shared.SERVICES_LOCK:
                scope = sorted(list(shared.SERVICES[path].peers))
            config[path] = {
                "updated": config_mtime,
                "csum": csum,
                "scope": scope,
            }

        # purge deleted services
        with shared.SERVICES_LOCK:
            for path in list(shared.SERVICES.keys()):
                if path not in config:
                    self.log.info("purge deleted object %s from daemon data", path)
                    del shared.SERVICES[path]
                    self.node_data.unset_safe(["services", "status", path])
                    self.transitions.discard(path)
        self.node_data.set(["services", "config"], config)
        return config

    def get_last_svc_status_mtime(self, path):
        """
        Return the mtime of the specified service configuration file on the
        local node. If unknown, return 0.
        """
        instance = self.get_service_instance(path, Env.nodename)
        if instance is None:
            return 0
        mtime = instance["updated"]
        if mtime is None:
            return 0
        return mtime

    def service_status_fallback(self, path):
        """
        Return the specified service status structure fetched from an execution
        of "om svc" -s <path> json status". As we arrive here when the
        status.json doesn't exist, we don't have to specify --refresh.
        """
        self.log.info("synchronous service status eval: %s", path)
        cmd = ["status", "--refresh"]
        proc = self.service_command(path, cmd, local=False)
        self.push_proc(proc=proc)
        proc.communicate()
        fpath = svc_pathvar(path, "status.json")
        return self.load_instance_status_cache(fpath)

    @staticmethod
    def load_instance_status_cache(fpath):
        try:
            with open(fpath, 'r') as filep:
                try:
                    return json.load(filep)
                except ValueError as exc:
                    # json corrupted
                    return
        except Exception as exc:
            # json not found
            return

    def update_services_status(self):
        """
        Return the local services status data, fetching data from status.json
        caches if their mtime changed or from the node data if not.

        Also update the monitor 'local_expect' field for each service.
        """
        for path, idata in self.iter_local_services_instances():
            data = {}
            smon = Storage(idata.get("monitor", {}))
            if not smon:
                continue
            if idata.get("avail") == "up" and \
               smon.global_expect is None and \
               smon.status == "idle" and \
               smon.local_expect not in ("started", "shutdown"):
                self.log.info("%s local expect set: started", path)
                data["local_expect"] = "started"
                data["local_expect_updated"] = time.time()

            placement = self.get_service_placement(path)
            if placement != smon.placement:
                data["placement"] = self.get_service_placement(path)

            if data:
                self.node_data.merge(["services", "status", path, "monitor"], data)

            # forget the stonith target node if we run the service
            if idata.get("avail", "n/a") == "up":
                self.node_data.unset_safe(["services", "status", path, "monitor", "stonith"])

    #########################################################################
    #
    # Service-specific monitor data helpers
    #
    #########################################################################

    def set_smon(self, path, status=None, local_expect=None,
                 global_expect=None, reset_retries=False,
                 stonith=None, expected_status=None,
                 defer=False, origin=""):
        msg = "defer" if defer else ""
        if origin:
            msg += " from %s" % origin
        instance = self.get_service_instance(path, Env.nodename)
        if not instance:
            self.node_data.set(["services", "status", path], {"resources": {}})
        smon_view = self.node_data.view(["services", "status", path, "monitor"])
        smon = Storage(smon_view.get([], default={"status": "idle"}))
        if instance and not instance.get("resources", {}) \
                and not status \
                and ((global_expect is None and local_expect is None and status == "idle")  # TODO refactor this
                     or global_expect not in (
                             "frozen",
                             "thawed",
                             "aborted",
                             "unset",
                             "deleted",
                             "purged")):
            # skip slavers, wrappers, scalers
            return False
        # will set changed to True if an update occur, this will avoid wake_monitor calls
        changed = False
        if not smon:
            smon_view.set([], {
                "status": "idle",
                "status_updated": time.time(),
                "global_expect_updated": time.time(),
            })
            changed = True
        if status:
            reset_placement = False
            if status != smon.status \
                    and (not expected_status or expected_status == smon.status):
                self.log.info(
                    "%s monitor status change: %s => %s %s",
                    path,
                    smon.status if
                    smon.status else "none",
                    status,
                    msg,
                )
                if smon.status is not None \
                        and "failed" in smon.status \
                        and "failed" not in status:
                    # the placement might become "leader" after transition
                    # from "failed" to "not-failed". recompute asap so the
                    # orchestrator won't take an undue "stop_instance"
                    # decision.
                    reset_placement = True
                if status != "scaling" and status.endswith("ing"):
                    self.transitions.add(path)
                else:
                    self.transitions.discard(path)

                smon.status = status
                smon.status_updated = time.time()
                changed = True
            if reset_placement:
                smon.placement = self.get_service_placement(path)
                smon.status_updated = time.time()
                changed = True

        if local_expect:
            if local_expect == "unset":
                local_expect = None
            if local_expect != smon.local_expect:
                self.log.info(
                    "%s monitor local expect change: %s => %s %s",
                    path,
                    smon.local_expect if
                    smon.local_expect else "none",
                    local_expect,
                    msg,
                )
                smon.local_expect = local_expect
                smon.local_expect_updated = time.time()
                changed = True

        if global_expect:
            if global_expect == "unset":
                global_expect = None
            elif global_expect == "restarted":
                global_expect = "restarted@%s" % time.time()
            if global_expect != smon.global_expect:
                self.log.info(
                    "%s monitor global expect change: %s => %s %s",
                    path,
                    smon.global_expect if
                    smon.global_expect else "none",
                    global_expect,
                    msg,
                )
                smon.global_expect = global_expect
                smon.global_expect_updated = time.time()
                changed = True
        if reset_retries and "restart" in smon:
            self.log.info("%s monitor resources restart count "
                          "reset %s", path, msg)
            del smon["restart"]
            changed = True

        if stonith:
            if stonith == "unset":
                stonith = None
            if stonith != smon.stonith:
                self.log.info(
                    "%s monitor stonith change: %s => %s %s",
                    path,
                    smon.stonith if
                    smon.stonith else "none",
                    stonith,
                    msg,
                )
                smon.stonith = stonith
                changed = True
        if changed:
            smon_view.set([], smon)
            shared.wake_monitor(reason="%s mon change" % path)

    def reset_smon_retries(self, path, rid):
        self.node_data.unset_safe(["services", "status", path, "monitor", "restart", rid])

    def get_smon_retries(self, path, rid):
        return self.node_data.get(["services", "status", path, "monitor", "restart", rid, "retries"], default=0)

    def get_smon_retries_updated(self, path, rid):
        return self.node_data.get(["services", "status", path, "monitor", "restart", rid, "updated"], default=0)

    def inc_smon_retries(self, path, rid, retries=0):
        smon = self.get_service_monitor(path)
        if not smon:
            return
        restart = {"retries": retries + 1, "updated": time.time()}
        try:
            self.node_data.merge(["services", "status", path, "monitor", "restart"], {rid: restart})
        except TypeError:
            self.node_data.set(["services", "status", path, "monitor", "restart"], {rid: restart})

    def all_nodes_frozen(self):
        for nodename in self.list_nodes():
            # when nodename data are present, but frozen state is unknown,
            # consider frozen,
            frozen = self.thread_data.get(["nodes", nodename, "frozen"], default=1)
            if not frozen:
                return False
        return True

    def all_nodes_thawed(self):
        for nodename in self.list_nodes():
            # when nodename data are present, but frozen state is unknown,# when nodename data are present,
            # consider thawed frozen,
            frozen = self.thread_data.get(["nodes", nodename, "frozen"], default=0)
            if frozen:
                return False
        return True

    def set_nmon_g_expect_from_status(self):
        global_expect = self.get_node_monitor().global_expect
        if global_expect is None:
            return
        if global_expect == "frozen" and self.all_nodes_frozen():
            self.log.info("node global expect %s is now reached on all nodes",
                          global_expect)
            self.set_nmon(global_expect="unset")
        elif global_expect == "thawed" and self.all_nodes_thawed():
            self.log.info("node global expect %s is now reached on all nodes",
                          global_expect)
            self.set_nmon(global_expect="unset")

    def set_smon_g_expect_from_status(self, path, smon, status):
        """
        Align global_expect with the actual service states.
        """
        try:
            ge, at = smon.global_expect.split("@", 1)
            handler = "handle_%s_at" % ge
        except AttributeError:
            return
        except ValueError:
            handler = "handle_" + smon.global_expect

        instance = self.get_service_instance(path, Env.nodename)
        if instance is None:
            return

        svc = self.get_service(path)
        if svc and not self.has_service_config_consensus(path, svc.peers):
            self.log.debug("%s has not yet config consensus", path)
            return
        agg = self.get_service_agg(path)

        def handle_stopped():
            local_frozen = instance.get("frozen", 0)
            stopped = status in STOPPED_STATES
            if not stopped or not local_frozen:
                return
            self.log.info("service %s global expect is %s and its global "
                          "status is %s", path, smon.global_expect, status)
            self.set_smon(path, global_expect="unset")

        def handle_shutdown():
            local_frozen = instance.get("frozen", 0)
            if not self.get_agg_shutdown(path) or not local_frozen:
                return
            self.log.info("service %s global expect is %s and its global "
                          "status is %s", path, smon.global_expect, status)
            self.set_smon(path, global_expect="unset")

        def handle_started():
            local_frozen = instance.get("frozen", 0)
            if smon.placement == "none":
                self.set_smon(path, global_expect="unset")
            if status in STARTED_STATES and not local_frozen:
                self.log.info("service %s global expect is %s and its global "
                              "status is %s", path, smon.global_expect, status)
                self.set_smon(path, global_expect="unset")
                return
            agg = self.get_service_agg(path)
            if agg.frozen != "thawed":
                return
            svc = self.get_service(path)
            if self.peer_warn(path, with_self=True):
                self.set_smon(path, global_expect="unset")
                return

        def handle_frozen():
            agg = self.get_service_agg(path)
            if agg.frozen != "frozen":
                return
            self.log.debug("service %s global expect is %s, already is",
                           path, smon.global_expect)
            self.set_smon(path, global_expect="unset")

        def handle_thawed():
            if agg.frozen != "thawed":
                return
            self.log.debug("service %s global expect is %s, already is",
                           path, smon.global_expect)
            self.set_smon(path, global_expect="unset")

        def handle_unprovisioned():
            stopped = status in STOPPED_STATES
            if agg.provisioned not in (False, "n/a") or not stopped:
                return
            self.log.debug("service %s global expect is %s, already is", path, smon.global_expect)
            self.set_smon(path, global_expect="unset")

        def handle_provisioned():
            if agg.provisioned not in (True, "n/a"):
                return
            if smon.placement == "none":
                self.set_smon(path, global_expect="unset")
            if self.instance_in_state(path, "provision failed"):
                # if a blocking_provision trigger failed, do not unfreeze,
                # even if opensvc considers all resources as correctly provisioned
                self.set_smon(path, global_expect="unset")
                return
            if agg.avail in ("up", "n/a"):
                # provision success, thaw
                self.set_smon(path, global_expect="thawed")
            else:
                self.set_smon(path, global_expect="started")

        def handle_purged():
            deleted = self.get_agg_deleted(path)
            purged = self.get_agg_purged(agg.provisioned, deleted)
            if purged is not True:
                return
            self.log.debug("service %s global expect is %s, already is",
                           path, smon.global_expect)
            self.node_data.unset_safe(["services", "status", path, "monitor"])

        def handle_deleted():
            deleted = self.get_agg_deleted(path)
            if deleted is not True:
                return
            self.log.debug("service %s global expect is %s, already is",
                           path, smon.global_expect)
            self.node_data.unset_safe(["services", "status", path, "monitor"])

        def handle_aborted():
            if not self.get_agg_aborted(path):
                return
            self.log.info("service %s action aborted", path)
            self.set_smon(path, global_expect="unset")
            if smon.status and smon.status.startswith("wait "):
                # don't leave lingering "wait" mon state when we no longer
                # have a target state to reach
                self.set_smon(path, status="idle")

        def handle_placed():
            if agg.frozen != "thawed":
                return
            if agg.placement in ("optimal", "n/a") and \
               agg.avail in ("up", "n/a"):
                self.set_smon(path, global_expect="unset")
                return
            svc = self.get_service(path)
            if svc is None:
                # foreign
                return
            candidates = self.placement_candidates(svc, discard_start_failed=False, discard_frozen=False)
            candidates = self.placement_leaders(svc, candidates=candidates)
            peers = self.peers_options(path, candidates, ["place failed"])
            if not peers and self.non_leaders_stopped(path, ["place failed"]):
                self.log.info("service %s global expect is %s, not optimal "
                              "and no options left", path, smon.global_expect)
                self.set_smon(path, global_expect="unset")
                return

        def handle_placed_at():
            target = smon.global_expect.split("@")[-1].split(",")
            if self.instances_started_or_start_failed(path, target):
                self.set_smon(path, global_expect="unset")

        try:
            fn = locals()[handler]
        except KeyError:
            return

        fn()

    def get_arbitrators_data(self):
        if self.arbitrators_data is None or self.last_arbitrator_ping < time.time() - self.arbitrators_check_period:
            votes = self.arbitrators_votes()
            self.last_arbitrator_ping = time.time()
            arbitrators_data = {}
            for arbitrator in shared.NODE.arbitrators:
                arbitrators_data[arbitrator["id"]] = {
                    "name": arbitrator["name"],
                    "status": "up" if arbitrator["name"] in votes else "down"
                }
                if self.arbitrators_data is None or \
                   self.arbitrators_data[arbitrator["id"]]["status"] != arbitrators_data[arbitrator["id"]]["status"]:
                    if arbitrators_data[arbitrator["id"]]["status"] == "up":
                        self.event("arbitrator_up", data={"arbitrator": arbitrator["name"]})
                    else:
                        self.event("arbitrator_down", data={"arbitrator": arbitrator["name"]})
            self.arbitrators_data = arbitrators_data
        return self.arbitrators_data

    def update_cluster_data(self):
        self.apply_deferred_smon()
        self.update_node_data()
        self.purge_left_nodes()
        self.merge_hb_data()
        self.update_agg_services()
        self.update_status()

    def _update_cluster_data(self):
        self.daemon_status_data.set(["cluster"], {
            "name": self.cluster_name,
            "id": self.cluster_id,
            "nodes": self.cluster_nodes,
        })

    def purge_left_nodes(self):
        left = set(self.list_nodes()) - set(self.cluster_nodes)
        for node in left:
            self.log.info("purge left node %s data", node)
            self.delete_peer_data(node)

    def apply_deferred_smon(self):
        with shared.DEFERRED_SET_SMON_LOCK:
            for path, status, local_expect, global_expect, reset_retries, \
                    stonith, expected_status, origin in shared.DEFERRED_SET_SMON:
                self.set_smon(path, status=status, local_expect=local_expect,
                              global_expect=global_expect,
                              reset_retries=reset_retries, stonith=stonith,
                              expected_status=expected_status,
                              defer=True, origin=origin)
            del shared.DEFERRED_SET_SMON[:]

    def update_node_data(self):
        """
        Rescan services config and status.
        """
        data = {
            "stats": shared.NODE.stats(),
            "frozen": self.node_frozen,
            "env": shared.NODE.env,
            "labels": shared.NODE.labels,
            "targets": shared.NODE.targets,
            "locks": shared.LOCKS,
            "speaker": self.speaker() and "collector" in shared.THREADS,
            "min_avail_mem": shared.NODE.min_avail_mem,
            "min_avail_swap": shared.NODE.min_avail_swap,
        }
        self.node_data.merge([], data)
        self.update_services_config()
        self.update_services_status()

        if self.quorum:
            self.node_data.set(["arbitrators"], self.get_arbitrators_data())
        else:
            self.node_data.unset_safe(["arbitrators"])

        # purge deleted service instances
        for path in self.node_data.keys(["services", "status"]):
            sconf = self.get_service_config(path, Env.nodename)
            if sconf:
                continue
            if not self.node_data.exists(["services", "status", path, "monitor"]):
                continue
            smon = self.get_service_monitor(path)
            global_expect = smon.global_expect
            global_expect_updated = smon.global_expect_updated or 0
            if global_expect is not None and time.time() < global_expect_updated + 3:
                # keep the smon around for a while
                # self.log.info("relay foreign service %s global expect %s",
                #               path, global_expect)
                continue
            try:
                self.log.info("purge deleted object %s from daemon status data", path)
                self.node_data.unset(["services", "status", path])
                self.transitions.discard(path)
            except KeyError:
                pass

    def update_hb_data(self):
        """
        Prepare the heartbeat data we send to other nodes.
        """

        if self.mon_changed():
            self.update_cluster_data()

        diff = self._update_hb_data_locked()

        if diff is None:
            return

        # don't store the diff if we have no peers
        if len(shared.LOCAL_GEN_MERGED_ON_PEER) == 0:
            return

        shared.GEN_DIFF[shared.GEN] = 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 _update_hb_data_locked(self):
        now = time.time()
        diff = self.daemon_status_data.pop_diff()

        # excluded from the diff: gen, updated
        updated = self.node_data.get(["updated"], default=now)

        if self.last_node_data is None:
            # first run
            self.last_node_data = True
            self.node_data.set(["gen"], self.get_gen(inc=True))
            self.node_data.set(["updated"], now)
            return

        if len(diff) == 0:
            self.node_data.set(["gen"], self.get_gen(inc=False))
            self.node_data.set(["updated"], updated)
            return

        self.last_node_data = True
        self.node_data.set(["gen"], self.get_gen(inc=True))
        self.node_data.set(["updated"], now)
        diff.append([["updated"], now])
        return diff

    def merge_hb_data(self):
        self.merge_hb_data_locks()
        self.merge_hb_data_compat()
        self.merge_hb_data_monitor()

    def merge_hb_data_locks(self):
        changed = False
        for nodename in self.list_nodes():
            if nodename == Env.nodename:
                continue
            locks = self.thread_data.get(["nodes", nodename, "locks"], default={})
            for name in list(locks):
                try:
                    lock = locks[name]
                except KeyError:
                    # deleted during iteration
                    continue
                if lock["requester"] != nodename:
                    # only trust locks from requester views
                    continue
                if lock["requester"] == Env.nodename and name not in shared.LOCKS:
                    # don't re-merge a released lock emitted by this node
                    continue
                with shared.LOCKS_LOCK:
                    if name not in shared.LOCKS:
                        self.log.info("merge lock %s from node %s", name, nodename)
                        shared.LOCKS[name] = lock
                        changed = True
                        continue

                    # Lock name is already present in shared.LOCKS
                    merge = False
                    if lock["requester"] == nodename:
                        if lock["requested"] < shared.LOCKS[name]["requested"]:
                            merge = "older"
                        elif lock["requested"] > shared.LOCKS[name]["requested"]:
                            merge = "newer"
                        if merge:
                            self.log.info("merge %s lock %s from node %s (id %s replaced by id %s)",
                                          merge, name, nodename, shared.LOCKS[name]["id"], lock["id"])
                            shared.LOCKS[name] = lock
                            changed = True
                            continue
        for name in list(shared.LOCKS):
            with shared.LOCKS_LOCK:
                try:
                    shared_lock = shared.LOCKS[name]
                except KeyError:
                    # deleted during iteration
                    continue
                shared_lock_requester = shared_lock["requester"]
                if shared_lock_requester == Env.nodename:
                    continue
                requester_lock = self.thread_data.get(["nodes", shared_lock_requester, "locks", name], default=None)
                if requester_lock is None:
                    self.log.info("drop lock %s from node %s", name, shared_lock_requester)
                    del shared.LOCKS[name]
                    changed = True
        if changed:
            with shared.LOCKS_LOCK:
                self.update_cluster_locks_lk()

    def merge_hb_data_compat(self):
        compat = set()
        for nodename, ndata in self.iter_nodes():
            try:
                compat.add(ndata["compat"])
            except KeyError:
                pass
        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.
        """
        other_nodenames = self.list_nodes()
        if Env.nodename not in other_nodenames:
            return
        other_nodenames.remove(Env.nodename)

        # merge node monitors, only select most recent not None global_expect
        local_frozen = self.node_data.get(["frozen"], default=0)
        local_global_expect = self.node_data.get(["monitor", "global_expect"], default=None)
        if local_global_expect is not None:
            global_expect = local_global_expect
            global_expect_updated_origin = self.node_data.get(["monitor", "global_expect_updated_origin"], default=0)
        else:
            global_expect = None
            global_expect_updated_origin = 0
        global_expect_source_node = None
        for nodename in other_nodenames:
            try:
                remote = self.get_node_monitor(nodename)
                remote_global_expect = remote.global_expect
                remote_global_expect_updated_origin = remote.global_expect_updated_origin or 0
            except KeyError:
                # sender daemon is outdated
                continue
            if remote_global_expect in ["frozen", "thawed"] and remote_global_expect_updated_origin > global_expect_updated_origin:
                global_expect = remote_global_expect
                global_expect_updated_origin = remote_global_expect_updated_origin
                global_expect_source_node = nodename

        if local_global_expect != global_expect and \
                ((global_expect == "frozen" and not local_frozen) or
                 (global_expect == "thawed" and local_frozen)):
            self.log.info("node %s wants local node %s", global_expect_source_node, global_expect)
            # Preserve global_expect_updated_origin
            self.set_nmon(global_expect=global_expect, global_expect_updated_origin=global_expect_updated_origin)

        # merge every service monitors
        for path, instance in self.iter_local_services_instances():
            if not instance:
                continue
            smon = Storage(instance.get("monitor", {}))
            if smon.global_expect == "aborted":
                # refuse a new global expect if aborting
                continue
            for nodename in other_nodenames:
                rinstance = self.get_service_instance(path, nodename)
                if rinstance is None:
                    continue
                if rinstance.get("stonith") is True and \
                   instance["monitor"].get("stonith") != nodename:
                    self.set_smon(path, stonith=nodename)
                global_expect = rinstance.get("monitor", {}).get("global_expect")
                if global_expect is None:
                    continue
                global_expect_updated = rinstance.get("monitor", 0).get("global_expect_updated")
                if smon.global_expect and global_expect_updated and \
                   smon.global_expect_updated and \
                   global_expect_updated < smon.global_expect_updated:
                    # we have a more recent update
                    continue
                if path in shared.SERVICES and shared.SERVICES[path].disabled and \
                   global_expect not in ("frozen", "thawed", "aborted", "deleted", "purged"):
                    continue
                if global_expect == smon.global_expect:
                    self.log.debug("node %s wants service %s %s, already targeting that",
                                   nodename, path, global_expect)
                    continue
                # else:
                #     self.log.info("node %s wants service %s %s, already is", nodename, path, global_expect)
                if self.accept_g_expect(path, instance, global_expect):
                    self.log.info("node %s wants service %s %s", nodename, path, global_expect)
                    self.set_smon(path, global_expect=global_expect)

    def accept_g_expect(self, path, instance, global_expect):
        agg = self.get_service_agg(path)
        smon = self.get_service_monitor(path)
        if global_expect not in ("aborted", "thawed", "frozen") and \
           self.abort_state(smon.status, global_expect, smon.placement):
            return False
        if global_expect == "stopped":
            local_avail = instance["avail"]
            local_frozen = instance.get("frozen", 0)
            if local_avail not in STOPPED_STATES or not local_frozen:
                return True
            else:
                return False
        elif global_expect == "shutdown":
            if instance["avail"] == "n/a" and instance.get("scale") is None:
                return False
            return not self.get_agg_shutdown(path)
        elif global_expect == "started":
            if instance["avail"] == "n/a" and instance.get("scale") is None:
                return False
            if smon.placement == "none":
                return False
            local_frozen = instance.get("frozen", 0)
            if agg.avail is None:
                return False
            if agg.avail in STOPPED_STATES or local_frozen:
                return True
            else:
                return False
        elif global_expect == "frozen":
            if agg.frozen and agg.frozen != "frozen":
                return True
            else:
                return False
        elif global_expect == "thawed":
            if agg.frozen and agg.frozen != "thawed":
                return True
            else:
                return False
        elif global_expect == "provisioned":
            if smon.placement == "none":
                return False
            if agg.provisioned not in (True, None):
                return True
            else:
                return False
        elif global_expect == "unprovisioned":
            if agg.provisioned not in (False, None):
                return True
            else:
                return False
        elif global_expect == "deleted":
            deleted = self.get_agg_deleted(path)
            if deleted is False:
                return True
            else:
                return False
        elif global_expect == "purged":
            if smon.placement == "none":
                return False
            deleted = self.get_agg_deleted(path)
            purged = self.get_agg_purged(agg.provisioned, deleted)
            if purged is False:
                return True
            else:
                return False
        elif global_expect == "aborted":
            aborted = self.get_agg_aborted(path)
            if aborted is False:
                return True
            else:
                return False
        elif global_expect == "placed":
            if instance["avail"] == "n/a" and instance.get("scale") is None:
                return False
            if smon.placement == "none":
                return False
            if agg.placement == "non-optimal" or agg.avail != "up" or agg.frozen == "frozen":
                svc = shared.SERVICES.get(path)
                if svc is None:
                    return True
                candidates = self.placement_candidates(svc, discard_start_failed=False, discard_frozen=False)
                candidates = self.placement_leaders(svc, candidates=candidates)
                peers = self.peers_options(path, candidates, ["place failed"])
                if not peers and self.non_leaders_stopped(path, ["place failed"]):
                    return False
                return True
            else:
                return False
        elif global_expect.startswith("placed@"):
            if instance["avail"] == "n/a" and instance.get("scale") is None:
                return False
            target = global_expect.split("@")[-1].split(",")
            if Env.nodename in target:
                if instance["avail"] in STOPPED_STATES:
                    return True
            else:
                if instance["avail"] not in STOPPED_STATES:
                    return True
        elif global_expect.startswith("restarted@"):
            try:
                ts = float(global_expect.split("@")[-1])
            except Exception:
                return False
            if smon.local_expect == "started" and smon.local_expect_updated and smon.local_expect_updated < ts:
                return True
        return False

    def instance_provisioned(self, instance):
        if instance is None:
            return False
        instance_provisioned = instance.get("provisioned", True)
        if instance_provisioned == "mixed":
            return False
        return instance_provisioned

    def instance_unprovisioned(self, instance):
        if instance is None:
            return True
        instance_provisioned = instance.get("provisioned", False)
        if instance_provisioned == "mixed":
            return False
        return not instance_provisioned

    def get_agg(self, path):
        data = self.get_agg_conf(path)
        data.avail = self.get_agg_avail(path)
        data.frozen = self.get_agg_frozen(path)
        data.overall = self.get_agg_overall(path)
        data.placement = self.get_agg_placement(path)
        data.provisioned = self.get_agg_provisioned(path)
        return data

    def update_agg_services(self):
        data = {}
        for path in self.list_cluster_paths():
            try:
                if self.get_service(path).topology == "span":
                    data[path] = Storage()
                    continue
            except Exception as exc:
                data[path] = Storage()
                pass
            data[path] = self.get_agg(path)
        self.daemon_status_data.set(["monitor", "services"], data)
        return data

    def update_completions(self):
        self.update_completion("services")
        self.update_completion("nodes")

    def update_completion(self, otype):
        try:
            if otype == "services":
                olist = self.list_cluster_paths()
            else:
                olist = self.cluster_nodes
            with open(os.path.join(Env.paths.pathvar, "list."+otype), "w") as filep:
                filep.write("\n".join(olist)+"\n")
        except Exception as exc:
            print(exc)
            pass

    def get_last_shutdown(self):
        try:
            return os.path.getmtime(Env.paths.last_shutdown)
        except Exception:
            return 0

    def merge_frozen(self):
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            return
        last_shutdown = self.get_last_shutdown()
        self.merge_node_frozen(last_shutdown)
        self.merge_service_frozen(last_shutdown)

    def merge_node_frozen(self, last_shutdown):
        """
        This method is only called at the end of 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 len(self.cluster_nodes) < 2:
            return
        try:
            frozen = self.node_data.get(["frozen"]) or 0
        except:
            return
        if frozen:
            return
        if self.node_frozen:
            return
        nmon = self.get_node_monitor()
        if nmon.global_expect == "thawed":
            return
        for peer in self.cluster_nodes:
            if peer == Env.nodename:
                continue
            try:
                frozen = self.thread_data.get(["nodes", peer, "frozen"]) or 0
            except:
                continue
            if not isinstance(frozen, float):
                # compat with older agent where frozen is a bool
                continue
            if frozen and frozen > last_shutdown:
                self.event("node_freeze", data={
                    "reason": "merge_frozen",
                    "peer": peer,
                })
                self.freezer.node_freeze()
                self.node_frozen = self.freezer.node_frozen()

    def merge_service_frozen(self, last_shutdown):
        """
        This method is only called at the end of 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.
        """
        last_shutdown = self.get_last_shutdown()
        for svc in shared.SERVICES.values():
            if svc.orchestrate == "no":
                continue
            if len(svc.peers) < 2:
                continue
            instance = self.get_service_instance(svc.path, Env.nodename)
            if not instance:
                continue
            frozen = instance.get("frozen", 0)
            if frozen:
                continue
            if self.instance_frozen(svc.path):
                continue
            smon = self.get_service_monitor(svc.path)
            if smon.global_expect == "thawed":
                continue
            for peer in svc.peers:
                if peer == Env.nodename:
                    continue
                instance = self.get_service_instance(svc.path, peer)
                if not instance:
                    continue
                frozen = instance.get("frozen", 0)
                if not isinstance(frozen, float):
                    continue
                if frozen > last_shutdown:
                    self.event("instance_freeze", data={
                        "reason": "merge_frozen",
                        "peer": peer,
                        "path": svc.path,
                    })
                    svc.freezer.freeze()
                    # reload the frozen state immediately so the monitor will
                    # not take action on this instance in the same loop.
                    self.reload_instance_frozen(svc.path)

    def reload_instance_frozen(self, path):
        try:
            self.node_data.set(["services", "status", path, "frozen"], shared.SERVICES[path].frozen())
        except Exception:
            pass

    def instance_frozen(self, path, nodename=None):
        nodename = nodename or Env.nodename
        return self.nodes_data.get([nodename, "services", "status", path, "frozen"], default=0)

    def kern_freeze(self):
        try:
            with open("/proc/cmdline", "r") as ofile:
                buff = ofile.read()
        except Exception:
            return
        if "osvc.freeze" in buff.split():
            self.event("node_freeze", data={"reason": "kern_freeze"})
            self.freezer.node_freeze()
            self.node_frozen = self.freezer.node_frozen()

    def missing_beating_peer_data(self):
        for node in self.cluster_nodes:
            if node == Env.nodename:
                continue
            if self.nodes_data.exists([node, "services"]):
                continue
            # node dataset is empty or a brief coming from a ping
            try:
                if any(shared.THREADS[thr_id].is_beating(node) for thr_id in shared.THREADS if thr_id.endswith(".rx")):
                    self.log.info("waiting for node %s dataset", node)
                    return True
            except Exception as exc:
                return True
        return False

    def update_node_gen(self, nodename, local_gen_merged_on_peer=0, peer_gen_merged=0):
        with shared.GEN_MERGED_LOCK:
            self._update_node_gen_locked(nodename, local_gen_merged_on_peer, peer_gen_merged)

    def _update_node_gen_locked(self, nodename, local_gen_merged_on_peer=0, peer_gen_merged=0):
        shared.LOCAL_GEN_MERGED_ON_PEER[nodename] = local_gen_merged_on_peer
        shared.PEER_GEN_MERGED[nodename] = peer_gen_merged
        gdata = {
            nodename: peer_gen_merged,
            Env.nodename: local_gen_merged_on_peer
        }
        if not self.nodes_data.exists([nodename]):
            self.nodes_data.set([nodename], {"gen": gdata})
        elif not self.nodes_data.exists([nodename, "gen"]):
            self.nodes_data.set([nodename, "gen"], gdata)
        else:
            self.nodes_data.merge([nodename, "gen"], gdata)

    def merge_rx(self):
        change = False
        while True:
            try:
                nodename, data, hbname = shared.RX.get_nowait()
            except queue.Empty:
                break
            with shared.GEN_MERGED_LOCK:
                change |= self._merge_rx_locked(nodename, data, hbname)
        return change

    def _merge_rx_locked(self, nodename, data, hbname):
        if data is None:
            self.log.info("drop corrupted rx data from %s", nodename)
        peer_gen_merged = shared.PEER_GEN_MERGED.get(nodename, 0)
        local_gen_merged_on_peer = shared.LOCAL_GEN_MERGED_ON_PEER.get(nodename, 0)
        local_gen_merged_on_peer_from_message = data.get("gen", {}).get(Env.nodename, 0)
        peer_gen_from_message = data.get("gen", {}).get(nodename, 0)
        kind = data.get("kind", "full")
        change = False
        # self.log.debug("received %s from node %s: current gen %d, our gen local:%s peer:%s",
        #                kind, nodename, current_gen, shared.LOCAL_GEN.get(nodename), our_gen_on_peer) # COMMENT

        if nodename not in self.cluster_nodes + self.cluster_drpnodes:
            self.log.info("drop %s message from non member node %s", kind, nodename)
            return change

        if kind == "patch":
            if not self.nodes_data.exists([nodename]):
                # happens during init, or after join. ignore the patch, and ask for a full
                self.log.info("%s was not yet in nodes data view, ask for a full", nodename)
                if local_gen_merged_on_peer_from_message == 0:
                    self.log.info("%s ignore us yet, will send a full", nodename)
                self.update_node_gen(nodename, peer_gen_merged=0, local_gen_merged_on_peer=0)
                return False
            if peer_gen_merged == 0:
                # waiting for a full: ignore patches
                # self.log.debug("waiting for a full: ignore patch %s received from %s", list(data.get("deltas", [])),
                #                nodename)
                if shared.PEER_GEN_MERGED.get(nodename) is None:
                    self.log.info("undefined gen for %s dataset, drop patch and "
                                  "ask for a full (peer has gen %s of our dataset)", nodename, local_gen_merged_on_peer_from_message)
                    self.update_node_gen(nodename,
                                         peer_gen_merged=0,
                                         local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
                return False
            deltas = data.get("deltas", [])
            gens = sorted([int(gen) for gen in deltas])
            gens = [gen for gen in gens if gen > peer_gen_merged]
            if len(gens) == 0:
                # self.log.info("no more recent gen in received deltas")
                if local_gen_merged_on_peer_from_message > local_gen_merged_on_peer:
                    shared.LOCAL_GEN_MERGED_ON_PEER[nodename] = local_gen_merged_on_peer_from_message
                    self.nodes_data.set([nodename, "gen", Env.nodename], local_gen_merged_on_peer_from_message)
                return False
            nodes_info_change = False
            for gen in gens:
                # self.log.debug("patch node %s dataset gen %d over %d (%d diffs)", nodename, gen, current_gen,
                #                len(deltas[str(gen)]))
                if gen - 1 != peer_gen_merged:
                    if peer_gen_merged:
                        # don't be alarming on daemon start: it is normal we receive a out-of-sequence patch
                        self.log.warning("unsynchronized node %s dataset. merged gen %d, received out of sequence %d. "
                                         "ask for a full.", nodename, peer_gen_merged, gen)
                    self.update_node_gen(nodename,
                                         peer_gen_merged=0,
                                         local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
                    break
                try:
                    self.nodes_data.patch([nodename], deltas[str(gen)])
                    peer_gen_merged = gen
                    self.update_node_gen(nodename,
                                         peer_gen_merged=peer_gen_merged,
                                         local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
                    self.log.debug("patch node %s dataset to gen %d, peer has gen %d of our dataset",
                                   nodename,
                                   peer_gen_merged,
                                   local_gen_merged_on_peer_from_message)
                    if not nodes_info_change:
                        nodes_info_change |= self.patch_has_nodes_info_change(deltas[str(gen)])
                    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)
                    self.update_node_gen(nodename,
                                         peer_gen_merged=0,
                                         local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
                    break
            if nodes_info_change:
                self.on_nodes_info_change()
            return change
        elif kind == "ping":
            self.update_node_gen(nodename,
                                 peer_gen_merged=0,
                                 local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
            self.nodes_data.set([nodename, "monitor"], data["monitor"])
            self.log.debug("reset node %s dataset gen, peer has gen %d of our dataset",
                           nodename, local_gen_merged_on_peer_from_message)
            change = True
        else:
            if peer_gen_from_message == 0:
                # GEN starts at 1, this message is 1st message after init_data gen: {}
                self.log.debug("no 'gen' in full dataset from %s: drop", nodename)
                return False
            if peer_gen_merged >= peer_gen_from_message:
                updated_at_from_msg = data.get("updated", 0)
                updated_at_applied = self.nodes_data.get([nodename, "updated"], default=0)
                if updated_at_from_msg > updated_at_applied:
                    self.log.debug("detect hb full dataset from restarted node %s (updated: %s->%s, gen: %d->%d)",
                                   nodename, updated_at_applied, updated_at_from_msg, peer_gen_merged, peer_gen_from_message)
                else:
                    self.log.debug("drop already applied hb full dataset from node %s (current gen: %d, msg gen %d)",
                                   nodename, peer_gen_merged, peer_gen_from_message)
                    return False
            node_status = data.get("monitor", {}).get("status")
            if node_status in ("init", "maintenance", "upgrade") and self.nodes_data.exists([nodename]):
                for path, _, idata in self.iter_services_instances(nodenames=[nodename]):
                    if path in data["services"]["status"]:
                        continue
                    if path not in data["services"]["config"]:
                        # don't preserve instance status when not anymore in config
                        continue
                    idata["preserved"] = True
                    data["services"]["status"][path] = idata

            self.nodes_data.set([nodename], data)
            self.update_node_gen(nodename,
                                 peer_gen_merged=peer_gen_from_message,
                                 local_gen_merged_on_peer=local_gen_merged_on_peer_from_message)
            self.log.info("install node %s full dataset gen %d, peer has gen %d of our dataset",
                          nodename,
                          peer_gen_from_message,
                          local_gen_merged_on_peer_from_message)
            self.on_nodes_info_change()
            change = True
        return change

 0707010001f27c000081a40000000000000000000000016a100daf00010492000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/daemon/shared.py   """
A module to share variables used by osvcd threads.
"""
import logging
import os
import sys
import threading
import time
import hashlib
import json
import tempfile
import shutil
from copy import deepcopy
from subprocess import Popen, PIPE

import foreign.six as six
# noinspection PyUnresolvedReferences
from foreign.six.moves import queue

import core.exceptions as ex
from core.node.nodedict import DEFAULT_COLLECTOR_DB_UPDATE_INTERVAL, DEFAULT_COLLECTOR_DB_MIN_UPDATE_INTERVAL, \
    DEFAULT_COLLECTOR_DB_MIN_PING_INTERVAL
from foreign.jsonpath_ng.ext import parse
from env import Env
from utilities.journaled_data import JournaledData
from utilities.lazy import lazy, unset_lazy
from utilities.naming import split_path, paths_data, factory, object_path_glob
from utilities.selector import selector_config_match, selector_value_match, selector_parse_fragment, selector_parse_op_fragment
from utilities.storage import Storage
from core.freezer import Freezer
from core.comm import Crypt
from .events import EVENTS


class OsvcJournaledData(JournaledData):
    def __init__(self):
        super(OsvcJournaledData, self).__init__(
            event_q=EVENT_Q,
            journal_head=["monitor", "nodes", Env.nodename],
            journal_exclude=[
                ["gen"],
                ["updated"],
            ],
            # disable journaling if we have no peer, as nothing purges the journal
            journal_condition=lambda: bool(LOCAL_GEN_MERGED_ON_PEER),
        )


# import utilities.dbglock
# RLock = utilities.dbglock.RLock
RLock = threading.RLock

# a global to store the Daemon() instance
DAEMON = None

# the event queue to feed to clients listening for changes
EVENT_Q = queue.Queue()

# daemon_status data
DAEMON_STATUS = OsvcJournaledData()

# disable orchestration if a peer announces a different compat version than
# ours
COMPAT_VERSION = 10

# expose api handlers version
API_VERSION = 7

# node and cluster conf lock to block reading changes during a multi-write
# transaction (ex daemon join)
CONFIG_LOCK = RLock()

# current generation of the dataset on the local node
GEN = 1

# track the generation of the local dataset on peer nodes
LOCAL_GEN_MERGED_ON_PEER = {}

# track the generation of the peer datasets we merged
PEER_GEN_MERGED = {}

# The lock to serialize PEER_GEN_MERGED & LOCAL_GEN_MERGED_ON_PEER updates from threads
GEN_MERGED_LOCK = RLock()

# 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 = RLock()

# A node object instance. Used to access node properties and methods.
NODE = None
NODE_LOCK = 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 = 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 = RLock()

# the node monitor states evicting a node from ranking algorithms
NMON_STATES_PRESERVED = (
   "maintenance",
   "upgrade",
   "init",
   "shutting",
)

# a boolean flag used to signal the monitor it has to do the long loop asap
MON_CHANGED = []

# cluster wide locks, aquire/release via the listener (usually the unix socket),
# consensus via the heartbeat links.
LOCKS = {}
LOCKS_LOCK = RLock()

RX = queue.Queue()

# DEFERRED_STOP_LISTENER_CLIENTS define set of listener client session-id marked to be stopped
DEFERRED_STOP_LISTENER_CLIENTS = set()
# DEFERRED_STOP_LISTENER_CLIENTS_LOCK serialize access to DEFERRED_STOP_LISTENER_CLIENTS
DEFERRED_STOP_LISTENER_CLIENTS_LOCK = threading.RLock()

# DEFERRED_SET_SMON define list of deferred smon changes
DEFERRED_SET_SMON = []
# DEFERRED_SET_SMON_LOCK serialize access to DEFERRED_SET_SMON
DEFERRED_SET_SMON_LOCK = threading.RLock()

# thread loop conditions and helpers
DAEMON_STOP = threading.Event()
MON_TICKER = threading.Condition()
COLLECTOR_TICKER = threading.Condition()
SCHED_RECONF = threading.Event()
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 = []

# a set of run action signatures done, fed by the crm to the lsnr,
# purged by the scheduler thread
RUN_DONE_LOCK = RLock()
RUN_DONE = set()

# min interval between thread stats refresh
STATS_INTERVAL = 1

# to prevent concurrent join handler execution
JOIN_LOCK = RLock()

# Agent as a relay heartbeart server
RELAY_DATA = {}
RELAY_LOCK = RLock()
RELAY_SLOT_MAX_AGE = 24 * 60 * 60
RELAY_JANITOR_INTERVAL = 10 * 60

# try to give a name to the locks, for debugging when using
# the pure python locks (native locks don't support setattr)
try:
    CONFIG_LOCK.name = "CONFIG"
    THREADS_LOCK.name = "THREADS"
    NODE_LOCK.name = "NODE"
    SERVICES_LOCK.name = "SERVICES"
    HB_MSG_LOCK.name = "HB_MSG"
    RUN_DONE_LOCK.name = "RUN_DONE"
    LOCKS_LOCK.name = "LOCKS"
    DEFERRED_STOP_LISTENER_CLIENTS_LOCK.name = "DEFERRED_STOP_LISTENER_CLIENTS"
except AttributeError:
    pass


def daemon_mutex_status(log=None):
    data = {
        "JOURNAL": str(DAEMON_STATUS.lock),
        "THREADS": str(THREADS_LOCK),
        "NODE": str(NODE_LOCK),
        "SERVICES": str(SERVICES_LOCK),
        "HB_MSG": str(HB_MSG_LOCK),
        "CONFIG": str(CONFIG_LOCK),
        "RUN_DONE": str(RUN_DONE_LOCK),
        "LOCKS": str(LOCKS_LOCK),
        "DEFERRED_STOP_LISTENER_CLIENTS": str(DEFERRED_STOP_LISTENER_CLIENTS_LOCK),
    }

    if log:
        # TODO find better way to get log hnadlers locks
        # logging.PlaceHolder is not in logging public api
        loggerDict = log.logger.manager.loggerDict
        logger_keys = list(loggerDict)
        for k in logger_keys:
            try:
                logger_item = loggerDict[k]
                if not isinstance(logger_item, logging.PlaceHolder):
                    i = 0
                    for h in logger_item.handlers:
                        i += 1
                        if h.lock:
                            data["logger[%s]-%d-handler[%s]" % (logger_item.name, i, repr(h))] = str(h.lock)
            except:
                # best effort to retrieve logger handlers locks
                pass
    return data


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 immediately
    """
    global MON_CHANGED
    if immediate and reason:
        reason += " (immediate)"
    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()


def reconfigure_scheduler():
    """
    Notify the scheduler thread to do they periodic job immediatly
    """
    SCHED_RECONF.set()
    with SCHED_TICKER:
        SCHED_TICKER.notify_all()


#############################################################################
#
# Base Thread class
#
#############################################################################
class OsvcThread(threading.Thread, Crypt):
    """
    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.log = None
        self.alerts = []
        self._stop_event = threading.Event()
        self._node_conf_event = threading.Event()
        self.created = time.time()
        self.configured = self.created
        self.threads = []
        self.procs = []
        self.tid = None
        self.stats_data = None
        self.last_stats_refresh = 0
        self.arbitrators_data = None

        # hash for log dups avoiding
        self.duplog_data = {}

        self.rankers = {
            "nodes order": "placement_ranks_nodes_order",
            "shift": "placement_ranks_shift",
            "spread": "placement_ranks_spread",
            "score": "placement_ranks_score",
            "load avg": "placement_ranks_load_avg",
            "none": "placement_ranks_none",
        }

        self.daemon_status_data = DAEMON_STATUS
        self.node_data = self.daemon_status_data.view(["monitor", "nodes", Env.nodename])
        self.nodes_data = self.daemon_status_data.view(["monitor", "nodes"])
        self.instances_status_data = self.node_data.view(["services", "status"])
        self.thread_data = self.daemon_status_data.view([self.name])

    def alert(self, lvl, fmt, *args):
        if lvl == "info":
            fn = self.log.info
        elif lvl == "warning":
            fn = self.log.warning
        elif lvl == "error":
            fn = self.log.error
        else:
            self.log.error('alert called with invalid lvl %s', lvl)
            fn = self.log.error
        fn(fmt, *args)
        self.alerts.append({
            "severity": lvl,
            "message": fmt % args,
        })

    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 = {
            "state": state,
            "created": self.created,
            "configured": self.configured,
        }
        if self.alerts:
            data["alerts"] = self.alerts
        if self.tid:
            data["tid"] = self.tid
        if hasattr(self, "ident"):
            data["ident"] = self.ident
        return data

    def exit(self, exit_status=0):
        """
        exit daemon thread helper, daemon threads data are only updated while
        running, we are stopping
        """
        if self.stopped():
            new_state = "stopped"
        else:
            new_state = "terminated"
        try:
            self.thread_data.set(["state"], new_state)
            name = getattr(self, "id", "")
            if name.startswith("hb#"):
                self.node_data.set(["hb", name, "state"], new_state)
        except KeyError:
            pass
        self.log.info('thread exits with code %d', exit_status)
        sys.exit(exit_status)

    def thread_stats(self):
        if self.tid is None:
            return
        now = time.time()
        if self.stats_data and now - self.last_stats_refresh < STATS_INTERVAL:
            return self.stats_data
        try:
            tid_cpu_time = NODE.tid_cpu_time(self.tid)
        except Exception:
            tid_cpu_time = 0.0
        try:
            tid_mem_total = NODE.tid_mem_total(self.tid)
        except Exception:
            tid_mem_total = 0
        self.stats_data = {
            "threads": len(self.threads),
            "procs": len(self.procs),
            "cpu": {
                "time": tid_cpu_time,
            },
            "mem": {
                "total": tid_mem_total,
            },
        }
        self.last_stats_refresh = now
        return self.stats_data

    def set_tid(self):
        self.tid = NODE.get_tid()

    def has_proc(self, cmd):
        for proc in self.procs:
            if proc.cmd == cmd:
                return True
        return False

    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,
                  cmd=None, session_id=None):
        """
        Enqueue a structure including a Popen() result and the success and
        error callbacks.
        """
        self.procs.append(Storage({
            "proc": proc,
            "cmd": cmd,
            "session_id": session_id,
            "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 kill_procs(self):
        """
        Send a kill() to all procs in the queue and wait for their
        completion.
        """
        try:
            ProcessLookupError
        except NameError:
            ProcessLookupError = OSError
        for data in self.procs:
            # noinspection PyUnboundLocalVariable
            if hasattr(data.proc, "poll"):
                # subprocess.Popen()
                ret = lambda: data.proc.returncode
                poll = lambda: data.proc.poll()
                kill = lambda: data.proc.kill()
            else:
                # multiprocessing.Process()
                ret = lambda: data.proc.exitcode
                poll = lambda: data.proc.is_alive()
                kill = lambda: data.proc.terminate()
            try:
                kill()
            except ProcessLookupError:  # pylint: disable=undefined-variable
                continue
            for _ in range(self.stop_tmo):
                poll()
                exit_code = ret()
                if exit_code is not None:
                    break
                time.sleep(1)

    def janitor_procs(self):
        done = []
        for idx, data in enumerate(self.procs):
            if data.proc is None:
                ret = 1
                comm = lambda: None
            elif hasattr(data.proc, "poll"):
                # subprocess.Popen()
                data.proc.poll()
                ret = data.proc.returncode
                comm = lambda: data.proc.communicate()
            elif hasattr(data.proc, "exitcode"):
                # multiprocessing.Process()
                ret = data.proc.exitcode
                comm = lambda: None
            else:
                ret = None
                comm = lambda: None
            if ret is not None:
                comm()
                done.append(idx)
                if ret == 0 and data.on_success:
                    getattr(self, data.on_success)(*data.on_success_args,
                                                   **data.on_success_kwargs)
                elif ret != 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, timeout=10):
        for thr in self.threads:
            if not hasattr(thr, "stop"):
                continue
            self.log.info("stop %s", thr)
            thr.stop()
        while timeout > 0:
            self.janitor_threads()
            if len(self.threads) == 0:
                return
            timeout -= 1
            time.sleep(1)
        self.log.warning("timeout waiting for threads to terminate. %s left alive.", len(self.threads))
        self.log_status()

    def log_status(self):
        from utilities.render.color import format_str_flat_json
        data = self.status()
        for line in format_str_flat_json(data).splitlines():
            self.log.info(line)

    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]
        return len(done)

    @lazy
    def freezer(self):
        return Freezer("node")

    def node_busy(self):
        return self.get_node_monitor().status not in (None, "idle")

    def instances_busy(self):
        data = self.node_data.get(["services", "status"])
        for path, sdata in data.items():
            status = sdata.get("monitor", {}).get("status")
            if status not in (None, "idle") and "failed" not in status:
                return True
        return False

    def reload_config(self):
        if not self._node_conf_event.is_set():
            return
        if self.node_busy():
            return
        if self.instances_busy():
            return
        self._node_conf_event.clear()
        self.event("node_config_change")
        unset_lazy(self, "config")
        unset_lazy(self, "quorum")
        unset_lazy(self, "split_action")
        unset_lazy(self, "vip")
        unset_lazy(NODE, "arbitrators")
        unset_lazy(self, "cluster_name")
        unset_lazy(self, "cluster_names")
        unset_lazy(self, "cluster_key")
        unset_lazy(self, "cluster_id")
        unset_lazy(self, "cluster_nodes")
        unset_lazy(self, "db_update_interval")
        unset_lazy(self, "db_min_update_interval")
        unset_lazy(self, "db_min_ping_interval")
        unset_lazy(self, "sorted_cluster_nodes")
        unset_lazy(self, "maintenance_grace_period")
        unset_lazy(self, "oc3_version")
        unset_lazy(self, "rejoin_grace_period")
        unset_lazy(self, "ready_period")
        self.arbitrators_data = None
        self.alerts = []
        if not hasattr(self, "reconfigure"):
            self.configured = time.time()
            return
        try:
            getattr(self, "reconfigure")()
        except Exception as exc:
            self.log.error("reconfigure error: %s, stopping thread", str(exc))
            self.stop()
        self.configured = time.time()

    @staticmethod
    def get_service(path):
        try:
            return SERVICES[path]
        except KeyError:
            return

    @staticmethod
    def patch_has_nodes_info_change(patch):
        for _patch in patch:
            try:
                if _patch[0][3] == "labels":
                    return True
            except KeyError:
                continue
            try:
                if _patch[0][3] == "targets":
                    return True
            except KeyError:
                continue
        return False

    def set_nmon(self, status=None, local_expect=None, global_expect=None, global_expect_updated_origin=None):
        changed = False
        nmon = self.get_node_monitor()
        if status:
            if status != nmon.status:
                self.log.info(
                    "node monitor status change: %s => %s",
                    nmon.status if
                    nmon.status else "none",
                    status
                )
                changed = True
                nmon.status = status
                nmon.status_updated = time.time()

        if local_expect:
            if local_expect == "unset":
                local_expect = None
            if local_expect != nmon.local_expect:
                self.log.info(
                    "node monitor local expect change: %s => %s",
                    nmon.local_expect if
                    nmon.local_expect else "none",
                    local_expect
                )
                changed = True
                nmon.local_expect = local_expect
                nmon.local_expect_updated = time.time()

        if global_expect:
            if global_expect == "unset":
                global_expect = None
            if global_expect != nmon.global_expect:
                self.log.info(
                    "node monitor global expect change: %s => %s",
                    nmon.global_expect if
                    nmon.global_expect else "none",
                    global_expect
                )
                changed = True
                nmon.global_expect = global_expect
                nmon.global_expect_updated = time.time()
                nmon.global_expect_updated_origin = global_expect_updated_origin or time.time()

        if changed:
            self.node_data.set(["monitor"], nmon)
            wake_monitor(reason="node mon change")

    @staticmethod
    def has_deferred_set_smon():
        with DEFERRED_SET_SMON_LOCK:
            return len(DEFERRED_SET_SMON) > 0

    @staticmethod
    def defer_set_smon(
            path, status=None, local_expect=None, global_expect=None,
            reset_retries=False, stonith=None, expected_status=None,
            origin=""):

        with DEFERRED_SET_SMON_LOCK:
            DEFERRED_SET_SMON.append((
                path,
                status,
                local_expect,
                global_expect,
                reset_retries,
                stonith,
                expected_status,
                origin,
            ))
        wake_monitor(reason="%s defer smon call" % path)

    def get_node_monitor(self, nodename=None):
        """
        Return the Monitor data of the node.
        """
        nodename = nodename or Env.nodename
        return Storage(self.daemon_status_data.get(["monitor", "nodes", nodename, "monitor"], default={}))

    def iter_service_monitors(self, path, nodenames=None):
        for _, nodename, data in self.iter_services_monitors(paths=[path], nodenames=nodenames):
            yield (nodename, data)

    def iter_local_services_monitors(self):
        for path, _, data in self.iter_services_monitors(nodenames=[Env.nodename]):
            yield (path, data)

    def iter_services_monitors(self, paths=None, nodenames=None):
        for path, nodename, data in self.iter_services_instances(paths=paths, nodenames=nodenames):
            try:
                yield (path, nodename, Storage(data.monitor))
            except (TypeError, KeyError):
                continue

    def iter_local_services_instances(self):
        for path, _, data in self.iter_services_instances(nodenames=[Env.nodename]):
            yield (path, data)

    def iter_service_instances(self, path, nodenames=None):
        for _, nodename, data in self.iter_services_instances(paths=[path], nodenames=nodenames):
            yield (nodename, data)

    def iter_services_instances(self, paths=None, nodenames=None):
        nodenames = nodenames or self.cluster_nodes
        for nodename in nodenames:
            for path in self.daemon_status_data.keys_safe(["monitor", "nodes", nodename, "services", "status"]):
                if paths and path not in paths:
                    continue
                try:
                    yield (path, nodename, Storage(self.daemon_status_data.get(["monitor", "nodes", nodename, "services", "status", path])))
                except KeyError:
                    continue

    def iter_services_configs(self, paths=None, nodenames=None):
        nodenames = nodenames or self.cluster_nodes
        for nodename in nodenames:
            for path in self.daemon_status_data.keys_safe(["monitor", "nodes", nodename, "services", "config"]):
                if paths and path not in paths:
                    continue
                try:
                    yield (path, nodename, Storage(self.daemon_status_data.get(["monitor", "nodes", nodename, "services", "config", path])))
                except KeyError:
                    continue

    def iter_nodes(self, nodenames=None):
        nodenames = nodenames or self.cluster_nodes
        for nodename in nodenames:
            try:
                yield (nodename, self.daemon_status_data.get(["monitor", "nodes", nodename]))
            except KeyError:
                continue

    def iter_nodes_monitor(self, nodenames=None):
        nodenames = nodenames or self.cluster_nodes
        for nodename in nodenames:
            try:
                yield (nodename, self.daemon_status_data.get(["monitor", "nodes", nodename, "monitor"]))
            except KeyError:
                continue

    def get_service_monitor(self, path):
        """
        Return the Monitor data of a service.
        """
        try:
            data = Storage(self.node_data.get(["services", "status", path, "monitor"]))
            data.placement = self.get_service_placement(path)
        except KeyError:
            data = Storage({
                "status": "idle",
                "placement": self.get_service_placement(path),
            })
        return data

    def get_service_placement(self, path):
        try:
            svc = SERVICES[path]
        except KeyError:
            return ""
        if self.placement_leader(svc, silent=True):
            return "leader"
        return ""

    def hook_command(self, cmd, data):
        """
        A generic Popen wrapper logging the begining of execution.
        """
        cmd = list(cmd)
        eid = data.get("data", {}).get("id")
        self.log.info("execute %s hook: %s", eid, " ".join(cmd))
        try:
            proc = Popen(cmd, stdout=None, stderr=None, stdin=PIPE,
                         close_fds=True)
            proc.stdin.write(json.dumps(data).encode())
            proc.stdin.close()
        except Exception as exc:
            self.log.error("%s hook %s execution error: %s", eid,
                           " ".join(cmd), exc)
            return
        return proc

    def node_command(self, cmd):
        """
        A generic node command Popen wrapper.
        """
        env = os.environ.copy()
        env["OSVC_ACTION_ORIGIN"] = "daemon"
        _cmd = [] + Env.om
        cmd = ["node"] + cmd
        self.log.info("execute: om %s", " ".join(cmd))
        proc = Popen(_cmd+cmd, stdout=None, stderr=None, stdin=None,
                     close_fds=True, env=env)
        return proc

    def service_command(self, path, cmd, stdout=None, stderr=None, stdin=None, local=True):
        """
        A generic object command Popen wrapper.
        """
        env = os.environ.copy()
        env["OSVC_ACTION_ORIGIN"] = "daemon"
        _cmd = [] + Env.om
        if path:
            cmd = ["svc", "-s", path] + cmd
        else:
            cmd = ["svc"] + cmd
        if local and "--local" not in cmd:
            cmd += ["--local"]
        self.log.info("execute: om %s", " ".join(cmd))
        if stdin is not None:
            _stdin = PIPE
        else:
            _stdin = None
        proc = Popen(_cmd+cmd, stdout=stdout, stderr=stderr, stdin=_stdin,
                     close_fds=True, env=env)
        if stdin:
            proc.stdin.write(stdin.encode())
        return proc

    def add_cluster_node(self, nodename):
        if not nodename:
            self.log.warning('add_cluster_node called with empty nodename')
            return
        NODE.unset_lazy("cd")
        NODE.unset_lazy("private_cd")
        unset_lazy(self, "cluster_nodes")
        unset_lazy(self, "sorted_cluster_nodes")
        if nodename in self.cluster_nodes:
            return
        nodes = self.cluster_nodes + [nodename]
        if "nodes" in NODE.private_cd.get("cluster", {}):
            NODE.set_multi(["cluster.nodes="+" ".join(nodes)], validation=False)
        else:
            from core.objects.ccfg import Ccfg
            svc = Ccfg()
            svc.set_multi(["cluster.nodes="+" ".join(nodes)], validation=False)
            del svc

    def remove_cluster_node(self, nodename):
        if not nodename:
            self.log.warning('remove_cluster_node called with empty nodename')
            return
        NODE.unset_lazy("cd")
        NODE.unset_lazy("private_cd")
        unset_lazy(self, "cluster_nodes")
        unset_lazy(self, "sorted_cluster_nodes")
        if nodename not in self.cluster_nodes:
            return
        nodes = [node for node in self.cluster_nodes if node != nodename]
        from core.objects.ccfg import Ccfg
        svc = Ccfg()
        buff = "cluster.nodes="+" ".join(nodes)
        self.log.info("set %s in cluster config" % buff)
        svc.set_multi(["cluster.nodes="+" ".join(nodes)], validation=False)
        self.log.info("unset cluster.nodes in node config")
        NODE.unset_multi(["cluster.nodes"])
        self.delete_peer_data(nodename)
        del svc

    @lazy
    def db_update_interval(self):
        return max(NODE.oget("node", "db_update_interval"), DEFAULT_COLLECTOR_DB_UPDATE_INTERVAL)

    @lazy
    def db_min_update_interval(self):
        return max(NODE.oget("node", "db_min_update_interval"), DEFAULT_COLLECTOR_DB_MIN_UPDATE_INTERVAL)

    @lazy
    def db_min_ping_interval(self):
        return max(NODE.oget("node", "db_min_ping_interval"), DEFAULT_COLLECTOR_DB_MIN_PING_INTERVAL)

    @lazy
    def quorum(self):
        return NODE.oget("cluster", "quorum")

    @lazy
    def split_action(self):
        return NODE.oget("node", "split_action")

    @lazy
    def maintenance_grace_period(self):
        return NODE.oget("node", "maintenance_grace_period")

    @lazy
    def rejoin_grace_period(self):
        return NODE.oget("node", "rejoin_grace_period")

    @lazy
    def ready_period(self):
        return NODE.oget("node", "ready_period")

    def in_maintenance_grace_period(self, nmon):
        return nmon.status in ("maintenance", "upgrade") and \
           nmon.status_updated > time.time() - self.maintenance_grace_period

    def arbitrators_votes(self):
        votes = []
        for arbitrator in NODE.arbitrators:
            ret, error, _ = NODE._ping(arbitrator["name"], arbitrator["timeout"])
            if ret == 0:
                votes.append(arbitrator["name"])
            elif error != "":
                self.log.warning("arbitrator %s stale, ping timeout %s: %s", arbitrator["name"], arbitrator["timeout"], error)
        return votes

    def live_nodes_count(self):
        return len(self.daemon_status_data.keys(["monitor", "nodes"]))

    @staticmethod
    def arbitrators_config_count():
        return len(NODE.arbitrators)

    def split_handler(self):
        if not self.quorum:
            self.log.info("cluster is split, ignore as cluster.quorum is false")
            return
        if self.freezer.node_frozen():
            self.log.info("cluster is split, ignore as the node is frozen")
            return
        total = len(self.cluster_nodes) + self.arbitrators_config_count()
        live = self.live_nodes_count()
        extra_votes = self.arbitrators_votes()
        n_extra_votes = len(extra_votes)
        if live + n_extra_votes > total / 2:
            self.log.info("cluster is split, we have quorum: "
                          "%(live)d+%(avote)d out of %(total)d votes (%(a)s)",
                          dict(live=live, avote=n_extra_votes, total=total, a=",".join(extra_votes)))
            return
        self.event(self.split_action, {
            "reason": "split",
            "node_votes": live,
            "arbitrator_votes": n_extra_votes,
            "voting": total,
            "pro_voters": self.list_nodes() + extra_votes,
        })
        # give a little time for log flush
        NODE.suicide(method=self.split_action, delay=2)

    def forget_peer_data(self, nodename, change=False, origin=None):
        """
        Purge a stale peer data if all rx threads are down.
        """
        if not self.nodes_data.exists([nodename]):
            return
        nmon = self.get_node_monitor(nodename=nodename)
        if not self.peer_down(nodename, exclude=[origin]):
            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
        nmon_status = nmon.status
        self.event(
            "forget_peer",
            {
                "reason": "no_rx",
                "peer": nodename,
            }
        )
        self.delete_peer_data(nodename)
        wake_monitor(reason="forget node %s data" % nodename)
        if nmon_status == "shutting":
            self.log.info("cluster is not split, the lost node %s last known "
                          "monitor state is '%s'", nodename, nmon_status)
        else:
            self.split_handler()

    def delete_peer_data(self, nodename):
        self.log.info('deleting node %s from nodes data', nodename)
        with GEN_MERGED_LOCK:
            self.nodes_data.unset_safe([nodename])
            try:
                del LOCAL_GEN_MERGED_ON_PEER[nodename]
            except KeyError:
                pass
            try:
                # will ask for a full when the node comes back again
                del PEER_GEN_MERGED[nodename]
            except KeyError:
                pass
        self.log.info('deleted node %s from nodes data', nodename)

    def peer_down(self, nodename, exclude=None):
        """
        Return True if no rx threads receive data from the specified peer
        node.
        """
        exclude = exclude or []
        for thr_id in list(THREADS):
            if not thr_id.endswith(".rx"):
                continue
            if thr_id in exclude:
                continue
            try:
                peer_status = self.daemon_status_data.get([thr_id, "peers", nodename, "beating"])
            except KeyError:
                continue
            if peer_status:
                return False
        return True

    def get_service_config(self, path, nodename):
        """
        Return the specified object status structure on the specified node.
        """
        try:
            return Storage(
                self.nodes_data.get([nodename, "services", "config", path])
            )
        except (TypeError, KeyError):
            return

    def get_service_instance(self, path, nodename):
        """
        Return the specified object status structure on the specified node.
        """
        data = self.daemon_status_data.get(["monitor", "nodes", nodename, "services", "status", path], None)
        if data is None:
            return
        return Storage(data)

    def get_service_instances(self, path, discard_empty=False):
        """
        Return the specified object status structures on all nodes.
        """
        instances = {}
        for nodename, instance in self.iter_service_instances(path):
            if not instance.updated:
                # foreign
                continue
            if discard_empty and not instance:
                continue
            instances[nodename] = instance
        return instances

    def get_service_nodes(self, path):
        return [n for (n, _) in self.iter_service_instances(path)]

    def list_nodes(self):
        return self.daemon_status_data.keys_safe(["monitor", "nodes"])

    def list_cluster_paths(self):
        paths = set()
        for path, _, _ in self.iter_services_configs():
            paths.add(path)
        return paths

    def get_service_agg(self, path):
        """
        Return the specified object aggregated status structure.
        """
        return Storage(self.daemon_status_data.get(["monitor", "services", path], default={}))

    #########################################################################
    #
    # Placement policies
    #
    #########################################################################
    def placement_candidates(self, svc, discard_frozen=True,
                             discard_na=True,
                             discard_overloaded=True,
                             discard_preserved=True,
                             discard_unprovisioned=True,
                             discard_constraints_violation=True,
                             discard_start_failed=True,
                             discard_affinities=True):
        """
        Return the list of object nodes meeting the following criteria:
        * we have valid instance data (not unknown, has avail)
        * the node is not in maintenance, shutting, init or upgrade (default)
        * the node is not frozen (default)
        * the node is not overloaded (default)
        * the object is not frozen (default)
        * the instance is provisioned (default)
        * the instance smon status is not "start failed" (default)
        * the instance constraints are eval'ed True (default)
        """
        def discard_hard_affinity(nodename):
            if not svc.hard_affinity:
                return False
            for path in svc.hard_affinity:
                try:
                    status = self.nodes_data.get([nodename, "services", "status", path, "avail"])
                except KeyError:
                    continue
                if status != "up":
                    return True
            return False

        def discard_hard_anti_affinity(nodename):
            if not svc.hard_anti_affinity:
                return False
            for path in svc.hard_anti_affinity:
                try:
                    status = self.nodes_data.get([nodename, "services", "status", path, "avail"])
                except KeyError:
                    continue
                if status == "up":
                    return True
            return False

        candidates = []
        if svc is None:
            return []
        for nodename in svc.peers:
            nmon = self.get_node_monitor(nodename)
            if not nmon:
                continue
            if discard_preserved and nmon.status in NMON_STATES_PRESERVED:
                continue
            try:
                frozen = self.daemon_status_data.get(["monitor", "nodes", nodename, "frozen"])
            except KeyError:
                continue
            if discard_frozen and frozen:
                # node frozen
                continue
            instance = self.get_service_instance(svc.path, nodename)
            if instance is None:
                continue
            if discard_na and instance.avail == "n/a":
                continue
            if discard_start_failed and \
               instance.get("monitor", {}).get("status") in (
                   "start failed",
                   "place 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
            if discard_affinities:
                if discard_hard_affinity(nodename):
                    continue
                if discard_hard_anti_affinity(nodename):
                    continue
            candidates.append(nodename)

        return candidates

    def placement_ranks(self, svc, candidates=None):
        if candidates is None:
            candidates = self.placement_candidates(svc)
        candidates = self.placement_ranks_policy(svc, candidates)
        candidates = self.placement_ranks_soft_affinities(svc, candidates)
        return candidates

    def placement_ranks_policy(self, svc, candidates):
        try:
            candidates = getattr(self, self.rankers[svc.placement])(svc, candidates)
        except AttributeError:
            candidates = [Env.nodename]
        return candidates

    def placement_ranks_soft_affinities(self, svc, candidates):
        if len(candidates) < 2:
            return candidates

        def ranked_up_candidates(o, candidates):
            l = self.up_service_instances(o)
            return [n for n in candidates if n in l]

        def ranked_non_up_candidates(o, candidates):
            l = self.up_service_instances(o)
            return [n for n in candidates if n not in l]

        def softaf(candidates):
            for o in sorted(svc.soft_affinity, reverse=True):
                l = ranked_up_candidates(o, candidates)
                candidates = l + [n for n in candidates if n not in l]
            return candidates

        def softaaf(candidates):
            for o in sorted(svc.soft_anti_affinity, reverse=True):
                l = ranked_non_up_candidates(o, candidates)
                candidates = l + [n for n in candidates if n not in l]
            return candidates

        candidates = softaf(candidates)
        candidates = softaaf(candidates)
        return candidates

    def up_service_instances(self, path):
        nodenames = []
        for nodename, instance in self.get_service_instances(path).items():
            if instance["avail"] == "up":
                nodenames.append(nodename)
            elif instance["monitor"].get("status") in ("restarting", "starting", "wait children", "provisioning",
                                                       "placing"):
                nodenames.append(nodename)
        return nodenames

    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_leaders(self, svc, candidates=None):
        ranks = self.placement_ranks(svc, candidates=candidates)
        if not ranks:
            return []
        elif svc.topology == "failover":
            return ranks[0:1]
        elif svc.topology == "flex":
            return ranks[0:svc.flex_target]
        else:
            return []

    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 "
                            "%(path)s on any node",
                            path=svc.path)
            return False
        if Env.nodename not in candidates:
            if not silent:
                self.duplog("info",
                            "placement constraints prevent us from starting "
                            "%(path)s on this node",
                            path=svc.path)
            return False
        if len(candidates) == 1:
            if not silent:
                self.duplog("info",
                            "we have the greatest placement priority for "
                            "%(path)s (alone)",
                            path=svc.path)
            return True

        ranks = self.placement_ranks(svc, candidates=candidates)
        if ranks == []:
            return False
        elif svc.topology == "failover":
            if Env.nodename == ranks[0]:
                if not silent:
                    self.duplog("info",
                                "we have the highest '%(placement)s' "
                                "placement priority for failover "
                                "%(path)s",
                                placement=svc.placement, path=svc.path)
                return True
            else:
                if not silent:
                    self.duplog("info",
                                "node %(nodename)s is alive and has a higher "
                                "'%(placement)s' placement priority for "
                                "failover %(path)s",
                                nodename=ranks[0], placement=svc.placement,
                                path=svc.path)
                return False
        elif svc.topology == "flex":
            index = ranks.index(Env.nodename) + 1
            if not silent:
                self.duplog("info",
                            "we have the %(idx)d/%(tgt)d '%(placement)s' "
                            "placement priority for flex %(path)s",
                            idx=index, tgt=svc.flex_target,
                            placement=svc.placement, path=svc.path)
            if index <= svc.flex_target:
                return True
            else:
                return False

    def placement_ranks_none(self, svc, candidates, silent=False):
        """
        Always return an empty list.
        """
        return []

    def placement_ranks_spread(self, svc, candidates, silent=False):
        """
        hash together each candidate nodename+path, 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.path+x))]

    def placement_ranks_score(self, svc, candidates, silent=False):
        data = []
        for nodename in candidates:
            try:
                load = self.daemon_status_data.get(["monitor", "nodes", nodename, "stats", "score"])
            except KeyError:
                continue
            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 = []
        for nodename in candidates:
            try:
                load = self.daemon_status_data.get(["monitor", "nodes", nodename, "stats", "load_15m"])
            except KeyError:
                try:
                    load = self.daemon_status_data.get(["monitor", "nodes", 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 first_available_node(self):
        for nodename, nmon in self.iter_nodes_monitor():
            if nmon:
                return nodename

    def get_oldest_gen(self, nodename=None):
        with GEN_MERGED_LOCK:
            return self.get_oldest_gen_locked(nodename)

    def get_oldest_gen_locked(self, nodename):
        """
        Get oldest generation of the local dataset on peers.
        """
        if nodename is None:
            gens = LOCAL_GEN_MERGED_ON_PEER.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_MERGED_ON_PEER:
                return 0, 0
            gen = LOCAL_GEN_MERGED_ON_PEER.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 = {Env.nodename: GEN}
        gen.update(PEER_GEN_MERGED)
        return gen

    def node_overloaded(self, nodename=None):
        nodename = nodename or Env.nodename
        for key in ("mem", "swap"):
            limit = self.daemon_status_data.get(["monitor", "nodes", nodename, "min_avail_" + key], default=0)
            total = self.daemon_status_data.get(["monitor", "nodes", nodename, "stats", key + "_total"], default=0)
            val = self.daemon_status_data.get(["monitor", "nodes", nodename, "stats", key + "_avail"], default=0)
            if total > 0 and val < limit:
                return True
        return False

    def nodes_info(self):
        data = {}
        for nodename in self.cluster_nodes:
            data[nodename] = {
                "labels": self.daemon_status_data.get(["monitor", "nodes", nodename, "labels"], default={}),
                "targets": self.daemon_status_data.get(["monitor", "nodes", nodename, "targets"], default={}),
            }
        return data

    def dump_nodes_info(self):
        try:
            with open(Env.paths.nodes_info, "r") as ofile:
                data = json.load(ofile)
        except Exception:
            data = {}
        new_data = {}
        for node, ndata in data.items():
            if node not in self.cluster_nodes:
                # drop nodes no longer in cluster
                continue
            new_data[node] = ndata
        for node, ndata in self.nodes_info().items():
            if not ndata and data.get(node):
                # preserve data we had info for
                continue
            new_data[node] = ndata
        if new_data == data:
            return False
        try:
            tmpf = tempfile.NamedTemporaryFile(delete=False, dir=Env.paths.pathtmp)
            fpath = tmpf.name
            tmpf.close()
            with open(fpath, "w") as ofile:
                json.dump(new_data, ofile)
            shutil.move(fpath, Env.paths.nodes_info)
        except Exception as exc:
            self.alert("warning", "failed to refresh %s: %s", Env.paths.nodes_info, exc)
        self.log.info("%s updated", Env.paths.nodes_info)
        return True

    def on_nodes_info_change(self):
        """
        Rewrite the on-disk nodes info cache and flush caches of all
        information referencing labels.

        Object configuration references to #nodes and nodes can change on
        label changes, so refresh the status.json to expose those changes
        in the cluster data.

        For example:
        nodes = mylabel=a
        flex_target={#nodes}
        """
        NODE.unset_lazy("nodes_info")
        changed = self.dump_nodes_info()
        if not changed:
            return
        for path in [p for p in SERVICES]:
            try:
                svc = SERVICES[path]
            except KeyError:
                # deleted
                continue
            svc.unset_conf_lazy()
            if self.get_node_monitor().status != "init":
                smon = self.daemon_status_data.get(["monitor", "nodes", Env.nodename, "services", "status", path, "monitor"], None)
                if not smon:
                    continue
                try:
                    # trigger status.json reload by the mon thread
                    data = svc.print_status_data_eval(refresh=False, write_data=True, clear_rstatus=True)
                    data["monitor"] = smon
                    self.daemon_status_data.set(["monitor", "nodes", Env.nodename, "services", "status", path], data)
                except Exception as exc:
                    self.log.error("on nodes info change, object %s status refresh:", path)
                    self.log.exception(exc)
        wake_monitor(reason="nodes info change")

    def speaker(self):
        return self.first_available_node() == Env.nodename

    def event(self, eid, data=None, log_data=None, level="info"):
        """
        Put an "event"-kind event in the events queue, then log in node.log
        and in the service.log if a path is provided in <data>. If a
        <log_data> is passed, merge it in <data> before formatting the messages
        to log.
        """
        evt = {
            "nodename": Env.nodename,
            "ts": time.time(),
            "kind": "event",
        }
        if not isinstance(data, dict):
            data = {}
        data["id"] = eid
        path = data.get("path")
        data["monitor"] = Storage(self.get_node_monitor())
        if path:
            try:
                data["service"] = Storage(self.get_service_agg(path))
            except TypeError:
                data["service"] = Storage()
            data["instance"] = self.get_service_instance(path, Env.nodename) or Storage()
            data["instance"].monitor = Storage(data["instance"].get("monitor", {}))
            rid = data.get("rid")
            resource = data.get("resource")
            if resource:
                try:
                    data["resource"] = Storage(data["resource"])
                except TypeError:
                    data["resource"] = Storage()
            elif rid:
                try:
                    data["resource"] = Storage(
                        data["instance"].get("resources", {}).get(rid, {})
                    )
                except TypeError:
                    data["resource"] = Storage()
            try:
                del data["instance"]["resources"]
            except KeyError:
                pass

        evt["data"] = data
        EVENT_Q.put(evt)
        hooks = NODE.hooks.get(eid, set()) | NODE.hooks.get("all", set())
        for hook in hooks:
            proc = self.hook_command(hook, evt)
            if proc:
                self.push_proc(proc, cmd=" ".join(hook))

        if not level:
            return

        key = eid, data.get("reason")
        fmt = EVENTS.get(key)
        if not fmt:
            # fallback to a generic message
            key = eid, None
            fmt = EVENTS.get(key)
        if not fmt:
            return

        fmt_data = {}
        fmt_data.update(data)
        if isinstance(log_data, dict):
            fmt_data.update(log_data)

        path = fmt_data.get("path")
        if path:
            # log to node.log with a "service <path> " prefix
            node_fmt = "service {path} "+fmt
            getattr(self.log, level)(node_fmt.format(**fmt_data))

            # log to <name>.log
            try:
                svc = SERVICES[path]
                getattr(svc.log, level)(fmt.format(**fmt_data))
            except KeyError:
                pass
        else:
            # log to node.log with no prefix
            getattr(self.log, level)(fmt.format(**fmt_data))

    @lazy
    def vip(self):

        def parse_vip(s):
            try:
                addr, netmask = s.split("/", 1)
                netmask, ipdev = netmask.split("@", 1)
                return addr, netmask, ipdev
            except Exception:
                return

        default_vip = NODE.oget("cluster", "vip")
        if not default_vip:
            return
        try:
            default_addr, default_netmask, default_ipdev = parse_vip(default_vip)
        except Exception:
            return
        template = [
            ("sync#i0", "disable", "true"),
            ("DEFAULT", "orchestrate", "ha"),
            ("DEFAULT", "nodes", "*"),
            ("DEFAULT", "monitor_action", "switch"),
            ("DEFAULT", "monitor_schedule", "@1m"),
            ("ip#0", "monitor", "true"),
            ("ip#0", "restart", "1"),
        ]
        self.log.info("cluster vip %s" % default_vip)
        for node in self.cluster_nodes:
            vip = NODE.oget("cluster", "vip", impersonate=node)
            if vip is None:
                self.log.info("cluster vip not set for node %s", node)
                continue
            try:
                addr, netmask, ipdev = parse_vip(vip)
            except Exception as exc:
                self.log.info("cluster vip not set or malformed: %s", exc)
                return
            t = ("ip#0", "ipname", addr)
            if t not in template:
                template.append(t)
            t = ("ip#0", "netmask", netmask)
            if t not in template:
                template.append(t)
            if ipdev == default_ipdev:
                t = ("ip#0", "ipdev", ipdev)
                if t not in template:
                    template.append(t)
            else:
                template += [
                    ("ip#0", "ipdev@"+node, ipdev),
                ]
        svc = factory("svc")("vip", namespace="system", node=NODE)
        existed = svc.exists()
        kws = []
        changes = []
        current = svc.print_config_data()
        for section, keyword, value in template:
            kws.append("%s.%s" % (section, keyword))
            try:
                val = svc._get("%s.%s" % (section, keyword))
            except (ex.OptNotFound, ex.RequiredOptNotFound):
                val = None
            if val != value:
                changes.append("%s.%s=%s" % (section, keyword, value))
        extraneous = []
        for kw in svc.print_config_data().get("ip#0", {}):
            if kw == "tags":
                # never discard tag customization (ex: tags=noaction)
                continue
            _kw = "ip#0."+kw
            if _kw not in kws:
                extraneous.append(_kw)
        if changes:
            for k in changes:
                self.log.info("set %s: %s", svc.path, k)
            svc.set_multi(changes, validation=False)
        if extraneous:
            for k in extraneous:
                self.log.info("unset %s: %s (undue)", svc.path, k)
            svc.unset_multi(extraneous)
        if not existed:
            self.defer_set_smon(svc.path, global_expect="provisioned")
        return svc

    def get_node(self):
        """
        helper for the comm module to find the Node(), for accessing
        its configuration.
        """
        return NODE

    def update_cluster_data(self):
        self.daemon_status_data.set(["cluster"], {
            "name": self.cluster_name,
            "id": self.cluster_id,
            "nodes": self.cluster_nodes,
        })

    def update_cluster_locks_lk(self):
        # this need protection with LOCKS_LOCK
        self.node_data.merge([], {"locks": deepcopy(LOCKS)})

    def update_status(self):
        data = self.status()
        self.thread_data.set([], data)

    def daemon_status(self):
        data = self.daemon_status_data.get_copy()
        return data

    def filter_daemon_status(self, data, namespace=None, namespaces=None, selector=None, relatives=False):
        if selector is None:
            selector = "**"
        keep = self.object_selector(selector=selector, namespace=namespace, namespaces=namespaces, relatives=relatives)
        for node in [n for n in data.get("monitor", {}).get("nodes", {})]:
            for path in [p for p in data["monitor"]["nodes"][node].get("services", {}).get("status", {})]:
                if path not in keep:
                    del data["monitor"]["nodes"][node]["services"]["status"][path]
            for path in [p for p in data["monitor"]["nodes"][node].get("services", {}).get("config", {})]:
                if path not in keep:
                    del data["monitor"]["nodes"][node]["services"]["config"][path]
        for path in [p for p in data.get("monitor", {}).get("services", {})]:
            if path not in keep:
                del data["monitor"]["services"][path]
        return data

    @staticmethod
    def data_without_non_updated_gens(data):
        """Return data without non updated gen
        when our gen information is not yet part of remote node gens, return original data
        """
        local_node = Env.nodename
        for other_node in [n for n in data.get("monitor", {"nodes": {}})["nodes"].keys() if n != local_node]:
            other_node_gens = data["monitor"]["nodes"][other_node].get("gen", {})
            # Only filter remote node known gens when local node gen is known by remote
            if local_node in other_node_gens:
                data["monitor"]["nodes"][other_node]["gen"] = {
                    local_node: other_node_gens[local_node]
                }
        return data

    def match_object_selector(self, selector=None, namespace=None, namespaces=None, path=None):
        if selector is None:
            selector = "**"
        return path in self.object_selector(selector=selector, namespace=namespace, namespaces=namespaces, paths=[path])

    def object_selector(self, selector=None, namespace=None, namespaces=None, kind=None, paths=None, relatives=False):
        if not selector:
            return []
        if namespace:
            if namespaces is not None and namespace not in namespaces:
                return []
            # noinspection PySetFunctionToLiteral
            namespaces = set([namespace])
        if "root" in namespaces:
            namespaces.add(None)

        if paths is None:
            # all objects
            paths = self.list_cluster_paths()
        pds = paths_data(paths)
        pds = [pd for pd in pds if pd["namespace"] in namespaces]
        if kind:
            pds = [pd for pd in pds if pd["kind"] == kind]
        if selector == "**":
            return [pd["display"] for pd in pds]

        # all services
        if selector == "*":
            kind = kind or "svc"
            return [pd["display"] for pd in pds if pd["kind"] == kind]

        def or_fragment_selector(s):
            expanded = []
            for _selector in s.split(","):
                for p in and_fragment_selector(_selector):
                    if p in expanded:
                        continue
                    expanded.append(p)
            return expanded

        def and_fragment_selector(s):
            expanded = None
            for _selector in s.split("+"):
                _expanded = fragment_selector(_selector)
                if expanded is None:
                    expanded = _expanded
                else:
                    expanded = [p for p in expanded if p in _expanded]
            return expanded

        def selector_parse_jsonpath_expr(param):
            if param.startswith("."):
                param = "$"+param

            if param.startswith("$."):
                jsonpath_expr = parse(param)
            else:
                jsonpath_expr = None
            return jsonpath_expr

        def fragment_selector(s):
            # empty
            if not s:
                return []

            # explicit object path
            if s in self.list_cluster_paths():
                if s not in paths:
                    return []
                return [s]

            # fnmatch expression
            negate, s, elts = selector_parse_fragment(s)

            if len(elts) == 1:
                return object_path_glob(s, pds=pds, namespace=namespace, kind=kind, negate=negate)

            try:
                param, op, value = selector_parse_op_fragment(elts)
            except ValueError:
                return []

            expanded = []
            jsonpath_expr = selector_parse_jsonpath_expr(param)

            for path in paths:
                ret = svc_matching(path, param, op, value, jsonpath_expr)
                if ret ^ negate:
                    expanded.append(path)

            return expanded

        def selector_status_matching(path, jsonpath_expr, op, value):
            try:
                data = self.object_data(path)
                matches = jsonpath_expr.find(data)
                for match in matches:
                    current = match.value
                    if selector_value_match(current, op, value):
                        return True
            except Exception:
                return False
            return False

        def svc_matching(path, param, op, value, jsonpath_expr):
            if jsonpath_expr:
                return selector_status_matching(path, jsonpath_expr, op, value)
            else:
                try:
                    svc = SERVICES[path]
                except KeyError:
                    return False
                return selector_config_match(svc, param, op, value)

        def add_relatives(selected):
            l = set()
            for p in selected:
                l.add(p)
                try:
                    l |= set(SERVICES[p].children_and_slaves)
                    l |= set(SERVICES[p].parents)
                except Exception:
                    pass
            return list(l)

        expanded = or_fragment_selector(selector)
        if relatives:
            expanded = add_relatives(expanded)
        return expanded

    def object_data(self, path):
        """
        Extract from the cluster data the structures refering to a
        path.
        """
        try:
            data = self.get_service_agg(path)
            data["nodes"] = {}
        except KeyError:
            return
        for node in self.cluster_nodes:
            try:
                data["nodes"][node] = {
                    "status": self.daemon_status_data.get(["monitor", "nodes", node, "services", "status", path]),
                    "config": self.daemon_status_data.get(["monitor", "nodes", node, "services", "config", path]),
                }
            except KeyError:
                pass
        return data
  0707010001f27d000081a40000000000000000000000016a100daf00000be1000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/winservice.py   r"""

 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

"""
try:
    import win32service
    import win32serviceutil
    import win32api
    import win32event
    import servicemanager
except ImportError:
    # to please pylint we run on a non-windows os
    raise
import os
import sys

from daemon.main import Daemon
from utilities.lock import lock, unlock
from env import Env


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 lock(self):
        try:
            self.lockfd = lock(lockfile=Env.paths.daemon_lock, timeout=1,
                               delay=0.1)
        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)
        self.set_last_shutdown()

    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__), ".."))
        self.daemon = Daemon()
        self.daemon.init()
        self.lock()
        try:
            self.loop_forever()
        finally:
            self.unlock()

    def loop_forever(self):
        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_)
                self.daemon.stop()
                break
            else:
                #servicemanager.LogInfoMsg("%s - ALIVE"%self._svc_name_)
                if not self.daemon.loop():
                    # stopped through listener
                    break
                pass

def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)
    win32serviceutil.HandleCommandLine(OsvcAgent)
   0707010001f26f000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002900000000root/usr/share/opensvc/opensvc/daemon/hb  0707010001f274000081a40000000000000000000000016a100daf00001b52000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/hb/relay.py """
Relay Heartbeat
"""
import sys
import os

import daemon.shared as shared
import core.exceptions as ex
from env import Env
from .hb import Hb

class HbRelay(Hb):
    """
    A class factorizing common methods and properties for the relay
    heartbeat tx and rx child classes.
    """
    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data["stats"] = self.stats
        data["config"] = {
            "timeout": self.timeout,
            "interval": self.interval,
        }
        if hasattr(self, "relay"):
            data["config"]["relay"] = self.relay
        return data

    def configure(self):
        self.reset_stats()
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        if self.name not in shared.NODE.cd:
            # this thread will be stopped. don't reconfigure to avoid logging errors
            return
        self.get_hb_nodes()
        self.peer_config = {}
        self.timeout = shared.NODE.oget(self.name, "timeout")
        self.interval = shared.NODE.oget(self.name, "interval")
        try:
            self.relay = shared.NODE.oget(self.name, "relay")
        except Exception:
            raise ex.AbortAction("no %s.relay is not set in node.conf" % self.name)
        try:
            self.secret = shared.NODE.oget(self.name, "secret")
        except Exception:
            raise ex.AbortAction("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.set_tid()
        self.flags = os.O_RDWR
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.error(exc)
            self.stop()
            self.exit(1)

        try:
            while True:
                self.do()
                if self.stopped():
                    self.exit()
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.interval)
        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.janitor_procs()
        self.reload_config()
        message, message_bytes = self.get_message()
        if message is None:
            return

        try:
            self.send(message)
            self.set_last()
            self.push_stats(message_bytes)
            #self.log.info("written to %s slot %s", self.dev, slot)
        except Exception as exc:
            self.push_stats()
            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": {
                "cluster_name": self.cluster_name,
                "cluster_id": self.cluster_id,
                "msg": message,
            },
        }
        resp = self.daemon_post(request, cluster_name="join", server="raw://"+self.relay, secret=self.secret, timeout=self.timeout)
        if resp is None:
            raise ex.Error("not responding")
        if resp.get("status", 1) != 0:
            raise ex.Error("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.set_tid()
        self.flags = os.O_RDWR
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.error(exc)
            self.stop()
            self.exit(1)
        self.log.info("receive from relay %s", self.relay)

        while True:
            self.do()
            if self.stopped():
                self.exit()
            with shared.HB_TX_TICKER:
                shared.HB_TX_TICKER.wait(self.interval)

    def do(self):
        self.janitor_procs()
        self.reload_config()
        for nodename in self.hb_nodes:
            if nodename == Env.nodename:
                continue
            try:
                updated, slot_data = self.receive(nodename)
                _clustername, _nodename, _data = self.decrypt(slot_data, sender_id=self.relay)
                if _clustername != self.cluster_name:
                    continue
                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.queue_rx_data(_data, nodename)
                self.push_stats(len(_data))
                self.set_last(nodename)
            except Exception as exc:
                self.push_stats()
                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,
                "cluster_id": self.cluster_id,
            },
        }
        resp = self.daemon_get(request, cluster_name="join", server="raw://"+self.relay, secret=self.secret, timeout=self.timeout)
        if resp is None:
            raise ex.Error("no response reading relay slot %s" % nodename)
        if resp.get("status", 1) != 0:
            raise ex.Error("return status not 0 reading relay slot %s" % nodename)
        if resp.get("data") is None:
            raise ex.Error("no data in response reading relay slot %s" % nodename)
        if resp.get("updated") is None:
            raise ex.Error("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"]

  0707010001f276000081a40000000000000000000000016a100daf0000260c000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/hb/ucast.py """
Unicast Heartbeat module
"""
import sys
import socket
import threading
import time
from errno import EAFNOSUPPORT

import foreign.six as six
import core.exceptions as ex
import daemon.shared as shared
from env import Env
from .hb import Hb
from utilities.render.listener import fmt_listener


class HbUcast(Hb):
    """
    A class factorizing common methods and properties for the unicast
    heartbeat tx and rx child classes.
    """
    def __init__(self, name, role=None):
        super(HbUcast, self).__init__(name, role)
        self.peer_config = {}
        self.config_change = False
        self.timeout = None

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data["stats"] = self.stats
        data["config"] = {
            "timeout": self.timeout,
        }
        try:
            data["config"]["addr"] = self.peer_config[Env.nodename]["addr"]
            data["config"]["port"] = self.peer_config[Env.nodename]["port"]
        except (TypeError, KeyError):
            # thread not configured yet
            pass
        return data

    def configure(self):
        self.reset_stats()
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        self.get_hb_nodes()
        peer_config = {}

        if self.name not in shared.NODE.cd:
            # this thread will be stopped. don't reconfigure to avoid logging errors
            return

        # peers
        for nodename in self.hb_nodes:
            if nodename not in peer_config:
                addr = shared.NODE.oget(self.name, "addr", impersonate=nodename)
                port = shared.NODE.oget(self.name, "port", impersonate=nodename)
                if addr is not None:
                    pass
                elif nodename == Env.nodename:
                    addr = "::"
                else:
                    addr = nodename
                peer_config[nodename] = {
                    "addr": addr,
                    "port": port,
                }

        if peer_config != self.peer_config:
            self.config_change = True
            self.peer_config = peer_config

        timeout = shared.NODE.oget(self.name, "timeout")
        if timeout != self.timeout:
            self.config_change = True
            self.timeout = timeout

        interval = shared.NODE.oget(self.name, "interval")
        if interval != self.interval:
            self.config_change = True
            self.interval = interval

        self.max_handlers = len(self.hb_nodes) * 4


class HbUcastTx(HbUcast):
    """
    The unicast heartbeat tx class.
    """
    sock_tmo = 1.0

    def __init__(self, name, role="tx"):
        super(HbUcastTx, self).__init__(name, role=role)

    def run(self):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.exception("error during configure step", exc)
            return

        try:
            while True:
                self.do()
                if self.stopped():
                    self.exit()
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.interval)
        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.janitor_procs()
        self.reload_config()
        message, message_bytes = self.get_message()
        if message is None:
            return

        for nodename, config in self.peer_config.items():
            if nodename == Env.nodename:
                continue
            self._do(message, message_bytes, nodename, config)

    def _do(self, message, message_bytes, nodename, config):
        sock = None
        try:
            # self.log.info("sending to %s:%s", config["addr"], config["port"])
            sock = socket.create_connection((config["addr"], config["port"]), self.sock_tmo)
            sock.sendall((message+"\0").encode())  # pylint: disable=no-member
            self.set_last(nodename)
            self.push_stats(message_bytes)
        except socket.timeout:
            self.push_stats()
            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.push_stats()
            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)
        except Exception as exc:
            self.push_stats()
            if self.get_last(nodename).success:
                self.log.error("send to %s (%s:%d) unexpected error: %s", nodename,
                                 config["addr"], config["port"], str(exc))
            self.set_last(nodename, success=False)
        finally:
            self.set_beating(nodename)
            if sock is not None:
                sock.close()


class HbUcastRx(HbUcast):
    """
    The unicast heartbeat rx class.
    """
    def __init__(self, name, role="rx"):
        super(HbUcastRx, self).__init__(name, role=role)
        self.sock = None
        self.sock_accept_tmo = 2.0
        self.sock_recv_tmo = 5.0

    def _configure(self):
        super(HbUcastRx, self)._configure()
        if not self.config_change:
            return
        self.config_change = False
        if self.sock:
            self.sock.close()
        local_exception = None
        for _ in range(3):
            try:
                self.configure_listener()
                return
            except socket.error as exc:
                local_exception = exc
                time.sleep(1)
        self.log.error("init error: %s", str(local_exception))
        raise ex.AbortAction

    def configure_listener(self):
        addr = self.peer_config[Env.nodename]["addr"]
        port = self.peer_config[Env.nodename]["port"]
        af = socket.AF_INET6 if ":" in addr else socket.AF_INET
        try:
            self.sock = socket.socket(af, socket.SOCK_STREAM)
        except socket.error as exc:
            if exc.errno == EAFNOSUPPORT and addr == "::" and af == socket.AF_INET6:
                addr = "0.0.0.0"
                self.peer_config[Env.nodename]["addr"] = addr
                self.log.info("ipv6 is disabled by the host, fallback to ipv4 only")
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            else:
                raise exc
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind((addr, port))
        self.sock.listen(5)
        self.sock.settimeout(self.sock_accept_tmo)
        self.log.info("listening on %s", fmt_listener(addr, port))

    def run(self):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.exception("error during configure step", exc)
            return
        except Exception as exc:
            self.log.error("%s", exc)
            raise exc

        while True:
            try:
                self.do()
            except Exception as exc:
                self.log.error("during do(): %s", exc)
                self.log.exception(exc)
                raise exc
            if self.stopped():
                self.join_threads()
                self.sock.close()
                self.exit()

    def do(self):
        self.reload_config()
        self.janitor_procs()
        self.janitor_threads()

        try:
            conn, addr = self.sock.accept()
            self.sock.settimeout(self.sock_recv_tmo)
        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
        try:
            thr = threading.Thread(target=self.handle_client, args=(conn, addr))
            thr.start()
            self.threads.append(thr)
        except RuntimeError as exc:
            self.log.warning(exc)
            conn.close()

    def handle_client(self, conn, addr):
        try:
            self._handle_client(conn, addr)
        finally:
            conn.close()

    def _handle_client(self, conn, addr):
        chunks = []
        buff_size = 4096
        while True:
            chunk = conn.recv(buff_size)
            if chunk:
                chunks.append(chunk)
            if not chunk or chunk.endswith(b"\x00"):
                break
        data = six.b("").join(chunks)
        self.push_stats(len(data))
        del chunks

        clustername, nodename, data = self.decrypt(data, sender_id=addr[0])
        if clustername != self.cluster_name:
            return
        if nodename is None or nodename == Env.nodename:
            # ignore hb data we sent ourself
            return
        if nodename not in self.hb_nodes:
            return
        if data is None:
            self.push_stats()
            self.set_beating(nodename)
            return
        try:
            self.queue_rx_data(data, nodename)
            self.set_last(nodename)
        except Exception as exc:
            if self.get_last(nodename).success:
                self.log.error("%s", exc)
            self.set_last(nodename, success=False)
        finally:
            self.set_beating(nodename)
0707010001f272000081a40000000000000000000000016a100daf000024be000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/daemon/hb/hb.py    """
 Heartbeat parent class
"""
import logging
import time
from copy import deepcopy

import daemon.shared as shared
import core.exceptions as ex
import utilities.ifconfig
from env import Env
from utilities.rfc3339 import RFC3339
from utilities.storage import Storage


class Hb(shared.OsvcThread):
    """
    Heartbeat parent class
    """
    interval = 5
    timeout = None

    # publish interval in seconds is the minimum interval between
    # 2 publications of node data heartbeat id statistics.
    publish_interval = 5 * 60

    def __init__(self, name, role=None):
        shared.OsvcThread.__init__(self)
        self.name = name
        self.id = name + "." + role
        self.thread_data = self.daemon_status_data.view([self.id])
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename + ".osvcd." + self.id),
                                         {"node": Env.nodename, "component": self.id})
        self.peers = {}
        self.reset_stats()
        self.hb_nodes = []
        self.get_hb_nodes()
        self.msg_type = None

        # has_changes bool is defined for early publication of
        # node data heartbeat statistic.
        self.has_changes = True
        self.last_published = time.time()

    def get_hb_nodes(self):
        try:
            new_nodes = [node for node in shared.NODE.conf_get(self.name, "nodes")
                         if node in self.cluster_nodes]
        except ex.OptNotFound:
            new_nodes = self.cluster_nodes
        if new_nodes != self.hb_nodes:
            self.log.info('hb nodes: %s', new_nodes)
            self.hb_nodes = new_nodes

    def push_stats(self, _bytes=-1):
        if _bytes < 0:
            self.stats.errors += 1
        else:
            self.stats.beats += 1
            self.stats.bytes += _bytes

    def reset_stats(self):
        self.stats = Storage({
            "since": time.time(),
            "beats": 0,
            "bytes": 0,
            "errors": 0,
        })

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        running = data.get("state") == "running"
        data["peers"] = {}
        for nodename in self.hb_nodes:
            if nodename == Env.nodename:
                data["peers"][nodename] = {}
                continue
            if "*" in self.peers:
                _data = self.peers["*"]
            else:
                _data = self.peers.get(nodename, Storage({
                    "last": 0,
                    "last_at": RFC3339().from_epoch(0),
                    "beating": False,
                    "success": True,
                }))
            data["peers"][nodename] = {
                "last": _data.last,
                "last_at": RFC3339().from_epoch(_data.last),
                "beating": _data.beating if running else False,
            }
        if self.has_changes or time.time() - self.last_published > self.publish_interval:
            # wait for initial node_data exists (pre-init)
            if self.node_data.exists(["hb"]):
                self.node_data.set(["hb", self.id], deepcopy(data))
                self.has_changes = False
                self.last_published = time.time()

        return data

    def set_last(self, nodename="*", success=True):
        if nodename not in self.peers:
            self.peers[nodename] = Storage({
                "last": 0,
                "last_at": RFC3339().from_epoch(0),
                "beating": False,
                "success": True,
            })
        if success:
            self.peers[nodename].last = time.time()
            if not self.peers[nodename].beating:
                self.event("hb_beating", data={
                    "nodename": nodename,
                    "hb": {"name": self.name, "id": self.id},
                })
                self.peers[nodename].beating = True
                self.has_changes = True
        self.peers[nodename].success = success

    def get_last(self, nodename="*"):
        if nodename in self.peers:
            return self.peers[nodename]
        return Storage({
            "last": 0,
            "last_at": RFC3339().from_epoch(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 list(self.peers.keys()):
            self.set_beating(nodename)

    def set_beating(self, nodename="*"):
        now = time.time()
        if nodename not in self.peers:
            self.peers[nodename] = Storage({
                "last": 0,
                "last_at": RFC3339().from_epoch(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
            self.has_changes = True
            if beating:
                self.event("hb_beating", data={
                    "nodename": nodename,
                    "hb": {"name": self.name, "id": self.id},
                })
            else:
                self.event("hb_stale", data={
                    "nodename": nodename,
                    "hb": {
                        "name": self.name, "id": self.id,
                        "timeout": self.timeout,
                        "interval": self.interval,
                        "last": self.peers[nodename].last,
                        "last_at": RFC3339().from_epoch(self.peers[nodename].last),
                    },
                }, level="warning")
            self.peers[nodename].beating = beating
        self.update_status()
        if not beating and self.peers[nodename].last > 0:
            self.forget_peer_data(nodename, change, origin=self.id)

    @staticmethod
    def get_ip_address(ifname):
        ifconfig = utilities.ifconfig.Ifconfig()
        intf = ifconfig.interface(ifname)
        if intf is None:
            raise AttributeError("interface %s not found" % ifname)
        if isinstance(intf.ipaddr, list):
            addr = intf.ipaddr[0]
        else:
            addr = intf.ipaddr
        return addr

    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 "*")
            if self.msg_type != 'ping':
                self.msg_type = 'ping'
                self.log.info('change message type to %s (gen %s)', self.msg_type, shared.GEN)
            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 "*")
            if not self.node_data.exists(["monitor"]):
                # no pertinent data to send yet (pre-init)
                self.log.debug("no pertinent data to send yet (pre-init)")
                return None, 0
            if self.msg_type != 'full':
                self.msg_type = 'full'
                self.log.info('change message type to %s (gen %s)', self.msg_type, shared.GEN)
            with shared.HB_MSG_LOCK:
                if shared.HB_MSG is not None:
                    return shared.HB_MSG, shared.HB_MSG_LEN
                data = self.node_data.get_full()
                shared.HB_MSG = self.encrypt(data, 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.debug("send gen %d-%d deltas to %s", begin, shared.GEN, nodename if nodename else "*")  # COMMENT
            if self.msg_type != 'patch':
                self.msg_type = 'patch'
                self.log.info('change message type to %s (gen %s)', self.msg_type, shared.GEN)
            data = {}
            try:
                for gen, delta in shared.GEN_DIFF.items():
                    if gen <= begin:
                        continue
                    data[gen] = delta
            except:
                # Protect from GEN_DIFF 'dictionary changed size' during iteration
                # - purge_log()
                # - reset during monitor crash init_data()
                self.log.info("wait next iteration to create patch message (gen %s)", shared.GEN)
                return None, 0
            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 queue_rx_data(self, data, nodename):
        shared.RX.put((nodename, data, self.name))
  0707010001f273000081a40000000000000000000000016a100daf00002ac5000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/hb/mcast.py """
Multicast Heartbeat
"""
import sys
import socket
import threading
import struct
import uuid
import json
import time

import core.exceptions as ex
import daemon.shared as shared
from env import Env
from utilities.chunker import chunker
from utilities.string import bdecode
from .hb import Hb

MAX_MESSAGES = 100
MAX_FRAGMENTS = 1000

class HbMcast(Hb):
    """
    A class factorizing common methods and properties for the multicast
    heartbeat tx and rx child classes.
    """
    src_addr = None
    port = None
    intf = None
    addr = None
    sock = None
    max_data = 1000

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data["stats"] = self.stats
        data["config"] = {
            "addr": self.addr,
            "port": self.port,
            "intf": self.intf,
            "src_addr": self.src_addr,
            "timeout": self.timeout,
            "interval": self.interval,
        }
        return data

    def configure(self):
        self.reset_stats()
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        pass

    def apply_changes(self):
        changed = False
        if self.name not in shared.NODE.cd:
            # this thread will be stopped. don't reconfigure to avoid logging errors
            return changed
        self.get_hb_nodes()
        prev = {
            "port": self.port,
            "addr": self.addr,
            "intf": self.intf,
            "timeout": self.timeout,
            "interval": self.interval
        }
        self.port = shared.NODE.oget(self.name, "port")
        self.addr = shared.NODE.oget(self.name, "addr")
        self.timeout = shared.NODE.oget(self.name, "timeout")
        self.interval = shared.NODE.oget(self.name, "interval")
        group = socket.inet_aton(self.addr)
        try:
            self.intf = shared.NODE.conf_get(self.name, "intf")
            self.src_addr = self.get_ip_address(self.intf)
            self.mreq = group + socket.inet_aton(self.src_addr)
        except Exception as exc:
            self.alert("warning", "fallback to any intf: %s", exc)
            self.intf = "any"
            self.src_addr = "0.0.0.0"
            self.mreq = struct.pack("4sl", group, socket.INADDR_ANY)
        self.max_handlers = len(self.hb_nodes) * 4

        # log changes
        changes = []
        for key, val in prev.items():
            new_val = getattr(self, key)
            if val != new_val:
                changed = True
                if val is not None:
                    changes.append("%s %s => %s" % (key, val, new_val))
        if changes:
            self.log.info(", ".join(changes))
        return changed

    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
        if self.src_addr != "0.0.0.0":
            try:
                self.log.info("set socket options: multicast source address %s", self.src_addr)
                self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.src_addr))
            except Exception as exc:
                self.log.error("%s", exc)

class HbMcastTx(HbMcast):
    """
    The multicast heartbeat tx class.
    """
    sock_tmo = 2

    def __init__(self, name):
        HbMcast.__init__(self, name, role="tx")

    def _configure(self):
        changed = self.apply_changes()
        if not changed:
            return
        if self.sock:
            try:
                self.sock.close()
            except:
                pass
        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()
            ttl = struct.pack('b', 32)
            self.sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
            self.sock.settimeout(self.sock_tmo)
            self.group = (self.addr, self.port)
        except socket.error as exc:
            self.log.error("init error: %s", str(exc))
            raise ex.AbortAction

    def run(self):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction:
            return
        try:
            while True:
                self.do()
                if self.stopped():
                    self.sock.close()
                    self.exit()
                with shared.HB_TX_TICKER:
                    shared.HB_TX_TICKER.wait(self.interval)
        except Exception as exc:
            self.log.exception(exc)

    def do(self):
        self.janitor_procs()
        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:
            idx = 1
            mid = str(uuid.uuid4())
            total = message_bytes // self.max_data
            if message_bytes % self.max_data:
                total += 1
            for chunk in chunker(message, self.max_data):
                payload = (json.dumps({
                    "id": mid,
                    "i": idx,
                    "n": total,
                    "c": chunk,
                }) + "\0").encode()
                sent = self.sock.sendto(payload, self.group)
                #self.log.info("send %s %d/%d", mid, idx, total)
                idx += 1
            self.set_last()
            self.push_stats(message_bytes)
        except socket.timeout as exc:
            self.push_stats()
            if self.get_last().success:
                self.log.warning("send timeout")
            self.set_last(success=False)
        except socket.error as exc:
            self.push_stats()
            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.
    """
    sock_tmo = 2
    fragments = {}

    def __init__(self, name):
        HbMcast.__init__(self, name, role="rx")

    def _configure(self):
        changed = self.apply_changes()
        if not changed:
            return
        if self.sock:
            try:
                self.sock.close()
            except:
                pass
        err = None
        for _ in range(3):
            try:
                self.configure_listener()
                return
            except socket.error as exc:
                time.sleep(1)
                err = str(exc)
        self.log.error("init error: %s", err)
        raise ex.AbortAction

    def configure_listener(self):
        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(self.sock_tmo)
        self.log.info("listening on %s:%s", self.addr, self.port)

    def run(self):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction:
            return

        while True:
            self.do()
            if self.stopped():
                self.join_threads()
                self.sock.close()
                self.exit()

    def do(self):
        def handle(data, addr):
            try:
                thr = threading.Thread(target=self.handle_client, args=(data, addr))
                thr.start()
                self.threads.append(thr)
            except RuntimeError as exc:
                self.log.warning(exc)

        self.reload_config()
        self.janitor_procs()
        self.janitor_threads()

        try:
            data, addr = self.sock.recvfrom(shared.MAX_MSG_SIZE)
            self.push_stats(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)
            self.fragments = {}
            return

        try:
            payload = json.loads(bdecode(data).rstrip("\0\x00"))
        except (ValueError, TypeError) as exc:
            # old format ? try decrypt. will blacklist if failed.
            handle(data, addr)
            return

        try:
            mid = payload["id"]
            chunk = payload["c"]
            idx = payload["i"]
            total = payload["n"]
        except KeyError:
            return

        # verify message DoS
        if addr not in self.fragments:
            self.fragments[addr] = {}
        elif len(self.fragments[addr]) > MAX_MESSAGES:
            self.log.warning("too many pending messages. purge")
            self.fragments[addr] = {}

        # verify fragment DoS
        if mid not in self.fragments[addr]:
            self.fragments[addr][mid] = {}
        elif len(self.fragments[addr][mid]) > MAX_FRAGMENTS:
            self.log.warning("too many pending message fragments. purge")
            del self.fragments[addr][mid]
            return

        # store fragment
        self.fragments[addr][mid][idx] = chunk

        if len(self.fragments[addr][mid]) != total:
            # not yet complete
            return

        #self.log.debug("message %s complete", mid)
        message = ""
        for idx in sorted(self.fragments[addr][mid].keys()):
            message += self.fragments[addr][mid][idx]
        handle(message, addr)
        self.fragments[addr] = {}

    def handle_client(self, message, addr):
        clustername, nodename, data = self.decrypt(message, sender_id=addr[0])
        if clustername != self.cluster_name:
            # surely from drp node
            return
        if nodename is None or nodename == Env.nodename:
            # ignore hb data we sent ourself
            return
        elif nodename not in self.hb_nodes:
            return
        if data is None:
            self.push_stats()
            self.set_beating(nodename)
            return
        try:
            self.queue_rx_data(data, nodename)
            self.set_last(nodename)
        except Exception as exc:
            if self.get_last(nodename).success:
                self.log.error("%s", exc)
            self.set_last(nodename, success=False)
        finally:
            self.set_beating(nodename)
            self.set_peers_beating()



   0707010001f271000081a40000000000000000000000016a100daf00003eb9000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/daemon/hb/disk.py  """
Disk Heartbeat
"""
import sys
import os
import mmap
import stat
import errno
import contextlib
import json
import time

import daemon.shared as shared
import core.exceptions as ex
from env import Env
from .hb import Hb
from utilities.string import bdecode
#from utilities.converters import print_duration

class HbDisk(Hb):
    """
    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

    def status(self, **kwargs):
        data = Hb.status(self, **kwargs)
        data["stats"] = self.stats
        data["config"] = {
            "dev": self.dev,
            "timeout": self.timeout,
            "interval": self.interval,
        }
        for peer in data["peers"]:
            data["peers"][peer].update(self.peer_config.get(peer, {}))
        return data

    def configure(self):
        self.dev = None
        self.reset_stats()
        self._configure()

    def reconfigure(self):
        self._configure()

    def _configure(self):
        if self.name not in shared.NODE.cd:
            # this thread will be stopped. don't reconfigure to avoid logging errors
            return

        self.get_hb_nodes()
        self.peer_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)

        self.timeout = shared.NODE.oget(self.name, "timeout")
        self.interval = shared.NODE.oget(self.name, "interval")
        try:
            new_dev = shared.NODE.oget(self.name, "dev")
        except ex.RequiredOptNotFound:
            raise ex.AbortAction("no %s.dev is not set in node.conf" % self.name)

        if not os.path.exists(new_dev):
            raise ex.AbortAction("%s does not exist" % new_dev)

        conf_dev = new_dev
        new_dev = os.path.realpath(new_dev)
        new_flags = os.O_RDWR
        statinfo = os.stat(new_dev)
        if Env.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  # (Darwin, SunOS) pylint: disable=no-member
            else:
                raise ex.AbortAction("%s must be a block device" % new_dev)
            if conf_dev.startswith("/dev/dm-"):
                raise ex.AbortAction("%s is not static enough a name to allow. please use a /dev/mapper/<name> or /dev/by-<attr>/<value> dev path" % conf_dev)
            if conf_dev.startswith("/dev/sd"):
                self.log.warning("%s is not a static name. using a /dev/mapper/<name> or /dev/by-<attr>/<value> dev path is safer", conf_dev)
        else:
            if not stat.S_ISCHR(statinfo.st_mode):
                raise ex.AbortAction("%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)
            # Take time to explain storage area
            self.log.info("storage area is metadata_size + (max_slots x slot_size): %d + (%d x %d)\n", self.METASIZE, self.MAX_SLOTS, self.SLOTSIZE)

        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.AbortAction("%s directio is not supported" % self.dev)
            else:
                raise ex.AbortAction("error opening %s: %s" % (self.dev, str(exc)))
        except Exception as exc:
            raise ex.AbortAction("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", "fsync file descriptor for %(dev)s %(exc)s", dev=self.dev, 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)
        try:
            fo.seek(offset, os.SEEK_SET)
        except Exception as exc:
            self.log.error("seek offset %d: %s" % (offset, exc))
            return None
        try:
            fo.readinto(self.meta_slot_buff)
        except Exception as exc:
            self.log.error("readinto from offset %d: %s" % (offset, exc))
            return None
        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.AbortAction()
        self.meta_slot_buff.seek(0)
        self.meta_slot_buff.write(data)
        offset = self.meta_slot_offset(slot)
        try:
            fo.seek(offset, os.SEEK_SET)
        except Exception as exc:
            raise ex.AbortAction("seek offset %d: %s" % (offset, exc))
        try:
            fo.write(self.meta_slot_buff)
        except Exception as exc:
            raise ex.AbortAction("write at offset %d: %s" % (offset, exc))
        try:
            fo.flush()
        except Exception as exc:
            raise ex.AbortAction("flush written at offset %d: %s" % (offset, exc))

    def slot_offset(self, slot):
        return self.METASIZE + slot * self.SLOTSIZE

    def read_slot(self, slot, fo=None):
        offset = self.slot_offset(slot)
        try:
            fo.seek(offset, os.SEEK_SET)
        except Exception as exc:
            raise ex.AbortAction("seek offset %d: %s" % (offset, exc))
        try:
            fo.readinto(self.slot_buff)
        except Exception as exc:
            raise ex.AbortAction("readinto from offset %d: %s" % (offset, exc))
        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.AbortAction()
        self.slot_buff.seek(0)
        self.slot_buff.write(data)
        offset = self.slot_offset(slot)
        try:
            fo.seek(offset, os.SEEK_SET)
        except Exception as exc:
            raise ex.AbortAction("seek to offset %d: %s" % (offset, exc))
        try:
            fo.write(self.slot_buff)
        except Exception as exc:
            raise ex.AbortAction("write at offset %d: %s" % (offset, exc))
        try:
            fo.flush()
        except Exception as exc:
            raise ex.AbortAction("flush written at offset %d: %s" % (offset, exc))

    def load_peer_config(self, fo=None, verbose=True):
        missing_nodes = {}
        for nodename in self.hb_nodes:
            if nodename not in self.peer_config:
                self.peer_config[nodename] = {
                    "slot": -1,
                }
                missing_nodes[nodename] = True
            elif self.peer_config[nodename]["slot"] < 0:
                missing_nodes[nodename] = True
        for slot in range(self.MAX_SLOTS):
            buff = self.meta_read_slot(slot, fo=fo)
            if buff is None or buff[0] == "\0":
                missing = ", ".join([k for k in missing_nodes.keys()])
                self.log.info("analysed slots %d, unknown slot for the following nodes: %s", slot, missing)
                return
            try:
                marker_pos = buff.index("\0")
                nodename = buff[:marker_pos]
            except ValueError:
                # buff.index may raise
                continue
            except IndexError:
                continue
            if nodename not in self.peer_config:
                continue
            if self.peer_config[nodename]["slot"] >= 0 and \
               slot != self.peer_config[nodename]["slot"]:
                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
            if nodename in missing_nodes:
                del missing_nodes[nodename]
            if len(missing_nodes) == 0:
                self.log.info("analysed slots %d, got slot informations for all nodes", slot)
                return

    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.log.info("candidate slot %d for %s", slot, Env.nodename)
                try:
                    nodename = bytes(Env.nodename, "utf-8")
                except TypeError:
                    nodename = Env.nodename
                try:
                    self.meta_write_slot(slot, nodename, fo=fo)
                    self.peer_config[Env.nodename]["slot"] = slot
                except Exception as exc:
                    self.log.error("error writing metadata to candidate slot %d: %s", slot, exc)
                    raise exc
                return
            break
        self.log.error("unable to allocate slot")


class HbDiskTx(HbDisk):
    """
    The disk heartbeat tx class.
    """
    def __init__(self, name):
        HbDisk.__init__(self, name, role="tx")

    def _configure(self):
        HbDisk._configure(self)
        if self.peer_config[Env.nodename]["slot"] < 0:
            self.allocate_slot()

    def run(self):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.error(exc)
            self.stop()
            self.exit(1)

        while True:
            try:
                self.do()
            except ex.AbortAction as exc:
                self.log.error(exc)
                self.set_last(success=False)
                self.set_beating()
            except Exception as exc:
                self.log.exception(exc)
                self.set_last(success=False)
                self.set_beating()
            if self.stopped():
                self.exit()
            with shared.HB_TX_TICKER:
                shared.HB_TX_TICKER.wait(self.interval)

    def do(self):
        self.janitor_procs()
        with self.hb_fo() as fo:
            self._do(fo)

    def _do(self, fo):
        self.reload_config()
        if Env.nodename not in self.peer_config:
            return
        slot = self.peer_config[Env.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.push_stats(message_bytes)
            #self.log.info("written to %s slot %s", self.dev, slot)
        except Exception as exc:
            self.push_stats()
            if self.get_last().success:
                self.log.error("write to %s slot %d error: %s", self.dev,
                               self.peer_config[Env.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):
        self.set_tid()
        try:
            self.configure()
        except ex.AbortAction as exc:
            self.log.error(exc)
            self.stop()
            self.exit(1)

        loop = 0
        while True:
            try:
                loop += 1
                if loop > 5:
                    loop = 0
                    missing = self.missing_peers()
                    if missing:
                        self.log.info("reload slots for missing peers: %s", ", ".join(missing))
                        with self.hb_fo() as fo:
                            self.load_peer_config(fo=fo)
                self.do()
            except ex.AbortAction as exc:
                self.log.error(exc)
                self.set_peers_beating()
            except Exception as exc:
                self.log.exception("%s", exc)
                self.set_peers_beating()
            if self.stopped():
                self.exit()
            with shared.HB_TX_TICKER:
                shared.HB_TX_TICKER.wait(self.interval)

    def missing_peers(self):
        missing = []
        for nodename in self.hb_nodes:
            try:
                slot = self.peer_config[nodename]["slot"]
            except KeyError:
                missing.append(nodename)
                continue
            if slot < 0:
                missing.append(nodename)
        return missing

    def do(self):
        self.janitor_procs()
        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 == Env.nodename:
                continue
            if data["slot"] < 0:
                continue
            try:
                slot_data = json.loads(self.read_slot(data["slot"], fo=fo))
                _clustername, _nodename, _data = self.decrypt(slot_data["msg"])
                if _clustername != self.cluster_name:
                    continue
                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 (age: %s)", nodename, print_duration(time.time()-updated))
                    continue
                if updated < time.time() - self.timeout:
                    # discard too old dataset
                    #self.log.info("node %s has a too old dataset (age: %s)", nodename, print_duration(time.time()-updated))
                    continue
                self.queue_rx_data(_data, nodename)
                self.push_stats(len(slot_data))
                self.set_last(nodename)
                self.last_updated[nodename] = updated
            except Exception as exc:
                self.push_stats()
                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)



   0707010001f270000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/daemon/hb/__init__.py  0707010001f27a000081a40000000000000000000000016a100daf00001ee5000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/daemon/rbac.py import daemon.shared as shared
from utilities.naming import split_path, factory

ALLOWED_HOST_PATHS = [
    "/etc/localtime",
    "/etc/timezone",
]

class ObjectCreateMixin(object):
    def rbac_create_data(self, payload=None , thr=None, **kwargs):
        if thr.usr is False:
            return
        if not payload:
            return
        all_ns = thr.get_all_ns()
        grants = thr.user_grants(all_ns)
        if "root" in grants:
            return []
        errors = []
        for path, cd in payload.items():
            errors += self.rbac_create_obj(path, cd, all_ns, thr=thr, **kwargs)
        return errors

    def rbac_create_obj(self, path, cd, all_ns, thr=None, **kwargs):
        errors = []
        name, namespace, kind = split_path(path)
        if namespace is None:
            namespace = "root"
        grants = thr.user_grants(all_ns | set([namespace]))
        if kind == "nscfg":
            if "squatter" not in grants:
                errors.append("%s: create the namespace %s config requires the squatter cluster role" % (path, namespace))
                return errors
            elif namespace not in grants["admin"]:
                thr.usr.set_multi(["grant+=admin:%s" % namespace])
                grants["admin"].add(namespace)
        elif namespace not in all_ns:
            if namespace == "system":
                errors.append("%s: create the new namespace system requires the root cluster role")
                return errors
            elif "squatter" not in grants:
                errors.append("%s: create the new namespace %s requires the squatter cluster role" % (path, namespace))
                return errors
            elif namespace not in grants["admin"]:
                thr.usr.set_multi(["grant+=admin:%s" % namespace])
                grants["admin"].add(namespace)
        if not "root" in grants and not "prioritizer" in grants:
            try:
                del cd["DEFAULT"]["priority"]
            except KeyError:
                pass
        thr.rbac_requires(roles=["admin"], namespaces=[namespace], grants=grants, **kwargs)
        try:
            orig_obj = factory(kind)(name, namespace=namespace, volatile=True, node=shared.NODE)
        except:
            orig_obj = None
        try:
            obj = factory(kind)(name, namespace=namespace, volatile=True, cd=cd, node=shared.NODE)
        except Exception as exc:
            errors.append("%s: unbuildable config: %s" % (path, exc))
            return errors
        if kind == "vol":
            errors.append("%s: volume create requires the root privilege" % path)
        elif kind == "ccfg":
            errors.append("%s: cluster config create requires the root privilege" % path)
        elif kind == "svc":
            groups = ["disk", "fs", "app", "share", "sync"]
            for r in obj.get_resources(groups):
                if r.rid == "sync#i0":
                    continue
                errors.append("%s: resource %s requires the root privilege" % (path, r.rid))
            for r in obj.get_resources("task"):
                if r.type not in ("task.podman", "task.docker"):
                    errors.append("%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type))
            for r in obj.get_resources("container"):
                if r.type not in ("container.podman", "container.docker"):
                    errors.append("%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type))
            for r in obj.get_resources("ip"):
                if r.type not in ("ip.cni"):
                    errors.append("%s: resource %s type %s requires the root privilege" % (path, r.rid, r.type))
        for section, sdata in cd.items():
            rtype = cd[section].get("type")
            errors += self.rbac_create_data_section(path, section, rtype, sdata, grants, obj, orig_obj, all_ns, thr=thr)
        return errors

    def rbac_create_data_section(self, path, section, rtype, sdata, user_grants, obj, orig_obj, all_ns, thr=None):
        errors = []
        for key, val in sdata.items():
            if "trigger" in key or key.startswith("pre_") or key.startswith("post_") or key.startswith("blocking_"):
                errors.append("%s: keyword %s.%s=%s requires the root role" % (path, section, key, val))
                continue
            _key = key.split("@")[0]
            try:
                _val = obj.oget(section, _key)
            except Exception as exc:
                errors.append("%s: %s" % (path, exc))
                continue
            # scopable
            for n in obj.nodes | obj.drpnodes:
                _val = obj.oget(section, _key, impersonate=n)
                if _key in ("container_data_dir") and _val:
                    if _val.startswith("/"):
                        errors.append("%s: keyword %s.%s=%s host paths require the root role" % (path, section, key, _val))
                        continue
                if _key in ("devices", "volume_mounts") and _val:
                    _errors = []
                    for __val in _val:
                        if __val.startswith("/"):
                            if __val.split(":")[0] in ALLOWED_HOST_PATHS:
                                continue
                            _errors.append("%s: keyword %s.%s=%s host paths require the root role" % (path, section, key, __val))
                            continue
                    if _errors:
                        errors += _errors
                        break
                if section == "DEFAULT" and _key == "monitor_action" and _val not in ("freezestop", "switch", None):
                    errors.append("%s: keyword %s.%s=%s requires the root role" % (path, section, key, _val))
                    break
                if section.startswith("container#") and _key == "netns" and _val == "host":
                    errors.append("%s: keyword %s.%s=%s requires the root role" % (path, section, key, _val))
                    break
                if section.startswith("container#") and _key == "privileged" and _val not in ("false", False, None):
                    errors.append("%s: keyword %s.%s=%s requires the root role" % (path, section, key, _val))
                    break
                if section.startswith("ip#") and _key == "netns" and _val in (None, "host"):
                    errors.append("%s: keyword %s.%s=%s requires the root role" % (path, section, key, _val))
                    break
            # unscopable
            if section == "DEFAULT" and _key == "cn":
                errors += self.rbac_kw_cn(path, _val, orig_obj)
            elif section == "DEFAULT" and _key == "grant":
                errors += self.rbac_kw_grant(path, _val, user_grants, all_ns, thr=thr)
        return errors

    def rbac_kw_grant(self, path, val, user_grants, all_ns, thr=None):
        errors = []
        req_grants = thr.parse_grants(val, all_ns)
        for role, namespaces in req_grants.items():
            if namespaces is None:
                # cluster roles
                if role not in user_grants:
                    errors.append("%s: keyword grant=%s requires the %s cluster role" % (path, val, role))
            else:
                # namespaces roles
                delta = set(namespaces) - set(user_grants.get(role, []))
                if delta:
                    delta = sorted(list(delta))
                    errors.append("%s: keyword grant=%s requires the %s:%s privilege" % (path, val, role, ",".join(delta)))
        return errors

    def rbac_kw_cn(self, path, val, orig_obj):
        errors = []
        try:
            orig_cn = orig_obj.oget("DEFAULT", "cn")
        except Exception:
            orig_cn = None
        if orig_cn == val:
            return []
        errors.append("%s: keyword cn=%s requires the root role" % (path, val))
        return errors

   0707010001f1b1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/__init__.py 0707010001f277000081a40000000000000000000000016a100daf00014a3f000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/listener.py """
Listener Thread
"""
import base64
import importlib
import json
import os
import pkgutil

import socket
import logging
import time
import select
import shutil
import traceback
import uuid
import fnmatch
import re
import datetime
from foreign.six.moves.urllib.parse import urlparse, parse_qs # pylint: disable=import-error
from subprocess import Popen
from errno import EADDRINUSE, ECONNRESET, EPIPE, EBADF, EAFNOSUPPORT

from core.objects.usr import Usr


try:
    import ssl
    import foreign.h2 as h2
    from foreign.h2.config import H2Configuration
    from foreign.h2.connection import H2Connection
    from foreign.hyper.common.headers import HTTPHeaderMap
    has_ssl = True
except Exception:
    has_ssl = False

try:
    import foreign.jwt as jwt
    from foreign.jwt.algorithms import RSAAlgorithm
    has_jwt = True
except Exception:
    has_jwt = False

import foreign.six as six
import daemon.shared as shared
import core.exceptions as ex
from foreign.six.moves import queue
from env import Env
from utilities.storage import Storage
from core.comm import Headers
from utilities.chunker import chunker
from utilities.naming import split_path, fmt_path, factory, split_fullname
from utilities.files import makedirs
from utilities.drivers import driver_import
from utilities.lazy import set_lazy, lazy, unset_lazy
from utilities.converters import print_duration
from utilities.string import bencode, bdecode
from utilities.uri import Uri
from utilities.render.listener import fmt_listener

if six.PY2:
    class _ConnectionResetError(Exception):
        pass
    class _ConnectionAbortedError(Exception):
        pass
    ConnectionResetError = _ConnectionResetError
    ConnectionAbortedError = _ConnectionAbortedError


LISTENER_SLOTS = 128
RE_LOG_LINE = re.compile(r"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]:[0-6][0-9]:[0-6][0-9],[0-9]{3} .* \| ")
JANITORS_INTERVAL = 0.5
ICON = base64.b64decode("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAABigAAAYoBM5cwWAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJKSURBVDiNbZJLSNRRFMZ/5/5HbUidRSVSuGhMzUKiB9SihYaJQlRSm3ZBuxY9JDRb1NSi7KGGELRtIfTcJBjlItsohT0hjcpQSsM0CMfXzP9xWszM35mpA/dy7+Wc7/vOd67wn9gcuZ8bisa3xG271LXthTdNL/rZ0B0VQbNzA+mX2ra+kL04d86NxY86QpEI8catv0+SIyOMNnr6aa4ba/aylL2cTdVI6tBwrbfUXvKeOXY87Ng2jm3H91dNnWrd++U89kIx7jw48+DMf0bcOtk0MA5gABq6egs91+pRCKc01lXOnG2tn4yAKUYkmWpATDlqevRjdb4PYMWDrSiVqIKCosMX932vAYoQQ8bCgGoVajcDmIau3jxP9bj6/igoFqiTuCeLkDQQQOSEDm3PMQEnfxeqhYlSH6Si6WF4EJjIZE+1AqiGCAZ3GoT1yYcEuSqqMDBacOXMo5JORDJBRJa9V0qMqkiGfHwt1vORlW3ND9ZdB/mZNDANJNmgUXcsnTmx+WCBvuH8G6/GC276BpLmA95XMxvVQdC5NOYkkC8ocG9odRCRzEkI0yzF3pn+SM2SKrfJiCRQYp9uqf9l/p2E3pIdr20DkCvBS6o64tMvtzLTfmTiQlGh05w1iSFyQ23+R3rcsjsqrlPr4X3Q5f6nOw7/iOwpX+wEsyLNwLcIB6TsSQzASon+1n83unbboTtiaczz3FVXD451VG+cawfyEAHPGcdzruPOHpOKp39SdcvzyAqdOh3GsyoBsLxJ1hS+F4l42Xl/Abn0Ctwc5dldAAAAAElFTkSuQmCC")

ROUTED_ACTIONS = {
    "node": {
        "logs": "node_logs",
        "backlogs": "node_backlogs",
    },
    "object": {
        "logs": "object_logs",
        "backlogs": "object_backlogs",
    },
}


def defer_stop_client(session_id):
    """add 'session_id' to list of listener clients to be stopped"""
    with shared.DEFERRED_STOP_LISTENER_CLIENTS_LOCK:
        shared.DEFERRED_STOP_LISTENER_CLIENTS.add(session_id)


class Close(Exception):
    pass


class DontClose(Exception):
    pass


class Listener(shared.OsvcThread):
    name = "listener"
    stage = "init"
    events_grace_period = True
    sock_tmo = 1.0
    sockmap = {}  # dict fd -> socket
    _sockmap = {}  # dict name -> (fd, socket)
    last_janitors = 0
    crl_expire = 0
    crl_mode = None
    sockux = None
    sockuxh2 = None
    sock = None
    tls_sock = None
    tls_context = None
    tls_port = -1
    tls_addr = None
    port = -1
    addr = None
    handlers = {}

    @lazy
    def certfs(self):
        mod = driver_import("resource", "fs")
        res = mod.Fs(rid="fs#certs", mount_point=Env.paths.certs, device="tmpfs", fs_type="tmpfs", mount_options="rw,nosuid,nodev,noexec,relatime,size=1m")
        set_lazy(res, "log",  self.log)
        return res

    @lazy
    def ca(self):
        secpaths = shared.NODE.oget("cluster", "ca")
        if not secpaths:
            secpaths = ["system/sec/ca-" + self.cluster_name]
        secs = []
        for secpath in secpaths:
            secname, namespace, kind = split_path(secpath)
            sec = factory("sec")(secname, namespace=namespace, volatile=True, node=shared.NODE)
            if not sec.exists():
                self.log.warning("ca %s does not exist: ignore", secpath)
                continue
            if "certificate_chain" not in sec.data_keys():
                self.log.warning("ca %s has no certificate key: ignore", secpath)
                continue
            secs.append(sec)
        return secs

    @lazy
    def cert(self):
        secpath = shared.NODE.oget("cluster", "cert")
        if secpath is None:
            secpath = "system/sec/cert-" + self.cluster_name
        secname, namespace, kind = split_path(secpath)
        return factory("sec")(secname, namespace=namespace, volatile=True, node=shared.NODE)

    def prepare_certs(self):
        makedirs(Env.paths.certs)
        if Env.sysname == "Linux" and self.ca and self.cert and self.cert.exists():
            self.certfs.start()
            os.chmod(Env.paths.certs, 0o0755)

        # concat ca certificates
        ca_certs = os.path.join(Env.paths.certs, "ca_certificates")
        try:
            open(ca_certs, 'w').close()
        except OSError:
            pass
        for ca in self.ca:
            data = ca.decode_key("certificate_chain")
            if data is None:
                self.log.warning("secret key %s.%s is not set" % (ca.path, "certificate_chain"))
                continue
            self.log.info("add %s certificate chain to %s", ca.path, ca_certs)
            with open(ca_certs, "a") as fo:
                fo.write(bdecode(data))

        # listener cert chain
        data = self.cert.decode_key("certificate_chain")
        if data is None:
            raise ex.InitError("secret key %s.%s is not set" % (self.cert.path, "certificate_chain"))
        cert_chain = os.path.join(Env.paths.certs, "certificate_chain")
        self.log.info("write %s", cert_chain)

        # listener private key
        with open(cert_chain, "w") as fo:
            fo.write(bdecode(data))
        data = self.cert.decode_key("private_key")
        if data is None:
            raise ex.InitError("secret key %s.%s is not set" % (self.cert.path, "private_key"))

        # listener private key
        private_key = os.path.join(Env.paths.certs, "private_key")
        self.log.info("write %s", private_key)
        with open(private_key, "w+") as fo:
            pass
        os.chmod(private_key, 0o0600)
        with open(private_key, "w") as fo:
            fo.write(bdecode(data))

        # revocations
        crl_path = self.fetch_crl()

        return ca_certs, cert_chain, private_key, crl_path

    def fetch_crl(self):
        crl = shared.NODE.oget("listener", "crl")
        if not crl:
            return

        if crl == Env.paths.crl:
            self.crl_mode = "internal"
            try:
                os.unlink(crl)
            except OSError:
                pass
            buff = ""
            for ca in self.ca:
                ca.unset_lazy("cd")
                if "crl" not in ca.data_keys():
                    continue
                try:
                    buff += bdecode(ca.decode_key("crl"))
                except Exception as exc:
                    self.log.error("decode %s crl error: %s", ca.path, exc)
            if buff:
                try:
                    with open(crl, "w") as fo:
                        fo.write(buff)
                    return crl
                except Exception as exc:
                    self.log.error("install %s error: %s", crl, exc)
            return

        self.crl_mode = "external"
        if os.path.exists(crl):
            return crl
        crl_path = os.path.join(Env.paths.certs, "certificate_revocation_list")
        secure = shared.NODE.oget("node", "secure_fetch")
        try:
            with Uri(crl, secure=secure).fetch() as fpath:
                shutil.copy(fpath, crl_path)
            # TODO: extract expire from crl
            self.crl_expire = time.time() + 60*60*24
            return crl_path
        except Exception as exc:
            self.log.warning("crl fetch failed: %s", exc)
            return

    def get_http2_ssl_context(self):
        """
        This function creates an SSLContext object that is suitably configured for
        HTTP/2. If you're working with Python TLS directly, you'll want to do the
        exact same setup as this function does.
        """
        ca_certs, cert_chain, private_key, crl = self.prepare_certs()
        # Get the basic context from the standard library.
        ctx = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
        #ctx.verify_mode = ssl.CERT_REQUIRED
        ctx.verify_mode = ssl.CERT_OPTIONAL
        ctx.load_cert_chain(cert_chain, keyfile=private_key)
        ctx.load_verify_locations(ca_certs)
        if crl:
            self.log.info("tls crl %s", crl)
            ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN
            ctx.load_verify_locations(crl)
        self.log.info("tls stats: %s", ctx.cert_store_stats())

        # RFC 7540 Section 9.2: Implementations of HTTP/2 MUST use TLS version 1.2
        # or higher. Disable TLS 1.1 and lower.
        ctx.options |= (
            ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
        )

        # RFC 7540 Section 9.2.1: A deployment of HTTP/2 over TLS 1.2 MUST disable
        # compression.
        ctx.options |= ssl.OP_NO_COMPRESSION

        # RFC 7540 Section 9.2.2: "deployments of HTTP/2 that use TLS 1.2 MUST
        # support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256". In practice, the
        # blacklist defined in this section allows only the AES GCM and ChaCha20
        # cipher suites with ephemeral key negotiation.
        ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20")

        # We want to negotiate using NPN and ALPN. ALPN is mandatory, but NPN may
        # be absent, so allow that. This setup allows for negotiation of HTTP/1.1.
        ctx.set_alpn_protocols(["h2", "http/1.1"])

        try:
            ctx.set_npn_protocols(["h2", "http/1.1"])
        except (NotImplementedError, AttributeError):
            pass

        return ctx

    def run(self):
        shared.NODE.listener = self
        self.set_tid()
        self.last_relay_janitor = 0
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.listener"), {"node": Env.nodename, "component": self.name})
        self.events_clients = []
        self.stats = Storage({
            "sessions": Storage({
                "accepted": 0,
                "auth_validated": 0,
                "tx": 0,
                "rx": 0,
                "alive": Storage({}),
                "clients": Storage({})
            }),
        })

        self.register_handlers()
        self.setup_socks()
        self.stage = "ready"

        while True:
            try:
                self.do()
                self.update_status()
            except socket.error as exc:
                self.log.warning(exc)
                self.setup_socks()
            except Exception as exc:
                self.log.exception(exc)
            if self.stopped():
                for sock in self.sockmap.values():
                    sock.close()
                self.join_threads()
                if Env.sysname == "Linux":
                    self.certfs.stop()
                self.exit()

    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 reconfigure(self):
        shared.NODE.listener = self
        unset_lazy(self, "ca")
        unset_lazy(self, "cert")
        unset_lazy(self, "certfs")
        self.setup_socks()

    def register_handlers(self):
        self.register_core_handlers()
        self.register_driver_handlers()

    def register_driver_handlers(self):
        from utilities.drivers import iter_drivers
        from core.objects.svcdict import KEYS, SECTIONS
        for mod in iter_drivers(SECTIONS):
            if not hasattr(mod, "DRIVER_HANDLERS"):
                continue
            for handler_class in mod.DRIVER_HANDLERS:
                handler = handler_class()
                for method, path in handler.routes:
                    path = "drivers/resource/%s/%s/%s" % (mod.DRIVER_GROUP, mod.DRIVER_BASENAME, path)
                    self.handlers[(method, path)] = handler
                    if method:
                        self.log.info("register handler %s /%s", method, path)

    def register_core_handlers(self):
        def onerror(name):
            import traceback
            traceback.print_exc()
        handlers_path = [os.path.join(Env.paths.pathsvc, Env.package, "daemon", "handlers")]
        for modinfo in pkgutil.walk_packages(handlers_path, 'daemon.handlers.', onerror=onerror):
            if hasattr(modinfo, "ispkg"):
                name = modinfo.name
                ispkg = modinfo.ispkg
            else:
                name = modinfo[1]
                ispkg = modinfo[2]
            if ispkg:
                continue
            if name.split(".")[-1] not in ("get", "post", "delete"):
                continue
            try:
                mod = importlib.import_module(name)
                handler = mod.Handler()
                if handler.path in self.handlers:
                    continue
                for method, path in handler.routes:
                    self.handlers[(method, path)] = handler
                    if method:
                        self.log.info("register handler %s /%s", method, path)
            except Exception as exc:
                self.alert("error", "error registering handler %s: %s" % (name, exc))
                continue

    def do(self):
        self.reload_config()
        ts = time.time()
        if ts > self.last_janitors + JANITORS_INTERVAL:
            self.janitor_crl()
            self.janitor_procs()
            self.janitor_clients_to_stop()
            self.janitor_threads()
            self.janitor_events()
            self.janitor_relay()
        self.last_janitors = ts

        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]
            conn = None
            try:
                conn, addr = sock.accept()
                self.stats.sessions.accepted += 1
                if fd == self.sockux.fileno():
                    tls = False
                    addr = ["local"]
                    scheme = "raw"
                    encrypted = False
                elif fd == self.sockuxh2.fileno():
                    tls = False
                    addr = ["local"]
                    scheme = "h2"
                    encrypted = False
                elif fd == self.sock.fileno():
                    scheme = "raw"
                    tls = False
                    encrypted = True
                elif fd == self.tls_sock.fileno():
                    scheme = "h2"
                    tls = True
                    encrypted = False
                else:
                    self.log.error("unexpected accepted fd %s", fd)
                    continue
                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:
                continue
            except OSError as exc:
                if conn and exc.errno == EBADF:
                    conn.close()
                continue
            except ConnectionAbortedError:
                if conn:
                    conn.close()
                continue
            except Exception as exc:
                self.log.exception(exc)
                if conn:
                    conn.close()
                continue
            try:
                thr = ClientHandler(self, conn, addr, encrypted, scheme, tls, self.tls_context)
                thr.start()
                self.threads.append(thr)
            except RuntimeError as exc:
                self.log.warning(exc)
                conn.close()

    def janitor_clients_to_stop(self):
        """
        tries to stop client handlers that have been added to CLIENT_STOP set
        cleanup CLIENT_STOP set when done
        """
        with shared.DEFERRED_STOP_LISTENER_CLIENTS_LOCK:
            if len(shared.DEFERRED_STOP_LISTENER_CLIENTS) == 0:
                return
            for thr in self.threads:
                thr_sid = thr.sid
                if thr_sid not in shared.DEFERRED_STOP_LISTENER_CLIENTS:
                    continue
                try:
                    self.log.info("ask client thread %s to stop", thr_sid)
                    thr.stop()
                except:
                    # not started, or already ended
                    pass
            shared.DEFERRED_STOP_LISTENER_CLIENTS.clear()

    def janitor_crl(self):
        if not self.tls_sock:
            return
        if not self.crl_mode:
            return
        try:
            mtime = os.path.getmtime(Env.paths.crl)
        except Exception:
            mtime = 0
        change = False
        has_crl = False

        if self.crl_mode == "internal":
            for ca in self.ca:
                ca.unset_lazy("cd")
                if "crl" in ca.data_keys():
                    has_crl = True
                    try:
                        refmtime = os.path.getmtime(ca.paths.cf)
                    except Exception:
                        continue
                    if mtime >= refmtime:
                        continue
                    change = True
                    self.log.info("refresh crl: installed version is %s older than %s", print_duration(refmtime-mtime), ca.path)
            if not has_crl and os.path.exists(Env.paths.crl):
                try:
                    self.log.info("remove %s", Env.paths.crl)
                    os.unlink(Env.paths.crl)
                    change = True
                except Exception as exc:
                    self.log.warning("remove %s: %s", Env.paths.crl, exc)
        elif self.crl_mode == "external":
            if mtime and mtime <= self.crl_expire:
                self.log.info("refresh crl: installed version is expired since %s", print_duration(self.crl_expire-mtime))
                change = True

        if change:
            self.setup_socktls(force=True)

    def janitor_relay(self):
        """
        Purge expired relay.
        """
        now = time.time()
        if now - self.last_relay_janitor < shared.RELAY_JANITOR_INTERVAL:
            return
        self.last_relay_janitor = now
        with shared.RELAY_LOCK:
            for key in [k for k in shared.RELAY_DATA]:
                age = now - shared.RELAY_DATA[key]["updated"]
                if age > shared.RELAY_SLOT_MAX_AGE:
                    self.log.info("drop relay slot %s aged %s", key, print_duration(age))
                    del shared.RELAY_DATA[key]

    def janitor_events(self):
        """
        Send queued events to all subscribed clients.

        Don't dequeue messages during the first 2 seconds of the listener lifetime,
        so clients have a chance to reconnect after a daemon restart and loose an
        event.
        """
        if self.events_grace_period:
            if time.time() > self.created + 2:
                self.events_grace_period = False
            else:
                return
        done = []
        while True:
            try:
                event = shared.EVENT_Q.get(False, 0)
            except queue.Empty:
                break
            to_remove = []
            for idx, thr in enumerate(self.events_clients):
                if thr not in self.threads:
                    to_remove.append(idx)
                    continue
                # make a copy, filter_event may change data, avoid being replaced while queued
                fevent = self.filter_event(json.loads(json.dumps(event)), thr)
                if fevent is None:
                    continue
                if thr.h2conn:
                    if not thr.events_stream_ids:
                        to_remove.append(idx)
                        continue
                thr.event_queue.put(fevent)
            for idx in to_remove:
                try:
                    del self.events_clients[idx]
                except IndexError:
                    pass

    def filter_event(self, event, thr):
        if event is None:
            return
        if thr.selector in (None, "**") and (thr.usr is False or "root" in thr.usr_grants):
            # root and no selector => fast path
            return event
        namespaces = thr.get_namespaces()
        kind = event.get("kind")
        if kind == "full":
            return event
        elif kind == "patch":
            return self.filter_patch_event(event, thr, namespaces)
        elif kind == "event":
            return self.filter_event_event(event, thr, namespaces)

    def filter_event_event(self, event, thr, namespaces):
        def valid(change):
            try:
                path = event["data"]["path"]
            except KeyError:
                return True
            if thr.selector and not self.match_object_selector(thr.selector, namespaces=namespaces, path=path):
                return False
            return False
        if valid(event):
            return event
        return None

    def filter_patch_event(self, event, thr, namespaces):
        def filter_change(change):
            try:
                key, value = change
            except:
                key = change[0]
                value = None
            try:
                key_len = len(key)
            except:
                return change
            if key_len == 0:
                if value is None:
                    return change
                value = self.filter_daemon_status(value, namespaces=namespaces, selector=thr.selector)
                return [key, value]
            elif key[0] == "monitor":
                if key_len == 1:
                    if value is None:
                        return change
                    value = self.filter_daemon_status({"monitor": value}, namespaces=namespaces, selector=thr.selector)["monitor"]
                    return [key, value]
                if key[1] == "services":
                    if key_len == 2:
                        if value is None:
                            return change
                        value = dict((k, v) for k, v in value.items() if self.match_object_selector(thr.selector, namespaces=namespaces, path=k))
                        return [key, value]
                    if key_len == 3:
                        if value is None:
                            # the deleted object may no longer match the selector
                            return change
                    if self.match_object_selector(thr.selector, namespaces=namespaces, path=key[2]):
                        return change
                    else:
                        return
                if key[1] == "nodes":
                    if key_len == 2:
                        if value is None:
                            return change
                        value = self.filter_daemon_status({"monitor": {"nodes": value}}, namespaces=namespaces, selector=thr.selector)["monitor"]["nodes"]
                        return [key, value]
                    if key_len == 3:
                        if value is None:
                            return change
                        value = self.filter_daemon_status({"monitor": {"nodes": {key[2]: value}}}, namespaces=namespaces, selector=thr.selector)["monitor"]["nodes"][key[2]]
                        return [key, value]
                    if key[3] == "services":
                        if key_len == 4:
                            if value is None:
                                return change
                            value = self.filter_daemon_status({"monitor": {"nodes": {key[2]: {"services": value}}}}, namespaces=namespaces, selector=thr.selector)["monitor"]["nodes"][key[2]]["services"]
                            return [key, value]
                        if key[4] == "status":
                            if key_len == 5:
                                if value is None:
                                    return change
                                value = dict((k, v) for k, v in value.items() if self.match_object_selector(thr.selector, namespaces=namespaces, path=k))
                                return [key, value]
                            if key_len == 6:
                                if value is None:
                                    # the deleted object may no longer match the selector
                                    return change
                            if self.match_object_selector(thr.selector, namespaces=namespaces, path=key[5]):
                                return change
                            else:
                                return
                        if key[4] == "config":
                            if key_len == 5:
                                if value is None:
                                    return change
                                value = dict((k, v) for k, v in value.items() if self.match_object_selector(thr.selector, namespaces=namespaces, path=k))
                                return [key, value]
                            if key_len == 6:
                                if value is None:
                                    # the deleted object may no longer match the selector
                                    return change
                            if self.match_object_selector(thr.selector, namespaces=namespaces, path=key[5]):
                                return change
                            else:
                                return
            return change

        changes = []
        for change in event.get("data", []):
            filtered_change = filter_change(change)
            if filtered_change:
                changes.append(filtered_change)
            #    print("ACCEPT", thr.usr.name if thr.usr else "", filtered_change)
            #else:
            #    print("DROP  ", thr.usr.name if thr.usr else "", change)
        event["data"] = changes
        return event

    def bind_inet(self, sock, addr, port):
        """
        Retry bind until the error is no longer "in use"
        """
        while True:
            if self.stopped():
                break
            try:
                sock.bind((addr, port))
                break
            except socket.error as exc:
                if exc.errno == EADDRINUSE:
                    time.sleep(0.5)
                    continue
                raise

    def af_inet_socket(self, addr):
        if addr:
            addrinfo = socket.getaddrinfo(addr, None)[0]
            addr = addrinfo[4][0]

        if addr and "." in addr:
            return socket.socket(socket.AF_INET, socket.SOCK_STREAM), addr

        try:
            # try to open a inet6 socket
            sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
        except socket.error as exc:
            if exc.errno == EAFNOSUPPORT and not addr:
                self.log.info("ipv6 is disabled by the host, fallback to ipv4 only")
                return socket.socket(socket.AF_INET, socket.SOCK_STREAM), "0.0.0.0"
            raise

        if addr == "::":
            try:
                sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
            except socket.error as exc:
                self.log.info("failed to disable ipv[46] dual-stack for [::]. continue with the host default.")
        elif not addr:
            addr = "::"
            try:
                sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
            except socket.error as exc:
                self.log.info("ipv[46] dual-stack is not available on the host. fallback to ipv6 only")
        return sock, addr

    def setup_socktls(self, force=False):
        self.vip
        if not has_ssl:
            self.log.info("skip tls listener init: ssl module import error")
            return
        port = shared.NODE.oget("listener", "tls_port")
        addr = shared.NODE.oget("listener", "tls_addr")
        if self.tls_port < 0 or self.tls_addr is None:
            self.tls_port = port
            self.tls_addr = addr
        elif force or port != self.tls_port or addr != self.tls_addr:
            try:
                self.tls_sock.close()
            except socket.error:
                pass
            self._del_sockmap("tls_sock")
            self.tls_port = port
            self.tls_addr = addr
        elif self._has_sockmap("tls_sock"):
            self.log.info("tls listener %s config unchanged", fmt_listener(self.tls_addr, self.tls_port))
            return

        try:
            self.tls_context = self.get_http2_ssl_context()
            self.tls_sock, addr = self.af_inet_socket(self.tls_addr)
            self.tls_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.bind_inet(self.tls_sock, addr, self.tls_port)
            self.tls_sock.listen(LISTENER_SLOTS)
            self.tls_sock.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.alert("error", "bind tls listener %s error: %s", fmt_listener(self.tls_addr, self.tls_port), exc)
            return
        except ex.InitError as exc:
            self.log.info("skip tls listener init: %s", exc)
            return
        except Exception as exc:
            self.log.info("failed tls listener init: %s", exc)
            return
        self.log.info("listening on %s using http/2 tls with client auth", fmt_listener(self.tls_addr, self.tls_port))
        self._update_sockmap("tls_sock", self.tls_sock)

    def setup_sock(self):
        port = shared.NODE.oget("listener", "port")
        addr = shared.NODE.oget("listener", "addr")
        if self.port < 0 or self.addr is None:
            self.port = port
            self.addr = addr
        elif port != self.port or addr != self.addr:
            try:
                self.sock.close()
            except socket.error:
                pass
            self._del_sockmap("sock")
            self.port = port
            self.addr = addr
        elif self._has_sockmap("sock"):
            self.log.info("aes listener %s config unchanged", fmt_listener(self.addr, self.port))
            return

        try:
            self.sock, addr = self.af_inet_socket(self.addr)
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.bind_inet(self.sock, addr, self.port)
            self.sock.listen(LISTENER_SLOTS)
            self.sock.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.alert("error", "bind aes listener %s error: %s", fmt_listener(self.addr, self.port), exc)
            return
        self.log.info("listening on %s using aes encryption", fmt_listener(self.addr, self.port))
        self._update_sockmap("sock", self.sock)

    def setup_sockux_h2(self):
        if os.name == "nt":
            return
        if self.sockuxh2:
            self.log.info("h2 listener %s config unchanged", Env.paths.lsnruxh2sock)
            return
        if not os.path.exists(Env.paths.lsnruxsockd):
            os.makedirs(Env.paths.lsnruxsockd)
        try:
            if os.path.isdir(Env.paths.lsnruxh2sock):
                shutil.rmtree(Env.paths.lsnruxh2sock)
            else:
                os.unlink(Env.paths.lsnruxh2sock)
        except Exception:
            pass
        try:
            self.sockuxh2 = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.sockuxh2.bind(Env.paths.lsnruxh2sock)
            self.sockuxh2.listen(LISTENER_SLOTS)
            self.sockuxh2.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.alert("error", "bind http/2 listener %s error: %s", Env.paths.lsnruxh2sock, exc)
            return
        self.log.info("listening on %s using http/2", Env.paths.lsnruxh2sock)
        self._update_sockmap("sockuxh2", self.sockuxh2)

    def setup_sockux(self):
        if os.name == "nt":
            return
        if self.sockux:
            self.log.info("raw listener %s config unchanged", Env.paths.lsnruxsock)
            return
        if not os.path.exists(Env.paths.lsnruxsockd):
            os.makedirs(Env.paths.lsnruxsockd)
        try:
            if os.path.isdir(Env.paths.lsnruxsock):
                shutil.rmtree(Env.paths.lsnruxsock)
            else:
                os.unlink(Env.paths.lsnruxsock)
        except Exception:
            pass
        try:
            self.sockux = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.sockux.bind(Env.paths.lsnruxsock)
            self.sockux.listen(LISTENER_SLOTS)
            self.sockux.settimeout(self.sock_tmo)
        except socket.error as exc:
            self.alert("error", "bind raw listener %s error: %s", Env.paths.lsnruxsock, exc)
            return
        self.log.info("listening on %s", Env.paths.lsnruxsock)
        self._update_sockmap("sockux", self.sockux)

    def setup_socks(self):
        self.setup_socktls()
        self.setup_sock()
        self.setup_sockux()
        self.setup_sockux_h2()

    def _del_sockmap(self, name):
        try:
            del self._sockmap[name]
        except KeyError:
            pass

    def _has_sockmap(self, name):
        return name in self._sockmap

    def _update_sockmap(self, name, s):
        self._sockmap[name] = (s.fileno(), s)
        self.sockmap = {fd: sock for fd, sock in self._sockmap.values()}


class ClientHandler(shared.OsvcThread):
    sock_tmo = 5.0
    name = "listener client"

    def __init__(self, parent, conn, addr, encrypted, scheme, tls, tls_context):
        shared.OsvcThread.__init__(self)
        self.parent = parent
        self.event_queue = None
        self.conn = conn
        self.addr = addr
        self.encrypted = encrypted
        self.scheme = scheme
        self.tls = tls
        self.tls_context = tls_context
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.listener"), {"node": Env.nodename, "component": "%s/%s" % (self.parent.name, addr[0])})
        self.streams = {}
        self.h2conn = None
        self.events_stream_ids = []
        self.usr_cf_sum = None
        self.same_auth = lambda h: False
        if scheme == "raw":
            self.usr = False
            self.usr_auth = "secret"
            self.usr_grants = {"root": None}
        else:
            self.usr = None
            self.usr_auth = None
            self.usr_grants = {}
        self.events_counter = 0
        self.sid = str(uuid.uuid4())


    def __str__(self):
        try:
            progress = self.parent.stats.sessions.alive[self.sid].progress
        except Exception:
            progress = "unknown"
        return "client handler thread (client addr: %s, usr: %s, auth: %s, scheme: %s, progress: %s)" % (
            self.addr[0],
            self.usr.name if self.usr else self.usr,
            self.usr_auth,
            self.scheme,
            progress,
        )

    def run(self):
        close = True
        try:
            self.parent.stats.sessions.alive[self.sid] = Storage({
                "created": time.time(),
                "addr": self.addr[0],
                "encrypted": self.encrypted,
                "progress": "init",
                "ident": self.ident,
            })
            if self.scheme == "h2":
                self.handle_h2_client()
            else:
                self.handle_raw_client()
        except Close:
            pass
        except DontClose:
            close = False
        except (OSError, socket.error) as exc:
            if exc.errno in (0, ECONNRESET):
                pass
        except RuntimeError as exc:
            self.log.error("%s", exc)
        except Exception as exc:
            try:
                ignore = exc.errno == 0
            except AttributeError:
                ignore = False
            if not ignore:
                self.log.error("unexpected: %s", exc)
                traceback.print_exc()
        finally:
            if close:
                del self.parent.stats.sessions.alive[self.sid]
                if self.h2conn:
                    self.h2conn.close_connection()
                self.conn.close()

    def negotiate_tls(self):
        """
        Given an established TCP connection and a HTTP/2-appropriate TLS context,
        this function:
        1. wraps TLS around the TCP connection.
        2. confirms that HTTP/2 was negotiated and, if it was not, throws an error.
        """
        if not self.tls:
            self.tls_conn = self.conn
            return

        try:
            self.tls_conn = self.tls_context.wrap_socket(self.conn, server_side=True)
        except OSError as exc:
            if exc.errno in (0, ECONNRESET):
                # 0: client => server after daemon restart
                # ECONNRESET: server => client after daemon restart
                raise
            raise RuntimeError("tls wrap error: %s"%exc)

        # Always prefer the result from ALPN to that from NPN.
        # You can only check what protocol was negotiated once the handshake is
        # complete.
        negotiated_protocol = self.tls_conn.selected_alpn_protocol()
        if negotiated_protocol is None:
            negotiated_protocol = self.tls_conn.selected_npn_protocol()

        if negotiated_protocol != "h2":
            raise RuntimeError("couldn't negotiate h2: %s" % negotiated_protocol)

    def current_usr_cf_sum(self):
        return self.node_data.get(["services", "config", self.usr.path, "csum"], default="unknown")

    def authenticate_client(self, headers):
        if self.usr is False:
            return

        if self.usr and self.usr_cf_sum == self.current_usr_cf_sum():
            # unchanged server side
            if self.same_auth(headers):
                # unchanged request auth
                return

        if self.addr[0] == "local":
            # only root can talk to the ux sockets
            self.usr = False
            self.usr_auth = "uxsock"
            self.last_auth = None
            self.same_auth = lambda h: True
            return

        secret = headers.get(Headers.secret)
        if secret:
            self.usr = self.authenticate_client_secret(secret)
            self.usr_auth = "secret"
            self.last_auth = secret
            self.same_auth = lambda h: h.get(Headers.secret) == self.last_auth
            return

        authorization = headers.get("authorization")
        if authorization:
            if authorization.startswith("Bearer "):
                self.usr = self.authenticate_client_jwt(authorization)
                self.usr_auth = "jwt"
                self.usr_grants = self.user_grants()
                self.last_auth = authorization
                self.same_auth = lambda h: h.get("authorization") == self.last_auth
                return
            elif authorization.startswith("Basic "):
                self.usr = self.authenticate_client_basic(authorization)
                self.usr_auth = "basic"
                self.usr_grants = self.user_grants()
                self.usr_cf_sum = self.current_usr_cf_sum()
                self.last_auth = authorization
                self.same_auth = lambda h: h.get("authorization") == self.last_auth
                return
        try:
            self.usr = self.authenticate_client_x509()
            self.usr_auth = "x509"
            self.usr_grants = self.user_grants()
            self.usr_cf_sum = self.current_usr_cf_sum()
            #self.log.info("loaded grants for %s, conf %s", self.usr.path, self.usr_cf_sum)
            self.last_auth = None
            self.same_auth = lambda h: h.get(Headers.secret) is None and h.get("authorization") is None
            return
        except Exception as exc:
            #self.log.warning("%s", exc)
            pass

        self.usr = None
        self.usr_grants = {}
        self.last_auth = None
        self.usr_cf_sum = None
        self.same_auth = lambda h: False
        raise ex.Error("refused %s auth" % str(self.addr))

    @lazy
    def jwt_provider_keys(self):
        import requests
        well_known_uri = shared.NODE.oget("listener", "openid_well_known")
        well_known_data = requests.get(well_known_uri).json()
        jwks_uri = well_known_data["jwks_uri"]
        jwks = requests.get(jwks_uri).json()
        keys = dict((k['kid'], RSAAlgorithm.from_jwk(json.dumps(k))) for k in jwks['keys'])
        return keys

    def authenticate_client_basic(self, authorization=None):
        if not authorization:
            raise ex.Error("no authorization header key")
        buff = authorization[6:].strip()
        buff = base64.b64decode(buff)
        name, password = bdecode(buff).split(":", 1)
        usr = factory("usr")(name, namespace="system", volatile=True, log=self.log, node=shared.NODE)
        if not usr.exists():
            raise ex.Error("user %s does not exist" % name)
        if not usr.has_key("password"):
            raise ex.Error("user %s has no password key" % name)
        if password != usr.decode_key("password"):
            raise ex.Error("user %s authentication failed: wrong password" % name)
        return usr

    def authenticate_client_jwt(self, authorization=None):
        if not authorization:
            raise ex.Error("no authorization header key")
        if not has_jwt:
            raise ex.Error("jwt is disabled (import error)")
        token = authorization[7:].strip()
        try:
            header = jwt.get_unverified_header(token)
        except Exception as exc:
            raise ex.Error(str(exc))
        key_id = header['kid']
        algorithm = header['alg']
        public_key = self.jwt_provider_keys[key_id]
        decoded = jwt.decode(token, public_key, audience=self.cluster_name, algorithms=algorithm)
        grant = decoded.get("grant", "")
        if isinstance(grant, list):
            grant = " ".join(grant)
        name = decoded.get("preferred_username")
        if not name:
            name = decoded.get("name", "unknown").replace(" ", "_")
        usr = factory("usr")(name, namespace="system", volatile=True, cd={"DEFAULT": {"grant": grant}}, log=self.log, node=shared.NODE)
        return usr

    def authenticate_client_secret(self, secret=None):
        if not secret:
            raise ex.Error("no secret header key")
        if self.blacklisted(self.addr[0]):
            raise ex.Error("sender %s is blacklisted" % self.addr[0])
        if bdecode(self.cluster_key) == secret:
            # caller will set self.usr to False, meaning superuser
            return False
        self.blacklist(self.addr[0])
        raise ex.Error("wrong secret")

    def authenticate_client_x509(self):
        try:
            cert = self.tls_conn.getpeercert()
        except Exception as exc:
            raise ex.Error("x509 auth: getpeercert failed")
        if cert is None:
            raise ex.Error("x509 auth: no client certificate received")
        subject = dict(x[0] for x in cert['subject'])
        cn = subject["commonName"]
        if cn.endswith(self.cluster_name) and cn.count(".") == 3:
            # service account
            name, namespace, kind = split_fullname(cn, self.cluster_name)
            usr = factory("usr")(name, namespace=namespace, volatile=True, log=self.log, node=shared.NODE)
        else:
            usr = factory("usr")(cn, namespace="system", volatile=True, log=self.log, node=shared.NODE)
        if not usr or not usr.exists():
            self.conn.close()
            raise ex.Error("x509 auth failed: %s (valid cert, unknown user)" % cn)
        return usr

    def get_user_info(self):
        if isinstance(self.usr, Usr):
            user = self.usr.name
        else:
            user = ""
        if self.addr:
            addr = self.addr[0]
        else:
            addr = ""
        return "%s@%s" % (user, addr)

    def prepare_response(self, stream_id, status, data, content_type="application/json", path=None):
        response_headers = [
            (':status', str(status)),
        ]
        if path:
            response_headers += [(":path", path)]
        response_headers += [
            ('content-type', content_type),
            ('server', 'opensvc-h2-server/1.0')
        ]
        if content_type == "text/event-stream":
            response_headers += [
                ('Cache-Control', 'no-cache'),
                ('Connection', 'keep-alive'),
                ('Transfer-Encoding', 'chunked'),
            ]
        if "json" in content_type:
            if data is None:
                data = {}
            data = json.dumps(data).encode()
        elif isinstance(data, six.string_types):
            data = bencode(data)
        elif data is None:
            data = "".encode()
        if data:
            response_headers += [
                ('content-length', str(len(data))),
            ]
        self.h2conn.send_headers(stream_id, response_headers)
        if stream_id not in self.streams:
            self.streams[stream_id] = {"outbound": b''}
        try:
            self.streams[stream_id]["outbound"] += data
        except TypeError as exc:
            pass
        self.send_outbound(stream_id)

    def can_end_stream(self, stream_id):
        if "request" not in self.streams[stream_id]:
            return True
        if self.streams[stream_id].get("pushers"):
            return False
        datalen = self.streams[stream_id].get("datalen", 0)
        headers = self.streams[stream_id].get("request_headers", {})
        if not headers:
            return True
        expectedlen = int(headers.get("Content-Length", [0])[0])
        if datalen >= expectedlen:
            return True
        return False

    def send_outbound(self, stream_id):
        data = self.streams[stream_id]["outbound"]
        end_stream = self.can_end_stream(stream_id)
        window_size = self.h2conn.local_flow_control_window(stream_id)
        window_size = min(window_size, len(data))
        will_send = data[:window_size]
        will_queue = data[window_size:]

        max_size = self.h2conn.max_outbound_frame_size
        for chunk in chunker(will_send, max_size):
            self.h2conn.send_data(stream_id, data=chunk, end_stream=False)
            data_to_send = self.h2conn.data_to_send()
            self.tls_conn.sendall(data_to_send)
            message_len = len(data_to_send)
            self.parent.stats.sessions.tx += message_len
            self.parent.stats.sessions.clients[self.addr[0]].tx += message_len

        self.streams[stream_id]["outbound"] = will_queue

        if not will_queue and end_stream:
            self.h2conn.end_stream(stream_id)
            self.tls_conn.sendall(self.h2conn.data_to_send())
            self.h2_cleanup_stream(stream_id)

    def h2_push_promise(self, stream_id, path, data, content_type):
        """
        Use to promise web resources to a browser.
        """
        promised_stream_id = self.h2conn.get_next_available_stream_id()
        request_headers = [h for h in self.streams[stream_id]["request"].headers if h[0] != ":path"]
        request_headers.insert(0, (":path", path))
        self.h2conn.push_stream(stream_id, promised_stream_id, request_headers)
        self.prepare_response(promised_stream_id, 200, data, content_type)

    def h2_router(self, stream_id):
        content_type = "application/json"
        stream = self.streams[stream_id]
        req = stream["request"]
        req_data = stream["data"]
        headers = dict((bdecode(a), bdecode(b)) for a, b in req.headers)
        path = headers.get(":path").lstrip("/")
        parsed_path = urlparse(path)
        path = parsed_path.path.strip("/")
        query = parse_qs(parsed_path.query)
        method = headers.get(":method", "GET")
        accept = headers.get("accept", "").split(",")
        sending_progress = "sending %s /%s result" % (method, path)
        if path == "favicon.ico":
            self.parent.stats.sessions.alive[self.sid].progress = sending_progress
            return self.favicon()
        elif path in ("", "index.html"):
            self.parent.stats.sessions.alive[self.sid].progress = sending_progress
            return self.index()
        elif path == "index.js":
            self.parent.stats.sessions.alive[self.sid].progress = sending_progress
            return self.index_js()
        elif "text/html" in accept:
            self.parent.stats.sessions.alive[self.sid].progress = sending_progress
            return self.index()
        multiplexed = stream["request_headers"].get(Headers.multiplexed) is not None
        node = stream["request_headers"].get(Headers.node)
        if node is not None:
            # rebuild the selector from split o-node header
            node = ",".join([bdecode(x) for x in stream["request_headers"].get(Headers.node)])
        options = json.loads(bdecode(req_data))
        options.update(dict((k, v if len(v) > 1 else v[0]) for (k, v) in query.items()))
        data = {
            "action": path,
            "method": method,
            "node": node,
            "multiplexed": multiplexed,
            "options": options,
        }
        data = self.update_data_from_path(data)
        try:
            handler = self.get_handler(method, data["action"])
        except ex.HTTP as exc:
            result = {"status": exc.status, "error": exc.msg}
            self.parent.stats.sessions.alive[self.sid].progress = sending_progress
            return exc.status, content_type, result

        try:
            self.authenticate_client(headers)
            self.parent.stats.sessions.auth_validated += 1
            self.parent.stats.sessions.clients[self.addr[0]].auth_validated += 1
        except ex.Error:
            if handler.access:
                status = 401
                result = {"status": status, "error": "Not Authorized"}
                self.parent.stats.sessions.alive[self.sid].progress = sending_progress
                return status, content_type, result

        try:
            result = self.router(None, data, stream_id=stream_id, handler=handler)
            status = 200
        except DontClose:
            raise
        except ex.HTTP as exc:
            status = exc.status
            result = {"status": exc.status, "error": exc.msg}
        except ex.Error as exc:
            status = 400
            result = {"status": status, "error": str(exc)}
        except Exception as exc:
            status = 500
            result = {"status": status, "error": str(exc), "traceback": traceback.format_exc()}
            self.log.exception(exc)
        try:
            content_type = self.streams[stream_id]["content_type"]
        except:
            pass
        self.parent.stats.sessions.alive[self.sid].progress = "sending %s result" % self.parent.stats.sessions.alive[self.sid].progress
        return status, content_type, result

    def h2_window_updated(self, event):
        if event.stream_id:
            try:
                self.send_outbound(event.stream_id)
            except KeyError:
                # stream cleaned up during iteration
                pass
        else:
            for stream_id in [sid for sid in self.streams]:
                try:
                    self.send_outbound(stream_id)
                except KeyError:
                    # stream cleaned up during iteration
                    pass

    def h2_request_received(self, event):
        stream_id = event.stream_id
        if event.stream_ended:
            data = b'{}'
        else:
            data = b''
        self.streams[stream_id] = {
            "request": event,
            "request_headers": HTTPHeaderMap(event.headers),
            "data": data,
            "datalen": 0,
            "stream_ended": False,
            "pushers": [],
            "outbound": b'',
        }
        if event.stream_ended:
            status, content_type, data = self.h2_router(stream_id)
            self.prepare_response(stream_id, status, data, content_type)

    def h2_data_received(self, event):
        self.streams[event.stream_id]["data"] += event.data
        self.streams[event.stream_id]["datalen"] += event.flow_controlled_length
        self.streams[event.stream_id]["stream_ended"] = event.stream_ended
        if not event.stream_ended:
            return
        status, content_type, data = self.h2_router(event.stream_id)
        self.prepare_response(event.stream_id, status, data, content_type)

    def h2_stream_ended(self, event):
        pass

    def h2_stream_reset(self, event):
        self.h2_cleanup_stream(event.stream_id)

    def h2_cleanup_stream(self, stream_id):
        if self.streams[stream_id]["outbound"]:
            return
        try:
            del self.streams[stream_id]
        except KeyError:
            pass
        try:
            self.events_stream_ids.remove(stream_id)
            # the janitor will drop the thread from the relay list if
            # self.events_stream_ids is empty
        except ValueError:
            pass

    def h2_received(self, data):
        if not data:
            return
        try:
            events = self.h2conn.receive_data(data)
        except h2.exceptions.ProtocolError as exc:
            self.log.warning("%s", exc)
            self.stop()
            return
        for event in events:
            if isinstance(event, h2.events.RequestReceived):
                self.h2_request_received(event)
            elif isinstance(event, h2.events.WindowUpdated):
                self.h2_window_updated(event)
            elif isinstance(event, h2.events.DataReceived):
                self.h2_data_received(event)
            elif isinstance(event, h2.events.StreamEnded):
                self.h2_stream_ended(event)
            elif isinstance(event, h2.events.StreamReset):
                self.h2_stream_reset(event)
            elif isinstance(event, h2.events.ConnectionTerminated):
                self.stop()

    def handle_h2_client(self):
        self.negotiate_tls()
        self.tls_conn.settimeout(self.sock_tmo)

        # init h2 connection
        h2config = H2Configuration(client_side=False)
        self.h2conn = H2Connection(config=h2config)
        self.h2conn.initiate_connection()
        try:
            self.tls_conn.sendall(self.h2conn.data_to_send())
        except socket.error as exc:
            if exc.errno == EPIPE:
                # daemon restart with connected clients
                return
            raise

        while True:
            if self.stopped():
                break
            try:
                data = self.tls_conn.recv(65535)
                if not data:
                    break
                self.parent.stats.sessions.rx += len(data)
                self.parent.stats.sessions.clients[self.addr[0]].rx += len(data)
                self.h2_received(data)
            except ssl.SSLError:
                pass
            except socket.timeout:
                pass
            except h2.exceptions.StreamClosedError:
                return
            except ConnectionResetError:
                return
            except Exception as exc:
                self.log.error("exit on %s %s", type(exc), exc)
                traceback.print_exc()
                return

            # execute all registered pushers
            pushers_per_stream = [(stream_id, stream.get("pushers", [])) for stream_id, stream in self.streams.items() if stream.get("pushers")]
            for stream_id, pushers in pushers_per_stream:
                for pusher in pushers:
                    fn = pusher.get("fn")
                    args = pusher.get("args", [])
                    kwargs = pusher.get("kwargs", {})
                    if not fn:
                        continue
                    try:
                        getattr(self, fn)(stream_id, *args, **kwargs)
                    except Exception as exc:
                        print(exc)

            data_to_send = self.h2conn.data_to_send()
            if data_to_send:
                self.tls_conn.sendall(data_to_send)

    def handle_raw_client(self):
        chunks = []
        buff_size = 4096
        self.conn.setblocking(False)
        while True:
            if self.stopped():
                break
            ready = select.select([self.conn], [], [self.conn], self.sock_tmo)
            if ready[0]:
                chunk = self.sock_recv(self.conn, buff_size)
            else:
                self.log.warning("timeout waiting for data")
                return
            if ready[2]:
                self.log.debug("exceptional condition on socket")
                return
            self.parent.stats.sessions.rx += len(chunk)
            self.parent.stats.sessions.clients[self.addr[0]].rx += len(chunk)
            if chunk:
                chunks.append(chunk)
            if not chunk or chunk.endswith(b"\x00"):
                break
        if six.PY3:
            data = b"".join(chunks)
        else:
            data = "".join(chunks)
        del chunks
        self.handle_raw_client_data(data)

    def handle_raw_client_data(self, data):
        if six.PY3:
            dequ = data == b"dequeue_actions"
        else:
            dequ = data == "dequeue_actions"
        if dequ:
            self.parent.stats.sessions.alive[self.sid].progress = "dequeue_actions"
            self.log.info("call: om node dequeue action")
            p = Popen(Env.om + ["node", 'dequeue_actions'],
                      stdout=None, stderr=None, stdin=None,
                      close_fds=os.name!="nt")
            return

        def peer_clustername(nodename):
            cname = shared.NODE.oget("cluster", "name", impersonate=nodename)
            if not cname:
                return cname
            return cname.lower()

        if self.encrypted:
            clustername, nodename, data = self.decrypt(data, sender_id=self.addr[0])
            if nodename in self.cluster_drpnodes:
                result = {"status": 401, "error": "drp node %s is not allowed to request" % nodename}
                self.raw_send_result(result)
                return
            if clustername != "join" and peer_clustername(nodename) != clustername:
                result = {"status": 401, "error": "node %s is not a cluster %s node" % (nodename, clustername)}
                self.raw_send_result(result)
                return
        else:
            try:
                data = self.msg_decode(data)
            except ValueError:
                pass
            nodename = Env.nodename

        #self.log.info("received %s from %s", str(data), nodename)
        self.parent.stats.sessions.auth_validated += 1
        self.parent.stats.sessions.clients[self.addr[0]].auth_validated += 1
        if data is None:
            return
        try:
            result = self.router(nodename, data)
        except DontClose:
            raise
        except ex.Error as exc:
            result = {"status": 400, "error": str(exc)}
        except ex.HTTP as exc:
            result = {"status": exc.status, "error": exc.msg}
        except Exception as exc:
            result = {"status": 500, "error": str(exc), "traceback": traceback.format_exc()}
            self.log.exception(exc)
        self.raw_send_result(result)

    def raw_send_result(self, result):
        if result is None:
            return
        progress = "sending %s result" % self.parent.stats.sessions.alive[self.sid].progress
        self.parent.stats.sessions.alive[self.sid].progress = progress
        self.conn.setblocking(True)
        if self.encrypted:
            message = self.encrypt(result)
        else:
            message = self.msg_encode(result)
        for chunk in chunker(message, 64*1024):
            try:
                self.conn.sendall(chunk)
            except socket.error as exc:
                if exc.errno == EPIPE:
                    self.log.info("sendall '%s' while '%s'", exc, progress)
                else:
                    self.log.warning("sendall '%s' while '%s'", exc, progress)
                break
        message_len = len(message)
        self.parent.stats.sessions.tx += message_len
        self.parent.stats.sessions.clients[self.addr[0]].tx += message_len

    def log_request(self, msg, nodename, lvl="info", **kwargs):
        """
        Append the request origin to the message logged by the router action"
        """
        if not msg:
            return
        if not self.usr or not self.addr or self.addr[0] == "local":
            origin = "requested by %s" % nodename if nodename else "root via unix socket"
        else:
            origin = "requested by %s@%s" % (self.usr.name, self.addr[0])
        if lvl == "error":
            fn = self.log.error
        if lvl == "warning":
            fn = self.log.warning
        else:
            fn = self.log.info
        fn("%s %s", msg, origin)

    @staticmethod
    def options_path(options, required=True):
        for key in ("path", "svcpath", "svcname"):
            try:
                return options[key]
            except KeyError:
                pass
        if required:
            raise ex.HTTP(400, "object path not set")
        return None

    #########################################################################
    #
    # RBAC
    #
    #########################################################################
    def get_all_ns(self):
        data = set()
        for path in self.list_cluster_paths():
            _, ns, _ = split_path(path)
            if ns is None:
                ns = "root"
            data.add(ns)
        return data

    def get_namespaces(self, role="guest"):
        if self.usr is False or "root" in self.usr_grants:
            return self.get_all_ns()
        else:
            return self.usr_grants.get(role, [])

    def user_grants(self, all_ns=None):
        if self.usr is False or self.tls is False:
            return {"root": None}
        grants = self.usr.oget("DEFAULT", "grant")
        return self.parse_grants(grants, all_ns=all_ns)

    def parse_grants(self, grants, all_ns=None):
        data = {}
        if not grants:
            return data
        if all_ns is None:
            all_ns = self.get_all_ns()
        for _grant in grants.split():
            if ":" in _grant:
                role_sel, ns_sel = _grant.split(":", 1)
                for role in role_sel.split(","):
                    if role not in Env.ns_roles:
                        continue
                    if role not in data:
                        data[role] = set()
                    for ns in ns_sel.split(","):
                        for _ns in all_ns:
                            if _ns is None:
                                _ns = "root"
                            if fnmatch.fnmatch(_ns, ns):
                                data[role].add(_ns)
                                for equiv in Env.roles_equiv.get(role, ()):
                                    if equiv not in data:
                                        data[equiv] = set([_ns])
                                    else:
                                        data[equiv].add(_ns)
            else:
                role = _grant
                if role not in Env.cluster_roles:
                    continue
                if role not in data:
                    data[role] = None
        # make sure all ns roles have a key, to avoid checking the key existance
        for role in Env.ns_roles:
            if role not in data:
                data[role] = set()
        return data

    def rbac_requires(self, namespaces=None, roles=None, action=None, grants=None, path=None, **kwargs):
        if self.usr is False:
            # ux and aes socket are not constrainted by rbac
            return
        if roles is None:
            # world-usable
            return
        if grants is None:
            grants = self.usr_grants
        if "root" in grants:
            return
        if isinstance(namespaces, (list, tuple)):
            namespaces = set([ns if ns is not None else "root" for ns in namespaces])
        elif namespaces == "FROM:path":
            if path is None:
                raise ex.HTTP(400, "handler '%s' rbac access namespaces FROM:path but no path passed" % action)
            namespaces = set([split_path(path)[1] or "root"])
        for role in roles:
            if role not in grants:
                continue
            if role in Env.cluster_roles:
                return

            # namespaced role
            role_namespaces = grants[role]
            if not role_namespaces:
                # empty set
                continue
            if namespaces == "ANY":
                # role granted on at least one namespace
                return
            if not len(namespaces - role_namespaces):
                # role granted on all namespaces
                return
        raise ex.HTTP(403, "Forbidden: handler '%s' requested by user '%s' with "
                           "grants '%s' requires role '%s'" % (
                action,
                self.usr.name if self.usr else self.usr,
                self.format_grants(grants),
                ",".join(roles)
        ))

    @staticmethod
    def format_grants(grants):
        elements = []
        for role, namespaces in grants.items():
            if namespaces is None:
                elements.append(role)
            elif not namespaces:
                pass
            else:
                elements.append("%s:%s" % (role, ",".join([ns if ns is not None else "root" for ns in namespaces])))
        return " ".join(elements)

    #########################################################################
    #
    # Routing and Multiplexing
    #
    #########################################################################
    def get_handler(self, method, pathname):
        try:
            return self.parent.handlers[(method, pathname)]
        except KeyError:
            pass
        raise ex.HTTP(501, "handler %s %s is not supported" % (method, pathname))

    def multiplex(self, node, handler, options, data, original_nodename, action, stream_id=None):
        method = handler.routes[0][0]
        try:
            del data["node"]
        except Exception:
            pass
        data["multiplexed"] = True # prevent multiplex at the peer endpoint
        result = {"nodes": {}, "status": 0}
        path = self.options_path(options, required=False)
        if node == "ANY" and path:
            svcnodes = self.get_service_nodes(path)
            try:
                if Env.nodename in svcnodes:
                    # prefer to not relay, if possible
                    nodenames = [Env.nodename]
                else:
                    nodenames = [svcnodes[0]]
            except IndexError:
                return {"error": "unknown service", "status": 1}
        elif node == "ANY":
            nodenames = [Env.nodename]
        else:
            nodenames = shared.NODE.nodes_selector(node, data=self.nodes_data.get())
            if not nodenames:
                return {"info": "empty node selection", "status": 0}
            if path:
                svcnodes = self.get_service_nodes(path)
                nodenames = [n for n in nodenames if n in svcnodes]

        def do_node(nodename):
            if nodename == Env.nodename:
                try:
                    _result = handler.action(nodename, action=action, options=options, stream_id=stream_id, thr=self)
                except ex.HTTP as exc:
                    status = exc.status
                    _result = {"status": exc.status, "error": exc.msg}
                except ex.Error as exc:
                    status = 400
                    _result = {"status": status, "error": str(exc)}
                except Exception as exc:
                    status = 500
                    _result = {"status": status, "error": str(exc), "traceback": traceback.format_exc()}
                    self.log.exception(exc)
                result["nodes"][nodename] = _result
                try:
                    result["status"] += 1 if _result.get("status") else 0
                except AttributeError:
                    # result is not a dict
                    pass
            else:
                if handler.stream:
                    sp = self.socket_parms("https://"+nodename)
                    client_stream_id, conn, resp = self.h2_daemon_stream_conn(data, sp=sp)
                    self.streams[stream_id]["pushers"].append({
                        "fn": "push_peer_stream",
                        "args": [nodename, client_stream_id, conn, resp],
                    })
                    _result = {}
                else:
                    _result = self.daemon_request(data, server=nodename, silent=True, method=method)
                result["nodes"][nodename] = _result
                try:
                    result["status"] += _result.get("status", 0)
                except AttributeError:
                    # result is not a dict
                    pass

        for nodename in nodenames:
            try:
                do_node(nodename)
            except Exception:
                continue

        if handler.stream:
            return
        return result

    def push_peer_stream(self, stream_id, nodename, client_stream_id, conn, resp):
        if conn._sock.can_read:
            conn._recv_cb(client_stream_id)
        while True:
            for msg in self.h2_daemon_stream_fetch(client_stream_id, conn):
                self.h2_stream_send(stream_id, msg)
            if conn._sock.can_read:
                conn._recv_cb(client_stream_id)
            else:
                break

    def create_multiplex(self, handler, options, data, original_nodename, action, stream_id=None):
        h = {}
        template = options.get("template")
        path = options.get("path")
        if template:
            odata = shared.NODE.svc_conf_from_templ("dummy", None, "svc", template)
        else:
            odata = options.get("data", {})
        for path, svcdata in odata.items():
            nodes = svcdata.get("DEFAULT", {}).get("nodes")
            placement = svcdata.get("DEFAULT", {}).get("placement", "nodes order")
            if nodes:
                nodes = shared.NODE.nodes_selector(nodes, data=self.nodes_data.get())
            else:
                nodes = self.get_service_nodes(path)
            if nodes:
                if Env.nodename in nodes:
                    node = Env.nodename
                else:
                    node = nodes[0]
            else:
                node = Env.nodename
            if node not in h:
                h[node] = {}
            h[node][path] = svcdata
        result = {"nodes": {}, "status": 0}
        for nodename, optdata in h.items():
            _options = {}
            _options.update(options)
            _options["data"] = optdata
            if nodename == Env.nodename:
                _result = handler.action(nodename, action=action, options=_options, stream_id=stream_id, thr=self)
                result["nodes"][nodename] = _result
                result["status"] += _result.get("status", 0)
            else:
                _data = {}
                _data.update(data)
                _data["options"] = _options
                _data["multiplexed"] = True # prevent multiplex at the peer endpoint
                self.log_request("relay create/update %s to %s" % (",".join([p for p in optdata]), nodename), original_nodename)
                _result = self.daemon_post(_data, server=nodename, silent=True)
                result["nodes"][nodename] = _result
                result["status"] += _result.get("status", 0)
        return result

    @staticmethod
    def parse_path(s):
        l = s.split("/")
        path = None
        node = None
        if len(l) == 1:
            return node, path, s

        if l[0] == "node":
            node = l[1]
            action = "/".join(l[2:])
        elif l[0] == "object":
            if l[2] in Env.kinds:
                path = fmt_path(l[3], l[1], l[2])
                action = "/".join(l[4:])
            elif l[1] in Env.kinds:
                path = fmt_path(l[2], None, l[1])
                action = "/".join(l[3:])
            else:
                path = fmt_path(l[1], None, "svc")
                action = "/".join(l[2:])
        elif l[0] == "instance":
            node = l[1]
            if l[3] in Env.kinds:
                path = fmt_path(l[4], l[2], l[3])
                action = "/".join(l[4:])
            elif l[2] in Env.kinds:
                path = fmt_path(l[3], None, l[2])
                action = "/".join(l[4:])
            else:
                path = fmt_path(l[2], None, "svc")
                action = "/".join(l[3:])

        # translate action
        if path:
            action = ROUTED_ACTIONS["object"].get(action)
        elif node:
            action = ROUTED_ACTIONS["node"].get(action)
        else:
            action = s

        return node, path, action

    def update_data_from_path(self, data):
        action = data["action"]
        # url path router
        # ex: nodes/n1/logs => n1, None, node_logs
        node, path, action = self.parse_path(action)
        if action != data["action"]:
            data["action"] = action
        if node:
            data["node"] = node
        if path:
            if "options" in data:
                data["options"]["path"] = path
            else:
                data["options"] = {"path": path}
        return data

    def router(self, nodename, data, stream_id=None, handler=None):
        """
        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.
        """
        self.parent.stats.sessions.alive[self.sid]['tid'] = shared.NODE.get_tid()
        if not isinstance(data, dict):
            return {"error": "invalid data format", "status": 1}
        if "action" not in data:
            return {"error": "action not specified", "status": 1}

        if handler is None:
            method = data.get("method")
            action = data["action"].lstrip("/")
            handler = self.get_handler(method, action)
        else:
            method = handler.routes[0][0]
            action = handler.routes[0][1]

        # prepare options, sanitized for use as keywords
        options = {}
        for key, val in data.get("options", {}).items():
            options[str(key)] = val
        #print("addr:", self.addr, "tls:", self.tls, "action:", action, "options:", options)
        self.parent.stats.sessions.alive[self.sid].progress = "%s /%s" % (method, action)

        # validate rbac before multiplexing, before privs escalation
        if hasattr(handler, "rbac"):
            handler.rbac(nodename, action=action, options=options, stream_id=stream_id, thr=self)
        else:
            self.rbac_requires(action=action)

        if action in ("create", "object_create"):
            return self.create_multiplex(handler, options, data, nodename, action, stream_id=stream_id)
        node = data.get("node")
        if data.get("multiplexed") or handler.multiplex == "never":
            return handler.action(nodename, action=action, options=options, stream_id=stream_id, thr=self)
        if handler.multiplex == "always" or node:
            return self.multiplex(node, handler, options, data, nodename, action, stream_id=stream_id)
        return handler.action(nodename, action=action, options=options, stream_id=stream_id, thr=self)


    #########################################################################
    #
    # Handlers Helpers
    #
    #########################################################################

    def h2_push_action_events(self, stream_id):
        while True:
            try:
                msg = self.event_queue.get(False, 0)
            except queue.Empty:
                break
            self.h2_stream_send(stream_id, msg)

    def raw_push_action_events(self):
        while True:
            if self.stopped():
                break
            while True:
                try:
                    buff = self.conn.recv(4096)
                except Exception as exc:
                    break
                if not buff:
                    return
            try:
                msg = self.event_queue.get(True, 1)
            except queue.Empty:
                continue

            if self.encrypted:
                msg = self.encrypt(msg)
            else:
                msg = self.msg_encode(msg)

            self.conn.sendall(msg)

    def logskip(self, backlog, logfile):
        skip = 0
        if backlog > 0:
            fsize = os.path.getsize(logfile)
            if backlog > fsize:
                skip = 0
            else:
                skip = fsize - backlog
        return skip

    def _action_logs_open(self, logfile, backlog, obj):
        skip =  self.logskip(backlog, logfile)
        ofile = open(logfile, "r")
        if backlog > 0:
            self.log.debug("send %s log, backlog %d",
                           obj, 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, whole file", obj)
            ofile.seek(0)
        else:
            self.log.info("follow %s log", obj)
            ofile.seek(0, 2)

        if skip:
            # drop first line (that is incomplete as the seek placed the
            # cursor in the middle
            line = ofile.readline()
        return ofile

    def read_file_lines(self, ofile, sid=None, since=None, until=None):
        data = []
        buff = ""

        def convert_dt(ts_dt):
            # convert dt to timestamp float
            # dt can be timestamp, date time, or iso string
            if not ts_dt:
                return None
            try:
                return float(ts_dt)
            except ValueError:
                pass
            ts_dt = str(ts_dt)
            dt_str = ts_dt + "0000-01-01 00:00:00,000000"[len(ts_dt):]
            try:
                dt = datetime.datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S,%f")
                return time.mktime(dt.timetuple()) + dt.microsecond / 1000000
            except ValueError:
                pass
            try: # iso datetime (python3.7+ only)
                dt = datetime.datetime.fromisoformat(ts_dt)
                return dt.timestamp()
            except AttributeError:
                self.log.warning("invalid datetime format '%s' (iso not supported)", ts_dt)
            except ValueError:
                self.log.warning("invalid datetime format '%s'", ts_dt)
                return None

        since = convert_dt(since)
        until = convert_dt(until)

        def parse(_buff):
            head, message = _buff.split(" | ", 1)
            date_s, time_s, lvl, meta = head.split(None, 3)
            dt = datetime.datetime.strptime(date_s + " " + time_s, "%Y-%m-%d %H:%M:%S,%f")
            t = time.mktime(dt.timetuple()) + dt.microsecond / 1000000
            d = {
                "t": t,
                "l": lvl,
                "m": message.rstrip().split("\n"),
                "x": {},
            }
            for m in meta.split():
                k, v = m.split(":", 1)
                d["x"][k] = v
            return d

        while True:
            line = ofile.readline()
            if not line:
                break
            if RE_LOG_LINE.match(line):
                if buff:
                    # new msg, push pending buff
                    try:
                        parse_data = parse(buff)
                        if since and parse_data["t"] < since:
                            buff = line
                            continue
                        if until and parse_data["t"] > until:
                            buff = ""
                            break
                        if sid:
                            if parse_data["x"]["sid"] == sid:
                                data.append(parse_data)
                        else:
                            data.append(parse_data)
                    except ValueError:
                        pass
                buff = line
            else:
                buff += line
        if buff:
            # EOF, push pending buff
            try:
                parse_data = parse(buff)
                if since and parse_data["t"] < since:
                    return data
                if sid and parse_data["x"]["sid"] != sid:
                    return data
                data.append(parse_data)
            except ValueError:
                pass
        return data

    def h2_push_logs(self, stream_id, ofile, follow):
        lines = self.read_file_lines(ofile)
        if not follow:
            ofile.close()
            del self.streams[stream_id]["pushers"]
        if lines:
            self.h2_stream_send(stream_id, lines)

    def h2_sse_stream_send(self, stream_id, data):
        self.events_counter += 1
        msg = "id: %d\n" % self.events_counter
        msg += "data: %s\n\n" % json.dumps(data)
        self.streams[stream_id]["outbound"] += msg.encode()
        self.send_outbound(stream_id)

    def h2_stream_send(self, stream_id, data):
        try:
            content_type = self.streams[stream_id]["content_type"]
        except KeyError:
            content_type = None
        if content_type == "text/event-stream":
            self.h2_sse_stream_send(stream_id, data)
            return
        promised_stream_id = self.h2conn.get_next_available_stream_id()
        request_headers = self.streams[stream_id]["request"].headers
        self.h2conn.push_stream(stream_id, promised_stream_id, request_headers)
        self.prepare_response(promised_stream_id, 200, data)

    def load_file(self, path):
        fpath = os.path.join(Env.paths.pathhtml, path)
        with open(fpath, "r") as f:
            buff = f.read()
        return buff

    ##########################################################################
    #
    # App
    #
    ##########################################################################

    @staticmethod
    def ui():
        return shared.NODE.oget("listener", "ui")

    def favicon(self):
        if not self.ui():
            return 403, "", ""
        return 200, "image/x-icon", ICON

    def serve_file(self, rpath, content_type):
        try:
            return 200, content_type, self.load_file(rpath)
        except OSError:
            return 404, content_type, "The webapp is not installed."

    def index(self):
        if not self.ui():
            return 403, "", ""
        #data = self.load_file("index.js")
        #self.h2_push_promise(stream_id, "/index.js", data, "application/javascript")
        return self.serve_file("index.html", "text/html")

    def index_js(self):
        if not self.ui():
            return 403, "", ""
        return self.serve_file("index.js", "application/javascript")

 0707010001f1b3000081a40000000000000000000000016a100daf00001010000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/daemon/clusterlock.py  import time
import uuid
from copy import deepcopy

import daemon.shared as shared
from env import Env

DELAY_TIME = 0.5


class LockMixin(object):
    """
    Methods shared between lock/unlock handlers.
    """
    def lock_acquire(self, nodename, name, timeout=None, thr=None):
        begin = time.time()
        if timeout is None:
            timeout = 10
        if not nodename:
            nodename = Env.nodename
        elif nodename not in thr.cluster_nodes:
            return
        lock_id = None
        deadline = time.time() + timeout
        situation = 0
        while time.time() < deadline:
            if not lock_id:
                lock_id = self._lock_acquire(nodename, name, thr=thr)
                if not lock_id:
                    if situation != 1:
                        thr.log.info("claim %s lock refused (already claimed)", name)
                    situation = 1
                    time.sleep(DELAY_TIME)
                    continue
                thr.log.info("claimed %s lock: %s", name, lock_id)
            if shared.LOCKS.get(name, {}).get("id") != lock_id:
                thr.log.info("claim %s dropped", name)
                lock_id = None
                continue
            if self.lock_accepted(name, lock_id, thr=thr):
                thr.log.info("acquire %s %s duration (%s)", name, lock_id, int(time.time()-begin))
                return lock_id
            time.sleep(DELAY_TIME)
        thr.log.warning("claim timeout on %s lock (duration %s s)", name, int(time.time()-begin))
        self.lock_release(name, lock_id, silent=True, thr=thr)

    def lock_release(self, name, lock_id, timeout=None, silent=False, thr=None):
        begin = time.time()
        released = False
        if timeout is None:
            timeout = 5
        deadline = time.time() + timeout
        with shared.LOCKS_LOCK:
            if not lock_id or shared.LOCKS.get(name, {}).get("id") != lock_id:
                return
            del shared.LOCKS[name]
            if thr:
                thr.update_cluster_locks_lk()
        shared.wake_monitor(reason="unlock", immediate=True)
        if not silent:
            thr.log.info("released locally %s", name)
        while time.time() < deadline:
            if self._lock_released(name, lock_id, thr=thr):
                released = True
                break
            time.sleep(DELAY_TIME)
        if released is False:
            thr.log.warning('timeout waiting for lock %s %s release on peers', name, lock_id)
        else:
            thr.log.info("lock_released on %s lock %s (duration %s s)", name, lock_id, int(time.time()-begin))

    def lock_accepted(self, name, lock_id, thr=None):
        for nodename in thr.list_nodes():
            try:
                lock = thr.nodes_data.get([nodename, "locks", name])
            except KeyError:
                thr.log.info('lock not yet held by %s (id %s)', nodename, lock_id)
                return False
            if lock.get("id") != lock_id:
                thr.log.info('lock is held by %s with id %s', nodename, lock.get("id"))
                return False
        return True

    def _lock_released(self, name, lock_id, thr=None):
        """
        Verify if lock release has been written to cluster data.
        """
        for nodename in thr.list_nodes():
            try:
                lock = thr.nodes_data.get([nodename, "locks", name])
            except KeyError:
                continue
            if lock.get("id") == lock_id:
                return False
        return True

    def _lock_acquire(self, nodename, name, thr=None):
        lock_id = str(uuid.uuid4())
        with shared.LOCKS_LOCK:
            if name in shared.LOCKS:
                return
            shared.LOCKS[name] = {
                "requested": time.time(),
                "requester": nodename,
                "id": lock_id,
            }
            if thr:
                thr.update_cluster_locks_lk()
        shared.wake_monitor(reason="lock", immediate=True)
        return lock_id

    def locks(self):
        return deepcopy(shared.LOCKS)
0707010001f1b6000081a40000000000000000000000016a100daf00001387000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/daemon/events.py   """
Event messages to log, indexed by (event id, reason).
"""

from __future__ import print_function

EVENTS = {
    ("arbitrator_up", None): "arbitrator {arbitrator} is now reachable",
    ("arbitrator_down", None): "arbitrator {arbitrator} is no longer reachable",
    ("blacklist_add", None): "sender {sender} blacklisted",
    ("crash", "split"): "cluster is split, we don't have quorum: {node_votes}+{arbitrator_votes}/{voting} votes {pro_voters}",
    ("reboot", "split"): "cluster is split, we don't have quorum: {node_votes}+{arbitrator_votes}/{voting} votes {pro_voters}",
    ("forget_peer", "no_rx"): "no rx thread still receive from node {peer} and maintenance grace period expired. flush its data",
    ("hb_beating", None): "node {nodename} hb status stale => beating",
    ("hb_stale", None): "node {nodename} hb status beating => stale",
    ("node_config_change", None): "node config change",
    ("node_freeze", "target"): "freeze node",
    ("node_thaw", None): "thaw node",
    ("node_freeze", "kern_freeze"): "freeze node due to kernel cmdline flag.",
    ("node_freeze", "upgrade"): "freeze node for upgrade until the cluster is complete",
    ("node_freeze", "rejoin_expire"): "freeze node, the cluster is not complete on rejoin grace period expiration",
    ("node_freeze", "merge_frozen"): "freeze node, node {peer} was frozen while we were down",
    ("node_thaw", "upgrade"): "thaw node after upgrade, the cluster is complete",
    ("max_resource_restart", None): "max restart ({restart}) reached for resource {rid} ({resource.label})",
    ("max_stdby_resource_restart", None): "max restart ({restart}) reached for standby resource {rid} ({resource.label})",
    ("monitor_started", None): "monitor started",
    ("resource_toc", None): "toc for resource {rid} ({resource.label}) {resource.status} {resource.log}",
    ("resource_would_toc", "no_candidate"): "would toc for resource {rid} ({resource.label}) {resource.status} {resource.log}, but no node is candidate for takeover.",
    ("resource_degraded", None): "resource {rid} ({resource.label}) degraded to {resource.status} {resource.log}",
    ("resource_restart", None): "restart resource {rid} ({resource.label}) {resource.status} {resource.log}, try {try}/{restart}",
    ("stdby_resource_restart", None): "start standby resource {rid} ({resource.label}) {resource.status} {resource.log}, try {try}/{restart}",
    ("service_config_installed", None): "config fetched from node {from} is now installed",
    ("instance_abort", "target"): "abort {instance.topology} {instance.avail} instance {instance.monitor.local_expect} action to satisfy the {instance.monitor.global_expect} target",
    ("instance_delete", "target"): "delete {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_freeze", "target"): "freeze instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_freeze", "install"): "freeze instance on install",
    ("instance_freeze", "merge_frozen"): "freeze instance on rejoin because instance on {peer} is frozen",
    ("instance_provision", "target"): "provision {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_purge", "target"): "purge {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_start", "single_node"): "start idle single node {instance.avail} instance",
    ("instance_start", "from_ready"): "start {instance.topology} {instance.avail} instance ready for {since} seconds",
    ("instance_start", "target"): "start {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_stop", "target"): "stop {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_stop", "flex_threshold"): "stop {instance.topology} {instance.avail} instance to meet threshold constraints: {up}/{instance.flex_target}",
    ("instance_thaw", "target"): "thaw instance to satisfy the {instance.monitor.global_expect} target",
    ("instance_unprovision", "target"): "unprovision {instance.topology} {instance.avail} instance to satisfy the {instance.monitor.global_expect} target",
    ("scale_up", None): "misses {delta} instance to reach scale target {instance.scale}",
    ("scale_down", None): "exceeds {delta} instance to reach scale target {instance.scale}",
}


def doc():
    buff = "Daemon Events\n"
    buff += "=============\n\n"
    for (eid, reason), msg in sorted(EVENTS.items(), key=lambda x: x[0][0] + x[0][1] if x[0][1] else ""):
        if reason:
            title = "Id ``%s``, Reason ``%s``" % (eid, reason)
        else:
            title = "Id ``%s``" % eid
        length = len(title)
        buff += "%s\n" % title
        buff += "-" * length + "\n\n"
        buff += "%s\n\n" % msg
    return buff


if __name__ == "__main__":
    print(doc())
 0707010001f1b8000041ed0000000000000000000000206a102a9200000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/daemon/handlers    0707010001f246000041ed0000000000000000000000056a102a9200000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/daemon/handlers/relay  0707010001f247000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/__init__.py  0707010001f24b000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/status   0707010001f24d000081a40000000000000000000000016a100daf0000038a000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/status/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the relay's list of clients, with their last update time, payload size, ip address and cluster id/name.
    """
    routes = (
        ("GET", "relay_status"),
        ("GET", "daemon_relay_status"),
        (None, "daemon_relay_status"),
    )
    prototype = []
    access = {
        "roles": ["heartbeat"],
    }

    def action(self, nodename, thr=None, **kwargs):
        data = {}
        with shared.RELAY_LOCK:
            for _nodename, _data in shared.RELAY_DATA.items():
                data[_nodename] = {
                    "cluster_name": _data.get("cluster_name", ""),
                    "updated": _data.get("updated", 0),
                    "ipaddr": _data.get("ipaddr", ""),
                    "size": len(_data.get("msg", "")),
                }
        return data
  0707010001f24c000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/status/__init__.py   0707010001f248000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/rx   0707010001f24a000081a40000000000000000000000016a100daf000004e3000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/rx/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the last relay heartbeat payload emitted by <nodename>.
    """
    routes = (
        ("GET", "relay_rx"),
        (None, "relay_rx"),
    )
    prototype = [
        {
            "name": "cluster_id",
            "desc": "The cluster.id keyword value of the emitting node.",
            "required": False,
            "format": "string",
            "default": "",
        },
        {
            "name": "slot",
            "desc": "The name of the node to fetch the last heartbeat message from.",
            "required": True,
            "format": "string",
            "default": "",
        },
    ]
    access = {
        "roles": ["heartbeat"],
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        key = "/".join([options.cluster_id, options.slot])
        with shared.RELAY_LOCK:
            if key not in shared.RELAY_DATA:
                return {"status": 1, "error": "no data"}
            return {
                "status": 0,
                "data": shared.RELAY_DATA[key]["msg"],
                "updated": shared.RELAY_DATA[key]["updated"],
            }

 0707010001f249000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/rx/__init__.py   0707010001f24e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/tx   0707010001f24f000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/tx/__init__.py   0707010001f250000081a40000000000000000000000016a100daf0000067f000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/relay/tx/post.py   import time

import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Store a relay heartbeat payload emitted by <nodename>.
    """
    routes = (
        ("POST", "relay_tx"),
        (None, "relay_tx"),
    )
    prototype = [
        {
            "name": "cluster_id",
            "desc": "The cluster.id keyword value of the emitting node.",
            "required": False,
            "format": "string",
            "default": "",
        },
        {
            "name": "cluster_name",
            "desc": "The cluster.name keyword value of the emitting node.",
            "required": False,
            "format": "string",
            "default": "",
        },
        {
            "name": "msg",
            "desc": "The heartbeat message payload.",
            "required": False,
            "format": "string",
            "default": None,
        },
        {
            "name": "addr",
            "desc": "The sender [ipaddr, port] tuple.",
            "required": False,
            "format": "list",
            "default": [""],
        },
    ]
    access = {
        "roles": ["heartbeat"],
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        key = "/".join([options.cluster_id, nodename])
        with shared.RELAY_LOCK:
            shared.RELAY_DATA[key] = {
                "msg": options.msg,
                "updated": time.time(),
                "cluster_name": options.cluster_name,
                "cluster_id": options.cluster_id,
                "ipaddr": options.addr[0],
            }
        return {"status": 0}

 0707010001f1f0000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/daemon/handlers/key    0707010001f1f1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/key/__init__.py    0707010001f1f3000081a40000000000000000000000016a100daf000005fe000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/daemon/handlers/key/get.py import traceback

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.naming import split_path
from utilities.string import bdecode

class Handler(daemon.handler.BaseHandler):
    """
    Return the value of a usr, cfg or sec object key.
    """
    routes = (
        ("GET", "key"),
        (None, "get_key"),
        (None, "get_secret_key"),
    )
    access = "custom",
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "key",
            "desc": "The key name to provide value of.",
            "required": True,
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        try:
            return {"status": 0, "data": bdecode(shared.SERVICES[options.path].decode_key(options.key))}
        except ex.Error as exc:
            return {"status": 1, "error": str(exc)}
        except Exception as exc:
            return {"status": 1, "error": str(exc), "traceback": traceback.format_exc()}

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        name, namespace, kind = split_path(options.path)
        if kind == "cfg":
            role = "guest"
        else:
            # sec, usr
            role = "admin"
        thr.rbac_requires(roles=[role], namespaces=[namespace], **kwargs)

  0707010001f1f4000081a40000000000000000000000016a100daf00000551000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/key/post.py    import traceback

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Add or update the value of a usr, cfg or sec object key.
    """
    routes = (
        ("POST", "key"),
        (None, "set_key"),
    )
    access = {
        "roles": ["admin"],
        "namespaces": "FROM:path",
    }
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "key",
            "desc": "The name of the key to set a value for.",
            "required": True,
            "format": "string",
        },
        {
            "name": "data",
            "desc": "The key value to assign.",
            "required": True,
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        try:
            shared.SERVICES[options.path].add_key(options.key, options.data)
            return {"status": 0, "info": "key %s value set" % options.key}
        except ex.Error as exc:
            return {"status": 1, "error": str(exc)}
        except Exception as exc:
            return {"status": 1, "error": str(exc), "traceback": traceback.format_exc()}


   0707010001f1f2000081a40000000000000000000000016a100daf00000485000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/key/delete.py  import traceback

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Delete a key of a usr, cfg or sec object key.
    """
    routes = (
        ("DELETE", "key"),
    )
    access = {
        "roles": ["admin"],
        "namespaces": "FROM:path",
    }
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "key",
            "desc": "The name of the key to set a value for.",
            "required": True,
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        try:
            shared.SERVICES[options.path].remove_key(options.key)
            return {"status": 0, "info": "key %s value unset" % options.key}
        except ex.Error as exc:
            return {"status": 1, "error": str(exc)}
        except Exception as exc:
            return {"status": 1, "error": str(exc), "traceback": traceback.format_exc()}


   0707010001f266000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/handlers/wait   0707010001f267000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/wait/__init__.py   0707010001f268000081a40000000000000000000000016a100daf000016a2000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/wait/get.py    import json
import re
import time

from foreign.six.moves import queue
import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.naming import normalize_jsonpath
from foreign.jsonpath_ng.ext import parse
from utilities.converters import convert_boolean
from utilities.string import is_string


OPERATORS = (">=", "<=", "=", ">", "<", "~", " in ")
MAX_DURATION = 30

class Handler(daemon.handler.BaseHandler):
    """
    Wait <duration> for <condition> to become true.
    The <duration> is capped to 30 seconds.
    Upon timeout, it is up to the caller to re-submit the request until the condition becomes true.
    """
    routes = (
        ("GET", "wait"),
    )
    prototype = [
        {
            "name": "condition",
            "required": True,
            "format": "string",
            "desc": "An condition expressed as <jsonpath><operator><value>. The jsonpath is looked up in the daemon status dataset. Supported operators are %s." % " ".join(OPERATORS),
        },
        {
            "name": "duration",
            "required": False,
            "format": "duration",
            "desc": "How long to wait for the condition to become true. This duration is capped to %d seconds." % MAX_DURATION,
            "default": MAX_DURATION,
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        thr.selector = "**"
        options = self.parse_options(kwargs)
        duration = options.duration if (options.duration is not None and options.duration < MAX_DURATION) else MAX_DURATION
        timeout = time.time() + duration
        if not options.condition:
            return {"status": 0, "data": {"satisfied": True, "duration": duration, "elapsed": 0}}
        if not thr.event_queue:
            thr.event_queue = queue.Queue()
        if not thr in thr.parent.events_clients:
            thr.parent.events_clients.append(thr)
        neg, jsonpath_expr, oper, val = self.parse_condition(options.condition)
        if neg ^ self.match(jsonpath_expr, oper, val, {"kind": "patch"}, thr=thr):
            return {"status": 0, "data": {"satisfied": True, "duration": duration, "elapsed": 0}}
        end = False
        while True:
            left = timeout - time.time()
            if left < 0:
                left = 0
            try:
                msg = thr.event_queue.get(True, left if left < 3 else 3)
            except queue.Empty:
                msg = {"kind": "patch"}
                if left < 3:
                    end = True
            if neg ^ self.match(jsonpath_expr, oper, val, msg, thr=thr):
                return {"status": 0, "data": {"satisfied": True, "duration": duration, "elapsed": duration-left}}
            if end:
                return {"status": 1, "data": {"satisfied": False, "duration": duration, "elapsed": duration-left}}

    def parse_condition(self, condition):
        oper = None
        val = None

        if condition[0] == "!":
            path = condition[1:]
            neg = True
        else:
            path = condition
            neg = False

        for op in OPERATORS:
            idx = path.rfind(op)
            if idx < 0:
                continue
            val = path[idx+len(op):].strip()
            path = path[:idx].strip()
            oper = op
            if op == "~":
                if not val.startswith(".*") and not val.startswith("^"):
                    val = ".*" + val
                if not val.endswith(".*") and not val.endswith("$"):
                    val = val + ".*"
            break

        path = normalize_jsonpath(path)
        try:
            jsonpath_expr = parse(path)
        except Exception as exc:
            raise ex.Error(exc)

        return neg, jsonpath_expr, oper, val

    def eval_condition(self, jsonpath_expr, oper, val, data):
        for match in jsonpath_expr.find(data):
            if oper is None:
                if match.value:
                    return True
                else:
                    continue
            obj_class = type(match.value)
            try:
                if obj_class == bool:
                    val = convert_boolean(val)
                else:
                    val = obj_class(val)
            except Exception as exc:
                raise ex.Error("can not convert to a common type")
            if oper is None:
                if match.value:
                    return True
            if oper == "=":
                if match.value == val:
                    return True
            elif oper == ">":
                if match.value > val:
                    return True
            elif oper == "<":
                if match.value < val:
                    return True
            elif oper == ">=":
                if match.value >= val:
                    return True
            elif oper == "<=":
                if match.value <= val:
                    return True
            elif is_string(match.value) and oper == "~":
                if re.match(val, match.value):
                    return True
            elif oper == " in ":
                try:
                    l = json.loads(val)
                except:
                    l = val.split(",")
                if match.value in l:
                    return True
        return False

    def match(self, jsonpath_expr, oper, val, msg, thr=None):
        kind = msg.get("kind")
        if kind == "patch":
            if self.eval_condition(jsonpath_expr, oper, val, thr.daemon_status_data.get()):
                return True
        elif kind == "event":
            if self.eval_condition(jsonpath_expr, oper, val, msg):
                return True
        return False

  0707010001f25d000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/template   0707010001f25f000081a40000000000000000000000016a100daf00000597000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/template/get.py    import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Return the <template> data from the trusted <catalog>.
    """
    routes = (
        ("GET", "template"),
        (None, "get_template"),
    )
    prototype = [
        {
            "name": "catalog",
            "required": True,
            "format": "string",
            "desc": "The name of the catalog hosting the template.",
        },
        {
            "name": "template",
            "required": True,
            "format": "string",
            "desc": "The name or id of the template in the catalog.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.catalog == "collector":
            if options.template is None:
                raise ex.HTTP(400, "template is not set")
            request_options = {
                "props": "tpl_definition"
            }
            try:
                data = shared.NODE.collector_rest_get("/provisioning_templates/%s" % options.template, request_options)
                return data["data"][0]["tpl_definition"]
            except IndexError:
                raise ex.HTTP(404, "template not found")
        raise ex.HTTP(400, "unknown catalog %s" % options.catalog)

 0707010001f25e000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/template/__init__.py   0707010001f201000041ed0000000000000000000000096a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/handlers/node   0707010001f20f000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/drain 0707010001f210000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/daemon/handlers/node/drain/__init__.py 0707010001f211000081a40000000000000000000000016a100daf00000b87000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/node/drain/post.py import time

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.naming import split_path

class Handler(daemon.handler.BaseHandler):
    """
    Freeze the node and shutdown all running object instances.
    Return only when done.
    """
    routes = (
        ("POST", "node_drain"),
        (None, "node_drain"),
    )
    prototype = [
        {
            "name": "wait",
            "desc": "Don't return until the node is drained.",
            "default": False,
            "required": False,
            "format": "boolean",
        },
        {
            "name": "time",
            "desc": "The maximum wait time. If not specified, no timeout is set.",
            "required": False,
            "format": "duration",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        thr.log_request("drain node", nodename, **kwargs)
        thr.event("node_freeze", data={"reason": "drain"})
        thr.freezer.node_freeze()
        nmon = thr.get_node_monitor()

        if thr.stopped() or nmon.status in ("draining", "shutting"):
            thr.log.info("already %s", nmon.status)
            # wait for service shutdown to finish before releasing the dup client
            if options.wait:
                elapse = 0.0
                while True:
                    nmon = thr.get_node_monitor()
                    if shared.THREADS["monitor"]._shutdown or nmon.status not in ("draining", "shutting"):
                        break
                    if options.time and elapse > options.time:
                        return {"status": 1, "error": "timeout"}
                    time.sleep(0.3)
                    elapse += 0.3
            return {"status": 0}
        try:
            thr.set_nmon("draining")
            for path, smon in thr.iter_local_services_monitors():
                _, _, kind = split_path(path)
                if kind not in ("svc", "vol"):
                    continue
                thr.defer_set_smon(path, local_expect="shutdown", origin=self.get_origin(thr.get_user_info))
            if options.wait:
                try:
                    self.wait_shutdown(timeout=options.time, thr=thr)
                except ex.TimeOut:
                    return {"status": 1, "error": "timeout"}
        except Exception as exc:
            thr.log.exception(exc)

        return {"status": 0}

    def wait_shutdown(self, timeout=None, thr=None):
        def still_shutting():
            if thr.has_deferred_set_smon():
                return True
            for path, smon in thr.iter_local_services_monitors():
                if smon.local_expect == "shutdown":
                    return True
            return False
        while still_shutting():
            if timeout is not None and timeout <= 0:
                raise ex.TimeOut
            timeout -= 1
            time.sleep(1)
 0707010001f202000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/node/__init__.py   0707010001f209000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/checks    0707010001f20a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/node/checks/__init__.py    0707010001f20b000081a40000000000000000000000016a100daf0000039b000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/node/checks/get.py import traceback

import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the node checkers data (filesystem usage, ...).
    """
    routes = (
        ("GET", "node_checks"),
    )
    prototype = [
        {
            "name": "checkers",
            "desc": "The list of checkers to provide data from: btrfs, eth, "
                    "fm, fs_i, fs_u, jstat, lag, mcelog, mpath, numa, raid, "
                    "sync, vg_u, zpool",
            "required": False,
            "format": "list",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        try:
            drvs = shared.NODE.checks_drivers(checkers=options.checkers)
            return drvs.do_checks()
        except Exception as exc:
            return {"status": "1", "error": str(exc), "traceback": traceback.format_exc()}

 0707010001f213000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/daemon/handlers/node/logs  0707010001f215000081a40000000000000000000000016a100daf00000407000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/node/logs/get.py   import os

import daemon.handler
from env import Env
from utilities.string import bdecode

try:
    from foreign.hyper.common.headers import HTTPHeaderMap
except Exception:
    HTTPHeaderMap = dict

class Handler(daemon.handler.BaseHandler):
    """
    Feed node logs.
    """
    routes = (
        ("GET", "node_logs"),
        (None, "node_logs"),
    )
    prototype = []
    stream = True

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        logfile = os.path.join(Env.paths.pathlog, "node.log")
        ofile = thr._action_logs_open(logfile, 0, "node")
        request_headers = HTTPHeaderMap(thr.streams[stream_id]["request"].headers)
        try:
            content_type = bdecode(request_headers.get("accept").pop())
        except:
            content_type = "application/json"
        thr.streams[stream_id]["content_type"] = content_type
        thr.streams[stream_id]["pushers"].append({
            "o": self,
            "fn": "h2_push_logs",
            "args": [ofile, True],
        })

 0707010001f214000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/node/logs/__init__.py  0707010001f206000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/backlogs  0707010001f207000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/__init__.py  0707010001f208000081a40000000000000000000000016a100daf000004e9000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/node/backlogs/get.py   import os

import daemon.handler
from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Return the object logs back to <backlog> bytes.
    """
    routes = (
        ("GET", "node_backlogs"),
        (None, "node_backlogs"),
    )
    prototype = [
        {
            "name": "backlog",
            "required": False,
            "format": "size",
            "default": "10k",
            "desc": "The per-instance backlog size.",
        },
        {
            "name": "since",
            "required": False,
            "format": "string",
            "default": None,
            "desc": "The timestamp to limit result since (timestamp or datetime)",
        },
        {
            "name": "until",
            "required": False,
            "format": "string",
            "default": None,
            "desc": "The timestamp to limit result until (timestamp or datetime)",
        },
    ]

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        options = self.parse_options(kwargs)
        logfile = os.path.join(Env.paths.pathlog, "node.log")
        ofile = thr._action_logs_open(logfile, options.backlog, "node")
        return thr.read_file_lines(ofile, since=options.since, until=options.until)

   0707010001f203000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/action    0707010001f204000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/node/action/__init__.py    0707010001f205000081a40000000000000000000000016a100daf00000fff000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/node/action/post.py    import os
from copy import deepcopy
from subprocess import Popen, PIPE

import daemon.handler
from env import Env
from utilities.render.command import format_command
from utilities.string import bdecode


class Handler(daemon.handler.BaseHandler):
    """
    Execute a node action.
    """
    routes = (
        ("POST", "node_action"),
        (None, "node_action"),
    )
    prototype = [
        {
            "name": "sync",
            "desc": "Execute synchronously and return the outputs.",
            "required": False,
            "default": True,
            "format": "boolean",
        },
        {
            "name": "action_mode",
            "desc": "If true, adds --local if not already present in <options>.",
            "required": False,
            "default": True,
            "format": "boolean",
        },
        {
            "name": "action",
            "desc": "The action to execute.",
            "required": False,
            "format": "string",
        },
        {
            "name": "options",
            "desc": "The action options.",
            "required": False,
            "format": "dict",
            "default": {},
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)

        if not options.cmd and not options.action:
            thr.log_request("node action ('action' not set)", nodename, lvl="error", **kwargs)
            return {
                "status": 1,
            }

        for opt in ("node", "server", "daemon"):
            if opt in options.options and options.action not in ("daemon_join", "daemon_rejoin"):
                del options.options[opt]
        if options.action_mode and options.options.get("local"):
            if "local" in options.options:
                del options.options["local"]
        for opt, ropt in (("jsonpath_filter", "filter"),):
            if opt in options.options:
                options.options[ropt] = options.options[opt]
                del options.options[opt]
        options.options["local"] = True

        if options.action.startswith("daemon_"):
            subsystem = "daemon"
            parser = "daemon"
            action = options.action[7:]
        elif options.action.startswith("net_"):
            subsystem = "net"
            parser = "network"
            action = options.action[4:]
        elif options.action.startswith("network_"):
            subsystem = "net"
            parser = "network"
            action = options.action[8:]
        elif options.action.startswith("pool_"):
            subsystem = "pool"
            parser = "pool"
            action = options.action[5:]
        else:
            subsystem = "node"
            parser = "node"
            action = options.action

        cmd = format_command(parser, action, options.options or {})
        fullcmd = Env.om + [subsystem] + cmd

        thr.log_request("run 'om %s %s'" % (subsystem, " ".join(cmd)), nodename, **kwargs)
        new_env = deepcopy(os.environ)
        if new_env.get('LOGNAME') is None:
            new_env['LOGNAME'] = "root"
        if options.sync:
            proc = Popen(fullcmd, stdout=PIPE, stderr=PIPE, stdin=None, close_fds=True, env=new_env)
            out, err = proc.communicate()
            result = {
                "status": 0,
                "data": {
                    "out": bdecode(out),
                    "err": bdecode(err),
                    "ret": proc.returncode,
                },
            }
        else:
            import uuid
            session_id = str(uuid.uuid4())
            new_env["OSVC_PARENT_SESSION_UUID"] = session_id
            proc = Popen(fullcmd, stdin=None, close_fds=True, env=new_env)
            thr.parent.push_proc(proc, cmd=fullcmd, session_id=session_id)
            result = {
                "status": 0,
                "data": {
                    "pid": proc.pid,
                    "session_id": session_id,
                },
                "info": "started node action %s" % " ".join(cmd),
            }
        return result
 0707010001f20c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/config    0707010001f20d000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/node/config/__init__.py    0707010001f20e000081a40000000000000000000000016a100daf00000636000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/node/config/get.py import codecs
import os
import traceback

import daemon.handler
import daemon.shared as shared
from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Return the node private configuration.
    """
    routes = (
        ("GET", "node_config"),
        (None, "get_node_config"),
    )
    prototype = [
        {
            "name": "format",
            "desc": "The data format to provide.",
            "candidates": ["json", "ini"],
            "default": "ini",
            "required": False,
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.format == "json":
            return self._node_config_json(nodename, thr=thr, **kwargs)
        else:
            return self._node_config_file(nodename, thr=thr, **kwargs)

    def _node_config_json(self, nodename, thr=None, **kwargs):
        try:
            return shared.NODE.print_config_data()
        except Exception as exc:
            return {"status": "1", "error": str(exc), "traceback": traceback.format_exc()}

    def _node_config_file(self, nodename, thr=None, **kwargs):
        fpath = os.path.join(Env.paths.pathetc, "node.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()
        thr.log.info("serve node config to %s", nodename)
        return {"status": 0, "data": buff, "mtime": mtime}

  0707010001f216000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/monitor   0707010001f217000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/node/monitor/__init__.py   0707010001f218000081a40000000000000000000000016a100daf000009c3000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/node/monitor/post.py   import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Set or unset properties of a node monitor.
    """
    routes = (
        ("POST", "node_monitor"),
        (None, "set_node_monitor"),
    )
    prototype = [
        {
            "name": "local_expect",
            "desc": "The expected state on node.",
            "required": False,
            "format": "string",
        },
        {
            "name": "global_expect",
            "desc": "The expected state clusterwide.",
            "required": False,
            "format": "string",
            "candidates": [
                "thawed",
                "frozen",
                "unset",
            ],
        },
        {
            "name": "status",
            "desc": "The state on node.",
            "required": False,
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        info = []
        error = []
        data = {"data": {}}
        try:
            self.validate_cluster_global_expect(options.global_expect, thr=thr)
        except ex.AbortAction as exc:
            info.append(str(exc))
        except ex.Error as exc:
            error.append(str(exc))
        else:
            thr.set_nmon(
                status=options.status,
                local_expect=options.local_expect,
                global_expect=options.global_expect,
            )
            if options.global_expect:
                data["data"]["global_expect"] = options.global_expect
            info.append("cluster target state set to %s" % options.global_expect)
        data["status"] = len(error)
        if info:
            data["info"] = info
        if error:
            data["error"] = error
        return data

    def validate_cluster_global_expect(self, global_expect, thr=None):
        if global_expect is None:
            return
        try:
            frozen = thr.daemon_status_data.get(["monitor", "frozen"])
        except KeyError:
            frozen = None
        if global_expect == "thawed":
            if frozen == "thawed":
                raise ex.AbortAction("cluster is already thawed")
        elif global_expect == "frozen":
            if frozen == "frozen":
                raise ex.AbortAction("cluster is already frozen")
        else:
            raise ex.Error("invalid global_expect value: %s" % global_expect)

 0707010001f212000081a40000000000000000000000016a100daf000001d6000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/node/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the node information.
    """
    routes = (
        ("GET", "node"),
        ("GET", "get_node"),
        (None, "get_node"),
    )
    prototype = []
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        data = shared.NODE.asset.get_asset_dict()
        return data

  0707010001f1c3000041ed0000000000000000000000046a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist  0707010001f1c8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/status   0707010001f1ca000081a40000000000000000000000016a100daf000001d6000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/get.py    import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Return the senders blacklist.
    """
    routes = (
        ("GET", "blacklist_status"),
        ("GET", "daemon_blacklist_status"),
        (None, "daemon_blacklist_status"),
    )
    prototype = []
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        return {"status": 0, "data": thr.get_blacklist()}

  0707010001f1c9000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/status/__init__.py   0707010001f1c4000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/__init__.py  0707010001f1c5000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear    0707010001f1c6000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/__init__.py    0707010001f1c7000081a40000000000000000000000016a100daf0000016b000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/blacklist/clear/post.py    import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Clear the senders blacklist.
    """
    routes = (
        ("POST", "blacklist_clear"),
    )
    prototype = []
    access = {
        "roles": ["blacklistadmin"],
    }

    def action(self, nodename, thr=None, **kwargs):
        thr.blacklist_clear()
        return {"status": 0}

 0707010001f251000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/daemon/handlers/rundone    0707010001f252000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/rundone/__init__.py    0707010001f253000081a40000000000000000000000016a100daf000004c3000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/rundone/post.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Signal the daemon a scheduler task is done.
    """
    routes = (
        ("POST", "run_done"),
        (None, "run_done"),
    )
    prototype = [
        {
            "name": "action",
            "desc": "The action executed by the scheduler task",
            "required": True,
            "format": "string",
        },
        {
            "name": "path",
            "desc": "An object selector expression.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "rids",
            "desc": "An object selector expression.",
            "required": False,
            "format": "list",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.rids is None:
            rids = options.rid
        else:
            rids = ",".join(sorted(options.rids))
        if not options.action:
            return {"status": 0}
        sig = (options.action, options.path, rids)
        with shared.RUN_DONE_LOCK:
            shared.RUN_DONE.add(sig)
        return {"status": 0}

 0707010001f1d3000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/collectorrpc   0707010001f1d4000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/__init__.py   0707010001f1d5000081a40000000000000000000000016a100daf000003eb000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/collectorrpc/post.py   import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Enqueue a remote procedure call to the collector, usually a information push.
    """
    routes = (
        ("POST", "collector_xmlrpc"),
        (None, "collector_xmlrpc"),
    )
    prototype = [
        {
            "name": "args",
            "default": [],
            "required": False,
            "desc": "The arguments list passed to the collector xmlrpc wrapper.",
        },
        {
            "name": "kwargs",
            "default": {},
            "required": False,
            "desc": "The keyword arguments dict passed to the collector xmlrpc wrapper.",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        shared.COLLECTOR_XMLRPC_QUEUE.insert(0, (options.args, options.kwargs))
        result = {
            "status": 0,
            "info": ["collector rpc queued"],
        }
        return result

 0707010001f1fb000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/handlers/lock   0707010001f1fd000081a40000000000000000000000016a100daf00000478000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/lock/post.py   import daemon.clusterlock
import daemon.handler

class Handler(daemon.handler.BaseHandler, daemon.clusterlock.LockMixin):
    """
    Acquire a clusterwide lock identified by <name>.
    Other lockers for the same <name> will wait for <timeout> until release.
    """
    routes = (
        ("POST", "lock"),
        (None, "lock"),
    )
    prototype = [
        {
            "name": "name",
            "desc": "The lock name.",
            "required": True,
            "format": "string",
        },
        {
            "name": "timeout",
            "desc": "The maximum time to wait for lock release before returning an error.",
            "required": False,
            "format": "duration",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        lock_id = self.lock_acquire(nodename, options.name, options.timeout, thr=thr)
        if lock_id:
            result = {
                "data": {
                    "id": lock_id,
                },
                "status": 0,
            }
        else:
            result = {"status": 1}
        return result


0707010001f1fc000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/lock/__init__.py   0707010001f1c0000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/authinfo   0707010001f1c1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/authinfo/__init__.py   0707010001f1c2000081a40000000000000000000000016a100daf000002d3000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/authinfo/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the supported authentication methods.
    """
    routes = (
        ("GET", "authinfo"),
        (None, "authinfo"),
    )
    prototype = []
    access = {}
    multiplex = "never"

    def action(self, nodename, thr=None, **kwargs):
        data = {
            "methods": ["basic"],
        }
        well_known_uri = shared.NODE.oget("listener", "openid_well_known")
        if well_known_uri:
            data["methods"].append("openid")
            data["openid"] = {
                "well_known_uri": well_known_uri,
                "client_id": thr.cluster_name,
            }
        return data

 0707010001f254000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/daemon/handlers/schedules  0707010001f256000081a40000000000000000000000016a100daf00000bec000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/schedules/get.py   import datetime

import daemon.handler
import daemon.shared as shared

from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Return a system's resources usage for the daemon threads and for each service.
    """
    routes = (
        ("GET", "schedules"),
    )
    prototype = [
        {
            "name": "selector",
            "desc": "An object selector expression to filter the dataset with.",
            "required": False,
            "format": "string",
        },
        {
            "name": "namespace",
            "desc": "A namespace name to filter the dataset with.",
            "required": False,
            "format": "string",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        data = []
        options = self.parse_options(kwargs)
        namespaces = thr.get_namespaces()
        with shared.THREADS_LOCK:
            sched_status = shared.THREADS["scheduler"].status()

        # node
        if not options.selector:
            for d in shared.NODE.sched.print_schedule_data(with_next=False):
                _d = {
                    "node": Env.nodename,
                    "path": "",
                    "action": d["action"],
                    "config_parameter": d["config_parameter"],
                    "last_run": d["last_run"],
                    "schedule_definition": d["schedule_definition"],
                }
                task = self.find_delayed("", d["action"], d["config_parameter"], sched_status["delayed"])
                if task:
                    _d["next"] = task["expire"]
                data.append(_d)

        # objects
        paths = thr.object_selector(selector=options.selector or '**', namespace=options.namespace, namespaces=namespaces)
        for path in paths:
            try:
                svc = shared.SERVICES[path]
            except KeyError:
                continue
            for d in svc.sched.print_schedule_data(with_next=False):
                _d = {
                    "node": Env.nodename,
                    "path": path,
                    "action": d["action"],
                    "config_parameter": d["config_parameter"],
                    "last_run": d["last_run"],
                    "schedule_definition": d["schedule_definition"],
                }
                task = self.find_delayed(path, d["action"], d["config_parameter"], sched_status["delayed"])
                if task:
                    _d["next_run"] = task["expire"]
                data.append(_d)

        return {"status": 0, "data": data}

    @staticmethod
    def find_delayed(path, action, param, delayed):
        rid = param.split(".", 1)[0]
        if not "#" in rid:
            rid = ""
        for d in delayed:
            if d["action"] != action:
                continue
            if rid and d["rid"] != rid:
                continue
            if path and d["path"] != path:
                continue
            return d

0707010001f255000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/schedules/__init__.py  0707010001f1fe000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/networks   0707010001f200000081a40000000000000000000000016a100daf000001d6000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/networks/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the cluster's network backends information.
    """
    routes = (
        ("GET", "networks"),
        (None, "get_networks"),
    )
    prototype = []
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        data = shared.NODE.network_status_data()
        return data

  0707010001f1ff000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/networks/__init__.py   0707010001f263000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/unlock 0707010001f264000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/unlock/__init__.py 0707010001f265000081a40000000000000000000000016a100daf0000046b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/daemon/handlers/unlock/post.py import daemon.clusterlock
import daemon.handler

class Handler(daemon.handler.BaseHandler, daemon.clusterlock.LockMixin):
    """
    Release a clusterwide lock identified by <name> and the <lock_id>
    returned by the lock handler call.
    """
    routes = (
        ("POST", "unlock"),
        (None, "unlock"),
    )
    prototype = [
        {
            "name": "name",
            "desc": "The lock name.",
            "required": True,
            "format": "string",
        },
        {
            "name": "lock_id",
            "desc": "The lock id returned by the lock handler call.",
            "required": True,
            "format": "string",
        },
        {
            "name": "timeout",
            "desc": "The maximum time to wait for lock release before returning an error.",
            "required": False,
            "format": "duration",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        self.lock_release(options.name, options.lock_id, timeout=options.timeout, thr=thr)
        result = {"status": 0}
        return result

 0707010001f1ed000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/handlers/join   0707010001f1ee000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/join/__init__.py   0707010001f1ef000081a40000000000000000000000016a100daf00000d7b000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/join/post.py   import os

import daemon.clusterlock
import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from env import Env

LOCK_TIMEOUT = 120


class Handler(daemon.handler.BaseHandler, daemon.clusterlock.LockMixin):
    """
    Join the cluster.
    """
    routes = (
        ("POST", "join"),
        (None, "join"),
    )
    prototype = []

    def action(self, nodename, thr=None, **kwargs):
        thr.log.info('-> join request from %s', nodename)
        lock_id = self.lock_acquire(Env.nodename, "join", timeout=LOCK_TIMEOUT, thr=thr)
        if not lock_id:
            thr.log.warning('<- join request from %s refused (Lock not acquired)', nodename)
            raise ex.HTTP(503, "Lock not acquired")
        try:
            with shared.JOIN_LOCK:
                thr.log.info('-> join request from %s with lock %s', nodename, lock_id)
                data = self.join(nodename, thr=thr, **kwargs)
                thr.log.info('<- join request from %s with lock %s', nodename, lock_id)
        finally:
            self.lock_release("join", lock_id, timeout=LOCK_TIMEOUT, thr=thr)
        thr.log.info('<- join request from %s', nodename)
        return data

    def join(self, nodename, thr=None, **kwargs):
        if nodename in thr.cluster_nodes:
            new_nodes = thr.cluster_nodes
            thr.log.info("node %s rejoins", nodename)
        else:
            new_nodes = thr.cluster_nodes + [nodename]
            thr.log.info("node %s joins", nodename)
            thr.add_cluster_node(nodename)
        result = {
            "status": 0,
            "data": {
                "node": {
                    "data": {
                        "node": {},
                        "cluster": {},
                    },
                },
            },
        }
        config = shared.NODE.private_cd
        node_section = config.get("node", {})
        cluster_section = config.get("cluster", {})
        if "env" in node_section:
            result["data"]["node"]["data"]["node"]["env"] = shared.NODE.env
        if "nodes" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["nodes"] = " ".join(new_nodes)
        if "name" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["name"] = thr.cluster_name
        if "drpnodes" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["drpnodes"] = " ".join(thr.cluster_drpnodes)
        if "id" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["id"] = thr.cluster_id
        if "quorum" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["quorum"] = thr.quorum
        if "dns" in cluster_section:
            result["data"]["node"]["data"]["cluster"]["dns"] = " ".join(shared.NODE.dns)
        for section in config:
            if section.startswith("hb#") or \
               section.startswith("stonith#") or \
               section.startswith("pool#") or \
               section.startswith("network#") or \
               section.startswith("arbitrator#"):
                result["data"]["node"]["data"][section] = config[section]
        from core.objects.ccfg import Ccfg
        svc = Ccfg(volatile=True, node=shared.NODE)
        if svc.exists():
            result["data"]["cluster"] = {
                "data": svc.print_config_data(),
                "mtime": os.stat(svc.paths.cf).st_mtime,
            }
        return result

 0707010001f1f5000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/keywords   0707010001f1f6000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/keywords/__init__.py   0707010001f1f7000081a40000000000000000000000016a100daf0000040f000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/keywords/get.py    import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.naming import factory

class Handler(daemon.handler.BaseHandler):
    """
    Return the supported <kind> object keywords.
    """
    routes = (
        ("GET", "keywords"),
        (None, "get_keywords"),
    )
    prototype = [
        {
            "name": "kind",
            "format": "string",
            "required": True,
            "candidates": ["node", "svc", "vol", "usr", "sec", "cfg", "ccfg"],
            "desc": "The object kind or 'node'.",
        },
    ]
    access = {
      "roles": ["guest"],
      "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.kind == "node":
            obj = shared.NODE
        elif options.kind:
            obj = factory(options.kind)(name="dummy", node=shared.NODE, volatile=True)
        else:
            raise ex.HTTP(400, "A kind must be specified.")
        return obj.full_kwstore.dump()

 0707010001f21c000041ed00000000000000000000000e6a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/object 0707010001f233000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/keys    0707010001f234000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/object/keys/__init__.py    0707010001f235000081a40000000000000000000000016a100daf000003ac000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/object/keys/get.py import traceback

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Return the list of keys of a usr, cfg or sec object key.
    """
    routes = (
        ("GET", "object_keys"),
    )
    access = {
        "roles": ["guest"],
        "namespaces": "FROM:path",
    }
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        try:
            return {"status": 0, "data": shared.SERVICES[options.path].data_keys()}
        except ex.Error as exc:
            return {"status": 1, "error": str(exc)}
        except Exception as exc:
            return {"status": 1, "error": str(exc), "traceback": traceback.format_exc()}

0707010001f21e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/action  0707010001f220000081a40000000000000000000000016a100daf00001970000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/object/action/post.py  import importlib
import json
import os
from subprocess import Popen, PIPE

import daemon.handler
import daemon.rbac
import daemon.shared as shared
import core.exceptions as ex
from env import Env
from utilities.naming import split_path
from utilities.render.command import format_command
from utilities.string import bdecode

GUEST_ACTIONS = (
    "eval",
    "get",
    "keys",
    "print_config_mtime",
)

OPERATOR_ACTIONS = (
    "clear",
    "disable",
    "enable",
    "freeze",
    "push_status",
    "push_resinfo",
    "push_config",
    "push_encap_config",
    "presync",
    "prstatus",
    "resource_monitor",
    "restart",
    "resync",
    "run",
    "scale",
    "snooze",
    "start",
    "startstandby",
    "status",
    "stop",
    "stopstandby",
    "thaw",
    "unsnooze",
)

ADMIN_ACTIONS = (
    "add",
    "boot",
    "decode",
    "delete",
    "gen_cert",
    "install",
    "pg_kill",
    "pg_freeze",
    "pg_thaw",
    "provision",
    "run",
    "set_provisioned",
    "set_unprovisioned",
    "shutdown",
    "unprovision",
    "unset",
)


class Handler(daemon.handler.BaseHandler, daemon.rbac.ObjectCreateMixin):
    """
    Execute an object instance action.
    """
    routes = (
        ("POST", "object_action"),
        ("POST", "service_action"),
        (None, "service_action"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The path of the object to execute the action on.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "sync",
            "desc": "Execute synchronously and return the outputs.",
            "required": False,
            "default": True,
            "format": "boolean",
        },
        {
            "name": "cmd",
            "desc": "The command vector.",
            "required": False,
            "format": "list",
            "deprecated": True,
        },
        {
            "name": "action",
            "desc": "The action to execute.",
            "required": False,
            "format": "string",
        },
        {
            "name": "options",
            "desc": "The action options.",
            "required": False,
            "format": "dict",
            "default": {},
        },
    ]
    access = "custom"

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        name, namespace, kind = split_path(options.path)

        if options.action in GUEST_ACTIONS:
            role = "guest"
        elif options.action in OPERATOR_ACTIONS:
            role = "operator"
        elif options.action in ADMIN_ACTIONS:
            role = "admin"
        else:
            role = "root"

        if options.action == "set":
            # load current config
            try:
                cf = shared.SERVICES[options.path].print_config_data()
            except Exception:
                cf = {}

            # purge unwanted sections
            try:
                del cf["metadata"]
            except Exception:
                pass

            # merge changes in current config
            for buff in options.options.get("kw", []):
                k, v = buff.split("=", 1)
                if k[-1] in ("+", "-"):
                    k = k[:-1]
                k = k.strip()
                try:
                    s, k = k.split(".", 1)
                except Exception:
                    s = "DEFAULT"
                if s not in cf:
                    cf[s] = {}
                cf[s][k] = v

            # apply object create rbac to the amended config
            payload = {options.path: cf}
            errors = self.rbac_create_data(payload=payload, thr=thr, **kwargs)
            if errors:
                raise ex.HTTP(403, errors)
        else:
            thr.rbac_requires(roles=[role], namespaces=[namespace], **kwargs)

        if options.cmd:
            # compat, requires root
            kwargs["roles"] = ["root"]
            thr.rbac_requires(**kwargs)

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        name, namespace, kind = split_path(options.path)

        if thr.get_service(options.path) is None and options.action not in ("create", "deploy"):
            thr.log_request("service action (%s not installed)" % options.path, nodename, lvl="warning", **kwargs)
            raise ex.HTTP(404, "%s not found" % options.path)
        if not options.action and not options.cmd:
            thr.log_request("service action (no action set)", nodename, lvl="error", **kwargs)
            raise ex.HTTP(400, "action not set")

        for opt in ("node", "daemon", "svcs", "service", "s", "parm_svcs", "local", "id"):
            if opt in options.options:
                del options.options[opt]
        for opt, ropt in (("jsonpath_filter", "filter"),):
            if opt in options.options:
                options.options[ropt] = options.options[opt]
                del options.options[opt]
        options.options["local"] = True

        if options.cmd:
            cmd = [options.cmd]
        else:
            cmd = format_command(kind, options.action, options.options or {})

        fullcmd = Env.om + ["svc", "-s", options.path] + cmd
        thr.log_request("run 'om %s %s'" % (options.path, " ".join(cmd)), nodename, **kwargs)
        if options.sync:
            proc = Popen(fullcmd, stdout=PIPE, stderr=PIPE, stdin=None, close_fds=True)
            out, err = proc.communicate()
            try:
                result = json.loads(out)
            except Exception:
                result = {
                    "status": 0,
                    "data": {
                        "out": bdecode(out),
                        "err": bdecode(err),
                        "ret": proc.returncode,
                    },
                }
        else:
            import uuid
            session_id = str(uuid.uuid4())
            env = {}
            env.update(os.environ)
            env["OSVC_PARENT_SESSION_UUID"] = session_id
            proc = Popen(fullcmd, stdin=None, close_fds=True, env=env)
            thr.parent.push_proc(proc, cmd=fullcmd, session_id=session_id)
            result = {
                "status": 0,
                "data": {
                    "pid": proc.pid,
                    "session_id": session_id,
                },
                "info": "started %s action %s" % (options.path, " ".join(cmd)),
            }
        return result
0707010001f21f000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/object/action/__init__.py  0707010001f236000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/logs    0707010001f237000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/object/logs/__init__.py    0707010001f238000081a40000000000000000000000016a100daf000005e4000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/object/logs/get.py import os

import daemon.handler
import core.exceptions as ex
from utilities.string import bdecode

try:
    from foreign.hyper.common.headers import HTTPHeaderMap
except Exception:
    HTTPHeaderMap = dict

class Handler(daemon.handler.BaseHandler):
    """
    Feed the <path> object logs.
    """
    routes = (
        ("GET", "object_logs"),
        (None, "object_logs"),
        (None, "service_logs"),
    )
    prototype = [
        {
            "name": "path",
            "required": True,
            "format": "object_path",
            "desc": "The object path.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "FROM:path",
    }
    stream = True

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        options = self.parse_options(kwargs)
        svc = thr.get_service(options.path)
        if svc is None:
            raise ex.HTTP(404, "%s not found" % options.path)
        request_headers = HTTPHeaderMap(thr.streams[stream_id]["request"].headers)
        try:
            content_type = bdecode(request_headers.get("accept").pop())
        except:
            content_type = "application/json"
        thr.streams[stream_id]["content_type"] = content_type
        logfile = os.path.join(svc.log_d, svc.name+".log")
        ofile = thr._action_logs_open(logfile, 0, svc.path)
        thr.streams[stream_id]["pushers"].append({
            "o": self,
            "fn": "h2_push_logs",
            "args": [ofile, True],
        })

0707010001f227000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/config  0707010001f228000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/object/config/__init__.py  0707010001f229000081a40000000000000000000000016a100daf00000c1c000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/object/config/get.py   import codecs
import os
import traceback

import daemon.handler
import daemon.shared as shared
from utilities.naming import svc_pathcf

class Handler(daemon.handler.BaseHandler):
    """
    Return the <path> object configuration data.
    """
    routes = (
        ("GET", "object_config"),
        (None, "get_service_config"),
    )
    access = {
        "roles": ["admin"],
        "namespaces": "FROM:path",
    }
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "format",
            "desc": "The data format to provide.",
            "candidates": ["json", "ini"],
            "default": "ini",
            "required": False,
            "format": "string",
        },
        {
            "name": "evaluate",
            "desc": "Provide evaluated configuration, ie scoped, dereferenced and computed.",
            "default": False,
            "required": False,
            "requires": [
                {
                    "option": "format",
                    "op": "equals",
                    "value": "json",
                },
            ],
            "format": "boolean",
        },
        {
            "name": "impersonate",
            "desc": "Provide impersonated configuration, ie scoped with the specified node name.",
            "required": False,
            "requires": [
                {
                    "option": "format",
                    "op": "equals",
                    "value": "json",
                },
            ],
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.format == "json":
            return self._object_config_json(nodename, options.path, evaluate=options.evaluate, impersonate=options.impersonate, thr=thr, **kwargs)
        else:
            return self._object_config_file(nodename, options.path, thr=thr, **kwargs)

    def _object_config_json(self, nodename, path, thr=None, evaluate=False, impersonate=None, **kwargs):
        try:
            return shared.SERVICES[path].print_config_data(evaluate=evaluate, impersonate=impersonate)
        except Exception as exc:
            return {"status": "1", "error": str(exc), "traceback": traceback.format_exc()}

    def _object_config_file(self, nodename, path, thr=None, **kwargs):
        smon = thr.get_service_monitor(path)
        if smon.status in ("purging", "deleting") or \
           smon.global_expect in ("purged", "deleted"):
            return {"error": "delete in progress", "status": 2}
        fpath = svc_pathcf(path)
        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()
        thr.log.info("serve service %s config to %s", path, nodename)
        return {"status": 0, "data": buff, "mtime": mtime}

0707010001f23c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/selector    0707010001f23d000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/selector/__init__.py    0707010001f23e000081a40000000000000000000000016a100daf000004ab000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/daemon/handlers/object/selector/get.py import daemon.handler

from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Return the object list expanded from the <selector> expression.
    """
    routes = (
        ("GET", "object_selector"),
        (None, "object_selector"),
    )
    prototype = [
        {
            "name": "selector",
            "required": False,
            "format": "string",
            "desc": "An object selector expression.",
        },
        {
            "name": "namespace",
            "required": False,
            "format": "string",
            "desc": "A namespace to limit the expansion to.",
        },
        {
            "name": "kind",
            "required": False,
            "format": "string",
            "candidates": Env.kinds,
            "desc": "A kind of object to limit the expansion to.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        namespaces = thr.get_namespaces()
        return thr.object_selector(options.selector, namespace=options.namespace, namespaces=namespaces, kind=options.kind)

 0707010001f22d000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/create  0707010001f22f000081a40000000000000000000000016a100daf00001136000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/object/create/post.py  import json
from subprocess import PIPE

import daemon.handler
import daemon.rbac
import core.exceptions as ex
from utilities.naming import validate_paths
from utilities.string import bdecode


class Handler(daemon.handler.BaseHandler, daemon.rbac.ObjectCreateMixin):
    """
    Create new objects.
    """
    routes = (
        ("POST", "object_create"),
        ("POST", "create"),
        (None, "create"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The path of the object to execute the action on.",
            "required": False,
            "format": "object_path",
        },
        {
            "name": "template",
            "desc": "The name of a template to get the configuration from.",
            "required": False,
            "format": "string",
        },
        {
            "name": "namespace",
            "desc": "The namespace to create the objects in, overriding the namespace part of object paths.",
            "required": False,
            "format": "string",
        },
        {
            "name": "data",
            "desc": "The dictionary of object configurations, indexed by object path."
                    " If template is set, the data option can be used to pass env section overrides."
                    " Examples: {'k': 'v'} or {'env': {'k': 'v'}} or {'ns1/svc/svc1': {'k': 'v'}}"
                    " or {'ns1/svc/svc1': {'env': {'k': 'v'}}}",
            "required": False,
            "format": "dict",
            "default": {},
        },
        {
            "name": "provision",
            "desc": "If true, the object is marked for provisioning upon create.",
            "required": False,
            "format": "boolean",
            "default": False,
        },
        {
            "name": "restore",
            "desc": "If true, the object id provided in the configuration data is preserve."
                    " The default is to generate a new object id upon create.",
            "required": False,
            "format": "boolean",
            "default": False,
        },
        {
            "name": "sync",
            "desc": "If true, the object is created synchronously.",
            "required": False,
            "format": "boolean",
            "default": True,
        },
    ]
    access = "custom"

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.template is not None:
            thr.rbac_requires(roles=["admin"], namespaces=[options.namespace], **kwargs)
            return
        if not options.data:
            return
        errors = self.rbac_create_data(payload=options.data, thr=thr, **kwargs)
        if errors:
            raise ex.HTTP(403, errors)

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if not options.data and not options.template:
            return {"status": 0, "info": "no data"}
        if options.template is not None:
            if options.path:
                paths = [options.path]
                cmd = ["create", "-s", options.path, "--template=%s" % options.template, "--env=-"]
            else:
                paths = [p for p in options.data]
                cmd = ["create", "--template=%s" % options.template, "--env=-"]
        else:
            paths = [p for p in options.data]
            cmd = ["create", "--config=-"]
        validate_paths(paths)
        if options.namespace:
            cmd.append("--namespace="+options.namespace)
        if options.restore:
            cmd.append("--restore")
        if options.provision:
            cmd.append("--provision")
        thr.log_request("create/update %s" % ",".join(paths), nodename, **kwargs)
        proc = thr.service_command(None, cmd, stdout=PIPE, stderr=PIPE, stdin=json.dumps(options.data), local=False)
        if options.sync:
            out, err = proc.communicate()
            result = {
                "status": proc.returncode,
                "data": {
                    "out": bdecode(out),
                    "err": bdecode(err),
                    "ret": proc.returncode,
                },
            }
        else:
            thr.parent.push_proc(proc)
            result = {
                "status": 0,
                "info": "started %s action %s" % (options.path, " ".join(cmd)),
            }
        return result
  0707010001f22e000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/object/create/__init__.py  0707010001f221000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/backlogs    0707010001f222000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/__init__.py    0707010001f223000081a40000000000000000000000016a100daf00000777000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/daemon/handlers/object/backlogs/get.py import os

import daemon.handler
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Return the <path> object logs back to <backlog> bytes.
    """
    routes = (
        ("GET", "object_backlogs"),
        (None, "object_backlogs"),
        (None, "service_backlogs"),
    )
    prototype = [
        {
            "name": "path",
            "required": True,
            "format": "object_path",
            "desc": "The object path.",
        },
        {
            "name": "backlog",
            "required": False,
            "format": "size",
            "default": "10k",
            "desc": "The per-instance backlog size.",
        },
        {
            "name": "sid",
            "required": False,
            "format": "string",
            "default": None,
            "desc": "The session id to filter from the log file tail.",
        },
        {
            "name": "since",
            "required": False,
            "format": "string",
            "default": None,
            "desc": "The timestamp to limit result since (timestamp or datetime)",
        },
        {
            "name": "until",
            "required": False,
            "format": "string",
            "default": None,
            "desc": "The timestamp to limit result until (timestamp or datetime)",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "FROM:path",
    }

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        options = self.parse_options(kwargs)
        svc = thr.get_service(options.path)
        if svc is None:
            raise ex.HTTP(404, "%s not found" % options.path)
        logfile = os.path.join(svc.log_d, svc.name+".log")
        ofile = thr._action_logs_open(logfile, options.backlog, svc.path)
        return thr.read_file_lines(ofile, sid=options.sid, since=options.since, until=options.until)

 0707010001f239000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/monitor 0707010001f23b000081a40000000000000000000000016a100daf00002bb5000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/daemon/handlers/object/monitor/post.py import time

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.storage import Storage
from utilities.naming import split_path, fmt_path, is_service
from core.objects.svc import ACTION_ASYNC

ALLOWED_KINDS = { str(d.get("target")).split("@", 1)[0] : d.get("kinds") for d in ACTION_ASYNC.values() }

class Handler(daemon.handler.BaseHandler):
    """
    Set or unset properties of an object instance monitor.
    These properties are used by the monitor in the orchestration policies and object management by target state.
    """
    routes = (
        ("POST", "object_monitor"),
        (None, "set_service_monitor"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "local_expect",
            "desc": "The expected object instance state on node. If 'started', the resource restart orchestration is active. A 'avail up' instance has local_expect set to 'started' automatically.",
            "required": False,
            "candidates": [
                "started",
                "unset",
            ],
            "format": "string",
            "strict_candidates": False,
        },
        {
            "name": "global_expect",
            "desc": "The expected object state clusterwide. This is the property used for object target state orchestration.",
            "required": False,
            "format": "string",
            "candidates": [
                "deleted",
                "purged",
                "provisioned",
                "unprovisioned",
                "thawed",
                "frozen",
                "started",
                "stopped",
                "aborted",
                "placed",
                "placed@.*",
                "shutdown",
                "scaled",
                "unset",
            ],
            "strict_candidates": False,
        },
        {
            "name": "status",
            "desc": "The current object instance monitor state on node. This is where the current running action, the last action failures are stored. The normal state is 'idle'.",
            "required": False,
            "format": "string",
        },
        {
            "name": "reset_retries",
            "desc": "If true, reset the resources retry counter. This rearms the resource restart orchestration.",
            "required": False,
            "format": "boolean",
            "default": False,
        },
        {
            "name": "stonith",
            "desc": "If 'unset' unarm the stonith trigger on cluster split. Any other value arms the trigger.",
            "required": False,
            "format": "string",
        },
    ]
    access = "custom"

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        name, namespace, kind = split_path(options.path)
        role = "admin"
        operator = (
            # (local_expect, global_expect, reset_retries)
            (None, None, True),
            (None, "thawed", False),
            (None, "frozen", False),
            (None, "started", False),
            (None, "stopped", False),
            (None, "aborted", False),
            (None, "placed", False),
            (None, "shutdown", False),
        )
        _global_expect = options.global_expect.split("@")[0] if options.global_expect else options.global_expect
        if (options.local_expect, _global_expect, options.reset_retries) in operator:
            role = "operator"
        thr.rbac_requires(roles=[role], namespaces=[namespace], **kwargs)

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        paths = set([options.path])
        if options.global_expect != "scaled":
            paths |= self.get_service_slaves(options.path, thr=thr)
        errors = []
        info = []
        data = {"data": {}}
        for path in paths:
            try:
                self.validate_global_expect(path, options.global_expect, thr=thr)
                new_ge = self.validate_destination_node(path, options.global_expect, thr=thr)
            except ex.AbortAction as exc:
                info.append(str(exc))
            except ex.Error as exc:
                errors.append(str(exc))
            else:
                if new_ge:
                    options.global_expect = new_ge
                if options.global_expect:
                    data["data"]["global_expect"] = options.global_expect
                info.append("%s defer target state set to %s" % (path, options.global_expect))
                thr.defer_set_smon(
                    path, status=options.status,
                    local_expect=options.local_expect,
                    global_expect=options.global_expect,
                    reset_retries=options.reset_retries,
                    stonith=options.stonith,
                    origin=self.get_origin(thr.get_user_info),
                )
        data["status"] = len(errors)
        if info:
            data["info"] = info
        if errors:
            data["error"] = errors
        return data

    def get_service_slaves(self, path, slaves=None, thr=None):
        """
        Recursive lookup of object slaves.
        """
        if slaves is None:
            slaves = set()
        _, namespace, _ = split_path(path)

        def set_ns(path, parent_ns):
            name, _namespace, kind = split_path(path)
            if _namespace:
                return path
            else:
                return fmt_path(name, parent_ns, kind)

        for nodename, data in thr.iter_service_instances(path):
            slaves.add(path)
            new_slaves = set(data.get("slaves", [])) | set(data.get("scaler_slaves", []))
            new_slaves = set([set_ns(slave, namespace) for slave in new_slaves])
            new_slaves -= slaves
            for slave in new_slaves:
                slaves |= self.get_service_slaves(slave, slaves, thr=thr)
        return slaves

    def validate_global_expect(self, path, global_expect, thr=None):
        if global_expect is None:
            return
        _, _, kind = split_path(path)
        allowed_kinds = ALLOWED_KINDS.get(global_expect)
        if allowed_kinds is not None and kind not in allowed_kinds:
            raise ex.Error("reject set global_expect=%s request on %s: not supported on %s objects" % (global_expect, path, kind))
        if global_expect in ("frozen", "aborted", "provisioned"):
            # allow provision target state on just-created service
            return

        # wait for object to appear
        for i in range(5):
            instances = thr.get_service_instances(path)
            if instances:
                break
            if not is_service(path):
                break
            time.sleep(1)
        if not instances:
            raise ex.Error("object %s does not exist" % path)

        ges = set()
        for nodename, _data in instances.items():
            smon = _data.get("monitor", {})
            ge = smon.get("global_expect")
            ges.add(ge)
            if global_expect == ge:
                continue
            status = smon.get("status", "unknown")
            if status == "tocing" and global_expect == "placed":
                # Allow the "toc" action with the "switch" monitor_action
                # to change status from "tocing" to "start failed".
                pass
            elif status != "idle" and "failed" not in status and "wait" not in status:
                raise ex.Error("%s instance on node %s in %s state"
                                  "" % (path, nodename, status))

        if ges == set([global_expect]):
            raise ex.AbortAction("%s is already targeting %s" % (path, global_expect))

        if global_expect not in ("started", "stopped"):
            return
        agg = thr.get_service_agg(path)
        if global_expect == "started" and agg.avail == "up":
            raise ex.AbortAction("%s is already started" % path)
        elif global_expect == "stopped" and agg.avail in ("down", "stdby down", "stdby up"):
            raise ex.AbortAction("%s is already stopped" % path)
        if agg.avail in ("n/a", "undef"):
            raise ex.AbortAction()

    def validate_destination_node(self, path, global_expect, thr=None):
        """
        For a placed@<dst> <global_expect> (move action) on <path>,

        Raise an Error if
        * the object <path> does not exist
        * the object <path> topology is failover and more than 1
          destination node was specified
        * the specified destination is not a object candidate node
        * no destination node specified
        * an empty destination node is specified in a list of destination
          nodes

        Raise an AbortAction if
        * the avail status of the instance on the destination node is up
        """
        if global_expect is None:
            return
        try:
            global_expect, destination_nodes = global_expect.split("@", 1)
        except ValueError:
            return
        if global_expect != "placed":
            return
        instances = thr.get_service_instances(path)
        if not instances:
            raise ex.Error("object %s does not exist" % path)
        if destination_nodes == "<peer>":
            instance = list(instances.values())[0]
            if instance.get("topology") == "flex":
                raise ex.Error("no destination node specified")
            else:
                nodes = [node for node, inst in instances.items() \
                              if inst.get("avail") not in ("up", "warn", "n/a") and \
                              inst.get("monitor", {}).get("status") != "started"]
                count = len(nodes)
                if count == 0:
                    raise ex.Error("no candidate destination node")
                svc = thr.get_service(path)
                return "placed@%s" % thr.placement_ranks(svc, nodes)[0]
        else:
            destination_nodes = destination_nodes.split(",")
            count = len(destination_nodes)
            if count == 0:
                raise ex.Error("no destination node specified")
            instance = list(instances.values())[0]
            if count > 1 and instance.get("topology") == "failover":
                raise ex.Error("only one destination node can be specified for "
                                  "a failover service")
            for destination_node in destination_nodes:
                if not destination_node:
                    raise ex.Error("empty destination node")
                if destination_node not in instances:
                    raise ex.Error("destination node %s has no %s instance" % \
                                      (destination_node, path))
                instance = instances[destination_node]
                if instance["avail"] == "up":
                    raise ex.AbortAction("instance on destination node %s is "
                                            "already up" % destination_node)

   0707010001f23a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/monitor/__init__.py 0707010001f230000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/enter   0707010001f232000081a40000000000000000000000016a100daf00000ab9000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/object/enter/get.py    import os
import subprocess
import uuid

import daemon.handler
import core.exceptions as ex
from env import Env
from utilities.proc import which
from utilities.string import try_decode

MAX_TIMEOUT = 60

class Handler(daemon.handler.BaseHandler):
    """
    Return a url a client browser can connect to for 5 seconds to enter the
    container specified by <rid> of the object specified by <path>.

    The url path is random.
    The basic auth credential is random.
    The port is random.
    """
    routes = (
        ("GET", "object_enter"),
    )
    access = {
        "roles": ["admin"],
        "namespaces": "FROM:path",
    }
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "rid",
            "desc": "The resource id of the container to enter.",
            "required": True,
            "format": "string",
            "example": "container#1",
        },
        {
            "name": "timeout",
            "desc": "The time the tty server will stay alive waiting for a client. Maximum %d seconds." % MAX_TIMEOUT,
            "required": False,
            "default": 5,
            "format": "duration",
            "example": "30s",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        timeout = options.timeout if options.timeout and options.timeout < MAX_TIMEOUT else MAX_TIMEOUT
        if not which("gotty"):
            raise ex.HTTP(500, "The gotty executable is not installed")
        creds = "user:" + str(uuid.uuid4())
        private_key = os.path.join(Env.paths.certs, "private_key")
        cert_chain = os.path.join(Env.paths.certs, "certificate_chain")
        cmd = [
            "gotty",
            "--port", "0",
            "--random-url",
            "--tls",
            "--tls-crt", cert_chain,
            "--tls-key", private_key,
            "--timeout", str(timeout),
            "--once",
            "--ws-origin", ".*",
            "--permit-write",
            "om", options.path, "enter", "--rid", options.rid,
        ]
        env = dict(os.environ)
        env.update({
            "GOTTY_CREDENTIAL": creds,
        })
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
        thr.parent.push_proc(proc)
        for line in iter(proc.stderr.readline, ""):
            line = try_decode(line)
            if "https://" not in line:
                continue
            url = line.split("https://::", 1)[-1].strip()
            url = "https://" + creds + "@" + Env.nodename + url
            return {"data": {"url": url}}
   0707010001f231000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/object/enter/__init__.py   0707010001f22a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/object/confirmations   0707010001f22c000081a40000000000000000000000016a100daf0000044e000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/get.py    import daemon.handler
import daemon.shared as shared

from utilities.naming import split_path, factory

class Handler(daemon.handler.BaseHandler):
    """
    Return the list of the object resource identifiers that require a
    run confirmation.
    Used by the webapp to ask for confirmation when the user submits a
    run action.
    """
    routes = (
        ("GET", "object_confirmations"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object to return task confirmations of.",
            "required": True,
            "format": "string",
        },
    ]
    access = {
        "roles": ["operator"],
        "namespaces": "FROM:path",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        name, namespace, kind = split_path(options.path)
        svc = factory(kind)(name=name, namespace=namespace, volatile=True, node=shared.NODE)
        data = {
            "status": 0,
            "data": [res.rid for res in svc.get_resources("task") if res.confirmation],
        }
        return data

  0707010001f22b000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000005000000000root/usr/share/opensvc/opensvc/daemon/handlers/object/confirmations/__init__.py   0707010001f23f000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/status  0707010001f240000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/object/status/__init__.py  0707010001f241000081a40000000000000000000000016a100daf00000306000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/object/status/get.py   import time

import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from utilities.storage import Storage
from utilities.naming import split_path, fmt_path, is_service

class Handler(daemon.handler.BaseHandler):
    """
    Get the selected object status.
    """
    routes = (
        ("GET", "object_status"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "FROM:path",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        data = thr.object_data(options.path)
        return data

  0707010001f242000081a40000000000000000000000016a100daf000003ff000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/object/status/post.py  import daemon.handler
import daemon.shared as shared

from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Load an instance status data.
    """
    routes = (
        ("POST", "object_status"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
        {
            "name": "data",
            "desc": "The instance status dataset.",
            "required": True,
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        with thr.daemon_status_data.lock:
            options.data["monitor"] = thr.get_service_monitor(options.path)
            thr.daemon_status_data._set_lk(["monitor", "nodes", Env.nodename, "services", "status", options.path], options.data)
        shared.wake_monitor("%s status change" % options.path, immediate=True)
        return {"status": 0, "info": "instance status updated"}
 0707010001f21d000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/object/__init__.py 0707010001f224000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/object/clear   0707010001f225000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/object/clear/__init__.py   0707010001f226000081a40000000000000000000000016a100daf000004ed000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/object/clear/post.py   import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Clear the object monitor status. For example, a "start failed".
    Transient status are not clearable (those ending with 'ing', like starting).
    """
    routes = (
        ("POST", "object_clear"),
        (None, "clear"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": True,
            "format": "object_path",
        },
    ]
    access = {
        "roles": ["operator"],
        "namespaces": "FROM:path",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        smon = thr.get_service_monitor(options.path)
        if not smon.status:
            return {"info": "skip clear on instance (no monitor data)" % smon.status, "status": 0}
        if smon.status.endswith("ing"):
            return {"info": "skip clear on %s instance" % smon.status, "status": 0}
        thr.log_request("clear %s monitor status" % options.path, nodename, **kwargs)
        thr.defer_set_smon(options.path, status="idle", reset_retries=True, origin=self.get_origin(thr.get_user_info))
        return {"status": 0, "info": "%s instance cleared" % options.path}

   0707010001f1cb000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/daemon/handlers/catalogs   0707010001f1cd000081a40000000000000000000000016a100daf00000242000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/catalogs/get.py    import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the list of catalogs trusted by the cluster.
    """
    routes = (
        ("GET", "catalogs"),
        (None, "get_catalogs"),
    )
    prototype = []
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        data = []
        if shared.NODE.collector_env.dbopensvc is not None:
            data.append({
                "name": "collector",
            })
        return data

  0707010001f1cc000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/catalogs/__init__.py   0707010001f257000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/sshkey 0707010001f258000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/sshkey/__init__.py 0707010001f259000081a40000000000000000000000016a100daf00000469000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/sshkey/get.py  import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the a public key of the specified system's user, so a
    peer node can add it to its authorizations.
    """
    routes = (
        ("GET", "ssh_key"),
        (None, "ssh_key"),
    )
    prototype = [
        {
            "name": "user",
            "desc": "The system's user to fetch the public key for.",
            "format": "string",
            "required": False,
            "default": "root",
        },
        {
            "name": "key_type",
            "desc": "The type of key to retrieve.",
            "format": "string",
            "candidates": ["rsa", "dsa", "ecdsa", "ed25519"],
            "required": False,
            "default": "rsa",
        },
    ]
    access = {
        "roles": ["root"],
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        data = {
            "user": options.user,
            "key": shared.NODE.get_ssh_pubkey(options.user, options.key_type),
        }
        return {"status": 0, "data": data}

   0707010001f1d6000041ed0000000000000000000000086a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon 0707010001f1e1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stats   0707010001f1e2000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/__init__.py   0707010001f1e3000081a40000000000000000000000016a100daf000006d3000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stats/get.py    import time

import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return a system's resources usage for the daemon threads and for each service.
    """
    routes = (
        ("GET", "daemon_stats"),
        (None, "daemon_stats"),
    )
    prototype = [
        {
            "name": "selector",
            "desc": "An object selector expression to filter the dataset with.",
            "required": False,
            "format": "string",
        },
        {
            "name": "namespace",
            "desc": "A namespace name to filter the dataset with.",
            "required": False,
            "format": "string",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        data = {
            "timestamp": time.time(),
            "daemon": shared.DAEMON.stats(),
            "node": {
                "cpu": {
                    "time": shared.NODE.cpu_time(),
                 },
            },
            "services": {},
        }
        options = self.parse_options(kwargs)
        namespaces = thr.get_namespaces()
        paths = thr.object_selector(selector=options.selector or '**', namespace=options.namespace, namespaces=namespaces)
        with shared.THREADS_LOCK:
            for dthr_id, dthr in shared.THREADS.items():
                data[dthr_id] = dthr.thread_stats()
        for path in paths:
            try:
                svc = shared.SERVICES[path]
            except KeyError:
                continue
            _data = svc.pg_stats()
            if _data:
                data["services"][path] = _data
        return {"status": 0, "data": data}

 0707010001f1d8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex   0707010001f1da000081a40000000000000000000000016a100daf000001b1000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/get.py    import daemon.handler
from daemon.shared import daemon_mutex_status


class Handler(daemon.handler.BaseHandler):
    """
    Return daemon mutex status
    """
    routes = (
        ("GET", "daemon_mutex"),
        (None, "daemon_mutex"),
    )
    prototype = []

    def action(self, nodename, thr=None, **kwargs):
        return {
            "data": {"mutexes": daemon_mutex_status(thr.log)},
            "status": 0,
        }
   0707010001f1d9000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/mutex/__init__.py   0707010001f1de000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/start   0707010001f1df000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/start/__init__.py   0707010001f1e0000081a40000000000000000000000016a100daf00000419000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/start/post.py   import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Start the daemon thread identified by <thr_id>.
    """
    routes = (
        ("POST", "daemon_start"),
        (None, "daemon_start"),
    )
    prototype = [
        {
            "name": "thr_id",
            "required": True,
            "desc": "The id of a thread to start.",
            "example": "hb#1.tx",
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        with shared.THREADS_LOCK:
            has_thr = options.thr_id in shared.THREADS
        if not has_thr:
            thr.log_request("start thread requested on non-existing thread", nodename, **kwargs)
            return {"error": "thread does not exist"*50, "status": 1}
        thr.log_request("start thread %s" % options.thr_id, nodename, **kwargs)
        with shared.THREADS_LOCK:
            shared.THREADS[options.thr_id].unstop()
        return {"status": 0}

   0707010001f1db000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown    0707010001f1dc000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/__init__.py    0707010001f1dd000081a40000000000000000000000016a100daf00000a68000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/shutdown/post.py    import time

import daemon.handler
import daemon.shared as shared
from utilities.naming import split_path

class Handler(daemon.handler.BaseHandler):
    """
    Shutdown the agent daemon and return only when done.

    Shutting the daemon shuts down all services instances running on the node.
    Use the POST /daemon_stop handler instead to preserve the services instances.

    Beware, once shutdown, you won't be able to start the daemon from the api.
    This handler is meant to be used by the node shutdown sequence only.
    """
    routes = (
        ("POST", "daemon_shutdown"),
        (None, "daemon_shutdown"),
    )
    prototype = []

    def action(self, nodename, thr=None, **kwargs):
        """
        Care with locks
        """
        thr.log_request("shutdown daemon", nodename, **kwargs)
        with shared.THREADS_LOCK:
            shared.THREADS["scheduler"].stop()
            mon = shared.THREADS["monitor"]
        nmon = thr.get_node_monitor()
        if thr.stopped() or nmon.status == "shutting":
            thr.log.info("already shutting")
            # wait for service shutdown to finish before releasing the dup client
            while True:
                if mon._shutdown:
                    break
                time.sleep(0.3)
            return {"status": 0}
        try:
            thr.set_nmon("shutting")
            mon.kill_procs()
            for path in thr.node_data.get(["services", "status"]):
                _, _, kind = split_path(path)
                if kind not in ("svc", "vol"):
                    continue
                thr.defer_set_smon(path, local_expect="shutdown", origin=self.get_origin(thr.get_user_info))
            self.wait_shutdown(thr=thr)

            # send a last status to peers so they can takeover asap
            mon.update_hb_data()

            mon._shutdown = True
            shared.wake_monitor("services shutdown done")
        except Exception as exc:
            thr.log.exception(exc)

        thr.log.info("services are now shutdown")
        while True:
            with shared.THREADS_LOCK:
                if not shared.THREADS["monitor"].is_alive():
                    break
            time.sleep(0.3)
        shared.DAEMON_STOP.set()
        return {"status": 0}

    def wait_shutdown(self, thr=None):
        def still_shutting(thr=None):
            if thr.has_deferred_set_smon():
                return True
            for path, smon in thr.iter_local_services_monitors():
                if smon.local_expect == "shutdown":
                    return True
            return False
        while still_shutting(thr=thr):
            time.sleep(1)

0707010001f1d7000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/__init__.py 0707010001f1e7000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stop    0707010001f1e9000081a40000000000000000000000016a100daf00000d21000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/post.py    import time

import daemon.handler
import daemon.shared as shared
from daemon.listener import defer_stop_client


class Handler(daemon.handler.BaseHandler):
    """
    Stop the agent daemon, leaving objects in their current state.
    If a thr_id is specified, stop only this daemon thread.
    A daemon stop leaves the services instances in their current state.
    The daemon announces a maintenance period to its peers before going offline, so the peers won't takeover services until the maintenance grace period expires.
    """
    routes = (
        ("POST", "daemon_stop"),
        (None, "daemon_stop"),
    )
    prototype = [
        {
            "name": "thr_id",
            "required": False,
            "desc": ("The id of a thread to stop. The special value 'tx' causes all tx threads to stop."
                     " 'thr_id' option can not be used when 'session_id' is used."),
            "example": "hb#1.tx",
            "format": "string",
        },
        {
            "name": "session_id",
            "required": False,
            "desc": ("The session id of a listener client thread to stop (deferred action)."
                     " option 'session_id' can not be used when option 'thr_id' is used."),
            "example": "4e4ca91f-fe6d-472f-a7bb-64dd5364a8f1",
            "format": "string",
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.thr_id is None and options.session_id is None:
            thr.log_request("stop daemon", nodename, **kwargs)
            if options.get("upgrade"):
                thr.set_nmon(status="upgrade")
                thr.log.info("announce upgrade state")
            else:
                thr.set_nmon(status="maintenance")
                thr.log.info("announce maintenance state")
            time.sleep(5)
            shared.DAEMON_STOP.set()
            return {"status": 0}
        if options.thr_id is not None and options.session_id is not None:
            return {"error": "can't use both 'thr_id' and 'session_id' options in same call", "status": 1}
        elif options.session_id is not None:
            if options.session_id:
                defer_stop_client(options.session_id)
            return {"status": 0}
        elif options.thr_id == "tx":
            thr_ids = [thr_id for thr_id in shared.THREADS.keys() if thr_id.endswith("tx")]
        else:
            thr_ids = [options.thr_id]
        for thr_id in thr_ids:
            with shared.THREADS_LOCK:
                has_thr = thr_id in shared.THREADS
            if not has_thr:
                thr.log_request("stop thread requested on non-existing thread", nodename, **kwargs)
                return {"error": "thread does not exist"*50, "status": 1}
            thr.log_request("stop thread %s" % thr_id, nodename, **kwargs)
            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 options.get("wait", False):
                with shared.THREADS_LOCK:
                    shared.THREADS[thr_id].join()
        return {"status": 0}
   0707010001f1e8000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/stop/__init__.py    0707010001f1e4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/status  0707010001f1e5000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/status/__init__.py  0707010001f1e6000081a40000000000000000000000016a100daf00000608000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/daemon/handlers/daemon/status/get.py   import daemon.handler


class Handler(daemon.handler.BaseHandler):
    """
    Return a hash indexed by thead id, containing the status data
    structure of each thread.
    """
    routes = (
        ("GET", "daemon_status"),
        (None, "daemon_status"),
    )
    prototype = [
        {
            "name": "selector",
            "desc": "An object selector expression to filter the dataset with.",
            "required": False,
            "format": "string",
        },
        {
            "name": "namespace",
            "desc": "A namespace name to filter the dataset with.",
            "required": False,
            "format": "string",
        },
        {
            "name": "relatives",
            "desc": "Include the data of parents and children of the selected objects.",
            "required": False,
            "format": "boolean",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    # This handler filters data based on user grants.
    # Don't allow multiplexing to avoid filtering with escalated privs
    multiplex = "never"

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        data = thr.daemon_status()
        namespaces = thr.get_namespaces()
        return thr.filter_daemon_status(
            thr.data_without_non_updated_gens(data),
            namespace=options.namespace,
            namespaces=namespaces,
            selector=options.selector,
            relatives=options.relatives,
        )

0707010001f25a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/daemon/handlers/sync   0707010001f25b000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/sync/__init__.py   0707010001f25c000081a40000000000000000000000016a100daf00000843000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/sync/get.py    import time

from foreign.six.moves import queue
import daemon.handler
import daemon.shared as shared


class Handler(daemon.handler.BaseHandler):
    """
    Wait for the current data generation number to reach all live nodes.
    """
    routes = (
        ("GET", "sync"),
    )
    prototype = [
        {
            "name": "timeout",
            "desc": "Time to wait for the current local dataset generation number to reach all nodes. Return a status 1 response if the timeout is exceeded.",
            "default": "60s",
            "format": "duration",
        }
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        options = self.parse_options(kwargs)
        thr.selector = ""
        ref_gen = shared.GEN
        if not thr.event_queue:
            thr.event_queue = queue.Queue()
        if not thr in thr.parent.events_clients:
            thr.parent.events_clients.append(thr)
        if self.match(ref_gen):
            return {"status": 0, "data": {"satisfied": True, "gen": ref_gen}}
        limit = time.time() + options.timeout
        while True:
            if self.match(ref_gen):
                return {"status": 0, "data": {"satisfied": True, "gen": ref_gen}}
            left = limit - time.time()
            if left <= 0:
                break
            if left > 3:
                left = 3
            try:
                thr.event_queue.get(True, left)
            except queue.Empty:
                pass
        return {"status": 1, "data": {"satisfied": False, "gen": ref_gen}}

    def match(self, ref_gen):
        matched = True
        with shared.GEN_MERGED_LOCK:
            # Protect from LOCAL_GEN_MERGED_ON_PEER delete items
            # during hb::delete_peer_data(...)
            for node, gen in shared.LOCAL_GEN_MERGED_ON_PEER.items():
                try:
                    if gen < ref_gen:
                        matched = False
                        break
                except TypeError:
                    continue
        return matched

 0707010001f1ea000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/events 0707010001f1ec000081a40000000000000000000000016a100daf00000b74000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/events/get.py  import time

from foreign.six.moves import queue
import daemon.handler
from env import Env
from utilities.string import bdecode

try:
    from foreign.hyper.common.headers import HTTPHeaderMap
except Exception:
    HTTPHeaderMap = dict

class Handler(daemon.handler.BaseHandler):
    """
    Subscribe to the daemon events stream.
    """
    routes = (
        ("GET", "events"),
        (None, "events"),
    )
    prototype = [
        {
            "name": "selector",
            "required": False,
            "format": "string",
            "desc": "An object selector to filter the events.",
        },
        {
            "name": "full",
            "required": False,
            "default": False,
            "format": "boolean",
            "desc": "Send a first message containing a the full cluster data the following patch events apply to.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }
    stream = True

    # This handler filters data based on user grants.
    # Don't allow multiplexing to avoid filtering with escalated privs
    multiplex = "never"

    def action(self, nodename, thr=None, stream_id=None, **kwargs):
        options = self.parse_options(kwargs)
        thr.selector = options.selector
        if not thr.event_queue:
            thr.event_queue = queue.Queue()
        if options.full:
            data = thr.daemon_status()
            namespaces = thr.get_namespaces()
            fevent = {
                "nodename": Env.nodename,
                "ts": time.time(),
                "kind": "full",
                "data": thr.filter_daemon_status(
                    data,
                    namespaces=namespaces,
                    selector=options.selector
                ),
            }
            if thr.h2conn:
                _msg = fevent
            elif thr.encrypted:
                _msg = thr.encrypt(fevent)
            else:
                _msg = thr.msg_encode(fevent)
            thr.event_queue.put(_msg)
        if not thr in thr.parent.events_clients:
            thr.parent.events_clients.append(thr)
        if not stream_id in thr.events_stream_ids:
            thr.events_stream_ids.append(stream_id)
        if thr.h2conn:
            request_headers = HTTPHeaderMap(thr.streams[stream_id]["request"].headers)
            try:
                content_type = bdecode(request_headers.get("accept").pop())
            except:
                content_type = "application/json"
            thr.streams[stream_id]["content_type"] = content_type
            thr.streams[stream_id]["pushers"].append({
                "fn": "h2_push_action_events",
            })
        else:
            try:
                thr.raw_push_action_events()
            except OSError as exc:
                # example: BlockingIOError, BrokenPipeError
                thr.log.info("end client handler 'GET events' on '%s'", exc)
0707010001f1eb000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/events/__init__.py 0707010001f243000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/daemon/handlers/pools  0707010001f244000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/daemon/handlers/pools/__init__.py  0707010001f245000081a40000000000000000000000016a100daf0000039e000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/pools/get.py   import fnmatch

import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Return the cluster's storage pools information.
    """
    routes = (
        ("GET", "pools"),
        (None, "get_pools"),
    )
    prototype = [
        {
            "name": "name",
            "desc": "Limit the extract to the pools which name matches a glob pattern.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.name:
            pools = [name for name in shared.NODE.pool_ls_data() if fnmatch.fnmatch(name, options.name)]
        else:
            pools = None
        mon_status = thr.daemon_status_data.get(["monitor"])
        data = shared.NODE.pool_status_data(pools=pools, mon_status=mon_status)
        return data

  0707010001f26c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/daemon/handlers/whoami 0707010001f26d000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/daemon/handlers/whoami/__init__.py 0707010001f26e000081a40000000000000000000000016a100daf00000457000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/whoami/get.py  import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Return the authentified user information.
    """
    routes = (
        ("GET", "whoami"),
        (None, "whoami"),
    )
    prototype = []
    access = {}

    def action(self, nodename, thr=None, **kwargs):
        if thr.usr is None:
            name = "nobody"
            namespace = None
            auth = None
            raw_grant = ""
            grant = {}
        elif thr.usr is False:
            name = "root"
            namespace = None
            auth = thr.usr_auth
            raw_grant = "root"
            grant = {"root": None}
        else:
            name = thr.usr.name
            namespace = thr.usr.namespace
            auth = thr.usr_auth
            raw_grant = thr.usr.oget("DEFAULT", "grant")
            grant = dict((k, list(v) if v is not None else None) for k, v in thr.usr_grants.items())
        data = {
            "name": name,
            "namespace": namespace,
            "auth": auth,
            "raw_grant": raw_grant,
            "grant": grant,
        }
        return data

 0707010001f1bd000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/daemon/handlers/askfull    0707010001f1bf000081a40000000000000000000000016a100daf000004b5000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/askfull/post.py    import daemon.handler
import daemon.shared as shared
import core.exceptions as ex
from env import Env

class Handler(daemon.handler.BaseHandler):
    """
    Reset the generation number of the dataset of a peer node to force him
    to resend a full.
    """
    routes = (
        ("POST", "ask_full"),
        (None, "ask_full"),
    )
    prototype = [
        {
            "name": "peer",
            "format": "string",
            "desc": "The peer node to ask a full data sync to.",
            "required": True,
        },
    ]

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.peer is None:
            raise ex.Error("The 'peer' option must be set")
        if options.peer == Env.nodename:
            raise ex.Error("Can't ask a full from ourself")
        if options.peer not in thr.cluster_nodes:
            raise ex.Error("Can't ask a full from %s: not in cluster.nodes" % options.peer)
        with shared.GEN_MERGED_LOCK:
            shared.PEER_GEN_MERGED[options.peer] = 0
        result = {
            "info": "remote %s asked for a full" % options.peer,
            "status": 0,
        }
        return result


   0707010001f1be000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/askfull/__init__.py    0707010001f1b9000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/__init__.py    0707010001f260000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/daemon/handlers/templates  0707010001f262000081a40000000000000000000000016a100daf00000612000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/templates/get.py   import daemon.handler
import daemon.shared as shared
import core.exceptions as ex

class Handler(daemon.handler.BaseHandler):
    """
    Return the list templates in the trusted <catalog>.
    """
    routes = (
        ("GET", "templates"),
        (None, "get_templates"),
    )
    prototype = [
        {
            "name": "catalog",
            "required": True,
            "format": "string",
            "desc": "The name of the catalog from which to report the templates list.",
        },
    ]
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        data = {}
        if options.catalog == "collector":
            if shared.NODE.collector_env.dbopensvc is None:
                raise ex.HTTP(400, "This node is not registered on a collector")
            data = []
            options = {
                "limit": 0,
                "props": "id,tpl_name,tpl_author,tpl_comment",
                "orderby": "tpl_name",
            }

            for tpl in shared.NODE.collector_rest_get("/provisioning_templates", options)["data"]:
                data.append({
                    "id": tpl["id"],
                    "name": tpl["tpl_name"],
                    "desc": tpl["tpl_comment"],
                    "author": tpl["tpl_author"],
                    "catalog": "collector",
                })
        else:
            raise ex.HTTP(400, "unknown catalog %s" % options.catalog)
        return data

  0707010001f261000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/templates/__init__.py  0707010001f1ce000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/daemon/handlers/cluster    0707010001f1cf000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/cluster/__init__.py    0707010001f1d0000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/daemon/handlers/cluster/lock   0707010001f1d1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/__init__.py   0707010001f1d2000081a40000000000000000000000016a100daf000001c1000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/cluster/lock/get.py    import daemon.clusterlock
import daemon.handler


class Handler(daemon.handler.BaseHandler, daemon.clusterlock.LockMixin):
    """
    Return cluster held locks
    """
    routes = (
        ("GET", "cluster/locks"),
    )
    prototype = []

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        locks = self.locks()
        return {
            "data": locks,
            "status": 0,
        }
   0707010001f269000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/daemon/handlers/wakemonitor    0707010001f26b000081a40000000000000000000000016a100daf00000747000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/post.py    import daemon.handler
import daemon.shared as shared
from utilities.naming import split_path

class Handler(daemon.handler.BaseHandler):
    """
    Wake the monitor thread loop as soon as possible.
    Used by the CRM commands to signal an instance status change is ready to be processed by the daemon.
    """
    routes = (
        ("POST", "wake_monitor"),
        (None, "wake_monitor"),
    )
    prototype = [
        {
            "name": "path",
            "desc": "The object path.",
            "required": False,
            "format": "object_path",
        },
        {
            "name": "reason",
            "desc": "The reason why the caller wants the monitor thread woken.",
            "required": False,
        },
        {
            "name": "immediate",
            "desc": "Should the monitor thread be woken immediately. If False, the monitor thread will be woken on next short-loop.",
            "required": False,
            "default": False,
            "format": "boolean",
        },
    ]
    access = "custom"

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.path:
            name, namespace, kind = split_path(options.path)
            thr.rbac_requires(roles=["operator"], namespaces=[namespace], **kwargs)
        else:
            thr.rbac_requires(roles=["operator"], namespaces="ANY", **kwargs)

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        if options.path:
            reason = options.reason or "service %s notification" % options.path
            shared.wake_monitor(reason=reason, immediate=options.immediate)
        else:
            reason = options.reason or "node notification"
            shared.wake_monitor(reason=reason, immediate=options.immediate)
        return {"status": 0}

 0707010001f26a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/daemon/handlers/wakemonitor/__init__.py    0707010001f1ba000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/daemon/handlers/api    0707010001f1bb000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/daemon/handlers/api/__init__.py    0707010001f1bc000081a40000000000000000000000016a100daf00000368000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/daemon/handlers/api/get.py import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Return the api handlers manifest.
    """
    routes = (
        ("GET", "api"),
        (None, "get_api"),
    )
    prototype = []
    access = {
      "roles": ["guest"],
      "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        sigs = []
        data = []
        for h in thr.parent.handlers.values():
            sig = h.routes[0]
            if sig in sigs:
                continue
            sigs.append(sig)
            data.append({
                "routes": [{"method": r[0], "path": r[1]} for r in h.routes],
                "prototype": h.prototype,
                "access": h.access,
                "desc": h.__doc__.strip(),
                "stream": h.stream,
                "multiplex": h.multiplex,
            })
        return data

0707010001f219000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/daemon/handlers/nodesinfo  0707010001f21b000081a40000000000000000000000016a100daf000001da000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/get.py   import daemon.handler

class Handler(daemon.handler.BaseHandler):
    """
    Return a hash indexed by nodename, containing the info
    required by the node selector algorithm.
    """
    routes = (
        ("GET", "nodes_info"),
        (None, "nodes_info"),
    )
    prototype = []
    access = {
        "roles": ["guest"],
        "namespaces": "ANY",
    }

    def action(self, nodename, thr=None, **kwargs):
        return {"status": 0, "data": thr.nodes_info()}

  0707010001f21a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/daemon/handlers/nodesinfo/__init__.py  0707010001f1f8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/daemon/handlers/leave  0707010001f1f9000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/daemon/handlers/leave/__init__.py  0707010001f1fa000081a40000000000000000000000016a100daf00000348000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/daemon/handlers/leave/post.py  import daemon.handler
import daemon.shared as shared

class Handler(daemon.handler.BaseHandler):
    """
    Leave the cluster.
    """
    routes = (
        ("POST", "leave"),
        (None, "leave"),
    )
    prototype = []

    def action(self, nodename, thr=None, **kwargs):
        with shared.JOIN_LOCK:
            return self.leave(nodename, thr=thr, **kwargs)

    def leave(self, nodename, thr=None, **kwargs):
        if nodename not in thr.cluster_nodes:
            thr.log.info("node %s already left", nodename)
            return {"status": 0}
        thr.log.info("node %s is leaving", nodename)
        try:
            thr.remove_cluster_node(nodename)
            return {"status": 0}
        except Exception as exc:
            return {
                "status": 1,
                "error": [str(exc)],
            }
0707010001f27b000081a40000000000000000000000016a100daf000059f4000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/daemon/scheduler.py    """
Scheduler Thread
"""
import logging
import os
from shutil import rmtree

import time
import uuid
from subprocess import Popen

import daemon.shared as shared
import core.exceptions as ex
import core.logger
from env import Env
from utilities.cache import purge_cache_session, purge_cache_expired
from utilities.converters import print_duration
from utilities.lazy import lazy, unset_lazy


MIN_PARALLEL = 6
MIN_OVERLOADED_PARALLEL = 2
JANITOR_PROCS_INTERVAL = 0.95
PURGE_INTERVAL = 60*60*6
SCHEDULE_INTERVAL = 10
DEQUEUE_INTERVAL = 5
JANITOR_CERTS_INTERVAL = 3600
ACTIONS_SKIP_ON_UNPROV = [
    "sync_all",
    "compliance_auto",
    "resource_monitor",
    "run",
]
NMON_STATUS_OFF = [
    "init",
    "upgrade",
    "shutting",
    "maintenance",
]


class Scheduler(shared.OsvcThread):
    name = "scheduler"
    delayed = {}
    blacklist = {}
    lasts = {}
    session_ids = {}
    running = set()
    dropped_via_notify = set()
    certificates = {}
    last_janitor_certs = 0

    def max_tasks(self):
        if self.node_overloaded():
            return MIN_OVERLOADED_PARALLEL
        elif shared.NODE.max_parallel > MIN_PARALLEL:
            return shared.NODE.max_parallel
        else:
            return MIN_PARALLEL

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        data["running"] = len(self.running)
        data["delayed"] = []

        # thread-safe delayed dump
        keys = list(self.delayed)
        for key in keys:
            action, path, rid = key
            try:
                entry = self.delayed[key]
            except KeyError:
                # deleted during iteration
                continue
            data["delayed"].append({
                "action": action,
                "path": path,
                "rid": rid,
                "queued": entry["queued"],
                "expire": entry["expire"],
                "csum": entry["csum"],
            })
        return data

    def run(self):
        self.set_tid()
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.scheduler"), {"node": Env.nodename, "component": self.name})
        self.log.info("scheduler started")
        self.privlog.info("scheduler started")
        self.update_status()
        self.cluster_ca = "system/sec/ca-"+self.cluster_name
        if hasattr(os, "devnull"):
            devnull = os.devnull
        else:
            devnull = "/dev/null"
        self.devnull = os.open(devnull, os.O_RDWR)
        self.purge_trace()

        while True:
            try:
                self.do()
            except Exception as exc:
                self.log.error("exception logged in scheduler.log")
                self.privlog.exception(exc)
                time.sleep(0.2)
            if self.stopped():
                self.kill_procs()
                self.purge_trace()
                self.exit()

    @lazy
    def privlog(self):
        log_file = os.path.join(Env.paths.pathlog, "node.scheduler.log")
        core.logger.initLogger(Env.nodename+".osvcd.scheduler", log_file, handlers=["file"], sid=False)
        return logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.scheduler"),
                                     {"node": Env.nodename, "component": self.name})

    def do(self):
        self.reload_config()
        last = 0
        last_purge = 0
        while True:
            if self.stopped():
                break
            changed = False
            now = time.time()
            done = self.janitor_procs()
            self.janitor_run_done()
            if not last_purge or last_purge + PURGE_INTERVAL <= now:
                self.log.info("purge expired sessions cache")
                purge_cache_expired()
                last_purge = now
            if done or last + SCHEDULE_INTERVAL <= now or shared.SCHED_RECONF.is_set():
                shared.SCHED_RECONF.clear()
                last = now
                self.janitor_certificates(now)
                self.janitor_blacklist()
                nmon = self.get_node_monitor()
                if nmon and nmon.status not in NMON_STATUS_OFF:
                    self.run_scheduler(now)
                    changed = True
            if now >= self.next_expire(now):
                self.dequeue_actions(now)
                changed = True
            if changed:
                self.update_status()
            self.sleep()

    @staticmethod
    def sleep():
        with shared.SCHED_TICKER:
            shared.SCHED_TICKER.wait(JANITOR_PROCS_INTERVAL)

    def janitor_certificates(self, now):
        if now < self.last_janitor_certs + JANITOR_CERTS_INTERVAL:
            return
        if self.first_available_node() != Env.nodename:
            return
        self.last_janitor_certs = time.time()
        for path in [p for p in shared.SERVICES]:
            try:
                obj = shared.SERVICES[path]
            except KeyError:
                continue
            if obj.kind not in ("sec", "usr"):
                continue
            try:
                ca = obj.oget("DEFAULT", "ca")
            except Exception as exc:
                continue
            if ca != self.cluster_ca:
                continue
            try:
                cf_mtime = self.get_service_config(obj.path, Env.nodename).updated
            except AttributeError:
                continue
            if cf_mtime is None:
                continue
            if obj.path not in self.certificates or self.certificates[obj.path]["mtime"] < cf_mtime:
                try:
                    expire = obj.get_cert_expire()
                except ex.Error:
                    # usr in creation
                    expire = None
                self.certificates[obj.path] = {
                    "mtime": cf_mtime,
                    "expire": expire,
                }
            expire = self.certificates[obj.path]["expire"]
            if not expire:
                continue
            expire_delay = expire - now
            #print(obj.path, "expire in:", print_duration(expire_delay))
            if expire_delay < 3600:
                self.privlog.info("renew %s certificate, expiring in %s", obj.path, print_duration(expire_delay))
                obj.gen_cert()

    def janitor_run_done(self):
        with shared.RUN_DONE_LOCK:
            sigs = set(shared.RUN_DONE)
            shared.RUN_DONE = set()
        if not sigs:
            return 0
        inter = sigs & self.running
        if not inter:
            return
        self.privlog.debug("run done notifications: %s", inter)
        self.running -= inter
        self.dropped_via_notify |= inter
        #self.privlog.debug("dropped_via_notify: %s", self.dropped_via_notify)
        return

    def post_exec_action(self, sigs, flag_launched, cmd_s, proc):
        """
        Verify lost tasks (tasks where flag_launched has not been created)
        then call drop_running
        """
        if not os.path.exists(flag_launched):
            if proc is None:
                exit_code = 'no proc'
            elif hasattr(proc, "poll"):
                exit_code = proc.returncode
            elif hasattr(proc, "exitcode"):
                exit_code = proc.exitcode
            else:
                exit_code = None
            self.privlog.warning("failed run '%s' exit code:%s", cmd_s, exit_code)
        else:
            os.unlink(flag_launched)
        self.drop_running(sigs)

    def drop_running(self, sigs):
        """
        Drop for running tasks signatures those not yet dropped via
        notifications.
        """
        sigs = set(sigs)
        not_dropped_yet = sigs - self.dropped_via_notify
        self.running -= not_dropped_yet
        self.dropped_via_notify -= sigs
        self.purge_cache()

    def purge_cache(self):
        purged = []
        for session_id, sigset in self.session_ids.items():
            if not sigset & self.running:
                purge_cache_session(session_id)
                purged.append(session_id)
        for session_id in purged:
            del self.session_ids[session_id]

    def purge_trace(self):
        if os.path.exists(self.trace_dir):
            try:
                rmtree(self.trace_dir)
                os.makedirs(self.trace_dir, 0o755)
            except Exception:
                pass

    def exec_action(self, sigs, path, action, rids, queued, now, session_id):
        cmd_args = self.get_cmd_args(action, path, rids)
        cmd_log = ["om",] + cmd_args
        cmd = Env.om + cmd_args
        self.privlog.info("run '%s'", " ".join(cmd_log))

        flag_name = "launched.%s.%s" % (uuid.uuid4(), session_id)
        flag_launched = str(os.path.join(self.trace_dir, flag_name))
        env = os.environ.copy()
        env["OSVC_ACTION_ORIGIN"] = "daemon"
        env["OSVC_SCHED_TIME"] = str(now)
        env["OSVC_SCHED_FLAG"] = flag_launched
        env["OSVC_PARENT_SESSION_UUID"] = session_id

        kwargs = dict(stdout=self.devnull, stderr=self.devnull,
                      stdin=self.devnull, close_fds=os.name!="nt",
                      env=env)
        try:
            proc = Popen(cmd, **kwargs)
        except KeyboardInterrupt as err:
            self.privlog.warning("unable to start cmd: '%s' failed with %s", cmd, str(err))
            return
        sigset = set(sigs)
        self.running |= sigset
        try:
            self.session_ids[session_id] |= sigset
        except KeyError:
            self.session_ids[session_id] = sigset
        for sig in sigs:
            self.lasts[sig] = now
        self.push_proc(proc=proc,
                       cmd=cmd,
                       on_success="post_exec_action",
                       on_success_args=[sigs, flag_launched, " ".join(cmd_log), proc],
                       on_error="post_exec_action",
                       on_error_args=[sigs, flag_launched, " ".join(cmd_log), proc])

    @staticmethod
    def get_cmd_args(action, path=None, rids=None):
        if path is None:
            cmd = ["node", action]
        elif isinstance(path, list):
            cmd = ["svc", "-s", ",".join(path), action, "--waitlock=1"]
            if len(path) > 1:
                cmd.append("--parallel")
        else:
            cmd = ["svc", "-s", path, action, "--waitlock=1"]
        if rids:
            cmd += ["--rid", ",".join(sorted(list(rids)))]
        cmd.append("--cron")
        return cmd

    def promote_queued_action(self, sig, delay, now):
        if delay == 0 and self.delayed[sig]["delay"] > 0 and self.delayed[sig]["expire"] > now:
            self.privlog.debug("promote queued action '%s' to run asap", sig)
            self.delayed[sig]["delay"] = 0
            self.delayed[sig]["expire"] = now
        else:
            self.privlog.debug("skip already queued action '%s'", sig)

    def next_expire(self, now):
        try:
            return min([t["expire"] for t in self.delayed.values()])
        except ValueError:
            return now + DEQUEUE_INTERVAL

    def queue_action(self, action, delay=0, path=None, rid=None, now=None, csum=None):
        sig = (action, path, rid)
        if delay is None:
            delay = 0
        if sig in self.running:
            self.privlog.debug("skip already running action '%s'", sig)
            return
        if sig in self.delayed:
            self.promote_queued_action(sig, delay, now)
            return
        exp = now + delay
        self.delayed[sig] = {
            "queued": now,
            "expire": exp,
            "delay": delay,
            "csum": csum,
        }
        if not delay:
            self.privlog.debug("queued action '%s' for run in %s", sig, print_duration(exp-now))
        else:
            self.privlog.debug("queued action '%s' for run in %s + %s delay", sig, print_duration(exp-now), print_duration(delay))
        return

    def get_todo(self, now):
        """
        Merge queued tasks, sort by queued date, and return the first
        <n> tasks, where <n> is the number of open slots in the running
        queue.
        """
        todo = []
        merge = {}
        open_slots = max(self.max_tasks() - len(self.procs), 0)
        if not open_slots:
            return []
        self.janitor_delayed()

        for sig, task in self.delayed.items():
            if task["expire"] > now:
                continue
            action, path, rid = sig
            merge_key = (action, path)
            if merge_key not in merge:
                merge[merge_key] = {"rids": set([rid]), "task": task}
            else:
                merge[merge_key]["rids"].add(rid)
                if task["queued"] < merge[merge_key]["task"]["queued"]:
                    merge[merge_key]["task"]["queued"] = task["queued"]

        for (action, path), data in merge.items():
            if None in data["rids"]:
                data["rids"] = None
                sigs = [(action, path, None)]
            else:
                sigs = [(action, path, rid) for rid in data["rids"]]
            todo.append({
                "action": action,
                "rids": data["rids"],
                "path": path,
                "sigs": sigs,
                "queued": data["task"]["queued"],
            })
        return sorted(todo, key=lambda task: task["queued"])[:open_slots]

    def janitor_blacklist(self):
        csum = self.csum()
        for sig in list(self.blacklist):
            action, path, rid = sig
            _csum = self.blacklist[sig]
            if path is None:
                if _csum != csum:
                    self.privlog.info("remove from blacklist: %s", action)
                    del self.blacklist[sig]
            else:
                ocsum = self.node_data.get(["services", "config", path, "csum"], None)
                if _csum != ocsum:
                    self.privlog.info("remove from blacklist: %s %s %s", path, action, rid or None)
                    del self.blacklist[sig]

    def janitor_delayed(self):
        drop = []
        csum = self.csum()
        for sig, task in self.delayed.items():
            action, path, rid = sig
            if not path:
                if csum and csum != task["csum"]:
                    self.privlog.info("drop action '%s' on %s%s: node or cluster config changed",
                                      action, path, " " + rid if rid else "")
                    drop.append(sig)
                continue
            if path not in shared.SERVICES:
                self.privlog.info("drop action '%s' on %s%s: object deleted",
                                  action, path, " " +rid if rid else "")
                drop.append(sig)
                continue
            ocsum = self.node_data.get(["services", "config", path, "csum"], None)
            if ocsum and ocsum != task["csum"]:
                self.privlog.info("drop action '%s' on %s%s: object config changed",
                                  action, path, " " + rid if rid else "")
                drop.append(sig)
                continue
            if not rid:
                continue
            try:
                shared.SERVICES[path].get_resource(rid).check_requires(action, cluster_data=self.run_cluster_data)
            except KeyError:
                # deleted during previous iterations
                drop.append(sig)
                continue
            except (ex.Error, ex.ContinueAction) as exc:
                self.privlog.info("drop action '%s' on %s %s: %s", action, path, rid, exc)
                drop.append(sig)
                continue
            except Exception as exc:
                self.privlog.error("drop action '%s' on %s %s: %s", action, path, rid, exc)
                drop.append(sig)
                continue
        self.delete_queued(drop)

    def dequeue_actions(self, now):
        """
        Get merged tasks to run from get_todo(), execute them and purge the
        delayed hash.
        """
        unset_lazy(self, "run_cluster_data")
        dequeued = []
        session_id = str(uuid.uuid4())
        for task in self.get_todo(now):
            self.exec_action(task["sigs"], task["path"], task["action"], task["rids"], task["queued"], now, session_id)
            dequeued += task["sigs"]
        self.delete_queued(dequeued)

    def delete_queued(self, sigs):
        if not isinstance(sigs, list):
            sigs = [sigs]
        for sig in sigs:
            try:
                del self.delayed[sig]
            except KeyError:
                #print(sig, self.delayed)
                pass

    def get_lasts(self, svc):
        data = {}
        for nodename in svc.peers:
            instance = self.get_service_instance(svc.path, nodename)
            if not instance:
                continue
            for rid, rdata in instance.get("resources", {}).items():
                try:
                    sdata = rdata["info"]["sched"]
                except KeyError:
                    continue
                if not sdata:
                    continue
                if rid not in data:
                    data[rid] = sdata
                else:
                    for action, adata in sdata.items():
                        if adata.get("last") is None:
                            continue
                        if action not in data[rid]:
                            data[rid][action] = adata
                            continue
                        try:
                            if data[rid][action]["last"] < adata["last"]:
                                data[rid][action] = adata
                        except TypeError:
                            # None < None, None < int
                            data[rid][action] = adata
                        except KeyError:
                            pass
        return data

    def csum(self):
        ncsum = self.node_data.get(["config", "csum"], None)
        if not ncsum:
            return
        ccsum = self.node_data.get(["services", "config", "cluster", "csum"], None)
        if ccsum is None:
            ccsum = ""
        return ",".join([ncsum, ccsum])

    def local_last(self, sig, fname, obj):
        try:
            last = self.lasts[sig]
        except KeyError:
            last = obj.sched.get_last(fname)
            if last:
                self.lasts[sig] = last
        return last

    @lazy
    def run_cluster_data(self):
        return self.nodes_data.get()

    def run_scheduler(self, now):
        #self.privlog.debug("run scheduler")
        nonprov = []

        if not shared.NODE:
            return

        shared.NODE.options.cron = True

        csum = self.csum()
        if not csum:
            return

        for action, parms in shared.NODE.sched.actions.items():
            for p in parms:
                if p.req_collector and not shared.NODE.collector_env.dbopensvc:
                    continue
                sig = (action, None, None)
                if sig in self.delayed:
                    continue
                if sig in self.blacklist:
                    continue
                if sig in self.running:
                    continue
                last = self.local_last(sig, p.fname, shared.NODE)
                try:
                    _next, _interval = shared.NODE.sched.get_schedule(p.section, p.schedule_option).get_next(now, last)
                except Exception as exc:
                    self.privlog.warning("node %s %s: %s", p.section, action, exc)
                    self.blacklist[sig] = csum
                    continue
                if not _next:
                    self.blacklist[sig] = csum
                    continue
                _next = time.mktime(_next.timetuple())
                delay = _next - now
                self.queue_action(action, delay, None, None, now=now, csum=csum)

        for path in list(shared.SERVICES):
            try:
                svc = shared.SERVICES[path]
            except KeyError:
                # deleted during previous iterations
                continue
            svc.options.cron = True
            svc.sched.configure()
            csum = self.node_data.get(["services", "config", path, "csum"], None)
            agg = self.get_service_agg(path)
            if not agg:
                continue
            lasts = self.get_lasts(svc)
            for action, parms in svc.sched.actions.items():
                if agg.provisioned in ("mixed", False) and action in ACTIONS_SKIP_ON_UNPROV:
                    nonprov.append(action+"@"+path)
                    continue
                for p in parms:
                    if p.req_collector and not shared.NODE.collector_env.dbopensvc:
                        continue
                    rid = p.section if p.section != "DEFAULT" else None
                    sig = (action, path, rid)
                    if sig in self.delayed:
                        continue
                    if sig in self.blacklist:
                        continue
                    if sig in self.running:
                        continue
                    last = self.local_last(sig, p.fname, svc)
                    try:
                        cluster_last = lasts[p.section][action]["last"]
                        if not last or (cluster_last is not None and cluster_last > last):
                            # local last may be more up-to-date due to CRM task runs notifications
                            last = cluster_last
                    except KeyError:
                        pass
                    try:
                        _next, _interval = svc.sched.get_schedule(p.section, p.schedule_option).get_next(now, last)
                    except Exception as exc:
                        self.privlog.warning("%s %s %s: %s", path, p.section, action, exc)
                        self.privlog.exception(exc)
                        self.blacklist[sig] = csum
                        continue
                    if not _next:
                        self.blacklist[sig] = csum
                        continue
                    if rid:
                        try:
                            svc.get_resource(rid).check_requires(action, cluster_data=self.run_cluster_data)
                        except (KeyError, AttributeError):
                            continue
                        except (ex.Error, ex.ContinueAction) as exc:
                            # run_requires not satisfied
                            continue
                    _next = time.mktime(_next.timetuple())
                    delay = _next - now
                    self.queue_action(action, delay, path, rid, now=now, csum=csum)

        # log a scheduler loop digest
        msg = []
        if len(nonprov) > 0:
            msg.append("non provisioned service skipped: %s." % ", ".join(nonprov))
        if len(msg) > 0:
            self.privlog.debug(" ".join(msg))

    @property
    def trace_dir(self):
        return os.path.join(Env.paths.pathvar, "scheduler")
0707010001f1b2000081a40000000000000000000000016a100daf0000001e000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/daemon/__main__.py from .main import main
main()
  0707010001f1b5000081a40000000000000000000000016a100daf00005bcb000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/daemon/dns.py  """
Listener Thread
"""
import errno
import grp
import json
import logging
import os
import pwd
import re
import select
import socket
import shutil
import sys
import time

import foreign.six as six
import daemon.shared as shared
from env import Env
from foreign.six.moves import queue
from utilities.net.ipaddress import ip_address
from utilities.storage import Storage
from utilities.naming import split_path
from utilities.string import bdecode, bencode
from utilities.lazy import lazy

PTR_SUFFIX = ".in-addr.arpa."
PTR6_SUFFIX = ".ip6.arpa."

if six.PY2:
    MAKEFILE_KWARGS = {"bufsize": 0}
else:
    MAKEFILE_KWARGS = {"buffering": None}

def record(qtype, qname, content, ttl=60):
    return {
        "qname": qname,
        "qtype": qtype,
        "content": content,
        "ttl": ttl,
    }

class Dns(shared.OsvcThread):
    name = "dns"
    sock_tmo = 1.0

    def run(self):
        self.set_tid()
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.dns"), {"node": Env.nodename, "component": self.name})
        self.wait_monitor()
        self.cache = {}
        if not os.path.exists(Env.paths.dnsuxsockd):
            os.makedirs(Env.paths.dnsuxsockd)
        try:
            if os.path.isdir(Env.paths.dnsuxsock):
                shutil.rmtree(Env.paths.dnsuxsock)
            else:
                os.unlink(Env.paths.dnsuxsock)
        except Exception:
            pass
        try:
            self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            self.sock.bind(Env.paths.dnsuxsock)
            self.sock.listen(1)
        except socket.error as exc:
            self.alert("error", "bind %s error: %s", Env.paths.dnsuxsock, exc)
            return

        self.log.info("listening on %s", Env.paths.dnsuxsock)

        sock_uid = self.get_uid()
        sock_gid = self.get_gid()
        os.chown(Env.paths.dnsuxsock, sock_uid, sock_gid)
        self.log.info("chown %s:%s %s", sock_uid, sock_gid, Env.paths.dnsuxsock)

        self.zone = "%s." % self.cluster_name.strip(".")
        self.suffix = ".%s" % self.zone
        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.error("xx %s", exc)
                import traceback
                traceback.print_stack()
                time.sleep(0.2)
            if self.stopped():
                break

        self.sock.close()
        self.exit()

    def get_gid(self):
        s = shared.NODE.oget("listener", "dns_sock_gid")
        try:
            return int(s)
        except ValueError:
            return
        try:
            info = grp.getgrnam(s)
            return info[2]
        except KeyError:
            return

    def get_uid(self):
        s = shared.NODE.oget("listener", "dns_sock_uid")
        try:
            return int(s)
        except ValueError:
            pass
        else:
            return
        try:
            info = pwd.getpwnam(s)
            return info[2]
        except KeyError:
            return

    def wait_monitor(self):
        while True:
            nmon_status = self.node_data.get(["monitor", "status"], default="init")
            if nmon_status != "init":
                break
            time.sleep(0.2)

    def cache_key(self):
        data = self.get_gen(inc=False)
        key = []
        for node in self.cluster_nodes:
            try:
                key.append(data[node])
            except KeyError:
                continue
            except AttributeError:
                break
        return tuple(key)

    def status(self, **kwargs):
        data = shared.OsvcThread.status(self, **kwargs)
        if hasattr(self, "stats"):
            data["stats"] = self.stats
        return data

    def do(self):
        if six.PY3:
            sep = b"\n"
            emp = b""
        else:
            sep = "\n"
            emp = ""

        message_queues = {}
        data = {}
        inputs = [self.sock]
        outputs = []
        closed = set()

        while inputs or outputs:
            if self.stopped():
                self.log.info("stop event received")
                break
            readable, writable, exceptional = select.select(inputs, outputs, inputs, self.sock_tmo)
            #print(
            #        "=> inputs", [s.fileno() for s in inputs], "=> outputs",  [s.fileno() for s in outputs],
            #        "=>", "readable", [s.fileno() for s in readable], "writable", [s.fileno() for s in writable], "exceptional", [s.fileno() for s in exceptional],
            #)

            if not (readable or writable or exceptional):
                self.reload_config()
                self.janitor_procs()
                self.update_status()
                continue

            for s in readable:
                if s is self.sock:
                    conn, addr = s.accept()
                    conn.setblocking(0)
                    inputs.append(conn)
                    message_queues[conn] = queue.Queue()
                    data[conn] = emp
                    #print("=> new conn", conn.fileno())
                else:
                    chunk = s.recv(1024)
                    if chunk:
                        self.stats.sessions.rx += len(chunk)
                        data[s] += chunk
                        if chunk.endswith(sep):
                            #print("=> request", s.fileno(), data[s])
                            response = self.handle(data[s])
                            #print("=> response", s.fileno(), response)
                            data[s] = emp
                            self.stats.sessions.tx += len(response)
                            message_queues[s].put(response)
                            if s not in outputs:
                                outputs.append(s)
                    else:
                        #print("=> close (no data)", s.fileno())
                        closed.add(s)
            for s in writable:
                try:
                    next_msg = message_queues[s].get_nowait()
                except queue.Empty:
                    # No messages waiting so stop checking for writability.
                    #print('=> output queue for', s.fileno(), 'is empty')
                    outputs.remove(s)
                else:
                    b = bencode(json.dumps(next_msg) + "\n")
                    #print('=> sending "%s" to %s' % (b, s.fileno()))
                    s.sendall(b)
            for s in exceptional:
                #print("=> close (exceptional)", s.fileno())
                closed.add(s)
            for s in list(closed):
                closed.remove(s)
                if s in inputs:
                    inputs.remove(s)
                if s in outputs:
                    outputs.remove(s)
                del data[s]
                del message_queues[s]
                s.close()

    def handle(self, data):
        response = {"result": False}
        if not data:
            return response

        self.log.debug("received %s", data)

        try:
            data = bdecode(data)
            data = json.loads(data)
        except Exception as exc:
            self.log.error("error parsing request", exc)
            data = None

        if data is None or not isinstance(data, dict):
            return response

        try:
            response = self.router(data)
        except Exception as exc:
            self.log.error("dns request: %s => handler error: %s", data, exc)
            response = {"error": "unexpected backend error", "result": False}
        return response

    #########################################################################
    #
    # 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_getAllDomainMetadata(self, parameters):
        if parameters.get("name") != self.zone:
            return {}
        return {
                "ALLOW-AXFR-FROM": ["0.0.0.0/0", "AUTO-NS"],
        }

    def action_getAllDomains(self, parameters):
        return [
            {
                "zone": self.zone,
            },
        ]

    def action_getDomainMetadata(self, parameters):
        """
        Example request:
        {
            "method": "getDomainMetadata",
            "parameters": {
                "kind": "ALLOW-AXFR-FROM",
                "name": "svcdevops-front.default."
            }
        }
        """
        if parameters.get("name") != self.zone:
            return []
        kind = parameters.get("kind")
        if kind == "ALLOW-AXFR-FROM":
            return ["0.0.0.0/0", "AUTO-NS"]
        return []

    def action_lookup(self, parameters):
        qtype = parameters.get("qtype").upper()
        qname = parameters.get("qname").lower()
        #zone_id = parameters.get("zone-id")

        if qtype == "SOA":
            return self.soa_record(parameters)
        if qtype == "A":
            return self.a_record(parameters)
        if qtype == "AAAA":
            return self.aaaa_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 == "PTR6":
            return self.ptr6_record(parameters)
        if qtype == "CNAME":
            return self.cname_record(parameters)
        if qtype == "NS":
            return self.ns_record(parameters)
        if qtype == "ANY":
            if PTR_SUFFIX in qname:
                return self.ptr_record(parameters)
            if PTR6_SUFFIX in qname:
                return self.ptr6_record(parameters)
            if parameters["qname"].startswith("*."):
                return []
            return self.ns_record(parameters) + \
                   self.a_record(parameters) + \
                   self.aaaa_record(parameters) + \
                   self.srv_record(parameters) + \
                   self.txt_record(parameters) + \
                   self.cname_record(parameters) + \
                   self.soa_record(parameters)
        return []

    def action_list(self, parameters):
        zonename = parameters.get("zonename").lower()
        return self._action_list(zonename)

    def lookup_pattern(self, suffix):
        data = []
        for qname, contents in self.a_records().items():
            if not qname.endswith(suffix):
                continue
            for content in contents:
                data.append(record("A", qname, content))
        return data

    def _action_list(self, suffix):
        """
        Empty suffix is what "dns dump" uses.
        """
        data = []
        if suffix == "":
            suffix = self.zone
        elif suffix != self.zone:
            return []

        data += self.soa_record({"qname": suffix})
        data += self.zone_ns_records(suffix)

        for qname, contents in self.a_records().items():
            if suffix and not qname.endswith(suffix):
                continue
            for content in contents:
                qtype = "AAAA" if ":" in content else "A"
                data.append(record(qtype, qname, content))
        for qname, contents in self.srv_records().items():
            if suffix and not qname.endswith(suffix):
                continue
            for content in contents:
                data.append(record("SRV", qname, content))
        for qname, contents in self.ptr_records().items():
            if suffix and not qname.endswith(suffix):
                continue
            for content in contents:
                qtype = "PTR6" if ":" in qname else "PTR"
                data.append(record(qtype, qname, content))
        return data

    def remove_suffix(self, qname):
        return qname[:-self.suffix_len]

    @lazy
    def origin(self):
        return "dns.%s" % self.zone

    @lazy
    def contact(self):
        return "contact@opensvc.com"

    def ns_record(self, parameters):
        qname = parameters.get("qname").lower()
        if qname != self.zone:
            return []
        return self.zone_ns_records(self.zone)

    def zone_ns_records(self, zonename):
        data = []
        for i, dns in enumerate(shared.NODE.dns):
            content = "ns%d.%s" % (i, zonename)
            data.append(record("NS", zonename, content, ttl=3600))
        return data

    def soa_records(self):
        return [self.zone]

    def soa_records_rev(self):
        addrs = set([PTR_SUFFIX[1:]])
        for addr in set(self.svc_ips()):
            z1 = ".".join(reversed(addr.split(".")[:-1]))+PTR_SUFFIX
            z2 = ".".join(reversed(addr.split(".")[:-2]))+PTR_SUFFIX
            z3 = ".".join(reversed(addr.split(".")[:-3]))+PTR_SUFFIX
            addrs.add(z1)
            addrs.add(z2)
            addrs.add(z3)
        return addrs

    def soa_record(self, parameters):
        qname = parameters.get("qname").lower()
        if qname.endswith(PTR_SUFFIX):
            if qname not in self.soa_records_rev():
                return []
        elif qname != self.zone:
            return []
        return [record("SOA", qname, self.soa_content)]

    def cname_record(self, parameters):
        return []

    def txt_record(self, parameters):
        return []

    def srv_record(self, parameters):
        qname = parameters.get("qname").lower()
        if not qname.endswith(self.suffix):
            return []
        return [record("SRV", qname, content) for content in self.srv_records().get(qname, [])]

    def ptr_record(self, parameters):
        qname = parameters.get("qname").lower()
        return [record("PTR", qname, name) for name in self.ptr_records().get(qname, []) if "." in name]

    def ptr6_record(self, parameters):
        qname = parameters.get("qname").lower()
        return [record("PTR6", qname, name) for name in self.ptr_records().get(qname, []) if ":" in name]

    def a_record(self, parameters):
        qname = parameters.get("qname").lower()
        if not qname.endswith(self.suffix):
            return []
        return [record("A", qname, addr) for addr in self.a_records().get(qname, []) if "." in addr]

    def aaaa_record(self, parameters):
        qname = parameters.get("qname").lower()
        if not qname.endswith(self.suffix):
            return []
        return [record("AAAA", qname, addr) for addr in self.a_records().get(qname, []) if ":" in addr]

    def ptr_records(self):
        data = self.get_cache("ptr")
        if data is not None:
            return data
        names = {}
        key = self.cache_key()
        for path, nodename, status in self.iter_services_instances():
            name, namespace, kind = split_path(path)
            if kind != "svc":
                continue
            if not namespace:
                namespace = "root"
            for rid, resource in status.get("resources", {}).items():
                addr = resource.get("info", {}).get("ipaddr")
                if addr is None:
                    continue
                qname = ip_address(addr).reverse_pointer
                if qname not in names:
                    names[qname] = []
                try:
                    hostname = resource.get("info", {}).get("hostname").split(".")[0].lower()
                except Exception:
                    hostname = None
                gen_name = "%s.%s.%s.%s." % (name, namespace, kind, self.cluster_name)
                gen_name = gen_name.lower()
                if hostname and hostname != name:
                    target = "%s.%s" % (hostname, gen_name)
                else:
                    target = "%s" % gen_name
                if target in names[qname]:
                    continue
                names[qname].append(target)
        self.set_cache(key, "ptr", names)
        return names

    def set_cache(self, key, kind, data):
        if key not in self.cache:
            self.cache = {}
        self.cache[key] = {kind: data}

    def get_cache(self, kind):
        key = self.cache_key()
        if key not in self.cache:
            self.cache = {}
            return
        if kind not in self.cache[key]:
            return
        return self.cache[key].get(kind)

    @staticmethod
    def unique_name(addr):
        return addr.replace(".", "-").replace(":", "-")

    def a_records(self):
        data = self.get_cache("a")
        if data is not None:
            return data
        names = {}
        key = self.cache_key()
        for path, nodename, status in self.iter_services_instances():
            name, namespace, kind = split_path(path)
            if kind != "svc":
                continue
            if namespace:
                namespace = namespace.lower()
            else:
                namespace = "root"
            scaler_slave = status.get("scaler_slave")
            if scaler_slave:
                _name = name[name.index(".")+1:]
            else:
                _name = name

            zone = "%s.%s.%s." % (namespace, kind, self.cluster_name)
            qname = "%s.%s" % (_name, zone)
            if qname not in names:
                names[qname] = set()

            local_zone = "%s.%s.%s.node.%s." % (namespace, kind, nodename, self.cluster_name)
            local_qname = "%s.%s" % (_name, local_zone)
            if local_qname not in names:
                names[local_qname] = set()

            for rid, resource in status.get("resources", {}).items():
                addr = resource.get("info", {}).get("ipaddr")
                if addr is None:
                    continue
                hostname = resource.get("info", {}).get("hostname")
                names[qname].add(addr)
                names[local_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.split(".")[0] + "." + qname
                    if name not in names:
                        names[name] = set()
                    names[name].add(addr)
        names.update(self.dns_a_records())
        self.set_cache(key, "a", names)
        return names

    def dns_a_records(self):
        names = {}
        for i, ip in enumerate(shared.NODE.dns):
            dns = "ns%d.%s." % (i, self.cluster_name)
            names[dns] = set([ip])
        return names

    def srv_records(self):
        data = self.get_cache("srv")
        if data is not None:
            return data
        names = {}
        key = self.cache_key()
        for path, nodename, status in self.iter_services_instances():
            weight = self.daemon_status_data.get(["monitor", "nodes", nodename, "stats", "score"], default=10)
            name, namespace, kind = split_path(path)
            if kind != "svc":
                continue
            if namespace:
                namespace = namespace.lower()
            else:
                namespace = "root"
            scaler_slave = status.get("scaler_slave")
            if scaler_slave:
                _name = name[name.index(".")+1:]
            else:
                _name = name
            for rid, resource in status.get("resources", {}).items():
                addr = resource.get("info", {}).get("ipaddr")
                if addr is None:
                    continue
                for expose in resource.get("info", {}).get("expose", []):
                    if "#" in expose:
                        # expose data by reference
                        expose_data = status.get("resources", {}).get(expose, {}).get("info")

                        try:
                            port = expose_data["port"]
                            proto = expose_data["protocol"]
                        except (KeyError, TypeError):
                            continue
                    else:
                        # expose data inline
                        try:
                            port, proto = re.split("[/-]", expose.split(":")[0])
                            port = int(port)
                        except Exception as exc:
                            continue
                    qnames = set()
                    qnames.add("_%s._%s.%s.%s.%s.%s." % (str(port), proto, _name, namespace, kind, self.cluster_name))
                    try:
                        serv = socket.getservbyport(port)
                        qnames.add("_%s._%s.%s.%s.%s.%s." % (serv, proto, _name, namespace, kind, self.cluster_name))
                    except (socket.error, OSError) as exc:
                        # port/proto not found
                        pass
                    except Exception as exc:
                        self.log.warning("port %d resolution failed: %s", port, exc)
                    target = "%s.%s.%s.%s.%s." % (self.unique_name(addr), _name, namespace, kind, self.cluster_name)
                    content = "%(prio)d %(weight)d %(port)d %(target)s" % {
                        "prio": 0,
                        "weight": weight,
                        "port": port,
                        "target": target,
                    }
                    for qname in qnames:
                        if qname not in names:
                            names[qname] = set()
                        uend = " %d %s" % (port, target)
                        if any([True for c in names[qname] if c.endswith(uend)]):
                            # avoid multiple SRV entries pointing to the same ip:port
                            continue
                        names[qname].add(content)
        self.set_cache(key, "srv", names)
        return names

    def svc_ips(self):
        addrs = []
        for path, nodename, status in self.iter_services_instances():
            for rid, resource in status.get("resources", {}).items():
                addr = resource.get("info", {}).get("ipaddr")
                if addr is None:
                    continue
                addrs.append(addr)
        return addrs


 0707010001f1b4000081a40000000000000000000000016a100daf0000600f000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/daemon/collector.py    """
Collector Thread
"""
import glob
import json
import os
import logging
import time
from copy import deepcopy

import daemon.shared as shared
import core.oc3path as oc3path
from env import Env
from utilities.lazy import lazy, unset_lazy
from utilities.naming import svc_pathvar, split_path
from utilities.semver import Semver

MAX_QUEUED = 1000
RESCAN_OC3_VERSION_INTERVAL = 24 * 60 * 60
REPLAY_OC3_INTERVAL = 5 * 60


class Collector(shared.OsvcThread):
    name = "collector"
    last_oc3_replay = time.time() - REPLAY_OC3_INTERVAL

    def reset(self):
        self.last_comm = None
        self.last_config = {}
        self.last_status = {}
        self.last_status_changed = set()

        # force oc3 replay on collector startup or reset
        self.last_oc3_replay = time.time() - REPLAY_OC3_INTERVAL

    def run(self):
        self.set_tid()
        self.log = logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd.collector"), {"node": Env.nodename, "component": self.name})
        self.log.info("collector started")
        previous_interval_signature = ""
        previous_oc3_version = shared.NODE.oc3_version()
        last_scan_oc3 = time.time()
        self.reset()
        self.log.info("collector oc3 detected version: %s", self.oc3_version)

        while True:
            if self.stopped():
                self.exit()
            try:

                if time.time() - last_scan_oc3 > RESCAN_OC3_VERSION_INTERVAL:
                    unset_lazy(self, "oc3_version")
                if previous_oc3_version != self.oc3_version:
                    self.log.info("collector oc3 refresh version file %s: %s -> %s",
                                  Env.paths.oc3_version, previous_oc3_version, self.oc3_version)
                    previous_oc3_version = self.oc3_version
                    with open(Env.paths.oc3_version, 'w') as f:
                        json.dump({"version": str(self.oc3_version)}, f)

                self.do()
                self.update_status()
                interval_signature = "%d-%d-%d" % (self.db_update_interval, self.db_min_update_interval, self.db_min_ping_interval)
                if interval_signature != previous_interval_signature:
                    self.log.info("collector thread config: update_interval=%d, min_update_interval=%d, min_ping_interval=%d",
                                  self.db_update_interval, self.db_min_update_interval, self.db_min_ping_interval)
                    previous_interval_signature = interval_signature
            except Exception as exc:
                self.log.exception(exc)
                time.sleep(1)

    def get_last_status(self, data):
        """
        Identify changes in data
        """
        last_status = {}
        last_status_changed = set()

        def add_parents(_path, done=None):
            """
            Propagate change to the service parents
            """
            if done is None:
                done = []
            if _path in done:
                # break recursion loop
                return
            for nodename, ndata in data["nodes"].items():
                for path, sdata in ndata.get("services", {}).get("status", {}).items():
                    slaves = sdata.get("slaves", []) + sdata.get("scaler_slaves", [])
                    if _path not in slaves:
                        continue
                    if path in last_status_changed:
                        continue
                    last_status_changed.add(path)
                    add_parents(path, done=done+[_path])

        for path, nodename in self.last_status:
            if path is None:
                # node disappeared
                if data["nodes"].get(nodename) is None:
                    last_status_changed.add("@"+nodename)
            else:
                # instance disappeared
                if data["nodes"].get(nodename, {}).get("services", {}).get("status", {}).get(path) is None:
                    last_status_changed |= set([path, path+"@"+nodename])


        def hb_state_signature(node_hb_data):
            return " ".join(
                [
                    ",".join(
                        [
                            "%s:%s:%s:%s" % (hb_id, hb_data.get("state", ""), peer_name, peer_data.get("beating", ""))
                            for peer_name, peer_data in hb_data.get("peers", {}).items()
                        ])
                    for hb_id, hb_data in node_hb_data.items()
                ]
            )

        for nodename, ndata in data["nodes"].items():
            # detect node frozen, or hb changes
            node_frozen = ndata.get("frozen")
            node_hb = hb_state_signature(ndata.get("hb", {}))
            last_node_frozen = self.last_status.get((None, nodename), {"frozen": 0}).get("frozen", 0)
            last_node_hb = self.last_status.get((None, nodename), {"hb": ""}).get("hb", "")
            if node_frozen != last_node_frozen or node_hb != last_node_hb:
                last_status_changed.add("@"+nodename)
            last_status[(None, nodename)] = {"frozen": node_frozen, "hb": node_hb}

            # detect instances status changes
            for path, sdata in ndata.get("services", {}).get("status", {}).items():
                status_csum = sdata.get("csum", "") + \
                    str(sdata.get("monitor", {}).get("status_updated")) + \
                    str(sdata.get("monitor", {}).get("global_status_updated"))
                prev_status_csum = self.last_status.get((path, nodename))
                if status_csum != prev_status_csum:
                    last_status_changed.add(path+"@"+nodename)
                    if path not in last_status_changed:
                        last_status_changed.add(path)
                        add_parents(path)
                last_status[(path, nodename)] = status_csum

        return last_status, last_status_changed

    def config_sent_file(self, path):
        return os.path.join(svc_pathvar(path), "config_sent.json")

    def load_config_sent(self, path):
        p = self.config_sent_file(path)
        with open(p, "r") as f:
            return json.load(f)

    def dump_config_sent(self, path, csum):
        p = self.config_sent_file(path)
        with open(p, "w") as f:
            json.dump({
                "sent": time.time(),
                "csum": csum,
            }, f)

    def purge_configs_sent(self, paths):
        for p in paths:
            self.purge_config_sent(p)

    def purge_config_sent(self, path):
        self.log.info("purging sent config %s", path)
        p = self.config_sent_file(path)
        try:
            os.unlink(p)
        except:
            pass
        try:
            del self.last_config[path]
        except KeyError:
            pass

    def changed_config(self, data):
        """
        returns {"<path>": "csum", ...} where path is defined into daemondata nodes.<localhost>.services.config
        and where the config csum differ from the value detected during last changed_config function call.

        It uses config cache: self.last_config (dict {"<path>": "csum", ...}) to detect changes since last call.
        self.last_config is refreshed on each call.

        Already sent path:csum is not expected to be returned again, so during daemon startup, so we populate the
        self.last_config from the object's config_sent.json file. This file is updated when send_service_config succeed,
        so if send_service_config fails, same path:csum will be returned again during next daemon startup.

        To force a path:csum to be returned again, we have to remove its object's config_sent.json file and delete the
        live cache value self.last_config[path].
        """
        last_config = {}
        last_config_changed = {}
        for path, sdata in data["nodes"].get(Env.nodename, {}).get("services", {}).get("config", {}).items():
            _, _, kind = split_path(path)
            if kind in ("sec", "cfg", "ccfg", "usr"):
                # the collector drops updates for these kinds, so save the calls
                continue
            if path not in self.last_config:
                # first time we see this path, try populating our cache from the object's config_sent.json
                try:
                    self.last_config[path] = self.load_config_sent(path)["csum"]
                except Exception as exc:
                    self.last_config[path] = "force_send"
            config_csum = sdata.get("csum", 0)
            prev_config_csum = self.last_config.get(path, 0)
            if prev_config_csum and config_csum != prev_config_csum:
                # don't send all configs on daemon start
                last_config_changed[path] = config_csum
            last_config[path] = config_csum
        self.last_config = last_config
        return last_config_changed

    def init_collector(self):
        if shared.NODE.collector.reinit():
            self.log.info("the collector is reachable")
            self.reset()

    def do(self):
        self.reload_config()
        self.init_collector()
        if shared.NODE.collector_env.uuid == "":
            # don't even queue
            pass
        elif shared.NODE.collector.disabled():
            self.queue_limit()
        else:
            self.run_collector()
            self.unqueue_xmlrpc()
            self.oc3_replay()
        if not self.stopped():
            with shared.COLLECTOR_TICKER:
                shared.COLLECTOR_TICKER.wait(self.db_update_interval)

    def queue_limit(self):
        overlimit = len(shared.COLLECTOR_XMLRPC_QUEUE) - MAX_QUEUED
        if overlimit > 0:
            #self.log.warning("drop %d queued messages", overlimit)
            for _ in range(overlimit):
                shared.COLLECTOR_XMLRPC_QUEUE.pop()

    def unqueue_xmlrpc(self):
        while True:
            if self.stopped():
                return
            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.error("call %s: %s", args[0], exc)
                time.sleep(0.2)
                shared.NODE.collector.disable()

    def send_containerinfo(self, path):
        if self.stopped():
            return

        with shared.SERVICES_LOCK:
            if path not in shared.SERVICES:
                return
            service = shared.SERVICES[path]
            if not service.has_encap_resources:
                return
            containers = [container.send_containerinfo_arg()
                          for container in service.get_resources('container')]

        if len(containers) == 0:
            return

        self.log.info("send service %s container info", path)
        try:
            shared.NODE.collector.call("push_containerinfo", path, containers)
        except Exception as exc:
            self.log.error("call push_containerinfo %s: %s", path, exc)
            shared.NODE.collector.disable()

    def send_service_config(self, path, csum):
        if self.stopped():
            return

        if self.oc3_version >= Semver(1, 0, 3):
            with shared.SERVICES_LOCK:
                if path not in shared.SERVICES:
                    return
                try:
                    data = shared.SERVICES[path].oc3_object_config_body()
                except Exception as err:
                    self.log.info("skip send service %s config: %s",path, str(err))
                    return
        else:
            with shared.SERVICES_LOCK:
                if path not in shared.SERVICES:
                    return
                data = shared.SERVICES[path].send_service_config_args()

        sent = False
        try:
            if self.oc3_version >= Semver(1, 0, 3):
                begin = time.time()
                api_verb = "POST"
                api_path = oc3path.FEED_OBJECT_CONFIG
                headers = {"Accept": "application/json", "Content-Type": "application/json"}
                self.log.info("%s %s object config %s", api_verb, api_path, path)
                status_code, _ = shared.NODE.oc3_request_feed(api_verb, api_path, data=data, headers=headers)
                if status_code != 202:
                    self.log.warning("%s %s unexpected status code %d for object %s completed in %0.3f",
                                     api_verb, api_path, status_code, path, time.time() - begin)
                else:
                    sent = True
            else:
                self.log.info("send service %s config", path)
                shared.NODE.collector.call("push_config", data)
                sent = True
        except Exception as exc:
            self.log.error("call push_config %s: %s", path, exc)
            shared.NODE.collector.disable()
            sent = False

        if sent:
            try:
                self.dump_config_sent(path, csum)
            except Exception as exc:
                self.log.warning("writing config sent persistent cache file: %s", exc)

    def send_daemon_status(self, data):
        if self.last_status_changed:
            self.log.debug("send daemon status, %d changes", len(self.last_status_changed))
        else:
            self.log.debug("send daemon status, resync")
        try:
            if self.oc3_version >= Semver(1, 0, 4):
                begin = time.time()
                api_verb = "POST"
                api_path = oc3path.FEED_DAEMON_STATUS
                body = {
                    "version": "2.1",
                    "data": data,
                    "changes": list(self.last_status_changed)
                }
                status_code, response_body = shared.NODE.oc3_request_feed(api_verb, api_path, data=body)
                if status_code != 202:
                    self.log.warning("collector %s %s unexpected status code %d %0.3f", api_verb, api_path, status_code, time.time() - begin)
                else:
                    self.log.debug("collector %s %s status code %d %0.3f", api_verb, api_path, status_code, time.time() - begin)
                    object_without_config = response_body.get("object_without_config", [])
                    if len(object_without_config) > 0:
                        # purge configs sent of object_without_config
                        # => next run will send service config to collector
                        self.purge_configs_sent(object_without_config)
            else:
                shared.NODE.collector.call("push_daemon_status", data, list(self.last_status_changed))
        except Exception as exc:
            self.log.error("call push_daemon_status: %s", exc)
            shared.NODE.collector.disable()
        self.last_comm = time.time()

    def ping(self, data):
        if self.stopped():
            return
        self.log.debug("ping the collector")
        try:
            if self.oc3_version >= Semver(1, 0, 4):
                begin = time.time()
                api_verb = "POST"
                api_path = oc3path.FEED_DAEMON_PING
                body = {
                    "version": "2.1",
                    "nodes": list(data.get("nodes", {}).keys()),
                    "objects": list(data.get("services", {}).keys()),
                }
                self.log.debug("%s %s %s", api_verb, api_path, body)
                status_code, response_body = shared.NODE.oc3_request_feed(api_verb, api_path, data=body)
                self.log.debug("%s %s %d %0.3f", api_verb, api_path, status_code, time.time() - begin)
                if status_code == 202:
                    object_without_config = response_body.get("object_without_config", [])
                    if len(object_without_config) > 0:
                        # purge configs sent of object_without_config
                        # => next run will send service config to collector
                        self.purge_configs_sent(object_without_config)
                elif status_code == 204:
                    self.log.debug("ping rejected, collector ask for resync")
                    self.send_daemon_status(data)
                else:
                    self.log.warning("%s %s unexpected status code %d completed in %0.3f", api_verb, api_path, status_code, time.time() - begin)
            else:
                result = shared.NODE.collector.call("daemon_ping")
                if result and result.get("info") == "resync":
                    self.log.info("ping rejected, collector ask for resync")
                    self.send_daemon_status(data)
        except Exception as exc:
            self.log.error("call daemon_ping: %s", exc)
            shared.NODE.collector.disable()
        self.last_comm = time.time()

    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 = self.daemon_status_data.get_copy(["monitor"])
        _data = {
            "cluster_id": self.cluster_id,
            "cluster_name": self.cluster_name,
            "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"]
                node_frozen = data["nodes"][nodename]["frozen"]
            except (TypeError, KeyError):
                continue
            if instances_status is None:
                continue
            if instances_config is None:
                continue
            for path in list(instances_status.keys()):
                if path not in instances_config:
                    # deleted object instance
                    continue
                if path not in data["services"]:
                    # deleted object
                    continue
                if not instances_status[path]:
                    continue
                if instances_status[path].get("encap") is True:
                    continue
                if nodename not in _data["nodes"]:
                    _data["nodes"][nodename] = {
                        "services": {
                            "config": {},
                            "status": {},
                        },
                    }
                _data["nodes"][nodename]["frozen"] = node_frozen
                _data["nodes"][nodename]["services"]["status"][path] = instances_status[path]
                _data["nodes"][nodename]["services"]["config"][path] = instances_config[path]
                _data["nodes"][nodename]["hb"] = deepcopy(data["nodes"][nodename]["hb"])
                _data["services"][path] = data["services"][path]
        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

        # TODO: move send service config to speaker block, this require first:
        #  1- send_service_config should support non local objects.
        #  2- changed_config should also return the non local objects.
        #  3- send_containerinfo should be removed (its tasks should be done
        #     during send_daemon_status) should be sufficient
        #  This will allow speaker to use the "object_without_config" oc3 response
        #  and force collector object config deleted to be pushed again.
        #  Until this, we can only fix a collector object config deleted that exists
        #  on the collector speaker.
        changed_config = self.changed_config(data)
        for path, csum in changed_config.items():
            self.send_service_config(path, csum)
            self.send_containerinfo(path)

        if self.speaker():
            last_status, last_status_changed = self.get_last_status(data)
            now = time.time()
            self.last_status_changed |= last_status_changed
            if self.last_comm is None:
                self.send_daemon_status(data)
            elif self.last_status_changed:
                if self.last_comm <= now - self.db_min_update_interval:
                    self.send_daemon_status(data)
                    self.last_status_changed = set()
                else:
                    # avoid storming the collector with daemon status updates
                    pass
            elif self.last_comm <= now - self.db_min_ping_interval:
                self.ping(data)
            self.last_status = last_status

    def oc3_replay(self):
        if time.time() - self.last_oc3_replay < REPLAY_OC3_INTERVAL:
            return
        elif self.oc3_version < Semver(1, 0, 11):
            self.last_oc3_replay = time.time()
            return

        self.last_oc3_replay = time.time()

        replay_dir = os.path.join(Env.paths.pathtmpv, "oc3_replay")
        os.makedirs(replay_dir, exist_ok=True)

        for f in glob.glob(os.path.join(replay_dir, "oc3_feed_instance_action_*.json")):
            self.log.debug("replaying %s", f)
            try:
                data = json.load(open(f))
                path = data.get("path", "")
                if path == "":
                    self.log.debug("replaying %s: unexpected empty path found", f)
                    continue
                begin = time.time()
                api_verb = "PUT"
                api_path = oc3path.FEED_INSTANCE_ACTION
                headers = {"Accept": "application/json", "Content-Type": "application/json"}

                self.log.debug("replay: %s %s instance action %s", api_verb, api_path, path)
                status_code, _ = shared.NODE.oc3_request_feed(api_verb, api_path, data=data, headers=headers, timeout=2)
                if status_code in [202, 400]:
                    self.log.debug("replay: %s %s status code %d for object %s completed in %0.3f",
                                  api_verb, api_path, status_code, path, time.time() - begin)
                    try:
                        os.unlink(f)
                    except Exception as exc:
                        self.log.debug("replaying %s unable to remove %s",f, str(exc))
                else:
                    self.log.debug("replay: %s %s unexpected status code %d for object %s completed in %0.3f",
                                   api_verb, api_path, status_code, path, time.time() - begin)
            except Exception as exc:
                self.log.debug("replaying %s failed: %s", f, str(exc))

    @lazy
    def oc3_version(self):
        """
        returns the oc3 feeder api version

        returned values:
            status code 200 => version from body
            status code 404 => null version 0.0.0
            else fallback to previous cached version value (that may be version 0.0.0 if no previous cache)
        """
        null_version = Semver(0, 0, 0)
        version = shared.NODE.oc3_version()
        api_verb = "GET"
        api_path = oc3path.FEED_VERSION
        try:
            if not shared.NODE.collector_env.feeder:
                return null_version
            status_code, schema = shared.NODE.oc3_request_feed(api_verb, api_path)
            if status_code == 200:
                if isinstance(schema, dict):
                    s = schema.get("version", "0.0.0")
                    version = Semver.parse(s)
            elif status_code in [404]:
                if version != Semver():
                    # collector has no anymore oc3 configured, reset to null
                    self.log.warning("oc3 version skip cache (%s %s http status code %d)", api_verb, api_path, status_code)
                version = null_version
            else:
                # 502 Bad Gateway, 503 Service Unavailable: oc3 is not yet ready
                self.log.warning("oc3 version preserve cache (%s %s http status code %d)", api_verb, api_path, status_code)
        except Exception as err:
            if version > null_version:
                self.log.warning("oc3 version preserve cache (%s %s error: %s)", api_verb, api_path, str(err))
        return version
 0707010001f278000081a40000000000000000000000016a100daf000050b7000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/daemon/main.py """
The opensvc daemon.
"""
from __future__ import print_function

import logging
import os
import sys
import threading
import time
from optparse import OptionParser

import daemon.shared as shared
import core.exceptions as ex
import core.logger
from core.capabilities import capabilities
from core.comm import CRYPTO_MODULE
from utilities.lock import LockTimeout, cmlock
from env import Env
from utilities.proc import daemon_process_running, process_args
from .hb.disk import HbDiskRx, HbDiskTx
from .hb.mcast import HbMcastRx, HbMcastTx
from .hb.relay import HbRelayRx, HbRelayTx
from .hb.ucast import HbUcastRx, HbUcastTx
from .collector import Collector
from .dns import Dns
from .listener import Listener
from .monitor import Monitor
from .scheduler import Scheduler
from core.node import Node
from utilities.lazy import lazy, unset_lazy

# node monitor status where start_threads is allowed
START_THREADS_ALLOWED_NMON_STATUS = (None, "idle", "init", "rejoin", "draining")

# track daemon pids for osvcd pid check
parent_pids = []

try:
    from utilities.os.linux import set_tname
except (ImportError, OSError):
    pass
else:
    if hasattr(threading.Thread, '_bootstrap'):
        def _bootstrap_named_thread(self):
            set_tname(self.name)
            threading.Thread._bootstrap_original(self)

        threading.Thread._bootstrap_original = threading.Thread._bootstrap
        threading.Thread._bootstrap = _bootstrap_named_thread

DAEMON_TICKER = threading.Condition()
DAEMON_INTERVAL = 2
STATS_INTERVAL = 1

HEARTBEATS = (
    ("multicast", HbMcastTx, HbMcastRx),
    ("unicast", HbUcastTx, HbUcastRx),
    ("disk", HbDiskTx, HbDiskRx),
    ("relay", HbRelayTx, HbRelayRx),
)

def printstack(sig, frame):
    try:
        import faulthandler
    except ImportError:
        return
    try:
        faulthandler.dump_traceback()
        with open(os.path.join(Env.paths.pathvar, "daemon.stack"), "w") as f:
            faulthandler.dump_traceback(file=f)
    except Exception:
        pass

try:
    import signal
    signal.signal(signal.SIGUSR1, printstack)
except ImportError:
    pass

def fork(func, args=None, kwargs=None):
    """
    A fork daemonizing function.
    """
    if args is None:
        args = []
    if kwargs is None:
        kwargs = {}

    # main pid
    parent_pids.append(os.getpid())

    if os.fork() > 0:
        # return to parent execution
        os._exit(0)

    # fork1 pid
    parent_pids.append(os.getpid())

    # 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)

    # fork2 pid
    parent_pids.append(os.getpid())

    # Add delay to ensure process main and fork1 processes exits (that had same args as
    # opensvc daemon process) before we check daemon pid processes.
    # This prevents daemon start abort on <detect running daemon process (from parents main or fork1 during boot)>
    # PID  DESC
    # i    main => fork() <defunct> level 1: 7966
    # i+1    fork1 => fork() <defunct> level 2: 7970
    # i+2      fork2 => read <i> from pidfile, check if <i> is alive => abort level 3: 7971
    #
    # reproducer ()
    # {
    #     echo om daemon stop ...;
    #     om daemon stop;
    #     typeset -i add=$1;
    #     typeset -i last_pid=$(set -- $(ps -o pid=); shift $(($#-1)); echo $1);
    #     typeset -i pid=add+last_pid;
    #     echo "set /var/lib/opensvc/osvcd.pid: $pid (from last_pid $last_pid + $add)";
    #     printf $pid > /var/lib/opensvc/osvcd.pid;
    #     om daemon start;
    #     sleep 1;
    #     echo "got /var/lib/opensvc/osvcd.pid: $(cat /var/lib/opensvc/osvcd.pid)"
    # }
    #
    # reproducer 21
    # detect running daemon process from /var/lib/opensvc/osvcd.pid: 7966 match parent pids [7966, 7970, 7971] (our pid/ppid is 7971/1)
    # reproducer 26
    # detect running daemon process from /var/lib/opensvc/osvcd.pid: 7971 match parent pids [7966, 7970, 7971] (our pid/ppid is 7971/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
        self.pid = os.getpid()
        self.stats_data = None
        self.last_stats_refresh = 0
        self.init_data()

    def init_data(self):
        initial_data = {
            "monitor": {
                "nodes": {},
                "services": {},
            }
        }
        if hasattr(threading, "get_ident"):
            initial_data["daemon"] = {"ident": threading.get_ident(), "state": "running"}  # pylint: disable=no-member
        shared.DAEMON_STATUS.set([], initial_data)

    @lazy
    def log(self):
        log_file = os.path.join(Env.paths.pathlog, "node.log")
        core.logger.initLogger(Env.nodename, log_file, handlers=self.handlers, sid=False)
        return logging.LoggerAdapter(logging.getLogger(Env.nodename+".osvcd"),
                                     {"node": Env.nodename, "component": "main"})

    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.pid = os.getpid()
        self._run()

    def set_last_shutdown(self):
        with open(Env.paths.last_shutdown, "w") as filep:
            filep.write("")
            # Do our best to have file sync on file system
            if hasattr(os, "fsync"):
                filep.flush()
                os.fsync(filep.fileno())

    def _run(self):
        """
        Acquire the osvcd lock, write the pid in a system-compatible pidfile,
        and start the daemon loop.
        """

        try:
            with cmlock(lockfile=Env.paths.daemon_lock, timeout=1, delay=1):
                if self._already_running():
                    self.log.error("abort start: a daemon process is already running")
                    sys.exit(1)
                self.write_pid()
        except LockTimeout:
            self.log.error("abort start: a daemon is already running, and holding the daemon lock")
            sys.exit(1)
        try:
            self.loop_forever()
        finally:
            self.set_last_shutdown()
            pass

    def _already_running(self):
        if daemon_process_running():
            try:
                with open(Env.paths.daemon_pid, "r") as pid_file:
                    last_pid_trace = pid_file.read()
            except:
                self.log.error("another daemon process detected, but file error on %s" % Env.paths.daemon_pid)
                return True
            if int(last_pid_trace) in parent_pids:
                self.log.debug("detect running daemon process from %s: %s match parent pids %s (our pid/ppid is %d/%d)" % (Env.paths.daemon_pid,  last_pid_trace, parent_pids, os.getpid(), os.getppid()))
                return False
            elif last_pid_trace == str(self.pid):
                return False
            else:
                self.log.error("detect running daemon process from %s: %s (our pid/ppid is %d/%d)" % (Env.paths.daemon_pid, last_pid_trace, os.getpid(), os.getppid()))
                return True
        else:
            return False

    def write_pid(self):
        pid = str(self.pid)
        with open(Env.paths.daemon_pid, "w") as ofile:
            ofile.write(pid)
        _, pid_args = process_args(self.pid)
        with open(Env.paths.daemon_pid_args, "w") as ofile:
            ofile.write(pid_args)

    def init(self):
        shared.NODE = Node(log_handlers=self.handlers)
        self.log.info("daemon started")
        self.log.info("versions:")
        self.log.info(" opensvc agent: %s", shared.NODE.agent_version)
        self.log.info(" opensvc api:   %s", shared.API_VERSION)
        self.log.info(" python:        %s", sys.version.split()[0])
        self.log.info(" crypto module: %s", CRYPTO_MODULE)
        caps = capabilities.scan(node=shared.NODE)
        caps = capabilities.as_list(caps)
        self.log.info("%d capabilities:", len(caps))
        for cap in caps:
            self.log.info(" %s", cap)

    def loop_forever(self):
        """
        Loop over the daemon tasks until notified to stop.
        """
        self.init()
        while self.loop():
            with DAEMON_TICKER:
                DAEMON_TICKER.wait(DAEMON_INTERVAL)
        self.log.info("daemon graceful stop")

    def loop(self):
        if shared.DAEMON_STOP.is_set():
            self.stop_threads()
            return False
        self.start_threads()
        return True

    def stats(self):
        now = time.time()
        if self.stats_data and now - self.last_stats_refresh < STATS_INTERVAL:
            return self.stats_data
        self.stats_data = {
            "cpu": self.cpu_stats(),
            "mem": self.mem_stats(),
        }
        self.last_stats_refresh = now
        return self.stats_data

    def cpu_stats(self):
        data = {}
        try:
            pid_cpu_time = shared.NODE.pid_cpu_time(self.pid)
        except Exception as exc:
            pid_cpu_time = 0.0
        return {
            "time": pid_cpu_time,
        }

    def mem_stats(self):
        data = {}
        try:
            mem_total = shared.NODE.pid_mem_total(self.pid)
        except Exception as exc:
            mem_total = 0.0
        return {
            "total": shared.NODE.pid_mem_total(self.pid),
        }

    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.
        """
        try:
            thr = self.threads[thr_id]
        except KeyError:
            return True
        if thr.stopped():
            return False
        if thr.is_alive():
            return False
        return True

    def start_thread(self, key, obj, name=None):
        try:
            if name is None:
                self.threads[key] = obj()
            else:
                self.threads[key] = obj(name)
            self.threads[key].start()
            return True
        except RuntimeError as exc:
            self.log.warning("failed to start a %s thread: %s", key, exc)
            return False

    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.
        """
        try:
            nmon_status = shared.DAEMON_STATUS.get(["monitor", "nodes", Env.nodename, "monitor", "status"])
        except (KeyError, TypeError):
            nmon_status = None
        if nmon_status not in START_THREADS_ALLOWED_NMON_STATUS:
            return

        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"):
            changed |= self.start_thread("dns", Dns)
        if self.need_start("listener"):
            changed |= self.start_thread("listener", Listener)
        if self._need_collector() and self.need_start("collector"):
            changed |= self.start_thread("collector", Collector)
        if self.need_start("monitor"):
            changed |= self.start_thread("monitor", Monitor)
        if self.need_start("scheduler"):
            changed |= self.start_thread("scheduler", Scheduler)

        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):
                    changed |= self.start_thread(hb_id, rxc, name)
                hb_id = name + ".tx"
                if self.need_start(hb_id):
                    changed |= self.start_thread(hb_id, txc, name)

        if config_changed:
            # clean up deleted heartbeats
            thr_ids = list(self.threads.keys())
            for thr_id in thr_ids:
                if not thr_id.startswith("hb#"):
                    continue
                name = thr_id.replace(".tx", "").replace(".rx", "")
                if self.hb_enabled(name):
                    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]
                shared.DAEMON_STATUS.unset_safe([thr_id])
                changed = True

            # clean up collector thread no longer needed
            if not self._need_collector() and "collector" in self.threads:
                self.log.info("stopping collector thread, no longer needed")
                self.threads["collector"].stop()
                self.threads["collector"].join()
                del self.threads["collector"]
                shared.DAEMON_STATUS.unset_safe(["collector"])
                changed = True

        if changed:
            with shared.THREADS_LOCK:
                shared.THREADS = self.threads

    @staticmethod
    def _need_collector():
        return (shared.NODE and
                (shared.NODE.collector_env.dbopensvc or shared.NODE.collector_env.feeder) and
                shared.NODE.collector_env.uuid)

    def init_nodeconf(self):
        if not os.path.exists(Env.paths.pathetc):
            self.log.info("create dir %s", Env.paths.pathetc)
            os.makedirs(Env.paths.pathetc)
        if not os.path.exists(Env.paths.nodeconf):
            self.log.info("create %s", Env.paths.nodeconf)
            with open(Env.paths.nodeconf, "a") as ofile:
                ofile.write("")
            os.chmod(Env.paths.nodeconf, 0o0600)

    def get_config_mtime(self):
        try:
            mtime = os.path.getmtime(Env.paths.nodeconf)
        except (OSError, IOError):
            self.init_nodeconf()
            mtime = os.path.getmtime(Env.paths.nodeconf)
        except Exception as exc:
            self.log.warning("failed to get node config mtime: %s", exc)
            mtime = 0
        try:
            cmtime = os.path.getmtime(Env.paths.clusterconf)
        except Exception as exc:
            cmtime = 0
        return mtime if mtime > cmtime else cmtime

    def read_config(self):
        locked = shared.CONFIG_LOCK.acquire(blocking=False)
        if not locked:
            return
        try:
            return self._read_config()
        finally:
            shared.CONFIG_LOCK.release()

    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()
                shared.NODE.set_rlimit()
                shared.NODE.network_setup()
            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 in self.threads.values():
                if thr.stopped():
                    thr.unstop()
                else:
                    thr.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 shared.NODE.conf_sections("hb", cd=shared.NODE.cd):
            try:
                section_type = shared.NODE.oget(section, "type")
            except Exception:
                continue
            try:
                hb_nodes = shared.NODE.conf_get(section, "nodes")
                if Env.nodename not in hb_nodes:
                    continue
            except ex.OptNotFound as exc:
                pass
            if section_type not in hbs:
                hbs[section_type] = [section]
            else:
                hbs[section_type].append(section)
        return hbs

    def hb_enabled(self, name):
        for names in self.config_hbs.values():
            if name in names:
                return True
        return False

#############################################################################
#
# Main
#
#############################################################################
def optparse(args=None):
    """
    Parse command line options for main().
    """
    parser = OptionParser()
    parser.add_option(
        "--debug", action="store_true",
        dest="debug"
    )
    parser.add_option(
        "-f", "--foreground", action="store_false",
        default=True, dest="daemon"
    )
    return parser.parse_args(args=args)


def main(args=None):
    """
    Start the daemon and catch Exceptions to reap it down cleanly.
    """
    options, _ = optparse(args=args)
    try:
        shared.DAEMON = Daemon()
        shared.DAEMON.run(daemon=options.daemon)
    except (KeyboardInterrupt, ex.Signal):
        shared.DAEMON.log.info("interrupted")
        shared.DAEMON.stop()
    except Exception as exc:
        shared.DAEMON.log.exception(exc)
        shared.DAEMON.stop()
 0707010001f1b7000081a40000000000000000000000016a100daf00000881000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/daemon/handler.py  import utilities.converters
import core.exceptions as ex
from utilities.storage import Storage

class BaseHandler(object):
    """
    Base handler class. Defines some defaults.
    """
    path = ""
    alt_paths = []
    method = "GET"
    access = {
        "roles": ["root"],
    }
    prototype = []
    stream = False
    multiplex = "on-demand"
    routes = (("", ""),)

    def get_origin(self, extra_info_func):
        return "%s /%s %s" % (self.routes[0][0], self.routes[0][1], extra_info_func())

    def rbac(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        kwargs.update(self.access)
        kwargs["path"] = options.path
        thr.rbac_requires(**kwargs)

    def parse_options(self, data):
        def options_path(options, required=True):
            for key in ("path", "svcpath", "svcname"):
                try:
                    return options[key]
                except KeyError:
                    pass
            if required:
                raise ex.HTTP(400, "required option path is not set")
            return None

        def get_option(data, opt):
            name = opt["name"]
            fmt = opt.get("format", "string")
            required = opt.get("required", False)
            if fmt != "object_path" and required and name not in data:
                raise ex.HTTP(400, "required option %s is not set" % name)
            value = data.get(name, opt.get("default"))
            if value is None:
                value = opt.get("default")
            try:
                value = getattr(utilities.converters, "convert_"+fmt)(value)
            except AttributeError:
                pass
            except Exception as exc:
                raise ex.HTTP(400, "option %s format conversion to %s error: %s" % (name, fmt, exc))
            if fmt == "object_path":
                value = options_path(data, required=required)
            return name, value

        options = Storage()
        request_options = data.get("options", {})
        for opt in self.prototype:
            name, value = get_option(request_options, opt)
            options[name] = value
        return options
   0707010001f4c2000041ed0000000000000000000000286a102a9300000000000000e600010003ffffffffffffffff0000002900000000root/usr/share/opensvc/opensvc/utilities  0707010001f520000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/mounts   0707010001f523000081a40000000000000000000000016a100daf0000058b000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/mounts/darwin.py import os

from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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
            src_dir_dev = None

        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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['mount'])
        for l in out.split('\n'):
            words = l.split()
            if len(words) < 4:
                break
            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 = Mount(dev, mnt, type, mnt_opt)
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    help(Mounts)
    for m in Mounts():
        print(m)
 0707010001f525000081a40000000000000000000000016a100daf000004d1000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/mounts/hpux.py   import os

from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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
            src_dir_dev = None

        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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['mount', '-v'])
        for l in out.split('\n'):
            if len(l.split()) != 12:
                break
            dev, null, mnt, null, type, mnt_opt, null, null, null, null, null, null = l.split()
            m = Mount(dev, mnt, type, mnt_opt.strip('()'))
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    help(Mounts)
    for m in Mounts():
        print(m)
   0707010001f529000081a40000000000000000000000016a100daf0000048d000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/mounts/sunos.py  from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['mount', '-p'])
        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 = Mount(dev, mnt, type, mnt_opt.strip('()'))
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    help(Mounts)
    M = Mounts()
    print(M)
   0707010001f524000081a40000000000000000000000016a100daf0000058b000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/mounts/freebsd.py    import os

from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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
            src_dir_dev = None

        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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['mount'])
        for l in out.split('\n'):
            words = l.split()
            if len(words) < 4:
                break
            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 = Mount(dev, mnt, type, mnt_opt)
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    help(Mounts)
    for m in Mounts():
        print(m)
 0707010001f52a000081a40000000000000000000000016a100daf00000422000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/mounts/windows.py    from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    def __init__(self, wmi=None):
        self.wmi = wmi
        super(Mounts, self).__init__()

    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 parse_mounts(self, wmi=None):
        if self.wmi is None:
            import foreign.wmi as wmi
            self.wmi = wmi.WMI()
        mounts = []
        for volume in self.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 = Mount(dev, mnt, type, mnt_opt)
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    # help(Mounts)
    for m in Mounts():
        print(m)
  0707010001f521000081a40000000000000000000000016a100daf000000d1000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/mounts/__init__.py   import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
Mounts = _os.Mounts
   0707010001f526000081a40000000000000000000000016a100daf000005b3000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/mounts/linux.py  import os

import utilities.devices.linux
from env import Env
from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    df_one_cmd = [Env.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 utilities.devices.linux.file_to_loop(dev):
            return True
        if dev.startswith(os.sep) and os.path.isdir(dev):
            # zfs datasets <pool>/<ds> might match the isdir test because the
            # daemon cwd is /, but we don't want them to be considered a match
            src_dir_dev = self.get_src_dir_dev(dev)
            if i.dev == src_dir_dev:
                return True
        return False

    def parse_mounts(self):
        out, err, ret = justcall([Env.syspaths.mount])
        out = out.replace(" (deleted)", "")
        mounts = []
        for l in out.split('\n'):
            if len(l.split()) != 6:
                continue
            dev, null, mnt, null, type, mnt_opt = l.split()
            m = Mount(dev, mnt, type, mnt_opt.strip('()'))
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    for m in Mounts():
        print(m)
 0707010001f528000081a40000000000000000000000016a100daf000003c3000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/mounts/osf1.py   from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['mount'])
        for l in out.split('\n'):
            l = l.replace(', ', ',')
            if len(l.split()) != 6:
                break
            dev, null, mnt, null, type, mnt_opt = l.split()
            m = Mount(dev, mnt, type, mnt_opt.strip('()'))
            mounts.append(m)
        return mounts


if __name__ == "__main__":
    help(Mounts)
    for m in Mounts():
        print(m)
 0707010001f527000081a40000000000000000000000016a100daf00000b7d000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/mounts/mounts.py import os
from subprocess import Popen, PIPE, STDOUT

import core.exceptions as ex
from utilities.string import bdecode


class Mount(object):
    def __init__(self, dev, mnt, type, mnt_opt):
        self.dev = dev.rstrip("/")
        self.mnt = mnt.rstrip("/")
        if mnt == "/":
            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 BaseMounts(object):
    src_dir_devs_cache = {}
    df_one_cmd = []

    def __init__(self):
        try:
            self.mounts = self.parse_mounts()  # pylint: disable=assignment-from-no-return
        except Exception as exc:
            self.mounts = None

    def __iter__(self):
        return iter(self.mounts or [])

    def match_mount(self, *args, **kwargs):
        """ OS dependent """
        pass

    def mount(self, dev, mnt):
        for i in self.mounts or []:
            if self.match_mount(i, dev, mnt):
                return i
        return None

    def parse_mounts(self):
        raise ex.Error("parse_mounts is not implemented")

    def has_mount(self, dev, mnt):
        if self.mounts is None:
            raise ex.Error("unable to parse mounts")
        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 or []:
            if getattr(i, param) == value:
                return i
        return None

    def sort(self, key='mnt', reverse=False):
        if len(self.mounts or []) == 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 or []:
            output += "\n  %s" % m.__str__()
        return output
   0707010001f522000081a40000000000000000000000016a100daf00000dba000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/mounts/aix.py    import os

from utilities.proc import justcall
from .mounts import BaseMounts, Mount


class Mounts(BaseMounts):
    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
            src_dir_dev = None

        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 parse_mounts(self):
        mounts = []
        out, err, ret = justcall(['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 = Mount(dev, mnt, type, mnt_opt)
            mounts.append(m)
        return mounts


"""
  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)
  0707010001f5b9000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/utilities/systemd  0707010001f5ba000081a40000000000000000000000016a100daf00000733000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/systemd/__init__.py  import os
import glob

from utilities.proc import justcall

def systemd_escape(s):
    def escape(s):
        return "".join([c if c.isalnum() else "\\x%02x"%ord(c) for c in s])
    return "-".join([escape(seg) for seg in s.split("/")])

def systemd_unescape(s):
    return "/".join([seg.encode("utf8").decode("unicode_escape") for seg in s.split("-")])

def systemd_system():
    try:
        return "systemd" in os.readlink("/proc/1/exe")
    except:
        return False

def format_unit(*args, **kwargs):
    elms = []
    t = kwargs.get("kind", "slice")
    for arg in args:
        if arg is None:
            continue
        elms.append(systemd_escape(arg))
    return "-".join(elms) + "." + t

def format_slice(*args):
    return format_unit(*args, t="slice")

def format_scope(*args):
    return format_unit(*args, t="scope")

def create_slice(*args, **kwargs):
    properties = kwargs.get("properties", {})
    name = format_slice(*args)
    props = []
    for key, val in properties.items():
        props += ["-p", "%s=%s" % (key, str(val))]

    props += ["-p", "MemoryAccounting=true"]
    cmd = ["systemd-run", "--quiet", "--scope", "--slice=%s" % name] + props + ["/bin/true"]
    print(" ".join(cmd))
    out, err, ret = justcall(cmd)
    print(ret, out, err)

def systemd_get_procs(unit):
    path = glob.glob("/sys/fs/cgroup/unified/system.slice/%s/cgroup.procs" % unit)
    if path:
        return path[0]
    path = glob.glob("/sys/fs/cgroup/systemd/system.slice/%s/tasks" % unit)
    if path:
        return path[0]

def systemd_join(unit):
    path = systemd_get_procs(unit)
    if not path:
        return
    with open(path, "w") as ofile:
        ofile.write(str(os.getpid()))

if __name__ == "__main__":
    s = "opensvc/foo-bar#1"
    print(s)
    s = systemd_escape(s)
    print(s)
    print(systemd_unescape(s))
 0707010001f536000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/utilities/os   0707010001f539000081a40000000000000000000000016a100daf0000035a000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/os/sunos.py  from utilities.proc import justcall
def get_solaris_version():
    """
    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
    """
    cmd = ['uname', '-r']
    out, err, ret = justcall(cmd)
    if ret != 0:
        return 0
    lines = out.split('\n')
    if len(lines) == 0:
        return 0
    try:
        base, osver = lines[0].split('.')
        osver = int(osver)
    except ValueError:
        osver = 0

    if osver >= 11:
        cmd = ['uname', '-v']
        out, err, ret = justcall(cmd)
        if ret == 0:
            elts = out.split("\n")[0].split(".")[:2]
            osver = ".".join(elts)

    return float(osver)

  0707010001f53b000081a40000000000000000000000016a100daf00000107000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/os/windows.py    try:
    from foreign.six.moves import winreg
except ImportError:
    pass

def get_registry_value(key, subkey, value):
    key = getattr(winreg, key)
    handle = winreg.OpenKey(key, subkey)
    value, type = winreg.QueryValueEx(handle, value)
    return value

 0707010001f538000081a40000000000000000000000016a100daf00000364000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/os/linux.py  import ctypes

SYS_gettid = 186

class NotAvail:
    pass

class LibC:
    _libc = None

    def _load_lib(self):
        if self._libc:
            return
        self._libc = ctypes.cdll.LoadLibrary("libc.so.6")

    def syscall(self, id):
        self._load_lib()
        return self._libc.syscall(id)

class LibCap:
    _libcap = None
    pr_set_name = 15

    def _load_lib(self):
        if self._libcap:
            return
        try:
            self._libcap = ctypes.cdll.LoadLibrary("libcap.so.2")
        except OSError:
            self._libcap = NotAvail

    def tname(self, name):
        self._load_lib()
        if self._libcap is NotAvail:
            return
        self._libcap.prctl(self.pr_set_name, name.encode())

libc = LibC()
libcap = LibCap()

def get_tid():
    return libc.syscall(SYS_gettid)

def set_tname(name):
    libcap.tname(name)
0707010001f537000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/os/__init__.py   0707010001f504000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/hash 0707010001f505000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/hash/__init__.py 0707010001f506000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/hash/md5 0707010001f508000081a40000000000000000000000016a100daf000024a4000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/hash/md5/md5.py  # pylint: skip-file

"""
/* 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 & 0xffffffff   # make shift unsigned
    return (((x) << (n)) | ((x) >> (32-(n)))) & 0xffffffff

#/* 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 = 0
        self.state = (0x67452301,
                      0xefcdab89,
                      0x98badcfe,
                      0x10325476,)
        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 & 0xffffffff, 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 = (0xffffffff & (state[0] + a),
                      0xffffffff & (state[1] + b),
                      0xffffffff & (state[2] + c),
                      0xffffffff & (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(repr(md5("/dev/sda").digest().encode('hex')))
    #from md5 import new
    #print(`new("/dev/sda").digest()`)

if __name__=="__main__":
    test()
0707010001f507000081a40000000000000000000000016a100daf000000fa000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/hash/md5/__init__.py try:
    from hashlib import md5
    def hexdigest(s):
        o = md5()
        o.update(s.encode('utf-8'))
        return o.hexdigest()
except ImportError:
    from .md5 import md5
    def hexdigest(s):
        return md5(s).digest().encode('hex')
  0707010001f4c3000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/__init__.py  0707010001f4dc000081a40000000000000000000000016a100daf00000544000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/dbglock.py   import threading
import time

from traceback import format_stack

class _Lock(object):
    def __init__(self, o):
        self._lock = o()
        self.t = 0
        self.name = ""
        self.holder = None

    def acquire(self, *args, **kwargs):
        s = "".join(format_stack()[2:-3])
        if self.holder:
            print("=== %s acquire\n%s" % (self.name, s))
            print(">>> %s held by\n%s" % (self.name, self.holder))
        self._lock.acquire(*args, **kwargs)
        self.holder = s

    def release(self, *args, **kwargs):
        self.holder = None
        self._lock.release()

    def __enter__(self):
        s = "".join(format_stack()[2:-3])
        if self.holder:
            print("=== %s acquire\n%s" % (self.name, s))
            print(">>> %s held by\n%s" % (self.name, self.holder))
        self.t = time.time()
        self._lock.acquire()
        self.holder = s

    def __exit__(self, type, value, traceback):
        self._lock.release()
        self.holder = None
        d = time.time() - self.t
        if d < 1:
            return
        #print("=== %s held %.2fs\n%s" % (self.name, d, "".join(format_stack()[2:-1])))

class Lock(_Lock):
    def __init__(self):
         _Lock.__init__(self, threading.Lock)

class RLock(_Lock):
    def __init__(self):
         _Lock.__init__(self, threading.RLock)

0707010001f52b000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/naming   0707010001f52c000081a40000000000000000000000016a100daf0000356c000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/naming/__init__.py   """
Namespaces functions
"""
import fnmatch
import glob
import importlib
import os
import re
from itertools import chain

import core.exceptions as ex
from core.contexts import want_context
from env import Env

# RFC952 + RFC1123 validation rule
VALID_NAME_RFC952_NO_DOT = (r"^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9]))*"
                            r"([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$")
VALID_NAME_RFC952 = r"^[a-zA-Z]([a-zA-Z0-9-]+[.]?)*[a-zA-Z0-9]$"

ANSI_ESCAPE = re.compile(r"\x1b\[([0-9]{1,3}(;[0-9]{1,3})*)?[mHJKG]", re.UNICODE)
ANSI_ESCAPE_B = re.compile(br"\x1b\[([0-9]{1,3}(;[0-9]{1,3})*)?[mHJKG]")

def is_service(f, namespace=None, data=None, local=False, kinds=None):
    if f is None:
        return
    f = re.sub(r"\.conf$", '', f)
    f = f.replace(Env.paths.pathetcns + os.sep, "").replace(Env.paths.pathetc + os.sep, "")
    try:
        name, _namespace, kind = split_path(f)
    except ValueError:
        return
    if kinds and kind not in kinds:
        return
    if not namespace:
        namespace = _namespace
    path = fmt_path(name, namespace, kind)
    if not local:
        try:
            data["services"][path]
            return path
        except Exception:
            if want_context():
                return
    cf = svc_pathcf(path)
    if not os.path.exists(cf):
        return
    return path


def list_services(namespace=None, kinds=None):
    l = []
    if namespace in (None, "root"):
        for name in glob_root_config():
            s = name[:-5]
            if len(s) == 0:
                continue
            path = is_service(name, kinds=kinds)
            if path is None:
                continue
            l.append(path)
    n = len(os.path.join(Env.paths.pathetcns, ""))
    for path in glob_ns_config(namespace):
        path = path[n:-5]
        if not path or path[-1] == os.sep:
            continue
        if kinds:
            try:
                name, namespace, kind = split_path(path)
            except ValueError:
                continue
            if kind not in kinds:
                continue
        if path.endswith("/namespace") and path.count("/") == 1:
            path = path[:-9]
        l.append(path)
    return l


def glob_root_config():
    GLOB_ROOT_SVC_CONF = os.path.join(Env.paths.pathetc, "*.conf")
    GLOB_ROOT_VOL_CONF = os.path.join(Env.paths.pathetc, "vol", "*.conf")
    GLOB_ROOT_CFG_CONF = os.path.join(Env.paths.pathetc, "cfg", "*.conf")
    GLOB_ROOT_SEC_CONF = os.path.join(Env.paths.pathetc, "sec", "*.conf")
    GLOB_ROOT_USR_CONF = os.path.join(Env.paths.pathetc, "usr", "*.conf")
    return chain(
        glob.iglob(GLOB_ROOT_SVC_CONF),
        glob.iglob(GLOB_ROOT_VOL_CONF),
        glob.iglob(GLOB_ROOT_CFG_CONF),
        glob.iglob(GLOB_ROOT_SEC_CONF),
        glob.iglob(GLOB_ROOT_USR_CONF),
    )


def glob_ns_config(namespace=None):
    if namespace is None:
        GLOB_CONF_NSCFG_CONF = os.path.join(Env.paths.pathetcns, "*", "namespace.conf")
        GLOB_CONF_NS = os.path.join(Env.paths.pathetcns, "*", "*", "*.conf")
    else:
        GLOB_CONF_NSCFG_CONF = os.path.join(Env.paths.pathetcns, namespace, "namespace.conf")
        GLOB_CONF_NS = os.path.join(Env.paths.pathetcns, namespace, "*", "*.conf")
    return chain(
        glob.iglob(GLOB_CONF_NSCFG_CONF),
        glob.iglob(GLOB_CONF_NS),
    )


def glob_services_config():
    return chain(glob_root_config(), glob_ns_config())


def is_ns_path(path):
    try:
        return path.endswith("/") and path.count("/") == 1
    except ValueError:
        return False

def split_path(path):
    if is_ns_path(path):
        return "namespace", path.strip("/") or None, "nscfg"
    path = path.strip("/")
    if path in ("node", "auth"):
        raise ValueError
    if "," in path or "+" in path:
        raise ValueError
    nsep = path.count("/")
    if nsep == 2:
        namespace, kind, name = path.split("/")
    elif nsep == 1:
        kind, name = path.split("/")
        namespace = "root"
    elif nsep == 0:
        name = path
        namespace = "root"
        kind = "svc"
    else:
        raise ValueError(path)
    if namespace == "root":
        namespace = None
        if name == "cluster":
            kind = "ccfg"
        elif name == "namespace":
            kind = "nscfg"
    return name, namespace, kind


def svc_pathcf(path, namespace=None):
    name, _namespace, kind = split_path(path)
    if namespace:
        if kind == "nscfg":
            return os.path.join(Env.paths.pathetcns, namespace, "namespace.conf")
        return os.path.join(Env.paths.pathetcns, namespace, kind, name + ".conf")
    elif _namespace:
        if kind == "nscfg":
            return os.path.join(Env.paths.pathetcns, _namespace, "namespace.conf")
        return os.path.join(Env.paths.pathetcns, _namespace, kind, name + ".conf")
    elif kind in ("svc", "ccfg"):
        return os.path.join(Env.paths.pathetc, name + ".conf")
    elif kind == "nscfg":
        return os.path.join(Env.paths.pathetc, "namespace.conf")
    else:
        return os.path.join(Env.paths.pathetc, kind, name + ".conf")


def svc_pathetc(path, namespace=None):
    return os.path.dirname(svc_pathcf(path, namespace=namespace))


def svc_pathtmp(path):
    name, namespace, kind = split_path(path)
    if namespace:
        return os.path.join(Env.paths.pathtmp, "namespaces", namespace, kind)
    elif kind in ("svc", "ccfg"):
        return os.path.join(Env.paths.pathtmp)
    else:
        return os.path.join(Env.paths.pathtmp, kind)


def svc_pathlog(path):
    name, namespace, kind = split_path(path)
    if namespace:
        return os.path.join(Env.paths.pathlog, "namespaces", namespace, kind)
    elif kind in ("svc", "ccfg"):
        return os.path.join(Env.paths.pathlog)
    else:
        return os.path.join(Env.paths.pathlog, kind)


def svc_pathvar(path, relpath=""):
    name, namespace, kind = split_path(path)
    if namespace:
        l = [Env.paths.pathvar, "namespaces", namespace, kind, name]
    else:
        l = [Env.paths.pathvar, kind, name]
    if relpath:
        l.append(relpath)
    return os.path.join(*l)


def fmt_path(name, namespace, kind):
    if namespace not in (None, "root"):
        ns = namespace.strip("/")
        if kind == "nscfg":
            return ns + "/"
        return "/".join((ns, kind, name))
    elif kind not in ("svc", "ccfg"):
        if kind == "nscfg":
            return "/"
        return "/".join((kind, name))
    else:
        return name


def split_fullname(fullname, clustername):
    fullname = fullname[:-(len(clustername) + 1)]
    return fullname.rsplit(".", 2)


def svc_fullname(name, namespace, kind, clustername):
    return "%s.%s.%s.%s" % (
        name,
        namespace if namespace else "root",
        kind,
        clustername
    )


def strip_path(paths, namespace):
    if not namespace:
        return paths
    if isinstance(paths, (list, tuple)):
        return [strip_path(path, namespace) for path in paths]
    else:
        path = re.sub("^%s/" % namespace, "", paths)  # strip current ns
        return re.sub("^svc/", "", path)  # strip default kind


def path_data(path):
    name, namespace, kind = split_path(path)
    if namespace is None:
        namespace = "root"
    return {
        "path": path,
        "name": name,
        "namespace": namespace,
        "kind": kind,
        "normalized": "%s/%s/%s" % (namespace, kind, name),
        "display": fmt_path(name, namespace, kind),
    }


def paths_data(paths):
    for path in paths:
        yield path_data(path)


def resolve_path(path, namespace=None):
    """
    Return the path, parented in <namespace> if specified and if not found
    in <path>.
    """
    name, _namespace, kind = split_path(path)
    if namespace and not _namespace:
        _namespace = namespace
    if _namespace == "root":
        _namespace = None
    return fmt_path(name, _namespace, kind)


def validate_paths(paths):
    [validate_path(p) for p in paths]


def validate_path(path):
    name, namespace, kind = split_path(path)
    validate_kind(kind)
    validate_ns_name(namespace)
    validate_name(name)


def validate_kind(name):
    if name not in Env.kinds:
        raise ValueError("invalid kind '%s'. kind must be one of"
                         " %s." % (name, ", ".join(Env.kinds)))


def validate_ns_name(name):
    if name is None:
        return
    if name in Env.kinds:
        raise ValueError("invalid namespace name '%s'. names must not clash with kinds"
                         " %s." % (name, ", ".join(Env.kinds)))
    if re.match(VALID_NAME_RFC952_NO_DOT, name):
        return
    raise ValueError("invalid namespace name '%s'. names must contain only letters, "
                     "digits and hyphens, start with a letter and end with "
                     "a digit or letter (rfc 952)." % name)


def validate_name(name):
    # strip scaler slice prefix
    name = re.sub(r"^[0-9]+\.", "", name)
    if name in Env.kinds:
        raise ex.Error("invalid name '%s'. names must not clash with kinds"
                          " %s." % (name, ", ".join(Env.kinds)))
    if re.match(VALID_NAME_RFC952, name):
        return
    raise ex.Error("invalid name '%s'. names must contain only dots, letters, "
                      "digits and hyphens, start with a letter and end with "
                      "a digit or letter (rfc 952)." % name)


def parse_path_selector(selector, namespace=None):
    if selector is None:
        if namespace:
            return "*", namespace, "svc"
        else:
            return "*", "*", "svc"
    elts = selector.split("/")
    elts_count = len(elts)
    if elts_count == 1:
        if elts[0] == "**":
            _namespace = namespace if namespace else "*"
            _kind = "*"
            _name = "*"
        elif elts[0] == "*":
            _namespace = namespace if namespace else "*"
            _kind = "svc"
            _name = "*"
        else:
            _namespace = namespace if namespace else "*"
            _kind = "svc"
            _name = elts[0]
    elif elts_count == 2:
        if elts[0] == "**":
            _namespace = namespace if namespace else "*"
            _kind = "*"
            _name = elts[1] if elts[1] not in ("**", "") else "*"
        elif elts[0] == "*":
            _namespace = namespace if namespace else "*"
            _kind = "svc"
            _name = elts[1] if elts[1] not in ("**", "") else "*"
        elif elts[1] == "**":
            _namespace = namespace if namespace else elts[0]
            _kind = "*"
            _name = "*"
        elif elts[0] == "*":
            _namespace = namespace if namespace else elts[0]
            _kind = "svc"
            _name = "*"
        else:
            _namespace = "root"
            _kind = elts[0]
            _name = elts[1]
    elif elts_count == 3:
        _namespace = namespace if namespace else elts[0]
        _kind = elts[1]
        _name = elts[2]
    else:
        raise ValueError("invalid path selector %s" % selector)
    return _name, _namespace, _kind


def format_path_selector(selector, namespace=None, maxlen=None):
    try:
        _name, _namespace, _kind = parse_path_selector(selector, namespace)
        buff = "%s/%s/%s" % (_namespace, _kind, _name)
    except ValueError:
        buff = selector
    if maxlen:
        if len(buff) > maxlen:
            buff = buff[:maxlen-3] + "..."
    return buff

def normalize_jsonpath(path):
    if path and path[0] == ".":
        path = path[1:]
    return path


def abbrev(l):
    if len(l) < 1:
        return l
    paths = [n.split(".")[::-1] for n in l]
    trimable = [n for n in paths if len(n) > 1]
    if len(trimable) <= 1:
        return [n[-1] + ".." if n in trimable else n[0] for n in paths]
    for i in range(10):
        try:
            if len(set([t[i] for t in trimable])) > 1:
                break
        except IndexError:
            break
    if i == 0:
        return l
    return [".".join(n[:i - 1:-1]) + ".." if n in trimable else n[0] for n in paths]


def factory(kind):
    """
    Return a Svc or Node object
    """
    if kind == "node":
        from core.node import Node
        return Node
    try:
        mod = importlib.import_module("core.objects."+kind)
        return getattr(mod, kind.capitalize())
    except Exception as exc:
        print(exc)
        pass
    raise ValueError("unknown kind: %s" % kind)


def new_id():
    import uuid
    return str(uuid.uuid4())

def object_path_glob(pattern, pds=None, namespace=None, kind=None, negate=False):
    pds = pds or []
    if kind:
        pds = [pd for pd in pds if pd["kind"] == kind]
    l = pattern.split("/")
    n = len(l)
    if n == 3:
        if l[1] == "nscfg":
            # */nscfg/*
            _selector = "%s/nscfg/namespace" % l[0]
        elif not l[2]:
            # test/svc/
            _selector = "%s/%s/*" % (l[0], l[1])
        else:
            # a*/b*/c*
            _selector = pattern
    elif n == 2:
        if not l[1]:
            # pg1/
            _selector = "%s/nscfg/namespace" % l[0]
        elif l[1] == "**":
            # prod/**
            _selector = "%s/*/*" % l[0]
        elif l[0] == "**":
            # **/s*
            _selector = "*/*/%s" % l[1]
        else:
            # svc/s*
            _selector = "%s/%s/%s" % (namespace or "root", l[0], l[1])
    elif n == 1:
        if l[0] == "**":
            _selector = "*/*/*"
        else:
            _selector = "%s/%s/%s" % (namespace or "root", kind or "svc", l[0])
    else:
        return []
    return [pd["display"] for pd in pds if negate ^ fnmatch.fnmatch(pd["normalized"], _selector)]


0707010001f593000041ed0000000000000000000000046a102a9300000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/utilities/stats    0707010001f594000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/stats/__init__.py    0707010001f595000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/stats/collector  0707010001f596000081a40000000000000000000000016a100daf000000d3000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/utilities/stats/collector/__init__.py  import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
collect = _os.collect
 0707010001f598000081a40000000000000000000000016a100daf00000aa4000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/stats/collector/darwin.py    import datetime
import os

from env import Env
from utilities.proc import justcall


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
        lines = out.split('\n')
        if len(lines) < 2:
            return
        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(Env.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')

    data = fs_u()
    if data:
        node.collector.call('push_stats_fs_u', data)
    mem_u()
0707010001f599000081a40000000000000000000000016a100daf00000d9f000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/stats/collector/hpux.py  import datetime
import os
from subprocess import *

from env import Env
from utilities.proc import justcall, is_exe


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(Env.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(Env.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)
        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)
 0707010001f59a000081a40000000000000000000000016a100daf000013e0000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/stats/collector/linux.py from __future__ import print_function

import datetime
import json
import os
import re
import time

from env import Env
from utilities.proc import justcall, which

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(Env.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
        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(Env.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)
0707010001f59c000081a40000000000000000000000016a100daf00000905000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/stats/collector/windows.py   from __future__ import print_function

import datetime
import json
import os
import time

from env import Env
from foreign.winstats import *


def collect(_=None):
    now = datetime.datetime.now()
    data = {
        "ts": str(now),
    }

    meminfo = get_mem_info()
    data["mem"] = {
        "tp": meminfo.TotalPhys // 1024,
        "ap": meminfo.AvailPhys // 1024,
        "ts": meminfo.TotalPageFile // 1024,
        "as": meminfo.AvailPageFile // 1024,
        "ml": meminfo.MemoryLoad,
    }

    pinfo = get_perf_info()
    data["prf"] = {
        "mc": pinfo.SystemCacheBytes,
        "pr": pinfo.ProcessCount,
        "ke": pinfo.KernelTotal,
    }

    counters = [
        r'\Processor(_Total)\% Processor Time',
        r'\PhysicalDisk(_Total)\% Disk Time',
        r'\PhysicalDisk(_Total)\Disk Read Bytes/sec',
        r'\PhysicalDisk(_Total)\Disk Write Bytes/sec',
        r'\PhysicalDisk(_Total)\Disk Reads/sec',
        r'\PhysicalDisk(_Total)\Disk Writes/sec',
        #        r'\Network Adapter(*)\Bytes Received/sec',
        #        r'\Network Adapter(*)\Bytes Sent/sec',
        #        r'\Network Adapter(*)\Packets Received/sec',
        #        r'\Network Adapter(*)\Packets Sent/sec',
    ]
    fmts = [
        "double",
        "double",
        "double",
        "double",
        "double",
        "double",
        #        "double",
        #        "double",
        #        "double",
        #        "double",
    ]
    mon = get_perf_data(counters, fmts=fmts, delay=2000, english=True)
    data["mon"] = {
        "pt": mon[0],
    }
    data["dev"] = {
        "tm": mon[1],
        "rb": mon[2],
        "wb": mon[3],
        "r": mon[2],
        "w": mon[3],
    }

    stats_d = os.path.join(Env.paths.pathvar, "stats")
    stats_p = os.path.join(stats_d, 'sa%d' % now.day)

    if not os.path.exists(stats_d):
        os.makedirs(stats_d)
    if not os.path.exists(stats_p):
        # create the stats file
        mode = 'w+'
    elif os.stat(stats_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_p, mode) as fd:
        json.dump(data, fd)
        fd.write(os.linesep)


if __name__ == "__main__":
    collect()
   0707010001f597000081a40000000000000000000000016a100daf00000047000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/stats/collector/aix.py   def collect(_):
    """Collect not yet implemented for aix"""
    pass
 0707010001f59b000081a40000000000000000000000016a100daf000016e6000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/stats/collector/sunos.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 datetime
import os
import platform
import subprocess
import time

from env import Env
from utilities.converters import convert_size
from utilities.proc import justcall, which


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 = str(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 = str(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 "id zone" not 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(Env.syspaths.zfs):
            return []
        cmd = [Env.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_size(l[1], _to="KB")
            if l[2] == '0':
                l[2] = '0K'
            avail = convert_size(l[2], _to="KB")
            total = used + avail
            pct = used / total * 100
            vals.append([now, node.nodename, l[0], str(total), str(pct)])
        return vals

    node.collector.call('push_stats_fs_u', fs_u())
  0707010001f59d000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/stats/provider   0707010001f59f000081a40000000000000000000000016a100daf000010b2000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/stats/provider/aix.py    import datetime
import os

from env import Env
from utilities.proc import call, which
from utilities.stats.provider import provider

today = datetime.datetime.today()
yesterday = today - datetime.timedelta(days=1)


class StatsProvider(provider.BaseStatsProviderUx):
    """Not yet implemented"""
    pass


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', Env.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', Env.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], Env.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 = []
    last_date = '00:00:00'
    for line in buff.split('\n'):
        l = line.split()
        if len(l) == 8:
            last_date = l[0]
        if len(l) == 7:
            l = [last_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], Env.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 []
  0707010001f59e000081a40000000000000000000000016a100daf000000df000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/stats/provider/__init__.py   import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
StatsProvider = _os.StatsProvider
 0707010001f5a3000081a40000000000000000000000016a100daf0000382b000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/stats/provider/linux.py  import datetime
import os

from env import Env
from utilities.proc import justcall
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProviderUx):
    def xentopfile(self, day):
        f = os.path.join(Env.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 "kbavail" in buff:
            fmt = 5
            cols = ['date',
                    'kbmemfree',
                    'kbavail',
                    'kbmemused',
                    'pct_memused',
                    'kbbuffers',
                    'kbcached',
                    'kbcommit',
                    'pct_commit',
                    'kbactive',
                    'kbinact',
                    'kbdirty',
                    'nodename']
        elif "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.splitlines():
            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(Env.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'))
 0707010001f5a6000081a40000000000000000000000016a100daf00002008000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/stats/provider/windows.py    import json
import os

from env import Env
from utilities.converters import convert_datetime
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProvider):
    """
    {
        "ts": "2018-10-18 15:38:01.921000",
        "mem": {
            "tp": 2096748,
            "ap": 275536,
            "ts": 5304664,
            "as": 3027048,
            "ml": 86
        },
        "prf": {
            "pr": 98,
            "ke": 53234,
            "mc": 298704896
        },
        "dev": {
            "w": 32456.789373365064,
            "r": 0.0,
            "tm": 0.003836151746131958,
            "wb": 32456.789373365064,
            "rb": 0.0
        },
        "mon": {
            "pt": 3.0798430885735417
        }
    }                                                                                                                                                           
    """
    def __init__(self, interval=2880, stats_dir=None, stats_start=None, stats_end=None):
        super(StatsProvider, self).__init__(interval, stats_dir, stats_start, stats_end)
        self.data = self.get_data(self.stats_start, self.stats_end)

    def _stat_transformer(self, stat_provider):
        return stat_provider()

    def sarfile(self, day):
        f = os.path.join(Env.paths.pathvar, 'stats', 'sa%s' % day)
        if os.path.exists(f):
            return f
        return None

    def get_data(self, start, end):
        def get(day):
            _data = []
            sa = self.sarfile(day)
            if not sa:
                return []
            with open(sa, "r") as fd:
                for line in fd.readlines():
                    try:
                        __data = json.loads(line)
                    except ValueError:
                        continue
                    ts = convert_datetime(__data["ts"])
                    if ts < start or ts > end:
                        continue
                    _data.append(__data)
            return _data

        data = get(start.day)
        while start.day < end.day:
            start += self.one_day
            data += get(start.day)
        return data

    def cpu(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'cpu',
                'usr',
                #                'nice',
                'sys',
                #                'iowait',
                #                'steal',
                'irq',
                #                'soft',
                #                'guest',
                #                'gnice',
                'idle',
                'nodename']
        lines = []
        for data in self.data:
            mon = data.get("mon", {})
            try:
                usr = mon["pt"]
                idle = 100 - usr
            except KeyError:
                continue
            lines.append([
                data["ts"],
                "all",
                usr,
                0,
                0,
                idle,
                Env.nodename
            ])
        return cols, lines

    def mem_u(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'kbmemfree',
                #                'kbavail',
                'kbmemused',
                'pct_memused',
                #                'kbbuffers',
                #                'kbcached',
                #                'kbcommit',
                #                'pct_commit',
                #                'kbactive',
                #                'kbinact',
                #                'kbdirty',
                'nodename']
        lines = []
        for data in self.data:
            mem = data.get("mem", {})
            try:
                kbmemfree = mem["ap"]
                kbmemused = mem["tp"] - kbmemfree
                pct_memused = 100 * kbmemused // mem["tp"]
            except KeyError:
                continue
            lines.append([
                data["ts"],
                kbmemfree,
                kbmemused,
                pct_memused,
                Env.nodename
            ])
        return cols, lines

    def fs_u(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'nodename',
                'mntpt',
                'size',
                'used']
        return [], []

    def proc(self):
        if self.data is None:
            return [], []
        cols = ['date',
                #                'runq_sz',
                'plist_sz',
                #                'ldavg_1',
                #                'ldavg_5',
                #                'ldavg_15',
                'nodename']
        lines = []
        for data in self.data:
            prf = data.get("prf", {})
            try:
                plist_sz = prf["pr"]
            except KeyError:
                continue
            lines.append([
                data["ts"],
                plist_sz,
                Env.nodename
            ])
        return cols, lines

    def swap(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'kbswpfree',
                'kbswpused',
                'pct_swpused',
                #                'kbswpcad',
                #                'pct_swpcad',
                'nodename']
        lines = []
        for data in self.data:
            mem = data.get("mem", {})
            try:
                kbswpfree = mem["as"]
                kbswpused = mem["ts"] - kbswpfree
                pct_swpused = 100 * kbswpused // mem["ts"]
            except KeyError:
                continue
            lines.append([
                data["ts"],
                kbswpfree,
                kbswpused,
                pct_swpused,
                Env.nodename
            ])
        return cols, lines

    def block(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'tps',
                'rtps',
                'wtps',
                'rbps',
                'wbps',
                'nodename']
        lines = []
        for data in self.data:
            dev = data.get("dev", {})
            try:
                rtps = dev["r"]
                wtps = dev["w"]
                tps = rtps + wtps
                rbps = dev["rb"]
                wbps = dev["wb"]
            except KeyError:
                continue
            lines.append([
                data["ts"],
                tps,
                rtps,
                wtps,
                rbps,
                wbps,
                Env.nodename
            ])
        return cols, lines

    def blockdev(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'dev',
                'tps',
                'rsecps',
                'wsecps',
                'avgrq_sz',
                'avgqu_sz',
                'await',
                'svctm',
                'pct_util',
                'nodename']
        lines = []
        return cols, lines

    def netdev(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'dev',
                'rxpckps',
                'txpckps',
                'rxkBps',
                'txkBps',
                'nodename']
        lines = []
        return cols, lines

    def netdev_err(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'dev',
                'rxerrps',
                'txerrps',
                'collps',
                'rxdropps',
                'txdropps',
                'nodename']
        lines = []
        return cols, lines

    def svc(self):
        if self.data is None:
            return [], []
        cols = ['date',
                'svcname',
                'cpu',
                'mem',
                'cap',
                'cap_cpu',
                'nodename']
        lines = []
        return cols, lines


if __name__ == "__main__":
    sp = StatsProvider(interval=200)
    print(sp.get('mem_u'))
0707010001f5a4000081a40000000000000000000000016a100daf00001017000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/stats/provider/provider.py   import datetime
import os

from env import Env
from utilities.converters import convert_datetime


class BaseStatsProvider(object):
    one_minute = datetime.timedelta(minutes=1)
    one_day = datetime.timedelta(days=1)

    def __init__(self, interval=2880, stats_dir=None, stats_start=None, stats_end=None):
        self.interval = interval
        self.stats_dir = stats_dir
        self.stats_start = None
        self.stats_end = None
        self.init_period(stats_start, stats_end, interval)

    def init_period(self, stats_start, stats_end, interval):
        if stats_end is None:
            self.stats_end = datetime.datetime.now()
        else:
            self.stats_end = convert_datetime(stats_end)

        if stats_start is None:
            self.stats_start = self.stats_end - datetime.timedelta(minutes=interval)
        else:
            self.stats_start = convert_datetime(stats_start)
            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=60 - self.stats_end.second)

    def get(self, stat_name):
        stat_name_provider = getattr(self, stat_name)
        if not stat_name_provider:
            print(stat_name, 'is not implemented')
            return [], []
        return self._stat_transformer(stat_name_provider)

    def _stat_transformer(self, stat_provider):
        return [], []


class BaseStatsProviderUx(BaseStatsProvider):
    def __init__(self, interval=2880, stats_dir=None, stats_start=None, stats_end=None):
        super(BaseStatsProviderUx, self).__init__(interval, stats_dir, stats_start, stats_end)
        self.nodename = Env.nodename

        self.minutes_first_day = 60 * self.stats_end.hour + self.stats_end.minute + 1

        self.ranges = []
        i = 0
        end = self.stats_end

        while end > self.stats_start:
            start = end - self.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 - self.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 _stat_transformer(self, stat_provider):
        lines = []
        cols = []
        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 = stat_provider(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 = BaseStatsProviderUx(interval=20)
    print(sp.get('cpu'))
    print(sp.get('swap'))
 0707010001f5a2000081a40000000000000000000000016a100daf00001aac000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/stats/provider/hpux.py   import os

from env import Env
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProviderUx):
    def glancefile(self, day):
        f = os.path.join(Env.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
0707010001f5a1000081a40000000000000000000000016a100daf000011af000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/stats/provider/freebsd.py    from utilities.proc import call
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProviderUx):
    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
 0707010001f5a0000081a40000000000000000000000016a100daf000012a9000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/stats/provider/darwin.py import os

from env import Env
from utilities.proc import call
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProviderUx):
    def customfile(self, metric, day):
        f = os.path.join(Env.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
   0707010001f5a5000081a40000000000000000000000016a100daf00001c7e000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/stats/provider/sunos.py  import datetime
import os

from utilities.proc import call
from utilities.stats.provider import provider


class StatsProvider(provider.BaseStatsProviderUx):
    def __init__(self, interval=2880, stats_dir=None, stats_start=None, stats_end=None):
        super(StatsProvider, self).__init__(interval, stats_dir, stats_start, 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)
        last_date = '00:00:00'
        for line in buff.split('\n'):
            l = line.split()
            if len(l) == 8:
                last_date = l[0]
            if len(l) == 7:
                l = [last_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
  0707010001f4fe000081a40000000000000000000000016a100daf00000e9a000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/drivers.py   import importlib
import pkgutil

from env import Env

DEFAULT_HEAD = "drivers"
SITE_HEAD = "site_opensvc.drivers"
_DRIVERS = set()


def driver_import(*args, **kwargs):
    fallback = kwargs.get('fallback', True)
    head = kwargs.get('head', DEFAULT_HEAD)
    try:
        return _driver_import(*args, head=head)
    except ImportError:
        pass

    initial_modname = "drivers." + ".".join(args)

    if head != SITE_HEAD:
        try:
            return _driver_import(*args, head=SITE_HEAD, initial_modname=initial_modname)
        except ImportError:
            pass

    if not fallback or len(args) <= 2:
        raise ImportError

    args = args[:-1]
    return driver_import(*args, head=head)


def _driver_import(*args, **kwargs):
    head = kwargs.get('head', DEFAULT_HEAD)
    initial_modname = kwargs.get('head')
    def fmt_element(s):
        if s is None:
            return ""
        # Linux => linux
        # SunOS => sunos
        # HP-UX => hpux
        return s.lower().replace("-", "")

    def fmt_modname(elements):
        l = [head]
        for i, e in enumerate(elements):
            if e == "":
                continue
            if i == 0:
                if e == "res":
                    e = "resource"
                l.append(e)
            else:
                l.append(fmt_element(e))
        return ".".join(l).rstrip(".")

    def import_mod(module_name):
        for mn in (module_name + "." + fmt_element(Env.sysname), module_name):
            try:
                m = importlib.import_module(mn)
                return m
            except ImportError:
                pass

    modname = fmt_modname(args)
    initial_modname = initial_modname or modname

    mod = import_mod(modname)
    if mod:
        if not hasattr(mod, "DRIVER_BASENAME") and "drivers.resource." in mod.__name__:
            raise ImportError("module %s does not set DRIVER_BASENAME" % mod.__file__)
        try:
            klass = driver_class(mod)
            _DRIVERS.add((mod, klass))
        except Exception:
            # Don't register loaded drivers without driver class
            pass
        return mod
    raise ImportError("no module found: %s" % initial_modname)


def driver_class(mod):
    """
    Inspect the module and format the expected resource class name
    based on the DRIVER_GROUP and DRIVER_BASENAME attributes.

    For example:
    mod.DRIVER_GROUP = "fs"
    mod.DRIVER_BASENAME = "xfs_two"

    formats and return the "FsXfsTwo" classname.
    """
    def pascalize(s):
        l = [e.capitalize() for e in s.split("_")]
        return "".join(l)

    try:
        classname = pascalize(mod.DRIVER_GROUP)
    except AttributeError:
        return
    try:
        classname += pascalize(mod.DRIVER_BASENAME)
    except AttributeError:
        pass
    return getattr(mod, classname)


def iter_drivers(groups=None):
    groups = groups or []
    for group in groups:
        try:
            package = importlib.import_module("drivers.resource."+group)
        except ImportError:
            continue
        for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
            if not ispkg:
                continue
            try:
                yield driver_import("resource", group, modname)
            except ImportError:
                continue


def load_drivers(groups=None):
    for mod in iter_drivers(groups):
        pass


def rtypes_with_callable(func_name):
    """
    returns drivers rtypes from loaded drivers class that implement callable func_name
    """
    return ["%s.%s" % (drv.DRIVER_GROUP, drv.DRIVER_BASENAME) for drv, klass in _DRIVERS
            if hasattr(drv, "DRIVER_GROUP") and callable(getattr(klass, func_name, None))]
  0707010001f53c000041ed0000000000000000000000046a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/packages 0707010001f548000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/packages/update  0707010001f54f000081a40000000000000000000000016a100daf000006eb000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/packages/update/sunos.py import os
from subprocess import *

import utilities.os.sunos

repo_subdir = "sunos-pkg"

def gen_adminfile():
    filename = "/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):
    try:
        # causing: pkgadd "unable to create temporary directory" errors
        del os.environ["TMPDIR"]
    except:
        pass
    # 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

    admin = gen_adminfile()
    cmd = ['pkgrm', '-a', admin, '-n', 'opensvc']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    if p.returncode != 0:
        return 1
    osver = utilities.os.sunos.get_solaris_version()
    if osver < 10.0:
        opts = ''
    else:
        if GlobalOnly is True:
            opts = '-G'
        else:
            opts = ''
    opts += " -a %s " % admin
    cmd = 'pkgadd %s -d %s all' % (opts, fpath)
    print(cmd)
    return os.system(cmd)
 0707010001f54e000081a40000000000000000000000016a100daf000002a1000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/packages/update/osf1.py  from __future__ import print_function

import os
import sys
import tarfile

from env import Env

repo_subdir = "tar"

def update(fpath):
    cmd = sys.executable + ' ' + Env.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 + ' ' + Env.paths.postinstall
    return os.system(cmd)
   0707010001f54a000081a40000000000000000000000016a100daf000000df000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/packages/update/aix.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
 0707010001f549000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/utilities/packages/update/__init__.py  0707010001f54d000081a40000000000000000000000016a100daf0000026c000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/packages/update/linux.py import os
from subprocess import *


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/SUSE-brand') or \
     os.path.exists('/etc/redhat-release'):
    repo_subdir = "rpms"
    update = update_rpm
0707010001f550000081a40000000000000000000000016a100daf000000b2000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/packages/update/windows.py   from subprocess import *

repo_subdir = "exe"

def update(fpath):
    cmd = [fpath, '/S']
    print(' '.join(cmd))
    p = Popen(cmd)
    p.communicate()
    return p.returncode
  0707010001f54b000081a40000000000000000000000016a100daf000001c0000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/packages/update/darwin.py    import os
from subprocess import *

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
0707010001f54c000081a40000000000000000000000016a100daf00000108000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/packages/update/hpux.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
0707010001f53e000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/packages/list    0707010001f541000081a40000000000000000000000016a100daf00000440000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/packages/list/darwin.py  from env import Env
from utilities.proc import call, which


"""
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():
    """
    Return a list of ips packages installed.
    where pkg is (nodename, pkgname, version, "")
    """
    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 = [Env.nodename, line, pkgversion(line), ""]
        lines.append(x)
    return lines


def listpatch():
    return []
0707010001f545000081a40000000000000000000000016a100daf00000587000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/packages/list/osf1.py    from env import Env
from utilities.proc import justcall

"""
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 = [Env.nodename, name, pkgvers]
            patch.append(x)
        else:
            x = [Env.nodename, name, pkgvers, pkgarch]
            pkg.append(x)
    return pkg, patch


def listpkg():
    """
    Return a list of ips packages installed.
    where pkg is (nodename, pkgname, version, arch)
    """
    return _list()[0]


def listpatch():
    return _list()[1]
 0707010001f53f000081a40000000000000000000000016a100daf000000ee000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/packages/list/__init__.py    import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
listpkg = _os.listpkg
listpatch = _os.listpatch

  0707010001f546000081a40000000000000000000000016a100daf00000c93000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/packages/list/sunos.py   import os
from stat import ST_MTIME

from env import Env
from utilities.proc import justcall, which


def listpkg_ips():
    """
    Return a list of ips packages installed.
    where pkg is (nodename, pkgname, version, arch, "ips", None)
    Note: installed date is omitted (None)
    """

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

    if which('uname') is None:
        return []
    cmd = ['uname', '-p']
    out, _, _ = justcall(cmd)
    arc = out.split('\n')[0]
    if which('pkg') is None:
        return []
    cmd = ['pkg', 'list', '-H']
    out, _, _ = justcall(cmd)
    lines = []
    for line in out.split('\n'):
        elems = line.split()
        if len(elems) != 3:
            continue
        data = [Env.nodename, elems[0], elems[1], arc, "ips", None]
        lines.append(data)
    return lines


def listpkg_legacy():
    """
    Return a list of legacy packages installed.
    where pkg is (nodename, pkgname, version, arch, pkg, installed_epoch or None)
    """
    if which('pkginfo') is None:
        return []
    cmd = ['pkginfo', '-l']
    out, _, _ = justcall(cmd)
    lines = []
    for line in out.splitlines():
        elems = line.split(':', 1)
        if len(elems) != 2:
            continue
        key, val = [elem.strip() for elem in elems]
        if key == "PKGINST":
            data = [Env.nodename, val, "", "", "pkg", None]
        elif key == "VERSION":
            data[2] = val
        elif key == "ARCH":
            data[3] = val
            lines.append(data)

    for idx, line in enumerate(lines):
        # pkg install date
        try:
            mtime = os.stat("/var/sadm/pkg/"+line[1])[ST_MTIME]
        except Exception:
            mtime = None
        lines[idx][5] = mtime

    return lines


def listpkg():
    """
    Return a list of ips and legacy packages installed.
    where pkg is (nodename, pkgname, version, arch, "ips" or "pkg", installed_epoch or None)
    """
    return listpkg_legacy() + listpkg_ips()


def listpatch():
    """
    Return a list of patches installed.

    Patch: patchnum-rev Obsoletes: num-rev[,patch-rev]... Requires: .... Incompatibles: ... Packages: ...
    """
    if which('showrev') is None:
        return []
    cmd = ['showrev', '-p']
    out, _, _ = justcall(cmd)
    lines = []
    nodename = Env.nodename
    for line in out.splitlines():
        elems = line.split(' ')
        if len(elems) > 3:
            _elems = elems[1].split('-')
            if len(_elems) != 2:
                continue
            else:
                lines.append([nodename, _elems[0], _elems[1]])

    for idx, line in enumerate(lines):
        # pkg install date
        try:
            mtime = os.stat("/var/sadm/patch/"+line[1])[ST_MTIME]
        except Exception:
            mtime = None
        lines[idx].append(mtime)

    return lines


if __name__ == "__main__":
    print(listpkg())
 0707010001f542000081a40000000000000000000000016a100daf00000485000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/packages/list/freebsd.py from env import Env
from utilities.cache import cache
from utilities.proc import justcall, which


def listpkg():
    """
    Return a list of ips packages installed.
    where pkg is (nodename, pkgname, version, arch or "")
    """
    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 = [Env.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 = [Env.nodename] + l
        lines.append(x)
    return lines


def listpatch():
    return []
   0707010001f544000081a40000000000000000000000016a100daf00000a40000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/packages/list/linux.py   import os
from collections import namedtuple
from stat import *

from env import Env
from utilities.proc import justcall, which


def listpkg_dummy():
    print("pushpkg not supported on this system")
    return []


def listpkg_snap():
    """
    Example:

        Name      Version    Rev   Tracking  Publisher   Notes
        core      16-2.35.4  5662  stable    canonical*  core
        inkscape  0.92.3     4274  stable    inkscape*   -
        skype     8.32.0.44  60    stable    skype*      classic

    """
    if not which("snap"):
        return []
    cmd = ["snap", "list", "--unicode=never", "--color=never"]
    out, err, ret = justcall(cmd)
    lines = []
    header = namedtuple("", "")
    for line in out.splitlines():
        if line.startswith('Name'):
            header = namedtuple("header", line)
            continue
        _data = header._make(line.split())
        lines.append([
            Env.nodename,
            _data.Name,
            "%s rev %s" % (_data.Version, _data.Rev),
            "",
            "snap",
        ])
    return lines


def listpkg_rpm():
    if not which("rpm"):
        return []
    cmd = ['rpm', '-qai', '--queryformat=XX%{n} %{v}-%{r} %{arch} rpm %{installtime}\n']
    out, err, ret = justcall(cmd)
    lines = []
    sig = ""
    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] = int(l[4])
        except:
            l[4] = None
        x = [Env.nodename] + l + [sig]
        lines.append(x)
        sig = ""
    return lines


def listpkg_deb():
    if not which("dpkg"):
        return []
    cmd = ['dpkg', '-l']
    out, err, ret = justcall(cmd)
    lines = []
    arch = ""
    for line in out.splitlines():
        l = line.split()
        if len(l) < 4:
            continue
        if l[0] != "ii":
            continue
        x = [Env.nodename] + l[1:3] + [arch, "deb"]
        try:
            t = os.stat("/var/lib/dpkg/info/"+l[1]+".list")[ST_MTIME]
        except:
            t = None
        x.append(t)
        lines.append(x)
    return lines


def listpkg():
    """
    returns [pkg, ...] where pkg is (nodename, pkgname, version, arch, [type, [installed_at, [sig]]])
    where installed_at is epoch timestamp or None when unknown
    """
    data = listpkg_deb()
    data += listpkg_rpm()
    data += listpkg_snap()
    return data


def listpatch():
    return []
0707010001f543000081a40000000000000000000000016a100daf0000037d000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/packages/list/hpux.py    from env import Env
from utilities.proc import call, which


def listpkg():
    """
    returns list of installed packages
    where pkg is: nodename, pkgname, version, "", "product" or "bundle", installed_epoch or None
    """
    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] = int(l[2])
        except:
            l[2] = None
        x = [Env.nodename, l[0], l[1], '', t, l[2]]
        lines.append(x)
    return lines


def listpatch():
    return []
   0707010001f547000081a40000000000000000000000016a100daf000011b6000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/packages/list/windows.py from collections import namedtuple
from ctypes import byref, create_unicode_buffer, windll
from ctypes.wintypes import DWORD
from itertools import count

from env import Env

# 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


def listpkg():
    plist = get_installed_products()
    lines = []
    for p in plist:
        x = [Env.nodename,
             p.ProductName,
             p.VersionString,
             "",
	     "msi",
             p.InstallDate]
        lines.append(x)
    return lines


def listpatch():
    import foreign.wmi as 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([
          Env.nodename,
          hotfix.HotFixID,
          "",
          t
        ])
    return lines

if __name__ == "__main__":
    print(get_installed_products())
  0707010001f540000081a40000000000000000000000016a100daf00000252000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/packages/list/aix.py from env import Env
from utilities.proc import justcall


def listpkg():
    """
    Return a list of ips packages installed.
    where pkg is (nodename, pkgname, version, "")
    """
    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 = [Env.nodename, pkgname, pkgvers, '']
        lines.append(x)
    return lines


def listpatch():
    return []
  0707010001f53d000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/packages/__init__.py 0707010001f502000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/utilities/files    0707010001f503000081a40000000000000000000000016a100daf00000cb7000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/files/__init__.py    import os
import stat

from errno import EEXIST

PROTECTED_DIRS = [
    '/',
    '/bin',
    '/boot',
    '/dev',
    '/dev/pts',
    '/dev/shm',
    '/home',
    '/opt',
    '/proc',
    '/sys',
    '/tmp',
    '/usr',
    '/var',
]


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 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()


def makedirs(path, mode=None, uid=None, gid=None):
    """
    Wraps os.makedirs with a more restrictive 755 mode and ignore
    already exists errors.
    """
    try:
        mode = mode or 0o755
        os.makedirs(path, mode)
    except OSError as exc:
        if exc.errno == EEXIST:
            pass
        else:
            raise
    uid = uid if uid is not None else -1
    gid = gid if gid is not None else -1
    os.chown(path, uid, gid)


def rmtree_busy(path):
    import shutil
    import errno

    def onerror(fn, path, exc):
        if exc[1].errno in (errno.EBUSY, errno.ENOTEMPTY):
            return
        raise exc[1]

    shutil.rmtree(path, onerror=onerror)


def create_protected_file(filepath, buff):
    import foreign.six
    def onfile(f):
        if os.name == 'posix':
            os.chmod(filepath, 0o0600)
        f.write(buff)

    if foreign.six.PY2:
        if isinstance(buff, foreign.six.text_type):
            import codecs
            with codecs.open(filepath, "w", "utf-8") as f:
                onfile(f)
        else:
            with open(filepath, "w") as f:
                onfile(f)
    else:
        with open(filepath, "w") as f:
            onfile(f)


def read_unicode_file(filepath):
    import foreign.six
    if foreign.six.PY2:
        import codecs
        with codecs.open(filepath, "r", "utf-8") as f:
            return f.read()
    else:
        with open(filepath, "r") as f:
            return f.read()


def assert_file_exists(filename):
    if not os.path.exists(filename):
        raise Exception("%s is not present" % filename)


def assert_file_is_root_only_writeable(filename):
    if not os.path.exists(filename):
        raise Exception("%s is not present." % filename)
    stat_info = os.stat(filename)
    if stat_info.st_uid != 0:
        raise Exception("%s does not belong to root" % filename)
    if stat_info.st_mode & stat.S_IWOTH:
        raise Exception("%s is world writable" % filename)

def unlink_and_sync(filename):
    os.unlink(filename)
    dirname = os.path.dirname(filename)
    try:
        fd = os.open(dirname, os.O_DIRECTORY)
    except AttributeError:
        fd = os.open(dirname, os.O_RDONLY)
    try:
        os.fsync(fd)
    finally:
        os.close(fd)

 0707010001f5c2000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/wakeonlan    0707010001f5c3000081a40000000000000000000000016a100daf000005e2000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/wakeonlan/__init__.py    import sys
import socket
import struct
import re


class wolrequest(object):
    sock_tmo = 5.0

    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)
        self.sock.settimeout(self.sock_tmo)

    def check_mac(self):
        if not ':' and not '-' in self.mac:
            return False
        regex_mac = re.compile('[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$')
        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):
        regex_broadcast = re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')
        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

  0707010001f5bb000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/timeit   0707010001f5bc000081a40000000000000000000000016a100daf00000789000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/timeit/__init__.py   from __future__ import print_function

import inspect
import os
import sys

TIMING = os.environ.get("OSVC_TIMING")
TIMING_DATA = []

if TIMING:
    import atexit
    import json
    import time

    exec_start = time.time()

    def print_timings():
        exec_end = time.time()
        exec_duration = (exec_end - exec_start) * 1000
        total_duration = 0
        total_calls = 0
        for d in sorted(TIMING_DATA, key=lambda x: x["duration"]):
            print(json.dumps(d, indent=4), file=sys.stderr)
            total_duration += d["duration"]
            total_calls += 1
        print("Total number of timed functions calls: %d" % total_calls, file=sys.stderr)
        print("Total duration of timed functions:     %2.2f ms" % total_duration, file=sys.stderr)
        print("Total duration of the execution:       %2.2f ms" % exec_duration, file=sys.stderr)

    atexit.register(print_timings)

def timeit(method):
    if TIMING != "1":
        return method
    def timed(*args, **kwargs):
        ts = time.time()
        result = method(*args, **kwargs)
        te = time.time()
        du = (te - ts) * 1000
        frame = inspect.stack()[1]
        try:
            cmd = " ".join(args[0])
        except:
            cmd = str(args)
        try:
            _, fr_filename, fr_lineno, fr_function, _, _ = frame
        except:
            fr_filename = frame.filename
            fr_lineno = frame.lineno
            fr_function = frame.function
        TIMING_DATA.append({
            "fn": method.__name__,
            "cmd": cmd,
            "start": ts,
            "duration": du,
            "frame": {
                "filename": fr_filename,
                "lineno": fr_lineno,
                "function": fr_function,
            },
        })
        #print("%r %2.2f ms\n  %s\n  %s\n  %s" % (method.__name__, du, frame, args, kwargs), file=sys.stderr)
        return result
    return timed


   0707010001f561000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/proc 0707010001f562000081a40000000000000000000000016a100daf00004a51000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/proc/__init__.py from __future__ import print_function

import locale
import logging
import os
import select
import shlex
import sys
import time
from errno import ENOENT, EACCES
from subprocess import Popen, PIPE

import core.exceptions as ex
import foreign.six as six
from env import Env
from utilities.string import bencode, bdecode, empty_string, is_string

# Os where os.access is invalid
OS_WITHOUT_OS_ACCESS = ['SunOS']

# lcall checkio() default timeout
LCALL_CHECK_IO_TIMEOUT = 0.2

if os.name == 'nt':
    close_fds = False
else:
    close_fds = True


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 oom_score_adj(pid="self", value=0):
    """update pid oom_score_adj value"""
    oom_score_adj_file = "/proc/%s/oom_score_adj" % pid
    if os.path.exists(oom_score_adj_file):
        with open(oom_score_adj_file, "w") as f:
            f.write(str(value))


def reset_self_oom_score_adj():
    """reset process oom_score_adj value to 0"""
    try:
        oom_score_adj(pid="self", value=0)
    except OSError:
        # Ignore OSError when decrease oom_score_adj
        pass

def get_updated_preexec_fn(preexec_fn):
    """
    Return updated preexec_fn function
    On Linux, the returned updated preexec_fn with reset process oom_score_adj
    value to 0.
    May be used to prevent inherited oom_score_adj values from opensvc daemon
    process.
    """
    if Env.sysname != "Linux":
        return preexec_fn

    if preexec_fn is None:
        return reset_self_oom_score_adj
    else:
        def func():
            reset_self_oom_score_adj()
            preexec_fn()

        return func


def justcall(argv=None, stdin=None, input=None):
    """
    Call subprocess' Popen(argv, stdout=PIPE, stderr=PIPE, stdin=stdin)
    The 'close_fds' value is autodectected (true on unix, false on windows).
    Returns (stdout, stderr, returncode)
    """
    if argv is None:
        argv = [Env.syspaths.false]
    if input:
        stdin = PIPE
        input = bencode(input)
    try:
        proc = Popen(argv, stdin=stdin, stdout=PIPE, stderr=PIPE,
                     close_fds=close_fds)
        out, err = proc.communicate(input=input)
        return bdecode(out), bdecode(err), proc.returncode
    except OSError as exc:
        if exc.errno in (ENOENT, EACCES):
            return "", "", 1
        raise


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) or not os.path.exists(fpath):
        return False
    if Env.sysname not in OS_WITHOUT_OS_ACCESS:
        return os.access(fpath, os.X_OK)
    else:
        return os_access_owner_ixusr(fpath)


def os_access_owner_ixusr(path):
    "alternative for os where root user os.access(path, os.X_OK) returns True"
    s_ixusr = 0o0100
    return bool(os.stat(path).st_mode & s_ixusr)


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()
    if "close_fds" not in kwargs:
        kwargs["close_fds"] = close_fds
    kwargs["preexec_fn"] = get_updated_preexec_fn(kwargs.get("preexec_fn"))
    os.environ["PYTHONIOENCODING"] = "UTF-8"
    rout, wout = os.pipe()
    rerr, werr = os.pipe()
    proc = Popen(cmd, stdout=wout, stderr=werr, **kwargs)
    log_level = {
        rout: outlvl,
        rerr: errlvl
    }
    pending = {
        rout: "",
        rerr: ""
    }
    terminated = False
    killed = False

    def check_io():
        logged = 0
        rlist, _, xlist = select.select([rout, rerr], [], [], LCALL_CHECK_IO_TIMEOUT)
        if xlist:
            return logged
        for io in rlist:
            buff = os.read(io, 32768)
            buff = buff.decode("utf-8", "ignore")
            if buff in ('', b''):
                continue
            buff = pending[io] + buff
            while True:
                l = buff.split("\n", 1)
                if len(l) == 1:
                    pending[io] = l[0]
                    break
                line, buff = l
                if logger:
                    logger.log(log_level[io], "| " + line)
                elif log_level[io] < logging.ERROR:
                    print(line)
                else:
                    print(line, file=sys.stderr)
                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:
                if logger:
                    logger.error("execution timeout (%.1f seconds). send SIGTERM." % timeout)
                else:
                    print("execution timeout (%.1f seconds). send SIGTERM." % timeout, file=sys.stderr)
                proc.terminate()
                terminated = True
            elif not killed and ellapsed > timeout * 2:
                if logger:
                    logger.error("SIGTERM handling timeout (%.1f seconds). send SIGKILL." % timeout)
                else:
                    print("SIGTERM handling timeout (%.1f seconds). send SIGKILL." % timeout, file=sys.stderr)
                proc.kill()
                killed = True

    while True:
        # check again to catch anything after the process exits
        logged = check_io()
        if logged == 0:
            break
    for io in rout, rerr:
        line = pending[io]
        if line:
            if logger:
                logger.log(log_level[io], "| " + line)
            elif log_level[io] < logging.ERROR:
                print(line)
            else:
                print(line, file=sys.stderr)
    os.close(rout)
    os.close(rerr)
    os.close(wout)
    os.close(werr)
    return proc.returncode


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):
    """
    Execute the command using Popen and 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 info:
        log.info(cmd)
    else:
        log.debug(cmd)

    if not hasattr(Env, "call_cache"):
        Env.call_cache = {}

    if cache and cmd not in Env.call_cache:
        log.debug("cache miss for '%s'" % cmd)

    preexec_fn = get_updated_preexec_fn(preexec_fn)

    if not cache or cmd not in Env.call_cache:
        try:
            process = Popen(argv, stdin=stdin, stdout=PIPE, stderr=PIPE, close_fds=close_fds, shell=shell,
                            preexec_fn=preexec_fn, cwd=cwd, env=env)
        except OSError as exc:
            if exc.errno == EACCES:
                log.error("command is not executable")
                return 1, "", ""
            elif exc.errno == ENOENT:
                log.error("command not found")
                return 1, "", ""
            raise
        buff = process.communicate()
        buff = tuple(map(lambda x: bdecode(x).strip(), buff))
        ret = process.returncode
        if ret == 0:
            if cache:
                log.debug("store '%s' output in cache" % cmd)
                Env.call_cache[cmd] = buff
        elif cmd in Env.call_cache:
            log.debug("discard '%s' output from cache because ret!=0" % cmd)
            del Env.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 = Env.call_cache[cmd]
        ret = 0
    if not empty_string(buff[1]):
        if err_to_info:
            log.info('stderr:')
            call_log(buff[1], log, "info")
        elif err_to_warn:
            log.warning('stderr:')
            call_log(buff[1], log, "warning")
        elif errlog:
            if ret != 0:
                call_log(buff[1], log, "error")
            elif warn_to_info:
                log.info('command successful but stderr:')
                call_log(buff[1], log, "info")
            else:
                log.warning('command successful but stderr:')
                call_log(buff[1], log, "warning")
        elif errdebug:
            log.debug('stderr:')
            call_log(buff[1], log, "debug")
    if not empty_string(buff[0]):
        if outlog:
            if ret == 0:
                call_log(buff[0], log, "info")
            elif err_to_info:
                log.info('command failed with stdout:')
                call_log(buff[0], log, "info")
            elif err_to_warn:
                log.warning('command failed with stdout:')
                call_log(buff[0], log, "warning")
            else:
                log.error('command failed with stdout:')
                call_log(buff[0], log, "error")
        elif outdebug:
            log.debug('output:')
            call_log(buff[0], log, "debug")

    return (ret, buff[0], buff[1])


def qcall(argv=None):
    """
    Execute command using Popen with no additional args, disgarding stdout and stderr.
    """
    if argv is None:
        return 1
    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 check_privs():
    if "OSVC_CONTEXT" in os.environ or "OSVC_CLUSTER" in os.environ:
        return
    if os.name == 'nt':
        return
    if os.geteuid() == 0:
        return
    print("Insufficient privileges", file=sys.stderr)
    sys.exit(1)


def action_triggers(self, trigger="", action=None, shell=False, **kwargs):
    """
    Executes a service or resource trigger. Guess if the shell mode is needed
    from the trigger syntax.
    """

    actions = [
        'provision',
        'unprovision',
        'start',
        'startstandby',
        'stop',
        'shutdown',
        'sync_nodes',
        'sync_drp',
        'sync_all',
        'sync_resync',
        'sync_update',
        'sync_restore',
        'run',
        'on_error', # tasks use that as an action
        'command',  # tasks use that as an action
    ]

    if hasattr(self, "svc"):
        svc = self.svc
        section = self.rid
    else:
        svc = self
        section = "DEFAULT"

    if action not in actions:
        return
    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

    try:
        if attr in self.skip_triggers:
            return
    except AttributeError:
        pass

    try:
        cmd = svc.conf_get(section, attr, use_default=False)
    except ValueError:
        # no corresponding keyword
        return
    except ex.OptNotFound:
        return

    if not cmd:
        svc.log.warning("empty trigger: %s.%s", section, attr)
        return

    try:
        if does_call_cmd_need_shell(cmd):
            shell = True
        cmdv = get_call_cmd_from_str(cmd, shell=shell)
    except ValueError as exc:
        raise ex.Error(str(exc))

    if not hasattr(self, "log_outputs") or getattr(self, "log_outputs"):
        self.log.info("%s: %s", attr, cmd)

    if svc.options.dry_run:
        return

    try:
        ret = self.lcall(cmdv, shell=shell, **kwargs)
    except OSError as osexc:
        ret = 1
        if osexc.errno == 8:
            self.log.error("%s exec format error: check the script shebang", cmd)
        else:
            self.log.error("%s error: %s", cmd, str(osexc))
    except Exception as exc:
        ret = 1
        self.log.error("%s error: %s", cmd, str(exc))

    if blocking and ret != 0:
        if action == "command":
            raise ex.ExecError("command return code", ret)
        else:
            raise ex.ExecError("%s: %s blocking error" % (attr, cmd), ret)

    if not blocking and ret != 0:
        if action == "command":
            self.log.warning("command return code [%d]" % ret)
        else:
            self.log.warning("%s: %s non-blocking error [%d]" % (attr, cmd, ret))


def has_option(option, cmd):
    """
    Return True if <option> is set in the <cmd> shlex list.
    """
    for word in cmd:
        if word == option:
            return True
        if word.startswith(option + "="):
            return True
    return False


def get_options(option, cmd):
    """
    Yield all <option> values in the <cmd> shlex list.
    """
    for i, word in enumerate(cmd):
        if word == option:
            yield cmd[i + 1]
        if word.startswith(option + "="):
            yield word.split("=", 1)[-1]


def get_option(option, cmd, boolean=False):
    """
    Get an <option> value in the <cmd> shlex list.
    """
    if boolean and option not in cmd:
        return False
    for i, word in enumerate(cmd):
        if word == option:
            if boolean:
                return True
            else:
                return cmd[i + 1]
        if word.startswith(option + "="):
            return word.split("=", 1)[-1]
    return


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 is True:
                to_drop += [i, i + 1]
            elif is_string(drop_value):
                if cmd[i + 1].startswith(drop_value):
                    to_drop += [i, i + 1]
                else:
                    # do not drop option
                    pass
            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 init_locale():
    try:
        locale.setlocale(locale.LC_ALL, ('C', 'UTF-8'))
    except locale.Error:
        pass
    if os.name != "posix":
        return
    locales = ["C.UTF-8", "en_US.UTF-8"]
    for loc in locales:
        if loc not in locale.locale_alias.values():
            continue
        try:
            locale.setlocale(locale.LC_ALL, loc)
        except locale.Error:
            continue
        os.environ["LANG"] = loc
        os.environ["LC_NUMERIC"] = "C"
        os.environ["LC_TIME"] = "C"
        if locale.getlocale()[1] == "UTF-8":
            return
    # raise ex.Error("can not set a C lang with utf8 encoding")
    os.environ["LANG"] = "C"
    os.environ["LC_NUMERIC"] = "C"
    os.environ["LC_TIME"] = "C"


def process_args(pid):
    cmd_args = ['/bin/ps', '-p', str(pid), '-o', 'args=']
    ret, stdout, stderr = call(cmd_args)
    if ret != 0:
        return False, ''
    else:
        return True, stdout


def process_match_args(pid, search_args=None):
    running, args = process_args(pid)
    if running:
        return search_args in args
    else:
        return False


def daemon_process_running():
    try:
        with open(Env.paths.daemon_pid, 'r') as pid_file:
            pid = int(pid_file.read())
        with open(Env.paths.daemon_pid_args, 'r') as pid_args_file:
            search_args = pid_args_file.read()
        return process_match_args(pid, search_args=search_args)
    except:
        return False


def find_editor():
    if "EDITOR" in os.environ:
        editor = os.environ["EDITOR"]
    elif os.name == "nt":
        editor = "notepad"
    else:
        editor = "vi"
    if not which(editor):
        raise ex.Error("%s not found" % editor)
    return editor


def get_extra_argv(argv=None):
    """
    Extract extra argv from "om array" and "om collector cli" argv.

    om node 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:
        if argv in (["array", "ls"], ["array", "show"]):
            return 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 call_log(buff="", log=None, level="info"):
    if not buff:
        return
    lines = buff.rstrip().split("\n")
    try:
        fn = getattr(log, level)
    except Exception:
        return
    for line in lines:
        fn("| " + line)


def get_call_cmd_from_str(cmd, shell=False):
    """
    Return the cmd arg usable by ?call
    """
    if shell:
        return cmd
    else:
        if six.PY2:
            cmdv = shlex.split(cmd.encode('utf8'))
            return [elem.decode('utf8') for elem in cmdv]
        else:
            return shlex.split(cmd)

def does_call_cmd_need_shell(cmd):
    return "|" in cmd or "&&" in cmd or ";" in cmd
   0707010001f4da000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/converters   0707010001f4db000081a40000000000000000000000016a100daf00002532000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/converters/__init__.py   """
Converters, used by arguments and config file parsers.
"""
import re
import shlex
import datetime
import json

import foreign.six as six

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


def convert_datetime(s):
    if s is None:
        return
    if isinstance(s, datetime.datetime):
        return s
    if isinstance(s, datetime.date):
        return datetime.datetime(s)
    s = str(s)
    mask = "1970-01-01 00:00:00"
    length = len(s)
    if length > 19:
        s = s[:19]
    try:
        s = s + mask[length:]
    except IndexError:
        raise ValueError("unsupported datetime format %s. expect YYYY-MM-DD "
                         "HH:MM:SS, or right trimmed substring of")
    s = re.sub(r"\s+", ".", s)
    s = s.replace(":", ".")
    s = s.replace("-", ".")
    return datetime.datetime.strptime(s, "%Y.%m.%d.%H.%M.%S")


def convert_json(s):
    try:
        return json.loads(s)
    except Exception:
        return


def convert_shlex(s):
    if s is None:
        return
    if isinstance(s, list):
        return s
    if six.PY2:
        return shlex.split(s.encode("utf-8"))
    else:
        return shlex.split(s)


def convert_expanded_shlex(s):
    """
    Like the shlex converter but expands -it into -i -t
    """
    args = convert_shlex(s)
    if s is None:
        return
    new_args = []
    for arg in args:
        if arg and len(arg) > 2 and "=" not in arg and arg[0] == "-" and arg[1] != "-":
            for flag in arg[1:]:
                new_args.append("-" + flag)
        else:
            new_args.append(arg)
    return new_args


def convert_lower(s):
    """
    Return <s> lowercased
    """
    if s is None:
        return
    return s.lower()


def convert_integer(s):
    """
    Return <s> cast to int.
    """
    if s is None:
        return
    try:
        return int(float(s))
    except ValueError:
        return


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 [member.lower() for member in 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 [word for word in re.split(r"\s*,\s*", s.strip()) if word != ""]


def convert_set(s):
    """
    Return convert_list result cast to a set.
    """
    if s is None:
        return 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 s is None:
        return 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_tristate(s):
    """
    A tri-state returns None for None, True for true values,
    False for false values.
    """
    if s is None:
        return
    return convert_boolean(s)


def convert_duration_minute(s):
    return convert_duration(s, _from="m")


def convert_duration_to_day(s):
    return convert_duration(s, _to="d")


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:
        return

    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(r"[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(",", ".")
    s = s.replace("B", "")
    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 ValueError:
        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, precision=3):
    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', 'Z', 'Y']
    units_index = {'': 0, 'K': 1, 'M': 2, 'G': 3, 'T': 4, 'E': 5, 'Z': 6, 'Y': 7}
    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
    done = False
    for u in units[units_index[unit]:]:
        if size == 0:
            return "0"
        u = u.lower() if compact else u
        if size < mult:
            done = True
            break
        size = size / mult
    if not done:
        size *= mult
    ufmt = "%d"
    for exp in range(0, precision - 1):
        if size < 10 ** (exp + 1):
            ufmt = "%." + str(precision - 1 - exp) + "f"
            break
    return (ufmt + '%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')


def print_duration(secs, _round=2):
    buff = ""
    idx = 0
    if secs >= 86400:
        n = secs // 86400
        secs = secs % 86400
        buff += "%dd" % n
        idx += 1
        if _round and idx >= _round:
            return buff
    if secs >= 3600:
        n = secs // 3600
        secs = secs % 3600
        buff += "%02dh" % n
        idx += 1
        if _round and idx >= _round:
            return buff
    if secs >= 60:
        n = secs // 60
        secs = secs % 60
        buff += "%02dm" % n
        idx += 1
        if _round and idx >= _round:
            return buff[:-1]
    if secs > 0:
        buff += "%05.2f" % secs
        if idx == 0:
            if buff[3:] == "00":
                return buff[:2] + "s"
            else:
                return buff[:2] + "s" + buff[3:]
        else:
            return buff[:-3]
    if buff:
        return buff
    return "-"


if __name__ == "__main__":
    print(print_duration(92000))
    print(print_duration(86400))
    print(print_duration(9200))
    print(print_duration(1))
  0707010001f591000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/utilities/ssl  0707010001f592000081a40000000000000000000000016a100daf00000fbf000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/ssl/__init__.py  import os
import datetime
import shutil
import time
from subprocess import Popen, PIPE

import core.exceptions as ex
from utilities.files import makedirs
from utilities.proc import justcall
from utilities.string import is_string
from utilities.net.ipaddress import ip_address

keymap = [
    ("C", "c"),
    ("ST", "st"),
    ("L", "l"),
    ("O", "o"),
    ("OU", "ou"),
    ("CN", "cn"),
    ("emailAddress", "email"),
]

def gen_cert(log=None, **data):
    for k in ("key", "crt"):
        d = os.path.dirname(data[k])
        if not os.path.isdir(d):
            makedirs(d)
    data["subject"] = format_subject(**data)
    data["alt_names"] = format_alt_names(**data)
    gen_csr(log=log, **data)
    if data.get("cakey") is None:
        gen_self_signed_cert(log=log, **data)
    else:
        gen_ca_signed_cert(log=log, **data)

def format_subject(**data):
    l = [""]
    for k, sk in keymap:
        if sk not in data:
            continue
        l.append(k+"="+data[sk])
    l.append("")
    subject = "/".join(l)
    return subject

def format_alt_names(**data):
    if "alt_names" not in data:
        return
    if len(data["alt_names"]) == 0:
        return
    l = []
    for i, d in enumerate(data["alt_names"]):
        try:
            ip = ip_address(d)
        except ValueError:
            ip = None
        if d.startswith("IP:") or d.startswith("DNS:"):
            l.append(d)
        elif ip:
            l.append("IP:%s" % d)
        else:
            l.append("DNS:%s" % d)
    return "subjectAltName = %s" % ",".join(l)

def gen_self_signed_cert(log=None, **data):
    days = data["validity"]
    if days < 1:
        days = 1
    cmd = ["openssl", "req", "-x509", "-nodes",
           "-key", data["key"],
           "-out", data["crt"],
           "-days", str(days),
           "-subj", "%s" % data["subject"]]
    if data.get("alt_names"):
        cmd += ["-addext", data.get("alt_names")]
    if log:
        log.info(" ".join(cmd))
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error(out+err)

def gen_ca_signed_cert(log=None, **data):
    sign_csr(log=log, **data)

def gen_csr(log=None, **data):
    cmd = ["openssl", "req", "-new", "-nodes",
           "-newkey", "rsa:%d" % data.get("bits", 4096),
           "-keyout", data["key"],
           "-out", data["csr"],
           "-subj", "%s" % data["subject"]]
    if data.get("alt_names"):
        cmd += ["-addext", data.get("alt_names")]
    if log:
        log.info(" ".join(cmd))
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error(out+err)

def sign_csr(log=None, **data):
    days = data["validity"]
    if days < 1:
        days = 1
    cmd = ["openssl", "x509", "-req",
           "-in", data["csr"],
           "-CA", data["cacrt"],
           "-CAkey", data["cakey"],
           "-CAcreateserial",
           "-out", data["crt"],
           "-days", str(days),
           "-sha256"]
    if data.get("alt_names"):
        write_openssl_cnf(data)
        cmd += ["-extfile", data["cnf"], "-extensions", "SAN"]
    if log:
        log.info(" ".join(cmd))
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error(out+err)

def write_openssl_cnf(data):
    openssl_cnf_location = [
        '/etc/ssl/openssl.cnf',
        '/etc/pki/tls/openssl.cnf',
    ]
    openssl_cnf = None
    for loc in openssl_cnf_location:
       if os.path.exists(loc):
           openssl_cnf = loc
    if not openssl_cnf:
        raise ex.Error("could not determine openssl.cnf location")
    shutil.copy(openssl_cnf, data["cnf"])
    with open(data["cnf"], "a") as f:
        f.write("\n[SAN]\n%s\n" % data["alt_names"])

def get_expire(data):
    if not data:
        return
    cmd = ["openssl", "x509", "-noout", "-enddate"]
    out, err, ret = justcall(cmd, input=data)
    out = out.split("=", 1)[-1].strip()
    if ret != 0:
        return
    try:
        return time.mktime(datetime.datetime.strptime(out, "%b %d %H:%M:%S %Y %Z").timetuple())
    except ValueError:
        return None

 0707010001f5a7000081a40000000000000000000000016a100daf0000024d000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/storage.py   from copy import deepcopy
try:
    import copy_reg
except ImportError:
    import copyreg as copy_reg


class Storage(dict):
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__
    __getitem__ = dict.get
    __getattr__ = dict.get
    __repr__ = lambda self: '<Storage %s>' % dict.__repr__(self)
    __copy__ = lambda self: Storage(self)  # pylint: disable=undefined-variable

    def __deepcopy__(self, memo=None):
        return Storage(deepcopy(dict(self), memo=memo))


def pickle_storage(s):
    return Storage, (dict(s),)


copy_reg.pickle(Storage, pickle_storage)
   0707010001f4e6000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/utilities/devtree  0707010001f4ef000081a40000000000000000000000016a100daf00002b17000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/devtree/sunos.py import glob
import os
import re

from subprocess import PIPE

from .devtree import DevTree as BaseDevTree
from .veritas import DevTreeVeritas
from utilities.devices.sunos import prtvtoc
from env import Env
from utilities.cache import cache
from utilities.proc import justcall

class DevTree(DevTreeVeritas, BaseDevTree):
    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
        """
        out = prtvtoc(d.devpath[0])
        if out is None:
            return
        for line in out.splitlines():
            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()
            self.load_dsk()

    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_dsk(self):
        for devpath in glob.glob("/dev/rdsk/*s2"):
            bdevpath = devpath.replace("/rdsk/", "/dsk/")
            devname = devpath.replace("/dev/rdsk/", "")[:-2]
            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_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
        """
        out, err, ret = justcall(["format", "-e"], stdin=PIPE)
        for line in out.splitlines():
            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
        out, err, ret = justcall(["metastat", "-p"])
        if ret != 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):
        out = self.zpool_list()
        if out is None:
            return
        for line in out.splitlines():
            l = line.split()
            if len(l) == 0:
                continue
            poolname = l[0]
            self.load_zpool1(poolname)

    @cache("zpool.list")
    def zpool_list(self):
        out, err, ret = justcall(["zpool", "list", "-H"])
        if ret != 0:
            return
        return out

    @cache("zfs.list.snapshots")
    def zfs_list_snapshots(self):
        out, err, ret = justcall(["zfs", "list", "-H", "-t", "snapshot"])
        if ret != 0:
            return
        return out

    def load_zpool1(self, poolname):
        out, err, ret = justcall(["zpool", "status", poolname])
        if ret != 0:
            return
        self.zpool_members[poolname] = []
        for line in out.splitlines():
            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(Env.paths.pathvar):
                devname = devname.split('/')[-1]
            d = self.get_dev(devname)
            if d is None:
                continue
            self.zpool_members[poolname].append(d)

        out, err, ret = justcall(["zpool", "iostat", poolname])
        if ret != 0:
            return
        lines = out.split('\n')
        lines = [l for l in lines if len(l) > 0]
        zpool_iostats = lines[-1].split()
        if zpool_iostats[0] != poolname:
            # may be a FAULTY zpool, so no stats
            return
        self.zpool_used[poolname] = self.read_size(zpool_iostats[1])
        zpool_free = self.read_size(zpool_iostats[2])
        self.zpool_size[poolname] = self.zpool_used[poolname] + zpool_free

        out, err, ret = justcall(["zfs", "list", "-H", "-r", "-t", "filesystem", poolname])
        if ret != 0:
            return
        self.zpool_datasets[poolname] = []
        self.zpool_datasets_used[poolname] = 0
        for line in out.splitlines():
            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

        out = self.zfs_list_snapshots()
        if out is None:
            return

        for line in out.splitlines():
            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, "linear")
            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)
                m.dg = poolname
                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 utilities.diskinfo 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()))
 0707010001f4f0000081a40000000000000000000000016a100daf00002024000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devtree/veritas.py   import glob
import os
from subprocess import *

from .devtree import DevTree as BaseDevTree
from core.capabilities import capabilities
from utilities.proc import justcall

class DevTreeVeritas(BaseDevTree):
    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 "node.x.vxdmpadm" not in capabilities:
            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")


0707010001f4ee000081a40000000000000000000000016a100daf00000be8000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/devtree/osf1.py  import re

from .devtree import DevTree as BaseDevTree
from utilities.diskinfo import DiskInfo
from utilities.proc import justcall


class DevTree(BaseDevTree):
    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
        bs = 0
        for line in out.split("\n"):
            if line.startswith("bytes/sector"):
                bs = int(line.split()[-1])
            if re.match(r'\W*[a-h]:', 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()))
0707010001f4e7000081a40000000000000000000000016a100daf000000d4000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/devtree/__init__.py  import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
DevTree = _os.DevTree

0707010001f4f1000081a40000000000000000000000016a100daf00000290000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devtree/windows.py   import foreign.wmi as wmi

from .devtree import DevTree as BaseDevTree

class DevTree(BaseDevTree):

    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()))
0707010001f4ea000081a40000000000000000000000016a100daf0000356e000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devtree/devtree.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

"""
import os

from utilities.render.forest import Forest
from utilities.converters import print_size
from env import Env
from utilities.render.color import color
from utilities.hash.md5 import hexdigest


class DevRelation(object):
    def __init__(self, parent, child, used=0):
        self.child = child
        self.parent = parent
        self.used = used
        self.used_set = False
        self.tree = None

    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
        self.tree = None
        self.dg = ""

        # 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":
            if not any(os.path.exists(p) for p in self.devpath):
                return set()
            return set([self])
        d = set()
        for parent in self.parents:
            dev = self.get_dev(parent.parent)
            if any(os.path.exists(p) for p in dev.devpath):
                d |= dev.get_top_devs()
        return d

    def get_top_devs_chain(self, chain=None):
        if chain is None:
            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=None, verbose=False):
        if highlight is None:
            highlight = []
        if relation is None:
            parent_size = 0
        else:
            parent_size = self.get_dev(relation.parent).get_size()
        if parent_size == 0:
            pct = "-"
        else:
            pct = "%.2f%%" % (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), align="right")
        node_dev.add_column(pct, align="right")
        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=None, node=None, highlight=None,
                            verbose=False):
        if highlight is None:
            highlight = []
        if chain is None:
            chain = []
        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 = "%.2f%%" % (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, align="right")
        node_dev.add_column(print_size(self.size), align="right")
        node_dev.add_column(pct, align="right")
        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=None):
        if l is None:
            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(Env.nodename, color.BOLD)
        node.add_column("Type", color.BOLD)
        node.add_column("Size", color.BOLD, align="right")
        node.add_column("Pct of Parent", color.BOLD, align="right")

        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)

        ftree.out()

    def print_tree_bottom_up(self, devices=None, verbose=False):
        ftree = Forest()
        node = ftree.add_node()
        node.add_column(Env.nodename, color.BOLD)
        node.add_column("Type", color.BOLD)
        node.add_column("Parent Use", color.BOLD, align="right")
        node.add_column("Size", color.BOLD, align="right")
        node.add_column("Ratio", color.BOLD, align="right")

        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)

        ftree.out()

    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 = hexdigest(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)
  0707010001f4eb000081a40000000000000000000000016a100daf00000074000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devtree/freebsd.py   from .devtree import DevTree as BaseDevTree


class DevTree(BaseDevTree):
    def load(self, di=None):
        pass
0707010001f4e9000081a40000000000000000000000016a100daf00000073000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/devtree/darwin.py    from .devtree import DevTree as BaseDevTree

class DevTree(BaseDevTree):
    def load(self, di=None):
        pass
 0707010001f4e8000081a40000000000000000000000016a100daf00000811000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/devtree/aix.py   import re
from subprocess import *

from .devtree import DevTree as BaseDevTree
import utilities.subsystems.lvm.aix
from utilities.proc import which

class DevTree(BaseDevTree):
    def load_lvm(self):
        lvm = utilities.subsystems.lvm.aix.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()))
   0707010001f4ed000081a40000000000000000000000016a100daf0000519c000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/devtree/linux.py from __future__ import division

import glob
import os
import re
from subprocess import *

import math

from .devtree import DevTree as BaseDevTree, Dev as BaseDev
from .veritas import DevTreeVeritas
import core.exceptions as ex
from core.capabilities import capabilities
from env import Env
from utilities.mounts import Mounts


class Dev(BaseDev):
    def remove_loop(self, r):
        cmd = [Env.syspaths.losetup, "-d", self.devpath[0]]
        ret, out, err = r.vcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        self.removed = True

    def remove_dm(self, r):
        cmd = [Env.syspaths.dmsetup, "remove", self.alias]
        ret, out, err = r.vcall(cmd)
        if ret != 0:
            raise ex.Error(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(DevTreeVeritas, BaseDevTree):
    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):
        try:
            return getattr(self, "dm_h")
        except AttributeError:
            pass
        self.dm_h = {}
        self._dm_h = {}
        if not os.path.exists("/dev/mapper"):
            return self.dm_h
        try:
            cmd = [Env.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 "node.x.multipath" not in capabilities:
            return None
        if not hasattr(self, 'multipath_l'):
            self.multipath_l = []
            cmd = [Env.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):
        try:
            return getattr(self, 'wwid_h')
        except AttributeError:
            pass
        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 "node.x.multipath" not in capabilities:
            return self.wwid_h
        cmd = [Env.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):
        try:
            return getattr(self, 'mp_h')
        except AttributeError:
            pass
        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 "node.x.powermt" not in capabilities:
            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
        name = None
        paths = []
        mp_h = {}
        did = ""
        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 "node.x.dmsetup" not in capabilities:
            return {}
        cmd = [Env.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):
        try:
            return getattr(self, "md_h")
        except AttributeError:
            pass
        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 "node.x.dmsetup" not in capabilities:
            return
        cmd = [Env.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 "node.x.dmsetup" not in capabilities:
            return
        cmd = [Env.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.dg = s.split("/", 1)[0]
                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 "node.x.udevadm" not in capabilities:
            return
        cmd = ["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):
        try:
            return getattr(self, "lv_linear")
        except AttributeError:
            pass
        self.lv_linear = {}
        if "node.x.dmsetup" not in capabilities:
            return self.lv_linear
        cmd = [Env.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 = [Env.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()
        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 "node.x.drbdadm" not in capabilities 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.iter('resource'):
            for host in res.findall('host'):
                if host.attrib['name'] != Env.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)
            if d.dg == "" and holdername in self._dm_h:
                alias = self._dm_h[holdername]
                s = alias.replace('--', ':').replace('-', '/').replace(':','-')
                d.dg = s.split("/", 1)[0]
            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.dg = s.split("/", 1)[0]
            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()
        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 utilities.diskinfo 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()))
0707010001f4ec000081a40000000000000000000000016a100daf00001804000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/devtree/hpux.py  import re
from subprocess import *

from .devtree import DevTree as BaseDevTree
from utilities.proc import which
from utilities.diskinfo import DiskInfo

di = DiskInfo()

class DevTree(BaseDevTree):
    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
        """
        d = None
        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 = {}
        size = 0
        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()
        vgname = ""
        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()))
0707010001f580000041ed0000000000000000000000076a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/snap 0707010001f582000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/snap/advfs   0707010001f584000081a40000000000000000000000016a100daf00000cf1000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/snap/advfs/osf1.py   import os

import core.exceptions as ex
import utilities.snap
import utilities.subsystems.advfs
from env import Env
from utilities.files import protected_mount
from utilities.mounts.osf1 import Mounts


class Snap(utilities.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 = utilities.subsystems.advfs.Fdmns()
        try:
            d = o.get_fdmn(dom)
        except utilities.subsystems.advfs.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(Env.paths.pathtmp, 'clonefset/%s/%s/osvc_sync'%(m.svc.fullname, 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.Error
        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 = utilities.subsystems.advfs.Fdmns()
        try:
            d = o.get_fdmn(dom)
        except utilities.subsystems.advfs.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.Error
        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.Error

        (ret, buff, err) = self.vcall(['rmfset', '-f', dom, clonefset])
        if ret != 0:
            raise ex.syncSnapDestroyError
   0707010001f583000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/snap/advfs/__init__.py   0707010001f58e000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/snap/zfs 0707010001f590000081a40000000000000000000000016a100daf0000059e000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/snap/zfs/sunos.py    import core.exceptions as ex
import utilities.snap
from env import Env
from utilities.subsystems.zfs import dataset_exists

class Snap(utilities.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([Env.syspaths.zfs, 'destroy', snapdev ])
            if ret != 0:
                raise ex.syncSnapDestroyError
        (ret, buff, err) = self.vcall([Env.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([Env.syspaths.zfs, 'destroy', snapdev ])
        if ret != 0:
            raise ex.syncSnapDestroyError
  0707010001f58f000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/snap/zfs/__init__.py 0707010001f588000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/snap/lvm 0707010001f58a000081a40000000000000000000000016a100daf000011d9000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/snap/lvm/linux.py    import os

import core.exceptions as ex
import utilities.snap
import utilities.devices.linux

from env import Env
from utilities.files import protected_mount
from utilities.proc import justcall

class Snap(utilities.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) = utilities.devices.linux.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 utilities.devices.linux.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(Env.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([Env.syspaths.mount, '-t', m.fs_type, '-o', self.mntopt_and_ro(m), snap_dev, snap_mnt])
        if ret != 0:
            self.vcall([Env.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.Error
        cmd = ['fuser', '-kmv', self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd, err_to_info=True)
        cmd = [Env.syspaths.umount, self.snaps[s]['snap_mnt']]
        (ret, out, err) = self.vcall(cmd)

        utilities.devices.linux.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])

   0707010001f589000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/snap/lvm/__init__.py 0707010001f58b000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/snap/vxfs    0707010001f58c000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/snap/vxfs/__init__.py    0707010001f58d000081a40000000000000000000000016a100daf00000aca000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/snap/vxfs/hpux.py    import os

import core.exceptions as ex
import utilities.snap
from utilities.files import protected_mount
from utilities.proc import qcall

class Snap(utilities.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.Error

        """ 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)

  0707010001f581000081a40000000000000000000000016a100daf00001157000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/snap/__init__.py import os

import core.exceptions as ex

from core.resource import Resource


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):
    try:
        rs = self.svc.get_resourcesets("fs")[0]
    except IndexError:
        rs = None
    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(Resource):
    """Defines a snap object
    """
    def __init__(self, rid, optional=False, disabled=False, tags=None):
        self.snaps = {}
        super(Snap, self).__init__(
            rid,
            "sync.snap",
            optional=optional,
            disabled=disabled,
            tags=tags or set()
        )

    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.Error
            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 list(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

 0707010001f585000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/snap/jfs2    0707010001f586000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/snap/jfs2/__init__.py    0707010001f587000081a40000000000000000000000016a100daf00001003000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/snap/jfs2/aix.py import os

import core.exceptions as ex
import utilities.snap
from utilities.files import protected_mount
from utilities.proc import qcall

class Snap(utilities.snap.Snap):
    def lv_exists(self, device):
        device = device.split("/")[-1]
        ret = qcall(['lslv', device])
        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.Error

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

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

        """ 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)

 0707010001f564000041ed0000000000000000000000086a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/render   0707010001f56e000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/render/instance  0707010001f56f000081a40000000000000000000000016a100daf000036c0000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/utilities/render/instance/__init__.py  import re
import os

import utilities.render.forest

from env import Env
from utilities.naming import split_path, strip_path, resolve_path, is_service
from utilities.render.color import color, colorize, STATUS_COLOR
from utilities.storage import Storage
from core.objects.svc import DEFAULT_STATUS_GROUPS

def fmt_flags(resource, idata):
    """
    Format resource flags as a vector of character.

    R  Running
    M  Monitored
    D  Disabled
    O  Optional
    E  Encap
    P  Provisioned
    S  Standby
    X  UserStopped
    """
    provisioned = resource.get("provisioned", {}).get("state")

    rid = resource.get("rid")
    restart = resource.get("restart", 0)
    i_restart = idata.get("monitor", {}).get("restart", {}).get(rid, {})
    if isinstance(i_restart, dict):
        retries = i_restart.get("retries", 0)
    else:  # fallback on non restart_delay support
        retries = i_restart
    if resource.get("stopped"):
        restart_flag = "X"
    elif not isinstance(restart, int) or restart < 1:
        restart_flag = "."
    else:
        remaining_restart = restart - retries
        if remaining_restart < 0:
            remaining_restart = 0
        if remaining_restart < 10:
            restart_flag = str(remaining_restart)
        else:
            restart_flag = "+"

    flags = ""
    flags += "R" if rid in idata.get("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 "."
    flags += "." if provisioned else "P" if provisioned is False else "/"
    flags += 'S' if resource.get("standby") else '.'
    flags += restart_flag
    return flags

def dispatch_resources(idata, discard_disabled=False):
    """
    Sorted resources.
    Honor the --discard-disabled arg.
    """
    subsets = {}
    for group in DEFAULT_STATUS_GROUPS:
        subsets[group] = {}

    for rid, resource in idata.get("resources", {}).items():
        if discard_disabled and resource.get("disable", False):
            continue
        group = resource["type"].split(".", 1)[0]
        if "subset" in resource:
            subset = group + ":" + resource["subset"]
        else:
            subset = group
        if group not in subsets:
            subsets[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, idata, discard_disabled=False):
    done = set()
    def do(group):
        try:
            subset_names = sorted(subsets[group])
        except KeyError:
            return
        for subset in subset_names:
            if subset != group:
                node_subset = node_nodename.add_node()
                node_subset.add_column(subset)
                parallel = idata.get("subsets", {}).get(subset, {}).get("parallel", False)
                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, idata, discard_disabled=discard_disabled)
    for group in DEFAULT_STATUS_GROUPS:
        done.add(group)
        do(group)

    # data resources
    left = sorted(list(set(subsets.keys()) - done))
    for group in left:
        do(group)

def get_svc_notice(data):
    svc_notice = []
    if "cluster" in data:
        overall = data["cluster"].get("overall", "n/a")
        if overall == "warn":
            svc_notice.append(colorize(overall, STATUS_COLOR[overall]))
        placement = data["cluster"].get("placement", "n/a")
        if placement not in ("optimal", "n/a"):
            svc_notice.append(colorize(placement + " placement", color.RED))
        compat = data["cluster"].get("compat", True)
        if not compat:
            svc_notice.append(colorize("incompatible versions", color.RED))
    return ", ".join(svc_notice)

def instance_notice(overall=None, frozen=None, node_frozen=None, constraints=None,
                    provisioned=None, monitor=None, priority=Env.default_priority):
    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))
    elif provisioned == "mixed":
        notice.append(colorize("part-provisioned", color.RED))
    if priority != Env.default_priority:
        notice.append(colorize("p%d" % priority, color.LIGHTBLUE))
    if monitor == {}:
        # encap monitor
        pass
    elif 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)

def add_res_node(resource, parent, idata, rid=None, discard_disabled=False):
    if discard_disabled and resource.get("disable", False):
        return
    ers = get_ers(idata)
    rid = resource["rid"]
    node_res = parent.add_node()
    node_res.add_column(rid)
    node_res.add_column(fmt_flags(resource, idata))
    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", "n/a"):
        edata = Storage(idata["encap"].get(rid))
        encap_notice = instance_notice(
            overall=edata.overall,
            frozen=edata.frozen,
            constraints=edata.get("constraints", True),
            provisioned=edata.provisioned,
            monitor={},
            priority=edata.get("priority", Env.default_priority),
        )
        col.add_text(encap_notice, color.LIGHTBLUE)
    for line in resource.get("log", []):
        if line.startswith("warn:"):
            scolor = STATUS_COLOR["warn"]
        elif line.startswith("error:"):
            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", "n/a"):
        return

    add_subsets(ers[rid], node_res, idata, discard_disabled=discard_disabled)

def get_scope(path, mon_data):
    nodes = []
    for ndata in mon_data.get("nodes", {}).values():
        try:
            nodes = ndata["services"]["config"][path]["scope"]
            break
        except KeyError:
            pass
    return sorted(nodes)

def add_instances(node, path, ref_nodename, mon_data):
    for nodename in get_scope(path, mon_data):
        if nodename == ref_nodename:
            continue
        add_instance(node, nodename, path, mon_data)

def add_instance(node, nodename, path, mon_data):
    node_child = node.add_node()
    node_child.add_column(nodename, color.BOLD)
    node_child.add_column()
    try:
        data = mon_data["nodes"][nodename]["services"]["status"][path]
        avail = data.get("avail", "n/a")
        node_frozen = mon_data["nodes"][nodename].get("frozen")
    except (TypeError, KeyError) as exc:
        avail = "undef"
        node_frozen = False
        data = Storage()
    node_child.add_column(avail, STATUS_COLOR[avail])
    notice = instance_notice(
        overall=data.get("overall", "n/a"),
        frozen=data.get("frozen"),
        node_frozen=node_frozen,
        constraints=data.get("constraints", True),
        provisioned=data.get("provisioned"),
        monitor=data.get("monitor"),
        priority=data.get("priority", Env.default_priority),
    )
    node_child.add_column(notice, color.LIGHTBLUE)

def add_parents(node, idata, mon_data, namespace):
    parents = idata.get("parents", [])
    if len(parents) == 0:
        return
    node_parents = node.add_node()
    node_parents.add_column("parents")
    for parent in parents:
        add_parent(parent, node_parents, mon_data, namespace)

def add_parent(path, node, mon_data, namespace):
    node_parent = node.add_node()
    try:
        path, nodename = path.split("@")
        path = resolve_path(path, namespace)
        node_parent.add_column(strip_path(path, os.environ.get("OSVC_NAMESPACE")) + "@" + nodename, color.BOLD)
        try:
            avail = mon_data["nodes"][nodename]["services"]["status"][path].get("avail", "n/a")
        except KeyError:
            avail = "undef"
    except ValueError:
        nodename = None
        path = resolve_path(path, namespace)
        node_parent.add_column(strip_path(path, os.environ.get("OSVC_NAMESPACE")), color.BOLD)
        try:
            avail = mon_data["services"][path].get("avail", "n/a")
        except KeyError:
            avail = "undef"
    node_parent.add_column()
    node_parent.add_column(avail, STATUS_COLOR[avail])

def add_children(node, idata, mon_data, namespace):
    children = idata.get("children", [])
    if not children:
        return
    node_children = node.add_node()
    node_children.add_column("children")
    for child in children:
        add_child(child, node_children, mon_data, namespace)

def add_slaves(node, idata, mon_data, namespace):
    slaves = idata.get("slaves", [])
    if not slaves:
        return
    node_slaves = node.add_node()
    node_slaves.add_column("slaves")
    for child in slaves:
        add_child(child, node_slaves, mon_data, namespace)

def add_scaler_slaves(node, idata, mon_data, namespace):
    slaves = idata.get("scaler_slaves", [])
    if not slaves:
        return
    node_slaves = node.add_node()
    node_slaves.add_column("scaler")
    for child in slaves:
        add_child(child, node_slaves, mon_data, namespace)

def add_child(path, node, mon_data, namespace):
    node_child = node.add_node()
    node_child.add_column(strip_path(path, os.environ.get("OSVC_NAMESPACE")), color.BOLD)
    node_child.add_column()
    path = resolve_path(path, namespace)
    try:
        avail = mon_data["services"][path].get("avail", "n/a")
    except KeyError:
        avail = "undef"
    node_child.add_column(avail, STATUS_COLOR[avail])

def get_ers(idata, discard_disabled=False):
    # encap resources
    ers = {}
    for rid in idata.get("resources", {}):
        if not rid.startswith("container"):
            continue
        try:
            ejs = idata["encap"][rid]
            ers[rid] = dispatch_resources(ejs, discard_disabled=discard_disabled)
        except (KeyError, TypeError):
            continue
        except Exception as exc:
            print(exc)
            ers[rid] = {}
    return ers

def add_node_node(node_instances, nodename, idata, mon_data, discard_disabled=False):
    subsets = dispatch_resources(idata, discard_disabled=discard_disabled)

    try:
        node_frozen = mon_data["nodes"][nodename].get("frozen")
    except (KeyError, AttributeError):
        node_frozen = False
    notice = instance_notice(
        overall=idata.get("overall", "n/a"),
        frozen=idata.get("frozen"),
        node_frozen=node_frozen,
        constraints=idata.get("constraints", True),
        provisioned=idata.get("provisioned"),
        monitor=idata.get("monitor"),
        priority=idata.get("priority", Env.default_priority),
    )

    node_nodename = node_instances.add_node()
    node_nodename.add_column(nodename, color.BOLD)
    node_nodename.add_column()
    node_nodename.add_column(idata.get("avail", "n/a"), STATUS_COLOR[idata.get("avail", "n/a")])
    node_nodename.add_column(notice, color.LIGHTBLUE)
    add_subsets(subsets, node_nodename, idata, discard_disabled=discard_disabled)


def service_nodes(path, mon_data):
    nodes = set()
    if not mon_data and is_service(path, local=True):
        return set([Env.nodename])
    for nodename, data in mon_data.get("nodes", {}).items():
        _nodes = set(data.get("services", {}).get("config", {}).get(path, {}).get("scope", []))
        nodes |= _nodes
    return nodes

def format_instance(path, idata, mon_data=None, discard_disabled=False, nodename=None):
    name, namespace, kind = split_path(path)
    svc_notice = get_svc_notice(idata)

    tree = utilities.render.forest.Forest(
        separator=" ",
        widths=(
            (14, None),
            None,
            10,
            None,
        ),
    )
    node_name = tree.add_node()
    node_name.add_column(strip_path(path, os.environ.get("OSVC_NAMESPACE")), color.BOLD)
    node_name.add_column()
    if "cluster" in idata:
        node_name.add_column(idata["cluster"].get("avail", "n/a"), STATUS_COLOR[idata["cluster"].get("avail", "n/a")])
    else:
        node_name.add_column()
    node_name.add_column(svc_notice)
    node_instances = node_name.add_node()
    node_instances.add_column("instances")
    add_instances(node_instances, path, nodename, mon_data)
    if nodename in service_nodes(path, mon_data):
        add_node_node(node_instances, nodename, idata, mon_data, discard_disabled=discard_disabled)
    add_parents(node_name, idata, mon_data, namespace)
    add_children(node_name, idata, mon_data, namespace)
    add_scaler_slaves(node_name, idata, mon_data, namespace)
    add_slaves(node_name, idata, mon_data, namespace)

    tree.out()

0707010001f567000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/render/cluster   0707010001f568000081a40000000000000000000000016a100daf00008849000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/render/cluster/__init__.py   import os
import time
import re

import foreign.six as six
from utilities.converters import print_duration, print_size
from utilities.render.color import colorize, color, unicons
from utilities.render.listener import fmt_listener
from env import Env
from core.status import colorize_status
from utilities.naming import ANSI_ESCAPE, ANSI_ESCAPE_B, split_path, strip_path, format_path_selector, abbrev
from utilities.storage import Storage

DEFAULT_SECTIONS = [
    "threads",
    "arbitrators",
    "nodes",
    "services",
]

if six.PY2:
    pad = " "
    def print_bytes(val):
        return val+"\n"
    def bare_len(val):
        val = ANSI_ESCAPE.sub('', val)
        val = bytes(val).decode("utf-8")
        return len(val)
else:
    pad = b" "
    def print_bytes(val):
        return val.decode("utf-8")+"\n"
    def bare_len(val):
        val = ANSI_ESCAPE_B.sub(b'', val)
        val = bytes(val).decode("utf-8")
        return len(val)

def get_nodes(data):
    try:
        return data["cluster"]["nodes"]
    except:
        return [Env.nodename]

def fmt_svc_uptime(key, stats_data):
    if stats_data is None:
        return ""
    total = 0
    now = time.time()
    top = 0
    for node, _data in stats_data.items():
        try:
            uptime = now - _data["services"][key]["created"]
            if uptime > top:
                top = uptime
        except (TypeError, KeyError) as exc:
            pass
    try:
        return print_duration(top)
    except Exception:
        return ""

def fmt_svc_tasks(key, stats_data):
    if stats_data is None:
        return ""
    count = 0
    total = 0
    for _data in stats_data.values():
        try:
            total += _data["services"][key]["tasks"]
            count += 1
        except Exception:
            pass
    if not count:
        return "-"
    return str(total)

def speed(get, prev_stats, stats):
    fmt = "%8s"
    if stats is None:
        return ""
    total = 0
    for node, _data in stats.items():
        try:
            curr = get(_data)
            prev = get(prev_stats[node])
            interval = _data["timestamp"] - prev_stats[node]["timestamp"]
            total += (curr - prev) / interval
        except Exception as exc:
            raise ValueError
    if total == 0:
        return fmt % "-"
    return fmt % (print_size(total, unit="b", compact=True) + "b/s")

def fmt_svc_blk_rbps(key, prev_stats_data, stats_data):
    try:
        return speed(lambda x: x["services"][key]["blk"]["rb"], prev_stats_data, stats_data)
    except ValueError:
        return "-"

def fmt_svc_blk_wbps(key, prev_stats_data, stats_data):
    try:
        return speed(lambda x: x["services"][key]["blk"]["wb"], prev_stats_data, stats_data)
    except ValueError:
        return "-"

def cpu_usage(get, prev_stats, stats):
    try:
        node_cpu_time = stats["node"]["cpu"]["time"]
        prev_node_cpu_time = prev_stats["node"]["cpu"]["time"]
        cpu_time = get(stats)
        prev_cpu_time = get(prev_stats)
        cpu = (cpu_time - prev_cpu_time) / (node_cpu_time - prev_node_cpu_time) * 100
    except Exception as exc:
        raise ValueError
    return cpu

def fmt_thr_cpu_usage(key, prev_stats_data, stats_data):
    return fmt_cpu_usage(lambda x: x[key]["cpu"]["time"], prev_stats_data, stats_data)

def fmt_svc_cpu_usage(key, prev_stats_data, stats_data):
    return fmt_cpu_usage(lambda x: x["services"][key]["cpu"]["time"], prev_stats_data, stats_data)

def fmt_cpu_usage(get, prev_stats_data, stats_data):
    if prev_stats_data is None or stats_data is None:
        return ""
    cpu = 0
    for _node, _stats in stats_data.items():
        try:
            cpu += cpu_usage(get, prev_stats_data[_node], _stats)
        except (KeyError, ValueError) as exc:
            pass
    if cpu == 0:
        return "-"
    return "%6.1f%%" % cpu

def fmt_svc_blk_rb(key, stats_data):
    return fmt_blk(lambda x: x["services"][key]["blk"]["rb"], stats_data)

def fmt_svc_blk_wb(key, stats_data):
    return fmt_blk(lambda x: x["services"][key]["blk"]["wb"], stats_data)

def fmt_blk(get, stats_data):
    if stats_data is None:
        return ""
    val = 0
    for _data in stats_data.values():
        try:
            val += get(_data)
        except (KeyError, TypeError) as exc:
            pass
    if val == 0:
        return "     -"
    try:
        return print_size(val, unit="b", compact=True)
    except Exception:
        return "     -"

def fmt_thr_tasks(key, stats_data):
    if stats_data is None:
        return ""
    threads = 0
    procs = 0
    for _data in stats_data.values():
        if not isinstance(_data, dict):
            continue
        threads += _data.get(key, {}).get("threads", 0)
        procs += _data.get(key, {}).get("procs", 0)
    if not threads and not procs:
        return ""
    return "%d/%d" % (threads, procs)

def fmt_thr_mem_total(key, stats_data):
    return fmt_mem_total(lambda x: x[key]["mem"]["total"], stats_data)

def fmt_svc_mem_total(key, stats_data):
    return fmt_mem_total(lambda x: x["services"][key]["mem"]["total"], stats_data)

def fmt_mem_total(get, stats_data):
    if stats_data is None:
        return ""
    mem = 0
    for _data in stats_data.values():
        try:
            mem += get(_data)
        except (KeyError, TypeError) as exc:
            pass
    if mem == 0:
        return "     -"
    try:
        return print_size(mem, unit="b", compact=True)
    except Exception:
        return "     -"

def fmt_thr_cpu_time(key, stats_data):
    return fmt_cpu_time(lambda x: x[key]["cpu"]["time"], stats_data)

def fmt_svc_cpu_time(key, stats_data):
    return fmt_cpu_time(lambda x: x["services"][key]["cpu"]["time"], stats_data)

def fmt_cpu_time(get, stats_data):
    if stats_data is None:
        return ""
    time = 0
    for _data in stats_data.values():
        try:
            time += get(_data)
        except (KeyError, TypeError) as exc:
            pass
    try:
        return print_duration(time)
    except Exception:
        return ""

def fmt_tid(_data, stats_data):
    if not stats_data:
        return ""
    tid = _data.get("tid")
    if tid:
        return "%d" % tid
    return ""

def list_print(data, right=None):
    if right is None:
        right = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    outs = ""
    if len(data) == 0:
        return ""
    column_count = max([0] + [len(line) for line in data])
    if column_count == 0:
        return
    widths = [0] * column_count
    _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):
            col_len = bare_len(val)
            if col_len > widths[i]:
                widths[i] = col_len
    for line in _data:
        _line = []
        for i, val in enumerate(line):
            if widths[i] == 0:
                continue
            if i in right:
                val = pad*(widths[i]-bare_len(val)) + val
            else:
                val = val + pad*(widths[i]-bare_len(val))
            _line.append(val)
        _line = pad.join(_line)
        outs += print_bytes(_line)
    return outs

def print_section(data):
    if len(data) == 0:
        return ""
    return list_print(data)


def format_cluster(paths=None, node=None, data=None, prev_stats_data=None,
                   stats_data=None, sections=None, selector=None,
                   namespace=None):
    if not data or data.get("status", 0) != 0:
        return
    if sections is None:
        sections = DEFAULT_SECTIONS
    out = []
    avail_nodenames = get_nodes(data)
    nodenames = sorted([n for n in avail_nodenames if n in node])
    show_nodenames = abbrev(nodenames)
    services = {}

    def load_header(title=""):
        if isinstance(title, list):
            line = title
        else:
            line = [
                title,
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
            ]
        for nodename in show_nodenames:
            line.append(colorize(nodename, color.BOLD))
        out.append(line)

    def load_svc(path, prefix=""):
        if path not in services:
            return
        data = services[path]
        if path in slave_parents and prefix == "":
            return
        try:
            topology = services[path].topology
        except KeyError:
            topology = ""

        # status
        status = colorize_status(data["avail"], lpad=0)
        if data["overall"] == "warn":
            status += colorize("!", color.BROWN)
        if data["placement"] == "non-optimal":
            status += colorize("^", color.RED)

        # info
        info = {
            "topology": data.get("topology", ""),
            "orchestrate": data.get("orchestrate", "-"),
            "status": "%d/1" % data["n_up"] if data["n_up"] is not None else 0,
        }

        if data.get("scale") is not None:
            info["status"] = "%d/%d" % (data["n_up"], data.get("scale"))
        elif data.get("wrapper"):
            info = {
                "topology": "",
                "orchestrate": "",
                "status": "",
            }
        elif topology == "flex":
            flex_target = str(data["flex_target"]) if data["flex_target"] else "#"
            info["status"] = "%d/%s" % (data["n_up"], flex_target)
        if data["avail"] == "n/a":
            info["status"] = ""
        info = "%(orchestrate)-5s %(status)-5s" % info
        line = [
            " "+colorize(prefix+strip_path(path, os.environ.get("OSVC_NAMESPACE")), color.BOLD),
            status,
            info,
            fmt_svc_uptime(path, stats_data),
            fmt_svc_tasks(path, prev_stats_data),
            fmt_svc_cpu_usage(path, prev_stats_data, stats_data),
            fmt_svc_cpu_time(path, stats_data),
            fmt_svc_mem_total(path, stats_data),
            fmt_svc_blk_rb(path, stats_data),
            fmt_svc_blk_wb(path, stats_data),
            fmt_svc_blk_rbps(path, prev_stats_data, stats_data),
            fmt_svc_blk_wbps(path, prev_stats_data, stats_data),
            "|" if nodenames else "",
        ]
        if not nodenames:
            states = []
            if data["frozen"] == "frozen":
                frozen = "frozen"
            elif data["frozen"] == "thawed":
                frozen = ""
            elif data["frozen"] == "n/a":
                frozen = ""
            elif data["frozen"] == "mixed":
                frozen = "part-frozen"
            else:
                frozen = ""
            if frozen:
                states.append(frozen)

            if data["provisioned"] is True:
                provisioned = ""
            elif data["provisioned"] is False:
                provisioned = "unprovisioned"
            elif data["provisioned"] == "n/a":
                provisioned = ""
            elif data["provisioned"] == "mixed":
                provisioned = "part-provisioned"
            else:
                provisioned = ""
            if provisioned:
                states.append(provisioned)

            mon_status_counts = {}
            ge = None
            for nodename in avail_nodenames:
                try:
                    _data = data["nodes"][nodename]
                except KeyError:
                    continue
                if _data is None:
                    continue
                ge = _data.get("global_expect")
                st = _data.get("mon")
                if st not in ("idle", None):
                    if st not in mon_status_counts:
                        mon_status_counts[st] = 1
                    else:
                        mon_status_counts[st] += 1
            if ge:
                states.append(">"+ge)
            for s, n in mon_status_counts.items():
                states.append("%s(%d)" % (s, n))
            line.append(", ".join(states))

        for nodename in nodenames:
            try:
                nd = data["nodes"][nodename]
            except KeyError:
                line.append("")
                continue
            if nd is None:
                line.append(colorize("?", color.RED))
                continue
            val = []

            # drp
            if nd.get("drp"):
                drp_icon = colorize("#", color.LIGHTBLUE)
            else:
                drp_icon = ""

            # frozen unicon
            if nd["frozen"]:
                frozen_icon = colorize(unicons["frozen"], color.BLUE)
            else:
                frozen_icon = ""
            # avail status unicon
            if data["wrapper"]:
                avail_icon = ""
            else:
                avail = nd["avail"]
                if avail == "unknown":
                    avail_icon = colorize("?", color.RED)
                else:
                    avail_icon = colorize_status(avail, lpad=0, agg_status=data["avail"]).replace(avail, unicons[avail])
                if nd.get("preserved"):
                    avail_icon += colorize("?", color.LIGHTBLUE)
            # overall status unicon
            if data["wrapper"]:
                overall_icon = ""
            else:
                overall = nd["overall"]
                if overall == "warn":
                    overall_icon = colorize_status(overall, lpad=0).replace(overall, unicons[overall])
                else:
                    overall_icon = ""
            # mon status
            smon = nd["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 = nd["global_expect"]
                if global_expect:
                    global_expect = colorize(" >" + str(global_expect), color.LIGHTBLUE)
                else:
                    global_expect = ""
            else:
                global_expect = ""
            # leader
            if data["wrapper"]:
                leader = ""
            else:
                if nd["placement"] == "leader":
                    leader = colorize("^", color.LIGHTBLUE)
                else:
                    leader = ""
            # provisioned
            if nd.get("provisioned") is False:
                provisioned = colorize("P", color.RED)
            elif nd.get("provisioned") == "mixed":
                provisioned = colorize("P", color.RED)
            else:
                provisioned = ""

            val.append(avail_icon)
            val.append(overall_icon)
            val.append(drp_icon)
            val.append(leader)
            val.append(frozen_icon)
            val.append(provisioned)
            val.append(smon)
            val.append(global_expect)
            val = "".join(val)
            line.append(val)
        out.append(line)

        for child in sorted(list(data.get("slaves", []))):
            load_svc(child, prefix=prefix+" ")

    def load_hb(key, _data):
        state = _data.get("state", "")
        if state == "running":
            state = colorize(state, color.GREEN)
        else:
            state = colorize(state, color.RED)
        if _data.get("alerts"):
            state += colorize("!", color.BROWN)
        cf = _data.get("config", {})
        addr = cf.get("addr", "")
        port = cf.get("port", "")
        dev = cf.get("dev", "")
        relay = cf.get("relay", "")
        if addr and port:
            config = fmt_listener(addr, port)
        elif dev:
            config = os.path.basename(dev)
        elif relay:
            config = relay
        else:
            config = ""
        line = [
            " "+colorize(key, color.BOLD),
            state,
            config,
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        peers = _data.get("peers", {})
        for nodename in nodenames:
            beating = peers.get(nodename, {}).get("beating")
            if beating is None:
                status = "n/a"
            elif 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 "state" not in _data:
            _data["state"] = "undef"
        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,
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
            "",
            "",
        ))

    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,
            fmt_listener(_data["config"]["addr"], _data["config"]["port"]),
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
            "",
            "",
        ))

    def load_scheduler(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,
            "",
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
            "",
            "",
        ))

    def load_daemon():
        key = "daemon"
        state = colorize("running", color.GREEN)
        line = [
            " "+colorize(key, color.BOLD),
            "%s" % state,
            "",
            str(data.get("pid", "")) if stats_data else "",
            "",
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            fmt_thr_mem_total(key, stats_data),
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        for nodename in nodenames:
            speaker = data["monitor"].get("nodes", {}).get(nodename, {}).get("speaker")
            if speaker:
                status = "up"
                status = colorize_status(status, lpad=0).replace(status, unicons[status])
            else:
                status = ""
            line.append(status)
        out.append(line)


    def load_collector(key, _data):
        if _data["state"] == "running":
            state = colorize(_data["state"], color.GREEN)
        else:
            state = colorize(_data["state"], color.RED)
        line = [
            " "+colorize(key, color.BOLD),
            state,
            "",
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        for nodename in nodenames:
            speaker = data["monitor"].get("nodes", {}).get(nodename, {}).get("speaker")
            if speaker:
                status = "up"
                status = colorize_status(status, lpad=0).replace(status, unicons[status])
            else:
                status = ""
            line.append(status)
        out.append(line)

    def load_generic_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,
            "",
            fmt_tid(_data, stats_data),
            fmt_thr_tasks(key, stats_data),
            fmt_thr_cpu_usage(key, prev_stats_data, stats_data),
            fmt_thr_cpu_time(key, stats_data),
            "",
            "",
            "",
        ))

    def load_threads():
        if "threads" not in sections:
            return
        load_header([
            "Threads",
            "",
            "",
            "pid/tid" if stats_data else "",
            "thr/sub" if stats_data else "",
            "usage" if stats_data else "",
            "time" if stats_data else "",
            "rss" if stats_data else "",
            "",
            "",
            "",
            "",
            "",
        ])
        load_daemon()
        for key in sorted([key for key in data if key not in ("cluster", "daemon")]):
            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])
            elif key == "collector":
                load_collector(key, data[key])
            else:
                try:
                    load_generic_thread(key, data[key])
                except Exception:
                    pass
        out.append([])

    def load_score():
        if "monitor" not in data:
            return
        line = [
            colorize(" score", color.BOLD),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        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),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        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),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        for nodename in nodenames:
            total = data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get(key+"_total")
            avail = data["monitor"]["nodes"].get(nodename, {}).get("stats", {}).get(key+"_avail")
            limit = 100 - data["monitor"]["nodes"].get(nodename, {}).get("min_avail_"+key, 0)
            if avail is None or total in (0, None):
                line.append(colorize("-", color.LIGHTBLUE))
                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),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        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 = ""
            global_expect = data["monitor"]["nodes"].get(nodename, {}).get("monitor", {}).get("global_expect")
            if global_expect:
                global_expect = colorize(" >" + str(global_expect), color.LIGHTBLUE)
            else:
                global_expect = ""
            line.append(str(nmon_state)+frozen+global_expect)
        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),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        for nodename in nodenames:
            compat = data["monitor"]["nodes"].get(nodename, {}).get("compat", "")
            line.append(str(compat))
        out.append(line)

    def load_node_version():
        if "monitor" not in data:
            return
        line = [
            colorize(" version", color.BOLD),
            colorize("warn", color.BROWN),
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "|" if nodenames else "",
        ]
        versions = []
        for nodename in nodenames:
            agent = data["monitor"]["nodes"].get(nodename, {}).get("agent", "")
            line.append(str(agent))
            if agent != "":
                versions.append(str(agent))
        if len(set(versions)) > 1:
            out.append(line)

    def load_arbitrators():
        if "arbitrators" not in sections:
            return
        arbitrators = []
        arbitrators_name = {}
        for nodename, ndata in data["monitor"]["nodes"].items():
            for aid, adata in ndata.get("arbitrators", {}).items():
                 if aid not in arbitrators:
                     arbitrators.append(aid)
                     arbitrators_name[aid] = adata["name"]
        if len(arbitrators) == 0:
            return
        load_header("Arbitrators")
        for aid in arbitrators:
            line = [
                colorize(" "+arbitrators_name[aid], color.BOLD),
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "",
                "|" if nodenames else "",
            ]
            for nodename in nodenames:
                status = data["monitor"]["nodes"].get(nodename, {}).get("arbitrators", {}).get(aid, {}).get("status", "undef")
                if status != "up":
                    line[1] = colorize_status("warn", lpad=0)
                status = colorize_status(status, lpad=0).replace(status, unicons[status])
                line.append(status)
            out.append(line)
        out.append([])

    def load_nodes():
        if "nodes" not in sections or not nodenames:
            return
        load_header("Nodes")
        load_metrics()
        load_node_compat()
        load_node_version()
        load_node_state()
        out.append([])

    def load_metrics():
        load_score()
        load_loadavg()
        load_free_total("mem")
        load_free_total("swap")

    # init the services hash
    slave_parents = {}
    if "monitor" in data:
        for _node in avail_nodenames:
            if _node not in data["monitor"]["nodes"]:
                continue
            try:
                node_svc_status = data["monitor"]["nodes"][_node]["services"]["status"]
            except KeyError:
                continue
            for path, _data in node_svc_status.items():
                if _data is None:
                    continue
                if paths is not None and path not in paths:
                    continue
                if path not in services:
                    services[path] = Storage({
                        "topology": _data.get("topology", ""),
                        "orchestrate": _data.get("orchestrate", ""),
                        "flex_target": _data.get("flex_target"),
                        "scale": _data.get("scale"),
                        "avail": "undef",
                        "overall": "",
                        "nodes": {},
                        "slaves": set(),
                        "n_up": 0,
                        "resources": set(),
                    })
                try:
                    services[path]["resources"] |= set(_data["resources"].keys())
                except KeyError:
                    pass
                slaves = _data.get("slaves", [])
                scale = _data.get("scale")
                if scale:
                    name, _namespace, kind = split_path(path)
                    if _namespace:
                        pattern = r"^%s/%s/[0-9]+\.%s$" % (_namespace, kind, name)
                    else:
                        pattern = r"^[0-9]+\.%s$" % name
                    for child in data["monitor"]["services"]:
                        if re.match(pattern, child) is None:
                            continue
                        slaves.append(child)
                        if node_svc_status.get(child, {}).get("avail") == "up":
                            services[path].n_up += 1
                else:
                    if node_svc_status.get(path, {}).get("avail") == "up":
                        services[path].n_up += 1
                for child in slaves:
                    if child not in slave_parents:
                        slave_parents[child] = set([path])
                    else:
                        slave_parents[child] |= set([path])
                global_expect = _data.get("monitor", {}).get("global_expect")
                if global_expect and "@" in global_expect:
                    global_expect = global_expect[:global_expect.index("@")+1]
                services[path].nodes[_node] = {
                    "avail": _data.get("avail", "undef"),
                    "preserved": _data.get("preserved"),
                    "overall": _data.get("overall", "undef"),
                    "frozen": _data.get("frozen", False),
                    "mon": _data.get("monitor", {}).get("status", ""),
                    "global_expect": global_expect,
                    "placement": _data.get("monitor", {}).get("placement", ""),
                    "provisioned": _data.get("provisioned"),
                    "drp": _data.get("drp"),
                }
                services[path].slaves |= set(slaves)
                services[path]["wrapper"] = (
                    services[path].resources == set() and
                    services[path].slaves != set() and
                    scale is None
                )
            try:
                # hint we have missing instances
                for path, cnf in data["monitor"]["nodes"][_node]["services"]["config"].items():
                    if path not in services:
                        continue
                    for __node in cnf.get("scope", []):
                        if __node not in services[path].nodes:
                            services[path].nodes[__node] = None
            except KeyError:
                pass
        for path, _data in data["monitor"]["services"].items():
            if paths is not None and path not in paths:
                continue
            if path not in services:
                services[path] = Storage({
                    "avail": "undef",
                    "overall": "",
                    "nodes": {}
                })
            services[path].avail = _data.get("avail", "n/a")
            services[path].overall = _data.get("overall", "n/a")
            services[path].placement = _data.get("placement", "n/a")
            services[path].frozen = _data.get("frozen", "n/a")
            services[path].provisioned = _data.get("provisioned", "n/a")

    def load_services(selector, namespace=None):
        if "services" not in sections:
            return
        selectors = []
        context = os.environ.get("OSVC_CONTEXT", "")
        if context:
            selectors.append(context)
        buff = format_path_selector(selector, namespace, maxlen=15)
        selectors.append(buff)
        header = [
            "/".join(selectors),
            "",
            "",
            "since" if stats_data else "",
            "tasks" if stats_data else "",
            "usage" if stats_data else "",
            "time" if stats_data else "",
            "mem" if stats_data else "",
            "blkrb" if stats_data else "",
            "blkwb" if stats_data else "",
            "blkrbps" if stats_data else "",
            "blkwbps" if stats_data else "",
            "",
        ]
        if not nodenames:
            header.append("")
        load_header(header)
        for path in sorted(list(services.keys())):
            load_svc(path)

    # load data in lists
    load_threads()
    load_arbitrators()
    load_nodes()
    load_services(selector, namespace)

    # print tabulated lists
    return print_section(out)


   0707010001f566000081a40000000000000000000000016a100daf00000084000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/render/banner.py def banner(text, ch='=', length=78):
    spaced_text = ' %s ' % text
    banner = spaced_text.center(length, ch)
    return banner

0707010001f56c000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/render/forest    0707010001f56d000081a40000000000000000000000016a100daf00003962000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/utilities/render/forest/__init__.py    # -*- coding: utf8 -*-
"""
Forest data representation module.
"""
from __future__ import print_function
from __future__ import unicode_literals

import foreign.six as six

from textwrap import wrap
from utilities.render.color import colorize, color
from utilities.render.term import term_width

LAST_NODE = "`- "
NEXT_NODE = "|- "
CONT_NODE = "|  "
CONT_LAST_NODE = "   "

def forest(data, columns=1, separator="  ", widths=None, force_width=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

    """
    if force_width:
        twidth = force_width
    else:
        twidth = term_width() - 4

    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.
        """
        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 not lasts:
            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, align):
        """
        Format the table cell, happending the separator, coloring the text and
        applying the cell padding for alignment.
        """
        if text in ("", None):
            return " " * width + separator
        if align == "right":
            fmt = "%"+str(width)+"s"
        else:
            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")
                align = col.get("align")
                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, align)
            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")
                align = fragment.get("align")
                if text is None:
                    text = ""
                lines += [(line, textcolor, align) 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, align = _tmp[idx]
                except IndexError:
                    line = ""
                __data.append({
                    "text": line,
                    "color": textcolor,
                    "align": align,
                })
            _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="", textcolor=None, align=None):
        """
        Add a phrase to this column.
        """
        if not isinstance(text, six.string_types):
            text = str(text)
        if six.PY2 and isinstance(text, str):
            text = text.decode("utf8")
        self.node.node["data"][self.idx].append({
            "text": text,
            "color": textcolor,
            "align": align,
        })

class Node(object):
    """
    The Forest Node object, offering methods to add columns to the node.
    """
    def __init__(self, head, node_id):
        self.forest = head
        self.node_id = node_id
        self.node = self.forest.get_node(node_id)

    def add_column(self, text="", textcolor=None, align=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"] = []
        if not isinstance(text, six.string_types):
            text = str(text)
        if six.PY2 and isinstance(text, str):
            text = text.decode("utf8")
        self.node["data"].append([{
            "text": text,
            "color": textcolor,
            "align": align,
        }])
        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):
        """
        Load data in the node.
        """
        head = self
        if title:
            head.add_column(title, color.BOLD)

        def add_list(head, _data):
            """
            Load data structured as list in the node.
            """
            for idx, val in enumerate(_data):
                leaf = head.add_node()
                leaf.add_column("[%d]" % idx)
                add_gen(leaf, val)

        def add_dict(head, _data):
            """
            Load data structured as dict in the node.
            """
            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):
            """
            Switch between data loaders
            """
            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 out(self):
        """
        Print the forest to stdout.
        """
        buff = forest(self.data, self.columns, separator=self.separator,
                      widths=self.widths)
        try:
            print(buff)
        except Exception:
            print(buff.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):
        """
        Load data in the Forest object.
        """
        head = self.add_node()
        head.load(*args, **kwargs)
  0707010001f574000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/render/term  0707010001f575000081a40000000000000000000000016a100daf00000353000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/render/term/__init__.py  import os
import re

from env import Env
from utilities.proc import justcall, which

def term_width():
    min_columns = 78
    detected_columns = _detect_term_width()
    if detected_columns >= min_columns:
        return detected_columns
    else:
        env_columns = int(os.environ.get("COLUMNS", 0))
        if env_columns >= min_columns:
            return env_columns
        else:
            return min_columns


def _detect_term_width():
    try:
        # python 3.3+
        return os.get_terminal_size().columns
    except (AttributeError, OSError):
        pass
    if Env.sysname != "Windows" and which("stty") is not None:
        out, err, ret = justcall(['stty', '-a'])
        if ret == 0:
            m = re.search(r'columns\s+(?P<columns>\d+);', out)
            if m:
                return int(m.group('columns'))
    return 0
 0707010001f565000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/render/__init__.py   0707010001f570000081a40000000000000000000000016a100daf0000008a000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/render/listener.py   def fmt_listener(addr, port):
    if ":" in addr:
        return "[%s]:%d" % (addr, port)
    else:
        return "%s:%d" % (addr, port)
  0707010001f569000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/render/color 0707010001f56a000081a40000000000000000000000016a100daf00002f7f000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/render/color/__init__.py from __future__ import print_function

import os
import sys
import datetime
import re

import core.exceptions as ex
import foreign.six as six
from utilities.string import is_string

try:
    from collections import OrderedDict
except ImportError:
    OrderedDict = dict

if os.name == "nt":
    import foreign.colorama as colorama
    colorama.init()

use_color = "auto"

unicons = {
    "frozen": "*",
    "stdby up": "S",
    "stdby down": "s",
    "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,
}

AUTO_COLORS = [
    color.BLUE,
    color.LIGHTRED,
    color.BROWN,
    color.LIGHTBLUE,
    color.PURPLE,
    color.CYAN,
    color.GREEN,
    color.YELLOW,
]
AUTO_COLORS_LEN = len(AUTO_COLORS)

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

colorize = ansi_colorize

def colorize_json(s):
    import re
    from core.status 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

    sort_keys = not isinstance(d, OrderedDict) and OrderedDict != dict

    kwargs = {
      "sort_keys": sort_keys,
      "ensure_ascii": False,
      "indent": 4,
      "separators": (',', ': '),
    }
    if six.PY2:
        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_flat_json(d):
    print(format_str_flat_json(d))

def format_str_flat_json(d):
    out = {}

    def flatten(x, name=''):
        if isinstance(x, dict):
            for a, v in x.items():
                a = str(a)
                if "/" in a or "#" in a or "." in a or "$" in a:
                    a = "'"+a+"'"
                flatten(v, name="%s.%s" % (name, a))
        elif isinstance(x, list):
            i = 0
            for a in x:
                flatten(a, name="%s[%d]" % (name, i))
                i += 1
        else:
            out[name] = x

    flatten(d)
    buff = ""
    for k, v in sorted(out.items(), key=lambda x: x[0]):
        buff += "%s = %s\n" % (k, v)
    if buff.endswith("\n"):
        buff = buff[:-1]
    if six.PY2:
        return buff.encode("utf-8")
    else:
        return buff

def format_table(d):
    import utilities.render.table
    utilities.render.table.print_table_tabulate(d)

def format_default(d):
    import utilities.render.table
    if "error" in d and is_string(d["error"]):
        print(d["error"], file=sys.stderr)
    utilities.render.table.print_table_default(d)

def format_csv(d):
    import utilities.render.table
    utilities.render.table.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 in [k for k in entry]:
            val = entry[key]
            if not isinstance(val, dict):
                continue
            for _key in [k for k in val]:
                _val = val[_key]
                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
        default_fmt = kwargs.get("default_fmt")
        if fmt is None and default_fmt:
            fmt = default_fmt

        _fmt_kwargs = {}
        if fmt == "json":
            _fmt = format_json
        elif fmt == "flat_json":
            _fmt = format_flat_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.Error("unsupported output format: %s" % str(fmt))

        data = fn(*args, **kwargs)

        if fmt in ("json", "flat_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

        path = args[0].options.jsonpath_filter
        if path:
            from foreign.jsonpath_ng.ext import parse
            try:
                jsonpath_expr = parse(path)
                data = [match.value for match in jsonpath_expr.find(data)]
            except Exception as exc:
                raise ex.Error(str(exc))
            if re.match(r"^[\w\.']+$", path):
                # single value expression
                try:
                    data = data[0]
                except IndexError:
                    return

        if not isinstance(data, (dict, list)):
            print(data)
            return

        try:
            _fmt(data)
        except IOError as ioexc:
            if ioexc.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.
    """
    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.Error(exc)

def colorize_log_line(line, last=None, auto=None):
    """
    Format a log line, colorizing the log level.
    Return the line as a string buffer.
    """
    msg = os.linesep.join(line["m"])
    extra = ""
    for k, v in line["x"].items():
        if k in ("n", "o") and auto:
            try:
                i = auto.index(v)
                v = colorize(v, AUTO_COLORS[i%AUTO_COLORS_LEN])
            except (ValueError, IndexError):
                pass
        extra += "%s:%s " % (k, v)
    extra = extra.rstrip()
    lvl = line["l"]
    t = datetime.datetime.fromtimestamp(line["t"]).strftime("%Y-%m-%d %H:%M:%S,%f")

    if lvl == "INFO":
        lvl = colorize("INFO   ", color.LIGHTBLUE)
    elif lvl == "WARNING":
        lvl = colorize("WARNING", color.BROWN)
    elif lvl == "ERROR":
        lvl = colorize("ERROR  ", color.RED)
    elif lvl == "DEBUG":
        lvl = "DEBUG  "

    if msg.startswith("do "):
        msg = colorize(msg, color.BOLD)

    if auto:
        for i, word in enumerate(auto):
            msg = re.sub(r"([\s,:@'\"]+)%s([\s,:@'\"]+)"%word, lambda m: m.group(1)+colorize(word, AUTO_COLORS[i%AUTO_COLORS_LEN])+m.group(2), msg)
    line = " ".join((t, lvl, extra, "|", msg))
    return line


 0707010001f571000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/render/table 0707010001f573000081a40000000000000000000000016a100daf000067a1000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/render/table/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 foreign.six as six
from foreign.six.moves import zip_longest
from foreign.six.moves import reduce
import re


if python_version_tuple()[0] < "3":
    _none_type = type(None)
    _int_type = int
    _float_type = float
    _binary_type = str
else:
    _none_type = type(None)
    _int_type = int
    _float_type = float
    _binary_type = bytes

_text_type = six.text_type
__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 = r"\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(zip_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=None, 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
    |}


    """

    if headers is None:
        headers = []
    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)
   0707010001f572000081a40000000000000000000000016a100daf0000120f000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/render/table/__init__.py from __future__ import print_function
from __future__ import unicode_literals

import sys

import core.exceptions as ex
import utilities.render.color
import utilities.render.term

from .tabulate import tabulate
from foreign.six.moves import reduce
from foreign.six import u as unicode
from textwrap import wrap

def parse_data(data):
    try:
        lines = data.splitlines()
    except AttributeError:
        raise ex.Error
    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):
    if isinstance(s, bool):
        s = str(s)
    try:
        return unicode(s)
    except:
        pass
    try:
        return unicode(s, errors="ignore") # pylint: disable=unexpected-keyword-arg
    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

    try:
        table = tabulate(data, headers="firstrow", tablefmt="plain").splitlines()
    except UnicodeEncodeError:
        table = tabulate(data, headers="firstrow", tablefmt="plain").encode("utf-8").splitlines()

    colors = [
        utilities.render.color.color.BGWHITE + utilities.render.color.color.BLACK,
        utilities.render.color.color.E_BGODD + utilities.render.color.color.BLACK,
    ]
    idx = 0
    tw = utilities.render.term.term_width()

    for line_idx, line in enumerate(table):
        if line.startswith("+-"):
            idx = (idx + 1) % 2
            continue
        if line_idx == 0:
            color = utilities.render.color.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(utilities.render.color.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] or ""),
                       initial_indent = "",
                       subsequent_indent = subsequent_indent,
                       width=78
                  ))
            try:
                print(utilities.render.color.colorize(fmt % (label+":"), utilities.render.color.color.LIGHTBLUE), val)
            except UnicodeEncodeError:
                print(utilities.render.color.colorize(fmt % (label+":"), utilities.render.color.color.LIGHTBLUE), val.encode("utf-8"))

def print_table_csv(data):
    try:
        data = validate_format(data)
    except ex.Error:
        raise ex.Error("unsupported format for this action")

    for d in data:
        print(";".join(map(lambda x: "'%s'" % unicode(x), d)))

 0707010001f56b000081a40000000000000000000000016a100daf000004f8000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/render/command.py    def format_command(kind, action, options):
    """
    Return a list-formatted CRM command from action and options.
    :param subsystem:
    :param action:
    :param options:
    :return: cmd array
    """
    def find_opt(local_parser, searched_opt):
        for k, o in local_parser.items():
            if o.dest == searched_opt:
                return o
            if o.dest == "parm_" + searched_opt:
                return o

    import importlib
    mod = importlib.import_module("commands.{kind}.parser".format(kind=kind))
    parser = mod.OPT
    cmd = [action]
    for opt, val in options.items():
        po = find_opt(parser, opt)
        if po is None:
            continue
        if val == po.default:
            continue
        if val is None:
            continue
        opt = po.get_opt_string()
        if po.action == "append":
            cmd += [opt + "=" + str(v) for v in val]
        elif po.action == "store_true" and val:
            cmd.append(opt)
        elif po.action == "store_false" and not val:
            cmd.append(opt)
        elif po.type == "string":
            opt += "=" + str(val)
            cmd.append(opt)
        elif po.type == "integer":
            opt += "=" + str(val)
            cmd.append(opt)
    return cmd
0707010001f57f000081a40000000000000000000000016a100daf000013f1000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/semver.py    import re
from functools import total_ordering

_semver_re = re.compile(
    r"""
    ^
    (?P<major>0|[1-9]\d*)\.
    (?P<minor>0|[1-9]\d*)\.
    (?P<patch>0|[1-9]\d*)
    (?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?
    (?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?
    $""",
    re.VERBOSE,
)

@total_ordering
class Semver(object):
    """
    Represents a semantic version as defined by the Semantic Versioning 2.0.0 standard.

    This class provides functionalities to create, parse, and compare semantic version strings.
    Semantic versioning is a versioning scheme intended for managing dependencies in software projects.
    It follows the format `MAJOR.MINOR.PATCH` where changes to each segment indicate specific types of updates.
    Additionally, the class supports optional prerelease and build metadata fields that allow to specify
    more detailed versioning information.

    :ivar major: The major version number. Incremented for incompatible changes.
    :type major: int
    :ivar minor: The minor version number. Incremented for backward-compatible functionality.
    :type minor: int
    :ivar patch: The patch version number. Incremented for backward-compatible bug fixes.
    :type patch: int
    :ivar prerelease: An optional prerelease identifier string (e.g., alpha, beta).
    :type prerelease: str
    :ivar build: An optional build metadata string (e.g., build identifiers for internal use).
    :type build: str
    """
    def __init__(self, major=0, minor=0, patch=0, prerelease=None, build=None):
        self.major = int(major)
        self.minor = int(minor)
        self.patch = int(patch)
        self.prerelease = prerelease or None
        self.build = build or None

    def __str__(self):
        s = "%d.%d.%d" % (self.major, self.minor, self.patch)
        if self.prerelease:
            s += "-%s" % self.prerelease
        if self.build:
            s += "+%s" % self.build
        return s

    @classmethod
    def parse(cls, s):
        if not isinstance(s, str):
            return cls()
        m = re.match(_semver_re, s)
        if not m:
            raise ValueError("Invalid semver")
        return cls(
            major=int(m.group("major")),
            minor=int(m.group("minor")),
            patch=int(m.group("patch")),
            prerelease=m.group("prerelease"),
            build=m.group("buildmetadata"))

    def __eq__(self, other):
        if not isinstance(other, Semver):
            return False
        if (self.major, self.minor, self.patch, self.prerelease) == (other.major, other.minor, other.patch, other.prerelease):
            return True
        return False

    def __lt__(self, other):
        if not isinstance(other, Semver):
            return NotImplemented
        if self.major < other.major:
            return True
        elif self.major == other.major:
            if self.minor < other.minor:
                return True
            elif self.minor == other.minor:
                if self.patch < other.patch:
                    return True
                elif self.patch == other.patch:
                    return self._parse_prerelease() < other._parse_prerelease()

        return False

    def _parse_prerelease(self):
        """
        Parses the prerelease component of a version string into a tuple
        that can be used for comparing versions by precedence according to
        the https://semver.org rules.

        The method differentiates between numeric and alphanumeric
        identifiers in the prerelease string, and constructs a precedence
        tuple accordingly.

        :return: A tuple representing the parsed prerelease component
                 with numeric and alphanumeric identifiers.
        :rtype: tuple
        """
        if not self.prerelease:
            return (1,)  # no prerelease = highest precedence

        parts = []
        for p in self.prerelease.split("."):
            if p.isdigit():
                parts.append((0, int(p)))  # numeric identifiers
            else:
                parts.append((1, p))  # alphanumeric identifiers
        return (0, parts)

    def __hash__(self):
        return hash((self.major, self.minor, self.patch, self.prerelease))

def as_semver(s):
    """
    Do our best to convert a string to a semver object from calendar versioning.
    Remove removing leading zeros from major, minor, patch version components
    before the first "-", or "+", if present.
    Docker example: 17.05.0-ce -> 17.5.0-ce.
    """
    has_release = False
    has_build = False
    suffix = ""
    if '-' in s:
        has_release = True
        base, suffix = s.split('-', 1)
    elif '+' in s:
        has_build = True
        base, suffix = s.split('+', 1)
    else:
        base = s
    clean_base = re.sub(r'(^|\.)0+(?=\d)', r'\1', base)
    if has_release:
        clean_v = "%s-%s" % (clean_base, suffix)
    elif has_build:
        clean_v = "%s+%s" % (clean_base, suffix)
    else:
        clean_v = clean_base
    return Semver.parse(clean_v)
   0707010001f57c000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/runfiles 0707010001f57d000081a40000000000000000000000016a100daf000006bd000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/runfiles/__init__.py import os
from env import Env

def has_running(run_dir, log=None, cleanup=False):
    try:
        return [info for info in (is_valid(os.path.join(run_dir, f), log=log, cleanup=cleanup) for f in os.listdir(run_dir)) if info]
    except FileNotFoundError:
        return []

def get_sid(run_file):
    try:
        with open(run_file, 'r') as f:
            sid = f.read().strip()
            if sid:
                return sid
            return "unknown"
    except FileNotFoundError:
        return "unknown"

def is_valid(run_file, log=None, cleanup=False):
    pid = os.path.basename(run_file)

    def do_cleanup(reason):
        if not cleanup:
            return
        if log:
            log.info("clean up stale run file (%s)", reason)
        try:
            os.unlink(run_file)
        except FileNotFoundError as e:
            pass
        except Exception as e:
            if log:
                log.warn(e)

    if str(os.getpid()) == pid:
        return { "pid": int(pid), "session_id": Env.session_uuid }

    try:
        run_file_mtime = os.path.getmtime(run_file)
    except FileNotFoundError:
        return False

    try:
        proc_pid_mtime = os.path.getmtime("/proc/"+pid)
    except FileNotFoundError:
        do_cleanup("proc %s does not run" % pid)
        return False

    if proc_pid_mtime <= run_file_mtime:
        return { "pid": int(pid), "session_id": get_sid(run_file) }

    do_cleanup("proc %s is not the one that created %s" % (pid, run_file))
    return False

def count(run_dir, log=None, cleanup=False):
    try:
        return sum(1 for entry in os.listdir(run_dir) if is_valid(os.path.join(run_dir, entry), log=log, cleanup=cleanup))
    except OSError:
        return 0

   0707010001f532000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/noop_log 0707010001f533000081a40000000000000000000000016a100daf0000010c000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/noop_log/__init__.py # noinspection PyUnusedLocal
def info(*args, **kwargs):
    pass


# noinspection PyUnusedLocal
def warn(*args, **kwargs):
    pass


# noinspection PyUnusedLocal
def debug(*args, **kwargs):
    pass


# noinspection PyUnusedLocal
def error(*args, **kwargs):
    pass
0707010001f5bd000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/utilities/uri  0707010001f5be000081a40000000000000000000000016a100daf00000a55000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/uri/__init__.py  import os
import sys

from contextlib import contextmanager

try:
    from foreign.six.moves.urllib.request import Request, urlopen
    from foreign.six.moves.urllib.parse import urlparse
except ImportError:
    # pylint false positive
    pass

class Uri(object):
    def __init__(self, uri, secure=True):
        self.uri = uri
        self.secure = secure

    @contextmanager
    def fetch(self):
        fpath = self._fetch_path()
        try:
            self._fetch(self.uri, fpath)
            yield fpath
        finally:
            try:
                os.unlink(fpath)
            except OSError:
                pass

    @staticmethod
    def _fetch_path():
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        fpath = tmpf.name
        tmpf.close()
        return fpath

    def _fetch(self, uri, fpath):
        """
        A chunked download method
        """
        request = Request(uri)
        kwargs = self._set_ssl_context()
        ufile = urlopen(request, **kwargs)
        with open(fpath, 'wb') as ofile:
            os.chmod(fpath, 0o0600)
            for chunk in iter(lambda: ufile.read(4096), b""):
                ofile.write(chunk)
        ufile.close()

    def _set_ssl_context(self, kwargs=None):
        """
        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.
        """
        kwargs = kwargs or {}
        if not self.secure:
            kwargs.update(ssl_context_kwargs())
        return kwargs

    def host_header(self):
        """
        Format http Host header
        """
        hdr = None
        if not self.uri:
            return hdr
        parsed = urlparse(self.uri)
        hdr = parsed.hostname
        if parsed.port:
            # Add non-standard ports
            if (parsed.scheme == 'http' and parsed.port != 80) or (parsed.scheme == 'https' and parsed.port != 443):
                hdr = hdr + ':' + str(parsed.port)
        return hdr

def ssl_context_kwargs():
    kwargs = {}
    try:
        import ssl
        if [sys.version_info.major, sys.version_info.minor] >= [3, 10]:
            # noinspection PyUnresolvedReferences
            # pylint: disable=no-member
            kwargs["context"] = ssl._create_unverified_context(protocol=ssl.PROTOCOL_TLS_CLIENT)
        else:
            kwargs["context"] = ssl._create_unverified_context()
        kwargs["context"].set_ciphers("DEFAULT")
    except (ImportError, AttributeError):
        pass
    return kwargs

   0707010001f57e000081a40000000000000000000000016a100daf00000ae3000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/selector.py  import re

import core.exceptions as ex

def selector_value_match(current, op, value):
    if op in ("<", ">", ">=", "<="):
        try:
            current = float(current)
        except (ValueError, TypeError):
            return False
    if op == "=":
        if str(current).lower() in ("true", "false"):
            match = str(current).lower() == value.lower()
        else:
            match = current == value
    elif op == "~=":
        if isinstance(current, (set, list, tuple)):
            match = value in current
        else:
            try:
                match = re.search(value, current)
            except TypeError:
                match = False
    elif op == "~":
        if isinstance(current, (set, list, tuple)):
            match = any([True for v in current if re.search(value, v)])
        else:
            try:
                match = re.search(value, current)
            except TypeError:
                match = False
    elif op == ">":
        match = current > value
    elif op == ">=":
        match = current >= value
    elif op == "<":
        match = current < value
    elif op == "<=":
        match = current <= value
    elif op == ":":
        match = True
    else:
        # unknown op value
        match = False
    return match

def selector_config_match(svc, param, op, value):
    if not param:
        return False
    try:
        current = svc._get(param, evaluate=True)
    except (ex.Error, 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.conf_sections() if group == "" or 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.Error, ex.OptNotFound, ex.RequiredOptNotFound):
                    continue
                if selector_value_match(_current, op, value):
                    return True
        return False
    if current is None:
        return op == ":"
    if selector_value_match(current, op, value):
        return True
    return False

def selector_parse_fragment(s):
    ops = r"(<=|>=|~=|<|>|=|~|:)"
    negate = s[0] == "!"
    s = s.lstrip("!")
    elts = re.split(ops, s)
    return negate, s, elts

def selector_parse_op_fragment(elts):
    param, op, value = elts
    if op in ("<", ">", ">=", "<="):
        try:
            value = float(value)
        except (TypeError, ValueError):
            raise ValueError
    return param, op, value

 0707010001f534000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/optparser    0707010001f535000081a40000000000000000000000016a100daf0000414d000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/optparser/__init__.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 utilities.render.color
import core.exceptions as ex
import re

from utilities.render.term import term_width
from utilities.string import is_string
from utilities.version import agent_version


def wipe_rest_markup(payload):
    payload = re.sub(r':(cmd|kw|opt|c-.*?):`(.*?)`', lambda pat: "'" + pat.group(2) + "'", payload, re.MULTILINE)
    payload = re.sub(r'``(.*?)``', lambda pat: "'" + pat.group(1) + "'", payload, re.MULTILINE)
    return payload


class Option(optparse.Option):
    pass


class OsvcHelpFormatter(optparse.TitledHelpFormatter):
    def format_option(self, option):
        if option in self.deprecated_options:
            return ""
        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):
    deprecated_options = []

    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)

    def exit(self, status=0, msg=None):
        """
        Override optparse.exit so sys.exit doesn't get called.
        """
        raise ex.Error(msg)

    def error(self, msg):
        """
        Override optparse.error so sys.exit doesn't get called.
        """
        raise ex.Error(msg)

    def print_version(self, file=None):
        """
        Override optparse.error so sys.exit doesn't get called.
        """
        if self.version:
            msg = self.get_version()
        else:
            msg = ""
        raise ex.Version(msg)


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_options=None,
                 deprecated_actions=None, actions_translations=None,
                 global_options=None, svc_select_options=None, colorize=True,
                 width=None, formatter=None, indent=6, async_actions=None):
        self.parser = None
        self.args = args
        self.prog = prog
        self.version = prog + " version undef"
        self.options = options
        self.actions = actions
        self.deprecated_options = [self.options[name] for name in deprecated_options] if deprecated_options else []
        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.deprecated_options = self.deprecated_options
        if async_actions is None:
            self.async_actions = {}
        else:
            self.async_actions = async_actions
        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:
            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 = "  " + utilities.render.color.colorize(fancya, utilities.render.color.color.BOLD)
        else:
            desc = "  " + fancya
        desc += '\n\n'
        if self.async_actions.get(action, {}).get("local"):
            preamble = "Asynchronous orchestrated action, unless --local or --node <node> is specified.\n\n"
        else:
            preamble = ""
        wrapper = textwrap.TextWrapper(width=self.width-self.indent, replace_whitespace=False)
        text = preamble + self.actions[section][action]["msg"]
        text = text.replace("``", "`")
        for phrase in text.splitlines():
            for line in wrapper.wrap(phrase):
                for _line in line.splitlines():
                    desc += self.subsequent_indent+_line
            desc += '\n'

        if options:
            desc += self.format_options(section, action)

        desc = wipe_rest_markup(desc)
        desc += '\n'
        return desc

    def format_digest(self, action=""):
        """
        Format and return a digest of supported actions matching <action>
        """
        action = action.rstrip("?")
        desc = self.usage
        desc = desc.replace("COMMAND", action + "...")
        desc = desc.replace("[ OPTIONS ] ", "")
        desc += "  --help   display action description and supported options.\n\n"
        desc += self.format_valid_actions(action)
        return desc

    def format_valid_actions(self, action):
        desc = ""
        for section in sorted(self.actions):
            valid_actions = self.get_valid_actions(section, action)
            if len(valid_actions) == 0:
                continue

            desc += section + "\n\n"

            for valid_action in valid_actions:
                desc += "  " + valid_action.replace("_", " ") + "\n"
            desc += "\n"
        if desc:
            return desc[:-2]
        return self.format_valid_actions("")

    def format_desc(self, svc=False, action=None, options=True):
        """
        Format and return the 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 the command.
        """
        actions = []
        for section in self.actions:
            actions += self.actions[section].keys()
        actions += self.deprecated_actions
        return actions

    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:
                developed_args = []
                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) == 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:
                version = 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():
            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.
        if options.parm_help and not action:
            self.print_digest()

        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:
            try:
                parser.add_option(option)
            except TypeError as exc:
                raise ex.Error("misclassified option: %s" % exc)
        options_discarded, args_discarded = parser.parse_args(self.args, optparse.Values())
        return options, action


    def print_digest(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_digest()
        self.parser.error("no action specified\n"+usage)

    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.
        """
        if self.args is not None:
            return
        usage = self.format_desc()
        self.parser.error("no action specified\n"+usage)

    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.
        """
        if self.args is not None:
            return
        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.error("no action specified\n"+usage)

    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:
            raise ex.Error(self.parser.format_help())
        else:
            usage = self.format_digest(action)
            raise ex.Error("%s" % usage)

   0707010001f509000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/ifconfig 0707010001f512000081a40000000000000000000000016a100daf000012ff000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/sunos.py    from subprocess import *

from .ifconfig import BaseIfconfig, Interface

class Ifconfig(BaseIfconfig):
    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].decode()
        self.parse(out)

    def get_mcast(self):
        cmd = ['netstat', '-gn']
        out = Popen(cmd, stdout=PIPE).communicate()[0].decode()
        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 = 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)
 0707010001f50b000081a40000000000000000000000016a100daf00000aa7000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/ifconfig/aix.py  from subprocess import Popen, PIPE
from utilities.cache import cache
from utilities.net.converters import hexmask_to_dotted

from .ifconfig import BaseIfconfig, Interface

class Ifconfig(BaseIfconfig):
    def __init__(self, mcast=False):
        super(Ifconfig, self).__init__(mcast=mcast)
        self.intf = []
        out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0]
        self.parse(out)

    def get_mac(self, intf):
        for line in self.get_netstat_in().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 ""

    @cache("netstat.in")
    def get_netstat_in(self):
        cmd = ['netstat', '-in']
        p = Popen(cmd, stdout=PIPE, stderr=PIPE)
        out, err = p.communicate()
        if p.returncode != 0:
            return ""
        return out

    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = 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

if __name__ == "__main__":
    ifaces = Ifconfig(mcast=True)
    print(ifaces)

 0707010001f511000081a40000000000000000000000016a100daf00000c7d000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/osf1.py from subprocess import *

from .ifconfig import BaseIfconfig, Interface

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(BaseIfconfig):
    def __init__(self, mcast=False):
        super(Ifconfig, self).__init__(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"]
        p = Popen(cmd, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=True)
        out, err = p.communicate()
        if p.returncode != 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 = 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)
   0707010001f50a000081a40000000000000000000000016a100daf000000d5000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/__init__.py import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
Ifconfig = _os.Ifconfig
   0707010001f50d000081a40000000000000000000000016a100daf000007b0000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/freebsd.py  from __future__ import print_function

from subprocess import *

from .ifconfig import BaseIfconfig, Interface

class Ifconfig(BaseIfconfig):
    def __init__(self, mcast=False):
        super(Ifconfig, self).__init__(mcast=mcast)
        out = Popen(['ifconfig', '-a'], stdout=PIPE).communicate()[0].decode()
        self.parse(out)

    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = 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

if __name__ == "__main__":
    o = Ifconfig()
    print(o)
0707010001f50e000081a40000000000000000000000016a100daf00001383000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/hpux.py from subprocess import *

import core.exceptions as ex

from .ifconfig import BaseIfconfig, Interface

class Ifconfig(BaseIfconfig):
    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.Error
            self.parse(out[0])

    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 = 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.Error("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
 0707010001f510000081a40000000000000000000000016a100daf00002780000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/linux.py    import copy

from utilities.net.converters import cidr_to_dotted
from env import Env
from utilities.proc import justcall
from core.capabilities import capabilities

from .ifconfig import BaseIfconfig, Interface

"""
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(BaseIfconfig):
    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 "node.x.ip" in capabilities:
            cmd = [Env.syspaths.ip, 'addr']
            out, _, _ = justcall(cmd)
            self.parse_ip(out)
        elif "node.x.ifconfig" in capabilities:
            cmd = ['ifconfig', '-a']
            out, _, _ = justcall(cmd)
            self.parse_ifconfig(out)

    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 = 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
                i.flag_no_carrier = 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
                            if 'NO-CARRIER' == w:
                                i.flag_no_carrier = 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 = 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
                i.flag_no_carrier = 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
            if 'NO-CARRIER' == w:
                i.flag_no_carrier = True

            prevprev = prev
            prev = w

    def get_mcast(self):
        if "node.x.ip" in capabilities:
            cmd = [Env.syspaths.ip, 'maddr']
            out, _, _ = justcall(cmd)
            return self.parse_mcast_ip(out)
        if "node.x.netstat" in capabilities:
            cmd = ["netstat", "-gn"]
            out, _, _ = justcall(cmd)
            return self.parse_mcast_netstat(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):
        """
        2:	wlp0s20f3
            link  01:00:5e:00:00:01 users 2
            link  01:00:5e:00:00:fb users 2
            link  33:33:00:00:00:01 users 2
            link  33:33:ff:c8:66:d7 users 2
            link  33:33:00:00:00:fb users 2
            link  33:33:ff:c5:a9:cb users 2
            link  33:33:ff:77:d4:87 users 2
            inet  224.0.0.1
        """
        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
            try:
                l = line.split()
                if l[0] in ("inet", "inet6"):
                    data[name].append(l[1])
            except IndexError:
                continue
        return data

if __name__ == "__main__":
    ifaces = Ifconfig(mcast=True)
    print(ifaces)

0707010001f50c000081a40000000000000000000000016a100daf00000b83000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/darwin.py   from subprocess import *

from utilities.proc import which
from .ifconfig import BaseIfconfig, Interface

class Ifconfig(BaseIfconfig):
    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)

    def parse(self, out):
        prev = ''
        prevprev = ''
        for w in out.split():
            if 'flags=' in w:
                i = 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


 0707010001f513000081a40000000000000000000000016a100daf00000551000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/windows.py  import foreign.wmi as wmi

from subprocess import *

from .ifconfig import BaseIfconfig, Interface


class Ifconfig(BaseIfconfig):
    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)

    def parse(self, intf, intf_cf):
        if intf_cf.IPAddress is None:
            return
        i = 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])

if __name__ == "__main__" :
    o = Ifconfig()
    print(o)
   0707010001f50f000081a40000000000000000000000016a100daf00001251000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/ifconfig/ifconfig.py 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)]
        a += [' flag_no_carrier = ' + str(self.flag_no_carrier)]
        if self.groupname:
            a += [' groupname = ' + str(self.groupname)]
        return '\n'.join(a)

    def __init__(self, name):
        self.name = name
        # defaults
        self.groupname = ''
        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
        self.flag_no_carrier = False

class BaseIfconfig(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
   0707010001f557000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/ping 0707010001f560000081a40000000000000000000000016a100daf00000107000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/ping/windows.py  from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping.exe',
           '-n', str(count),
           '-w', str(timeout),
           addr]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
 0707010001f55d000081a40000000000000000000000016a100daf00000175000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/ping/linux.py    from utilities.proc import justcall

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]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
   0707010001f55a000081a40000000000000000000000016a100daf00000174000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/ping/darwin.py   from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    if ':' in addr:
        ping = 'ping6'
    else:
        ping = 'ping'
    cmd = [ping, '-c', str(count),
                 '-W', str(timeout),
                 '-t', str(timeout),
                 addr]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False


0707010001f559000081a40000000000000000000000016a100daf00000121000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/utilities/ping/aix.py  from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', '-c', str(count), '-w', str(timeout)]
    if ':' in addr:
        cmd += ['-a', 'inet6']
    cmd += [addr]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
   0707010001f55e000081a40000000000000000000000016a100daf000000fa000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/ping/osf1.py from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', '-c', repr(count),
           '-t', repr(timeout),
           addr]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
  0707010001f55b000081a40000000000000000000000016a100daf00000129000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/ping/freebsd.py  from utilities.proc import justcall

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 = justcall(cmd)
    if ret == 0:
        return True
    return False
   0707010001f55c000081a40000000000000000000000016a100daf000000fa000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/ping/hpux.py from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', addr,
           '-n', repr(count),
           '-m', repr(timeout)]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
  0707010001f55f000081a40000000000000000000000016a100daf000000ca000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/ping/sunos.py    from utilities.proc import justcall

def check_ping(addr, timeout=5, count=1):
    cmd = ['ping', addr, str(timeout)]
    _, _, ret = justcall(cmd)
    if ret == 0:
        return True
    return False
  0707010001f558000081a40000000000000000000000016a100daf000000d9000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/ping/__init__.py import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
check_ping = _os.check_ping
   0707010001f51b000081a40000000000000000000000016a100daf00000778000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/utilities/lazy.py  from threading import RLock

_missing = object()

class threadsafe_lazy(object):
    def __init__(self, func):
        self.__name__ = "_lazy_" + func.__name__
        self.__module__ = func.__module__
        self.__doc__ = func.__doc__
        self.func = func
        self.lock = RLock()

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        with self.lock:
            value = obj.__dict__.get(self.__name__, _missing)
            if value is _missing:
                value = self.func(obj)
                obj.__dict__[self.__name__] = value
            return value

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
    return hasattr(self, attr_name)


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, exclude=None):
    """
    Unset all lazy property hidden property, iow flush the cache
    """
    exclude = exclude or []
    for attr in [attr for attr in self.__dict__]:
        if attr.startswith("_lazy_") and attr[6:] not in exclude:
            try:
                delattr(self, attr)
            except AttributeError:
                pass


def unset_lazy(self, attr):
    """
    Unset <attr> lazy property hidden property, iow flush the cache
    """
    attr_name = '_lazy_' + attr
    try:
        delattr(self, attr_name)
    except AttributeError:
        pass



0707010001f5aa000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/subsystems   0707010001f5b8000081a40000000000000000000000016a100daf00000c34000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/subsystems/zone.py   from utilities.proc import justcall

ZONEADM="/usr/sbin/zoneadm"

def is_zone():
    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))
0707010001f5b2000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/subsystems/lvm   0707010001f5b4000081a40000000000000000000000016a100daf00001f22000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/subsystems/lvm/aix.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)
  0707010001f5b5000081a40000000000000000000000016a100daf00000246000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/subsystems/lvm/linux.py  from env import Env
from utilities.cache import cache
from utilities.proc import justcall

@cache("lvs.attr")
def get_lvs_attr():
    cmd = [Env.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

  0707010001f5b3000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/utilities/subsystems/lvm/__init__.py   0707010001f5b1000081a40000000000000000000000016a100daf0000032e000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/subsystems/gce.py    import json

from utilities.proc import justcall
from utilities.string import is_string

class GceMixin(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

  0707010001f5ac000081a40000000000000000000000016a100daf00001922000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/subsystems/advfs.py  import os
import glob

from utilities.proc 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):
        self.domain = None
        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]
        ret, out, err = 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]
        ret, out, err = call(cmd)
        if ret != 0:
            raise ExInit()
        lines = out.split('\n')
        n_lines = len(lines)
        if n_lines == 0:
            return
        h = 0
        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)
  0707010001f5b0000081a40000000000000000000000016a100daf000006cc000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/subsystems/ethtool.py    from utilities.proc import justcall

"""
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):
        cmd = ['ethtool', self.intf]
        out, err, ret = justcall(cmd)
        if ret != 0:
            if not out and not err:
                raise LoadError("ethtool is not installed")
            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])))

0707010001f5ae000081a40000000000000000000000016a100daf00003528000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/subsystems/btrfs.py  import sys
import os
import logging
import subprocess

import core.exceptions as ex
import utilities.devices.linux

from env import Env
from utilities.proc import justcall, vcall

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

    def __init__(self, path=None, label=None, node=None, resource=None):
        self.path = path
        self.label = label
        self.node = node
        self.resource = resource
        self.subvols = None

        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.rootdir = os.path.join(Env.paths.pathvar, 'btrfs', self.label)
        self.path = self.rootdir

        self.setup_rootvol()

    def get_dev(self):
        if hasattr(self, "dev"):
            return
        if self.node is None:
            if self.resource is None:
                tree = None
            else:
                tree = self.resource.svc.node.devtree
            try:
                self.dev = utilities.devices.linux.label_to_dev(
                    "LABEL="+self.label,
                    tree=tree,
                )
            except ex.Error as exc:
                self.dev = None
        else:
            return
        if self.dev is not None:
            return
        if self.label is not None:
            self.dev = "LABEL="+self.label
            return
        raise ex.Error("no dev nor 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_snaps_of(self, path, refresh=False):
        """
        Snaps are subvols with the same top as the subvol with <path>
        """
        self.get_subvols(refresh=refresh)
        l = []
        ref = None
        for sv in self.subvols.values():
            if sv["path"] == path:
                ref = sv
                break
        if ref is None:
            return []
        for sv in self.subvols.values():
            if sv["parent_uuid"] == ref["uuid"]:
                l.append(sv)
        return l

    def get_subvols(self, refresh=False):
        """
        ID 9203 gen 19446 parent 5 top level 5 parent_uuid 9a272ac8-b089-d540-8777-87535ee8f4be received_uuid d77d16ad-a1ad-f84f-8392-5d2a66fc6cbf uuid 23a3e8aa-8996-1e41-b810-017d4afc4c8a path .opensvc/snapshots/bt1@sent

           1        3            5           8             10                                                 12                                   13   14                                        16
        """
        if not refresh and self.subvols is not None:
            return self.subvols
        self.subvols = {}
        cmd = ['btrfs', 'subvol', 'list', '-qupR', self.path]
        out, err, ret = self.justcall(cmd)
        if ret != 0:
            cmd_string = subprocess.list2cmdline(cmd)
            if self.node is not None:
                self.log.warning("command failed on %s: %s", self.node, cmd_string)
            raise InitError("error running '%s': %s\n" % (cmd_string, err))

        for line in out.split("\n"):
            if len(line) == 0:
                continue
            l = line.split()
            subvol = {}
            subvol['id'] = int(l[1])
            subvol['gen'] = int(l[3])
            subvol['parent'] = int(l[5])
            subvol['top'] = int(l[8])
            subvol['parent_uuid'] = l[10]
            subvol['received_uuid'] = l[12]
            subvol['uuid'] = l[14]
            subvol['path'] = line[line.index(" path ")+6:]
            self.subvols[subvol['id']] = subvol
        return self.subvols

    def subvol_delete_cmd(self, subvol=None):
        if subvol is None:
            subvol = []
        opts = []

        if isinstance(subvol, list):
            subvols = subvol
        else:
            subvols = [subvol]

        # discard already deleted subvols
        subvols = [subvol for subvol in subvols if self.has_subvol(subvol)]
        if len(subvols) == 0:
            return

        # delete in descending depth order
        subvols.sort(reverse=True)

        cmd = ["btrfs", "subvolume", "delete"] + opts + subvols
        return cmd

    def subvol_delete(self, subvol=None):
        cmd = self.subvol_delete_cmd(subvol)
        if not cmd:
            return
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError()

    def fsfreeze(self):
        cmd = ["fsfreeze", "--freeze", self.rootdir]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError()

    def fsunfreeze(self):
        cmd = ["fsfreeze", "--unfreeze", self.rootdir]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError()

    def get_subvols_in_path(self, path, refresh=False):
        self.get_subvols(refresh=refresh)
        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 snapshots(self, snaps, readonly=False):
        opts = []
        if readonly:
            opts.append('-r')

        cmds = []
        for s in snaps:
            origin = s[0]
            snap = s[1]
            cmd = ['btrfs', 'subvolume', 'snapshot'] + opts + [origin, snap]
            cmds += [subprocess.list2cmdline(cmd)]
        ret, out, err = self.vcall(" && ".join(cmds), shell=True)
        if ret != 0:
            raise ExecError(err)

    def snapshot_cmd(self, origin, snap, readonly=False):
        opts = []
        if readonly:
            opts.append('-r')
        cmd = ['btrfs', 'subvolume', 'snapshot'] + opts + [origin, snap]
        return cmd

    def snapshot(self, origin, snap, readonly=False):
        if self.has_subvol(snap):
            raise ExistError("snapshot %s already exists"%snap)
        cmd = self.snapshot_cmd(origin, snap, readonly)
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ExecError(err)

    def path_to_subvol(self, path):
        if path.startswith('/'):
            return path.replace(self.rootdir+'/', "")
        return path

    def get_subvol(self, subvol, refresh=False):
        self.get_subvols(refresh=refresh)

        subvol = self.path_to_subvol(subvol)
        for sub in self.subvols.values():
            if sub['path'] == subvol:
                return sub
        return None

    def has_subvol(self, subvol, refresh=False):
        # refresh subvol list
        self.get_subvols(refresh=refresh)

        if isinstance(subvol, str):
            subvol = self.path_to_subvol(subvol)
        for sub in self.subvols.values():
            if sub['path'] == subvol:
                return True
        return False

    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 vcall(self, cmd, shell=False):
        if self.node is not None:
            lcmd = Env.rsh.split() + [self.node]
            if shell:
                rcmd = cmd
                cmd = lcmd + [rcmd]
                cmd = subprocess.list2cmdline(cmd)
            else:
                rcmd = subprocess.list2cmdline(cmd)
                cmd = lcmd + [rcmd]

        return vcall(cmd, log=self.log, shell=shell)

    def justcall(self, cmd):
        if self.node is not None:
            cmd = subprocess.list2cmdline(cmd)
            cmd = Env.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_rootvol(self):
        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 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 ex.Error("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', self.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_subvol_path(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("get_subvol_path: %s\n%s"%(path, err))
        for line in out.split("\n"):
            return line
        raise ExecError("can't find %s path relative to the btrfs root\n" % path)

    def __str__(self):
        self.get_subvols()
        s = "label: %s\n" % self.label
        s += "subvolumes:\n"
        for sub in self.subvols.values():
            s += "uuid: %s parent: %s top: %s path: %s\n"%(sub['uuid'], sub['parent_uuid'], sub['top'], sub['path'])
        return s

if __name__ == "__main__":
    o = Btrfs(label=sys.argv[1])
    #print(o)
    for sub in o.get_snaps_of("bt1/child1/1a"):
        print(sub)
    #print(o.get_transid("/opt/opensvc/var/btrfs/deb1/deb1@sent"))

0707010001f5ab000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/subsystems/__init__.py   0707010001f5b6000081a40000000000000000000000016a100daf00000205000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/subsystems/veritas.py    import os

from core.capabilities import capabilities
from utilities.proc import justcall

def vx_dev_to_paths(dev):
    if "node.x.vxdmpadm" not in capabilities:
        return []
    dev = os.path.basename(dev)
    cmd = ["vxdmpadm", "list", "dmpnode", "dmpnodename="+dev]
    out, err, ret = justcall(cmd)
    data = []
    for line in out.splitlines():
        if not line.startswith("path"):
            continue
        path = "/dev/"+line.split("= ", 1)[1].split()[0]
        data.append(path)
    return data
   0707010001f5ad000081a40000000000000000000000016a100daf00000601000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/subsystems/amazon.py import json

import core.exceptions as ex
from utilities.proc import justcall

class AmazonMixin(object):
    instance_id = None
    instance_data = None

    def aws(self, cmd, verbose=True):
        try:
            _cmd = [self.svc.aws]
        except AttributeError:
            _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.Error(err)
        data = json.loads(out)
        return data

    def get_instance_id(self):
        if self.instance_id is not None:
            return self.instance_id
        try:
            import httplib
        except ImportError:
            raise ex.Error("the httplib module is required")
        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


   0707010001f5af000081a40000000000000000000000016a100daf000074cf000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/subsystems/docker.py # -*- coding: utf8 -*-

"""
The module implementing the DockerLib class.
"""
import errno
import json
import os
import re
import time

import utilities.lock
import core.status
import core.exceptions as ex

from env import Env
from utilities.lazy import threadsafe_lazy as lazy, unset_lazy, set_lazy
from utilities.proc import justcall
from foreign.looseversion import LooseVersion as V

def has_docker(program_list):
    for exe in program_list:
        try:
            out = justcall([exe, "--version"])[0]
        except:
            continue
        if out.startswith("Docker"):
            return True
    return False

class ContainerLib(object):
    """
    Instanciated as the 'dockerlib' Svc lazy attribute, this class abstracts
    docker daemon ops.
    """
    docker_cmd = ["/bin/true"]
    json_opt = ["--format", "{{json .}}"]
    container_type = "none"
    config_args_position_head = True

    def __init__(self, svc=None):
        self.svc = svc
        self.docker_info_done = False
        self.raw_container_data_dir = self.svc.oget("DEFAULT", "container_data_dir")

    def client_config_args(self, path):
        return ["--config", os.path.dirname(path)]

    @lazy
    def container_data_dir(self):
        if not self.raw_container_data_dir:
            return
        return self.svc.replace_volname(self.raw_container_data_dir, errors="ignore")[0]

    @lazy
    def docker_exe(self):
        return "/bin/true"

    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, "container_ps")
        return self.container_ps

    @lazy
    def container_ps(self):
        """
        The "docker ps -a --no-trunc" json loaded dicts assembled in a list.
        """
        cmd = self.docker_cmd + ["ps", "-a", "--no-trunc"] + self.json_opt
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        data = []
        for line in out.splitlines():
            data.append(json.loads(line))
        return data

    @lazy
    def container_by_label(self):
        """
        A hash of instances data as found in "docker ps", indexed by
        instance label.
        """
        data = {}
        for container in self.container_ps:  # pylint: disable=not-an-iterable
            labels = container.get("Labels", "")
            if isinstance(labels, dict):
                # podman
                for lname, lvalue in labels.items():
                    label = "%s=%s" % (lname, lvalue)
                    try:
                        data[label].append(container)
                    except KeyError:
                        data[label] = [container]
            else:
                # docker
                for label in labels.split(","):
                    try:
                        data[label].append(container)
                    except KeyError:
                        data[label] = [container]
        return data

    @lazy
    def container_by_name(self):
        """
        A hash of instances data as found in "docker ps", indexed by
        instance id.
        """
        data = {}
        for container in self.container_ps:  # pylint: disable=not-an-iterable
            for name in self._container_names(container):
                try:
                    data[name].append(container)
                except KeyError:
                    data[name] = [container]
        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 <namepace>..<name>.container.<n>
        where <n> is the identifier part of the resource id.
        """
        if refresh:
            unset_lazy(self, "container_ps")
            unset_lazy(self, "container_by_name")
        try:
            data = self.container_by_name[resource.name][0] # pylint: disable=unsubscriptable-object
            return data.get("ID", data.get("Id"))
        except Exception:
            return

    def get_container_id_by_label(self, resource, refresh=False):
        """
        Return the container id for the <resource> container resource.
        Lookup in docker ps by docker label com.opensvc.id=<...>.
        """
        if refresh:
            unset_lazy(self, "container_ps")
            unset_lazy(self, "container_by_label")
        try:
            data = self.container_by_label[resource.container_label_id][0]  # pylint: disable=unsubscriptable-object
            return data.get("ID", data.get("Id"))
        except Exception as exc:
            return

    def get_container_id(self, resource, refresh=False):
        if refresh:
            unset_lazy(self, "container_ps")
            unset_lazy(self, "container_by_label")
            unset_lazy(self, "container_by_name")
        cid = self.get_container_id_by_label(resource, refresh=False)
        if cid:
            return cid
        try:
            return self.get_container_id_by_name(resource, refresh=False)
        except:
            return

    @lazy
    def docker_info(self):
        """
        The output of "docker info".
        """
        try:
            self.docker_exe
        except ex.InitError:
            return {}
        cmd = [self.docker_exe, "info"] + self.json_opt
        try:
            data = json.loads(justcall(cmd)[0])
        except ValueError:
            data = {}
        return data

    @lazy
    def docker_version(self):
        """
        The docker version.
        """
        try:
            cmd = [self.docker_exe, "--version"]
        except ex.InitError:
            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.InitError:
            return False
        if V(self.docker_version) >= V(version):
            return True
        return False

    def get_running_instance_ids(self, refresh=False):
        """
        Return the list of running docker instances id.
        """
        if refresh:
            unset_lazy(self, "container_ps")
            unset_lazy(self, "running_instance_ids")
        return self.running_instance_ids

    @lazy
    def running_instance_ids(self):
        """
        The list of running docker instances id.
        """
        if self.docker_cmd is None:
            return []
        data = []
        for ps in self.container_ps: # pylint: disable=not-an-iterable
            if "Status" in ps:
                running = ps["Status"].startswith("Up ")
            elif "State" in ps:
                running = ps["State"] == "running"
            else:
                continue
            if running:
                cid = ps.get("Id", ps.get("ID"))
                if cid:
                    data.append(cid)
        return data

    def get_image_id(self, name):
        """
        Return the full image id
        """
        if name.startswith("sha256:"):
            return name[7:]
        image = self.get_image(name)
        if not image:
            return
        return image["id"]

    def get_image(self, name):
        """
        Lookup the image data by name
        """
        try:
            image_name, image_tag = name.rsplit(":", 1)
        except ValueError:
            image_name = name
            image_tag = "latest"
        if "/" in image_tag:
            image_name = name
            image_tag = "latest"

        for data in self.images:  # pylint: disable=not-an-iterable
            if data["tag"] != image_tag:
                continue
            if data["name"] == image_name:
                return data
            if data["name"] == "docker.io/library/"+image_name:
                return data
            if data["name"] == "docker.io/"+image_name:
                return data
            if data["name"].replace("docker.io/library/", "docker.io/") == image_name:
                return data
            if data["name"].replace("docker.io/", "docker.io/library/") == image_name:
                return data
            if image_name == "docker.io/library/"+data["name"]:
                return data
            if image_name == "docker.io/"+data["name"]:
                return data
            if image_name.replace("docker.io/library/", "docker.io/") == data["name"]:
                return data
            if image_name.replace("docker.io/", "docker.io/library/") == data["name"]:
                return data

    def login_as_service_args(self):
        uuid = self.svc.node.conf_get("node", "uuid")
        args = ["-u", self.svc.path+"@"+Env.nodename]
        args += ["-p", uuid]
        if self.docker_min_version("1.12"):
            pass
        elif self.docker_min_version("1.10"):
            args += ["--email", self.svc.path+"@"+Env.nodename]
        return args

    def docker_login(self, ref):
        if "/" not in ref:
            return
        reg = ref.split("/")[0]
        if reg == "docker.io":
            return
        try:
            cmd = self.docker_cmd + ["login", reg] + self.login_as_service_args()
        except Exception:
            self.svc.log.debug("skip registry login as service: node not registered")
            return
        justcall(cmd)

    def image_pull(self, ref, config=None):
        if config:
            extra = self.client_config_args(config)
        else:
            extra = []
            self.docker_login(ref)
        self.svc.log.info("pulling image %s" % ref)
        if self.config_args_position_head:
            cmd = self.docker_cmd + extra + ["pull", ref]
        else:
            cmd = self.docker_cmd + ["pull"] + extra + [ref]
        results = justcall(cmd)
        if results[2] != 0:
            raise ex.Error(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
            image_id = re.sub("^sha256:", "", elements[2])
            data.append({
                "name": elements[0],
                "tag": elements[1],
                "id": image_id,
            })
        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 = []
        di = dict(self.docker_info) # dict cast to please pylint
        if "Driver" in di:
            data.append(["", "storage_driver", di["Driver"]])
        if "ExecutionDriver" in di:
            data.append(["", "exec_driver", di["ExecutionDriver"]])
        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(self.container_type):
            image = self.get_image(resource.image)
            if image is None:
                continue
            images_done.append(image["id"])
            data.append([resource.rid, "image", resource.image])
            data.append([resource.rid, "image_id", image["id"]])
            data.append([resource.rid, "instance_id", resource.container_id])

        # unreferenced images
        for image in self.images:  # pylint: disable=not-an-iterable
            if image["id"] in images_done:
                continue
            data.append(["", "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.image:
            return resource.image
        if self.images is None:
            return resource.image
        image = self.get_image(resource.image)
        if image:
            return image["name"]
        return resource.image

    def docker_inspect(self, container_id):
        """
        Return the "docker inspect" data dict.
        """
        if container_id is None:
            raise IndexError("container id is None")
        for data in self.containers_inspect:  # pylint: disable=not-an-iterable
            if data and data.get("Id", data.get("ID")) == container_id:
                return data
        return {}

    @lazy
    def containers_inspect(self):
        ids = []
        for resource in self.svc.get_resources(self.container_type):
            if not resource.container_id:
                continue
            ids.append(resource.container_id)
        if not ids:
            return []
        try:
            self.docker_exe
        except ex.InitError:
            return []
        cmd = self.docker_cmd + ["inspect"] + ids
        out = justcall(cmd)[0]
        try:
            data = json.loads(out)
        except Exception:
            data = []
        return data

    def docker_volume_inspect(self, vol_id):
        """
        Return the "docker volume inspect" data dict.
        """
        try:
            self.docker_exe
        except ex.InitError:
            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 _container_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"):
            if resource.type == "fs.docker":
                continue
            if not hasattr(resource, "mount_point"):
                continue
            mntpts.append(resource.mount_point)
            mntpt_res[resource.mount_point] = resource
        for mntpt in sorted(mntpts, reverse=True):
            if self.container_data_dir.startswith(mntpt):  # pylint: disable=no-member
                return mntpt_res[mntpt]

    @staticmethod
    def _container_names(container):
        names = container.get("Names", [])
        if isinstance(names, str):
            # docker
            return names.split(",")
        else:
            # podman
            return names


class DockerLib(ContainerLib):
    sock_tmo = 5.0
    container_type = ["container.docker", "task.docker"]

    def __init__(self, svc=None):
        ContainerLib.__init__(self, svc=svc)
        self.max_wait_for_dockerd = 5

        try:
            self.docker_daemon_private = \
                self.svc.conf_get("DEFAULT", "docker_daemon_private")
        except ex.OptNotFound:
            if self.raw_container_data_dir:
                self.docker_daemon_private = True
            else:
                self.docker_daemon_private = False
        if Env.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

        if self.raw_container_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 self.docker_daemon_private:
            self.docker_socket = os.path.join(self.svc.var_d, "docker.sock")
            self.compat_docker_socket = os.path.join(Env.paths.pathvar, self.svc.name, "docker.sock")
        else:
            self.docker_socket = None

        if self.docker_daemon_private:
            self.docker_pid_file = os.path.join(self.svc.var_d, "docker.pid")
            self.compat_docker_pid_file = os.path.join(Env.paths.pathvar, self.svc.name, "docker.pid")
        else:
            self.docker_pid_file = None
            try:
                di = dict(self.docker_info) # dict cast to please pylint
                set_lazy(self, "container_data_dir", di["DockerRootDir"])
            except (KeyError, TypeError):
                set_lazy(self, "container_data_dir", None)

        try:
            self.docker_cmd = [self.docker_exe]
            if self.docker_socket:
                if self.test_sock(self.docker_socket) or not self.test_sock(self.compat_docker_socket):
                    sock = self.docker_socket
                else:
                    sock = self.compat_docker_socket
                self.docker_cmd += ["-H", "unix://"+sock]
        except:
            self.docker_cmd = None

    @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.
        """
        from core.capabilities import capabilities
        if capabilities.has("node.x.docker.io"):
            return "docker.io"
        if capabilities.has("node.x.docker"):
            return "docker"
        raise ex.InitError("docker executable not found")

    @lazy
    def dockerd_exe(self):
        from core.capabilities import capabilities
        if capabilities.has("node.x.dockerd"):
            return "dockerd"
        else:
            raise ex.InitError("dockerd executable not found")

    def test_sock(self, path):
        import socket
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.settimeout(self.sock_tmo)
        try:
            sock.connect(path)
        except Exception as exc:
            return False
        finally:
            sock.close()
        return True

    @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", "unix://"+self.docker_socket,
                "--data-root", self.container_data_dir,
                "-p", self.docker_pid_file
            ]
        elif self.docker_min_version("1.13"):
            cmd = [
                self.dockerd_exe,
                "-H", "unix://"+self.docker_socket,
                "-g", self.container_data_dir,
                "-p", self.docker_pid_file
            ]
        elif self.docker_min_version("1.8"):
            cmd = [
                self.docker_exe, "daemon",
                "-H", "unix://"+self.docker_socket,
                "-g", self.container_data_dir,
                "-p", self.docker_pid_file
            ]
        else:
            cmd = self.docker_cmd + [
                "-r=false", "-d",
                "-g", self.container_data_dir,
                "-p", self.docker_pid_file
            ]
        if self.docker_min_version("1.9") and "--exec-root" not in str(self.docker_daemon_args):
            # keep <104 length to please dockerd
            cmd += ["--exec-root", os.path.join(Env.paths.pathvar, "dockerx", self.svc.id)]
        cmd += self.docker_daemon_args
        return cmd

    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.container_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.Error("failed to kill docker daemon: %s" % str(exc))

    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.Error("docker executable not found")
        lockfile = os.path.join(self.svc.var_d, "lock.docker_start")
        try:
            lockfd = utilities.lock.lock(timeout=15, delay=1, lockfile=lockfile)
        except utilities.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():
            utilities.lock.unlock(lockfd)
            return

        if self.container_data_dir is None:
            utilities.lock.unlock(lockfd)
            return

        resource = self._container_data_dir_resource()
        if resource is not None:
            state = resource._status()
            if state not in (core.status.UP, core.status.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, core.status.Status(state))
                utilities.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,
        )

        try:
            for _ in range(self.max_wait_for_dockerd):
                if self._docker_working():
                    self.clear_daemon_caches()
                    return
                time.sleep(1)
        finally:
            utilities.lock.unlock(lockfd)

    def clear_daemon_caches(self):
        unset_lazy(self, "container_ps")
        unset_lazy(self, "container_by_name")
        unset_lazy(self, "container_by_label")
        unset_lazy(self, "docker_info")
        unset_lazy(self, "running_instance_ids")
        unset_lazy(self, "images")
        unset_lazy(self, "containers_inspect")

    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.

        Old docker daemons return {}.
        Recent docker daemons return {
            'ServerErrors': ['Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?'],
            'ClientInfo': {
                'Debug': False,
                'Context': 'default',
                'Plugins': [],
                'Warnings': None
            }
        }
        """
        di = dict(self.docker_info) # dict cast to please pylint
        return di.get("ServerVersion") is not None

    def _docker_running_private(self):
        """
        Return True if the docker daemon is running.
        """
        if os.path.exists(self.docker_pid_file):
            pid_file = self.docker_pid_file
        elif os.path.exists(self.compat_docker_pid_file):
            pid_file = self.compat_docker_pid_file
        else:
            self.svc.log.debug("docker_running: no pid file %s", self.docker_pid_file)
            return False
        try:
            with open(pid_file, "r") as ofile:
                buff = ofile.read()
        except IOError as exc:
            if exc.errno == errno.ENOENT:
                return False
            return ex.Error("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


class PodmanLib(ContainerLib):
    json_opt = ["--format=json"]
    container_type = ["container.podman", "task.podman"]
    config_args_position_head = False

    def __init__(self, svc=None):
        ContainerLib.__init__(self, svc=svc)

        self.docker_daemon_args = []
        self.docker_daemon_args += [
            "--cgroup-manager", "cgroupfs",
            "--cni-config-dir", self.svc.node.cni_config,
        ]

        if self.container_data_dir:
            self.docker_daemon_private = True
            self.docker_daemon_args += ["--root", self.container_data_dir]
            self.docker_daemon_args += ["--runroot", self.container_data_dir+"/run"]
        else:
            self.docker_daemon_private = False
        if Env.sysname != "Linux":
            self.docker_daemon_private = False

        self.docker_cmd = [self.docker_exe] + self.docker_daemon_args

    @lazy
    def docker_exe(self):
        return "/usr/bin/podman"

    @lazy
    def container_ps(self):
        """
        The "docker ps -a --no-trunc" json loaded dicts assembled in a list.
        """
        cmd = self.docker_cmd + ["ps", "-a", "--no-trunc"] + self.json_opt
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        return json.loads(out)

    def docker_stop(self):
        pass

    def docker_start(self):
        pass

    def login_as_service_args(self):
        uuid = self.svc.node.oget("node", "uuid")
        if not uuid:
            return []
        args = ["-u", self.svc.path+"@"+Env.nodename]
        args += ["-p", uuid]
        return args

    def client_config_args(self, path):
        return ["--authfile", path]


 0707010001f5b7000081a40000000000000000000000016a100daf00002541000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/subsystems/zfs.py    import logging
import sys

from env import Env
import core.exceptions as ex
from utilities.cache import cache
from utilities.proc import call, vcall, justcall

def dataset_exists(device, dstype):
    """
    Return True if the data exists.
    """
    return Dataset(device).exists(dstype)

def zpool_getprop(pool='undef_pool', propname='undef_prop'):
    """
    Return the zpool property <propname> value
    """
    cmd = [Env.syspaths.zpool, 'get', '-Hp', '-o', 'value', propname, pool]
    out, _, ret = justcall(cmd)
    if ret == 0:
        return out.split("\n")[0]
    else:
        return ""

def zpool_setprop(pool='undef_pool', propname='undef_prop', propval='undef_val', log=None):
    """
    Set the dataset property <propname> to value <propval>.
    """
    current = zpool_getprop(pool, propname)
    if current == "":
        # pool does not exist
        return False
    if current == propval:
        return True
    cmd = [Env.syspaths.zpool, 'set', propname + '='+ propval, pool]
    ret, _, _ = vcall(cmd, log=log)
    if ret == 0:
        return True
    else:
        return False

def zfs_getprop(dataset='undef_ds', propname='undef_prop'):
    """
    Return the dataset property <propname> value
    """
    cmd = [Env.syspaths.zfs, 'get', '-Hp', '-o', 'value', propname, dataset]
    out, _, ret = justcall(cmd)
    if ret == 0:
        return out.split("\n")[0]
    else:
        return ""

def zfs_setprop(dataset='undef_ds', propname='undef_prop', propval='undef_val', log=None):
    """
    Set the dataset property <propname> to value <propval>.
    """
    current = zfs_getprop(dataset, propname)
    if current == "":
        # dataset does not exist
        return False
    if current == propval:
        return True
    cmd = [Env.syspaths.zfs, 'set', propname + '='+ propval, dataset]
    ret, _, _ = vcall(cmd, log=log)
    if ret == 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 = [Env.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 = [Env.syspaths.zfs, 'list', self.name]
        else:
            cmd = [Env.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 = [Env.syspaths.zfs, 'rename', self.name, name]
        else:
            cmd = [Env.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 = [Env.syspaths.zfs, 'create', self.name]
        else:
            cmd = [Env.syspaths.zfs, 'create'] + option + [self.name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return True
        else:
            return False

    def destroy(self, options=None):
        """
        Destroy the dataset.
        """
        if options is None:
            options = []
        if not self.exists():
            return True
        cmd = [Env.syspaths.zfs, 'destroy'] + options + [self.name]
        if self.log:
            self.log.info(" ".join(cmd))
        _, err, ret = justcall(cmd)
        if ret == 0:
            return True
        elif "could not find any snapshot" in err:
            return True
        elif "dataset does not exist" in err:
            return True
        else:
            return False

    def getprop(self, propname):
        """
        Return a dataset propertie value or '' on error.
        """
        cmd = [Env.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 = [Env.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=None, 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.
        """
        if nv_pairs is None:
            nv_pairs = {}
        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.Error("snapname should be defined")
        snapdataset = self.name + "@" + snapname
        cmd = [Env.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 = [Env.syspaths.zfs, 'clone', self.name, name]
        else:
            cmd = [Env.syspaths.zfs, 'clone'] + option + [self.name, name]
        ret, _, _ = vcall(cmd, log=self.log)
        if ret == 0:
            return Dataset(name)
        else:
            return False

@cache("zpool.devs.{args[0]}")
def zpool_devs(poolname, node=None):
    """
    Search zpool vdevs from the output of "zpool status <poolname>" if
    imported.
    """
    devs = set()
    cmd = ['zpool', 'status']
    if Env.sysname == "Linux":
        cmd += ["-L", "-P"]
    cmd += [poolname]
    out, err, ret = justcall(cmd)
    if ret != 0:
        return []

    import re

    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 Env.sysname == "SunOS":
                if disk.startswith(Env.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 Env.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)
        elif Env.sysname == "Linux" and node:
            tdev = node.devtree.get_dev_by_devpath(d)
            if tdev is None:
                continue
            for path in tdev.devpath:
                if "/dev/mapper/" in path or "by-id" in path:
                    d = path
                    break
        vdevs.add(d)

    return list(vdevs)

   0707010001f4c5000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/utilities/asset    0707010001f4cd000081a40000000000000000000000016a100daf00002ac5000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/asset/osf1.py    import re

from .asset import BaseAsset
from utilities.proc import justcall

sim = False

class Asset(BaseAsset):
    def __init__(self, node):
        super(Asset, self).__init__(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):
        from foreign.looseversion import LooseVersion as V # pylint: disable=no-name-in-module,import-error
        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 re.match(r"^\W*[0-9]*:", 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())
   0707010001f4c9000081a40000000000000000000000016a100daf00000c79000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/asset/darwin.py  from .asset import BaseAsset
import core.exceptions as ex
from utilities.proc import justcall

class Asset(BaseAsset):
    def __init__(self, node):
        super(Asset, self).__init__(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 ex.Error("unexpected memory format")
        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']

   0707010001f4c7000081a40000000000000000000000016a100daf00000c2c000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/asset/aix.py from .asset import BaseAsset
import core.exceptions as ex
from utilities.proc import justcall

class Asset(BaseAsset):
    def __init__(self, node):
        super(Asset, self).__init__(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 ex.Error(err)
        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'
0707010001f4ce000081a40000000000000000000000016a100daf000024dd000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/asset/sunos.py   import datetime
import json
import os
import re

from .asset import BaseAsset
from foreign.six.moves.urllib.parse import quote # pylint: disable=import-error
from utilities.subsystems.zone import is_zone
from utilities.proc import justcall, which

class Asset(BaseAsset):
    def __init__(self, node=None):
        super(Asset, self).__init__(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]
        try:
            with open("/var/pkg/state/installed/catalog.summary.C", "r") as filep:
                data = json.load(filep)
            version = data["solaris"]["entire"][0]["version"]
            fpath = "/var/pkg/cache/publisher/solaris/pkg/entire/%s/manifest.set" % quote(version)
            with open(fpath, "r") as filep:
                buff = filep.read()
            for l in buff.splitlines():
                if 'set name=pkg.human-version' in l:
                    hv = l.split('"')[-2]
                    if 'SRU' in hv:
                        return ' '.join([lines[0], 'SRU', hv.split()[-1]])
                    elif lines[0] in hv:
                        return hv.split()[-1]
                    else:
                        return ' '.join([lines[0], hv.split()[-1]])
        except Exception:
            pass
        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'
        prev = ""
        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:
            hba_id = ''
            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 ''

    def get_boot_id_zone(self):
        pid = self.zsched_pid()
        if pid is None:
            return
        return str(os.path.getmtime("/proc/%s" % pid))

    def zsched_pid(self):
        cmd = ["pgrep", "zsched"]
        out, err, ret = justcall(cmd)
        pid = out.split()[0]
        if pid == "":
            return
        return pid

    def get_boot_id(self):
        if self.zone:
            return self.get_boot_id_zone()
        else:
            return super(Asset, self).get_boot_id()

    def get_last_boot(self):
        if self.zone:
            return self.get_last_boot_zone()
        else:
            return super(Asset, self).get_last_boot()

    def get_last_boot_zone(self):
        pid = self.zsched_pid()
        if pid is None:
            return
        last = os.path.getmtime("/proc/%s" % pid)
        return {
            "title": "last boot",
            "value": last,
            "source": self.s_probe
        }

if __name__ == "__main__":
    print(Asset()._get_cpu_model())
   0707010001f4c6000081a40000000000000000000000016a100daf000000d0000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/asset/__init__.py    import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
Asset = _os.Asset

0707010001f4ca000081a40000000000000000000000016a100daf000008d0000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/asset/freebsd.py import os

from .linux import Asset as BaseAsset
from utilities.proc import justcall

class Asset(BaseAsset):
    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]

    def get_boot_id(self):
        try:
            return super(Asset, self).get_boot_id()
        except:
            # /proc might not be mounted
            return str(os.path.getmtime("/dev"))

0707010001f4c8000081a40000000000000000000000016a100daf00005bf8000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/asset/asset.py   import codecs
import datetime
import os

import core.exceptions as ex
from utilities.converters import print_size
from env import Env
from utilities.proc import justcall, which

class BaseAsset(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.conf_get('node', 'mem_bytes')
            s = str(s/1024/1024)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_mem_bytes()
                source = self.s_probe
            except AttributeError:
                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.conf_get('node', 'mem_banks')
            s = str(s)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_mem_banks()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "mem banks",
            "value": s,
            "source": source
        }

    def get_mem_slots(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'mem_slots')
            s = str(s)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_mem_slots()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "mem slots",
            "value": s,
            "source": source
        }

    def get_os_vendor(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'os_vendor')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_os_vendor()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "os vendor",
            "value": s,
            "source": source
        }

    def get_os_release(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'os_release')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_os_release()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "os release",
            "value": s,
            "source": source
        }

    def get_os_kernel(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'os_kernel')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_os_kernel()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "os kernel",
            "value": s,
            "source": source
        }

    def get_os_arch(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'os_arch')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_os_arch()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "os arch",
            "value": s,
            "source": source
        }

    def get_cpu_freq(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'cpu_freq')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_cpu_freq()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "cpu freq",
            "value": s,
            "source": source
        }

    def get_cpu_threads(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'cpu_threads')
            s = str(s)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_cpu_threads()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "cpu threads",
            "value": s,
            "source": source
        }

    def get_cpu_cores(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'cpu_cores')
            s = str(s)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_cpu_cores()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "cpu cores",
            "value": s,
            "source": source
        }

    def get_cpu_dies(self):
        s = '0'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'cpu_dies')
            s = str(s)
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            try:
                s = self._get_cpu_dies()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "cpu dies",
            "value": s,
            "source": source
        }

    def get_cpu_model(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'cpu_model')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_cpu_model()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "cpu model",
            "value": s,
            "source": source
        }

    def get_serial(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'serial')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_serial()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "serial",
            "value": s,
            "source": source
        }

    def get_bios_version(self):
        s = ''
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'bios_version')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_bios_version()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "bios version",
            "value": s,
            "source": source
        }

    def get_sp_version(self):
        s = ''
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'sp_version')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_sp_version()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "sp version",
            "value": s,
            "source": source
        }

    def get_enclosure(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'enclosure')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_enclosure()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "enclosure",
            "value": s,
            "source": source
        }

    def get_tz(self):
        s = None
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'tz')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_tz()
                source = self.s_probe
            except AttributeError:
                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, model=None):
        s = None
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'connect_to')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_connect_to(model=model)
                source = self.s_probe
            except AttributeError:
                pass
        if s is None:
            return
        return {
            "title": "listener address",
            "value": s,
            "source": source
        }

    def _get_connect_to(self, model=None):
        if model != "Google":
            return
        if not which("gcloud"):
            return
        cmd = ["gcloud", "compute", "instances", "describe", "-q", "--format", "json", Env.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.conf_get('node', 'manufacturer')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_manufacturer()
                source = self.s_probe
            except AttributeError:
                pass
        return {
            "title": "manufacturer",
            "value": s,
            "source": source
        }

    def get_model(self):
        s = 'Unknown'
        source = self.s_default
        try:
            s = self.node.conf_get('node', 'model')
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound):
            try:
                s = self._get_model()
                source = self.s_probe
            except AttributeError:
                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_cluster_id(self):
        s = self.node.cluster_id
        return {
            "title": "cluster id",
            "value": s,
            "source": self.s_probe
        }

    def get_listener_port(self):
        s = str(Env.listener_port)
        source = self.s_default
        try:
            s = str(self.node.conf_get('listener', 'port'))
            source = self.s_config
        except (ex.OptNotFound, ex.RequiredOptNotFound, ValueError, TypeError):
            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:
            if exc.default is None:
                return None
            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 AttributeError:
            hba = []
        return hba

    def get_targets(self):
        try:
            s = self._get_targets()
        except AttributeError:
            s = []
        return s

    def get_hardware(self):
        try:
            s = self._get_hardware()
        except AttributeError:
            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 Env.sysname == "Windows":
            return []
        if not os.path.exists(p):
            return []
        try:
            with codecs.open(p, "r", "utf8") as f:
                buff = f.read()
        except:
            with codecs.open(p, "r", "latin1") 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 = l[0]
            d.append({
                keys[0]: name,
                keys[1]: l[2]
            })
        return d

    def get_lan(self):
        kwargs = {'mcast': True}
        if Env.sysname == 'HP-UX':
            kwargs['hwaddr'] = True
        import utilities.ifconfig
        ifconfig = utilities.ifconfig.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_boot_id(self):
        return str(os.path.getmtime("/proc/1"))

    def get_last_boot(self):
        last = os.path.getmtime("/proc/1")
        return {
            "title": "last boot",
            "value": last,
            "source": self.s_probe
        }

    def get_asset_properties(self):
        data = {}
        data['nodename'] = {
            "title": "nodename",
            "value": Env.nodename,
            "source": self.s_probe
        }
        data['fqdn'] = {
            "title": "fqdn",
            "value": Env.fqdn,
            "source": self.s_probe
        }
        data['version'] = self.get_version()
        data['os_name'] = {
            "title": "os name",
            "value": Env.sysname,
            "source": self.s_probe
        }
        data['os_vendor'] = self.get_os_vendor()
        data['os_release'] = self.get_os_release()
        data['os_kernel'] = self.get_os_kernel()
        data['os_arch'] = self.get_os_arch()
        data['mem_bytes'] = self.get_mem_bytes()
        data['mem_banks'] = self.get_mem_banks()
        data['mem_slots'] = self.get_mem_slots()
        data['cpu_freq'] = self.get_cpu_freq()
        data['cpu_threads'] = self.get_cpu_threads()
        data['cpu_cores'] = self.get_cpu_cores()
        data['cpu_dies'] = self.get_cpu_dies()
        data['cpu_model'] = self.get_cpu_model()
        data['serial'] = self.get_serial()
        data['model'] = self.get_model()
        data['manufacturer'] = self.get_manufacturer()
        data['bios_version'] = self.get_bios_version()
        data['sp_version'] = self.get_sp_version()
        data['node_env'] = self.get_node_env()
        data['enclosure'] = self.get_enclosure()
        data['listener_port'] = self.get_listener_port()
        data['cluster_id'] = self.get_cluster_id()
        connect_to = self.get_connect_to(model=data["model"]["value"])
        if connect_to is not None:
            data['connect_to'] = connect_to
        last_boot = self.get_last_boot()
        if last_boot is not None:
            data['last_boot'] = last_boot
        sec_zone = self.get_sec_zone()
        if sec_zone is not None:
            data['sec_zone'] = sec_zone
        asset_env = self.get_asset_env()
        if asset_env is not None:
            data['asset_env'] = asset_env
        tz = self.get_tz()
        if tz is not None:
            data['tz'] = tz
        loc_country = self.get_loc_country()
        if loc_country is not None:
            data['loc_country'] = loc_country
        loc_city = self.get_loc_city()
        if loc_city is not None:
            data['loc_city'] = loc_city
        loc_building = self.get_loc_building()
        if loc_building is not None:
            data['loc_building'] = loc_building
        loc_room = self.get_loc_room()
        if loc_room is not None:
            data['loc_room'] = loc_room
        loc_rack = self.get_loc_rack()
        if loc_rack is not None:
            data['loc_rack'] = loc_rack
        loc_addr = self.get_loc_addr()
        if loc_addr is not None:
            data['loc_addr'] = loc_addr
        loc_floor = self.get_loc_floor()
        if loc_floor is not None:
            data['loc_floor'] = loc_floor
        loc_zip = self.get_loc_zip()
        if loc_zip is not None:
            data['loc_zip'] = loc_zip
        team_integ = self.get_team_integ()
        if team_integ is not None:
            data['team_integ'] = team_integ
        team_support = self.get_team_support()
        if team_support is not None:
            data['team_support'] = team_support
        return data

    def get_asset_dict(self):
        data = self.get_asset_properties()
        hardware = self.get_hardware()
        if hardware is not None:
            data['hardware'] = hardware
        hba = self.get_hba()
        if hba is not None:
            data['hba'] = hba
        targets = self.get_targets()
        if targets is not None:
            data['targets'] = targets
        lan = self.get_lan()
        if lan is not None:
            data['lan'] = lan
        uids = self.get_uids()
        if uids is not None:
            data['uids'] = uids
        gids = self.get_gids()
        if gids is not None:
            data['gids'] = gids
        return data

    def get_system_dict(self):
        data = {}
        data["properties"] = self.get_asset_properties()
        hardware = self.get_hardware()
        if hardware is not None:
            data['hardware'] = hardware
        hba = self.get_hba()
        if hba is not None:
            data['hba'] = hba
        targets = self.get_targets()
        if targets is not None:
            data['targets'] = targets
        lan = self.get_lan()
        if lan is not None:
            data['lan'] = lan
        uids = self.get_uids()
        if uids is not None:
            data['uids'] = uids
        gids = self.get_gids()
        if gids is not None:
            data['gids'] = gids
        return data

    @classmethod
    def system_dict_to_asset_dict(cls, system_dict):
        system_dict = system_dict or {}
        data = {}
        for k, v in system_dict.get("properties", {}).items():
            data[k] = v
        for k, v in system_dict.items():
            if k == "properties":
                continue
            data[k] = v
        return data0707010001f4cb000081a40000000000000000000000016a100daf0000210e000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/utilities/asset/hpux.py    import os
from subprocess import Popen, PIPE

from .asset import BaseAsset
from utilities.proc import justcall

os.environ['PATH'] += ":/opt/ignite/bin:/opt/propplus/bin"

class Asset(BaseAsset):
    def __init__(self, node):
        super(Asset, self).__init__(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):
        try:
            return getattr(self, "hba")
        except AttributeError:
            pass
        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

  0707010001f4cc000081a40000000000000000000000016a100daf000062ae000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/asset/linux.py   import datetime
import os
import re
import time

from .asset import BaseAsset
from utilities.lazy import lazy
from utilities.storage import Storage
from utilities.proc import justcall, which
from utilities.string import bdecode


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(BaseAsset):
    def __init__(self, node):
        super(Asset, self).__init__(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')

    @lazy
    def os_release(self):
        os_release_f = os.path.join(os.sep, "etc", "os-release")
        data = Storage()
        if not os.path.exists(os_release_f):
            return data
        with open(os_release_f, "r") as filep:
            for line in filep.readlines():
                line = line.strip("\n")
                try:
                    var, val = line.split("=", 1)
                except:
                    continue
                data[var.lower()] = val.strip('"')
        return data

    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):
        from utilities.converters import convert_size
        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(":", 1)
            if len(l) < 2:
                continue
            return str(convert_size(l[-1], _to="MB"))
        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):
        vendors = {
            "alpine": "Alpine",
            "debian": "Debian",
            "ubuntu": "Ubuntu",
            "arch": "Arch",
            "vmware": "VMware",
            "oracle": "Oracle",
            "sles": "SuSE",
            "opensuse": "SuSE",
            "rhel": "Red Hat",
            "centos": "CentOS",
            "fedora": "Fedora",
            "caasp": "SuSE",
            "gentoo": "Gentoo",
        }
        if self.os_release.id in vendors:
            return vendors.get(self.os_release.id, "")
        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"
        if self.os_release.name:
            return self.os_release.name
        return 'Unknown'

    def _get_os_release_lsb(self):
        if not os.path.exists('/etc/lsb-release'):
            return
        r = None
        with open('/etc/lsb-release') as f:
            for line in f.readlines():
                if 'DISTRIB_RELEASE' in line:
                    r = line.split('=')[-1].replace('\n', '').strip('"')
                    if r:
                        break
                if 'DISTRIB_DESCRIPTION' in line:
                    r = line.split('=')[-1].replace('\n', '').strip('"')
                    if r:
                        break
        if r:
            r = r.replace(self._get_os_vendor(), '').strip()
        return r

    def _get_os_release_os_release(self):
        r = self.os_release.pretty_name
        if not r:
            return
        v = self._get_os_vendor()
        if v:
            pattern = re.compile(v, flags=re.I)
            r = pattern.sub("", r)
        return r.strip()

    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):
        r = self._get_os_release_os_release()
        if r == "Enterprise Linux" and self.os_release.version:
            # prior to el8, the pretty name did not include the version
            return r + " " + self.os_release.version
        if r and r not in (
            "/Linux",
            "Linux 7 (Core)"  # centos7 poor pretty_name
        ):
            return r
        files = ['/etc/debian_version',
                 '/etc/vmware-release',
                 '/etc/oracle-release',
                 '/etc/redhat-release',
                 '/etc/gentoo-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(), '').replace("GNU/Linux", "").replace("Linux", "").replace("release", "").strip()
        return 'Unknown'

    def _get_os_kernel(self):
        return os.uname()[2]

    def _get_os_arch(self):
        return os.uname()[4]

    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]

        p_raspbian = '/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq'
        if os.path.exists(p_raspbian):
            (out, err, ret) = justcall(['cat', p_raspbian])
            if ret != 0:
                return 'Unknown'
            return out[:-4]

        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(n_threads)

    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_raspbian(self):
        """ Raspbian serial is in /proc/cpuinfo
        """
        (out, err, ret) = justcall(['grep', '^Serial', '/proc/cpuinfo'])
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        l = lines[0].split(':')
        return l[1].strip()

    def _get_serial(self):
        if self.container:
            return 'n/a'
        serial = self._get_serial_1()
        if serial in ('Unknown', 'Not Specified', '0000000'):
            serial = self._get_serial_2()
        if serial in ('Unknown') and self.os_release['id'] == 'raspbian':
            serial = self._get_serial_raspbian()
        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_revision_raspbian(self):
        (out, err, ret) = justcall(['grep', '^Revision', '/proc/cpuinfo'])
        if ret != 0:
            return 'Unknown'
        lines = out.split('\n')
        l = lines[0].split(':')
        return l[1].strip()

    def _get_model(self):
        if self.container:
            return 'container'
        elif self.xenguest and len(self.dmidecode) < 5:
            return "Xen Virtual Machine (PVM)"
        elif self.os_release['id'] == 'raspbian':
            model = self._get_revision_raspbian()
            return model
        regexp = re.compile("^\s+Product Name:")
        for l in self.dmidecode:
            if not regexp.match(l):
                continue
            model = l[l.index(":")+1:].strip()
            if model == "Memory Drive Technology":
                continue
            return model
        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 env import Env
            l.append((Env.nodename, "virtual", ''))

        return [{"hba_id": e[0], "hba_type": e[1], "host": e[2]} for e in l]

    def _get_targets(self):
        def port_not_present(target):
            fpath = os.path.dirname(target)
            fpath = os.path.join(fpath, "port_state")
            try:
                with open(fpath, "r") as f:
                    buff = f.read().strip()
            except Exception:
                return False
            return buff == "Not Present"

        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 port_not_present(target):
                    continue
                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 env import Env
                data = json.loads(out)
                hba_id = Env.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 or "SMBIOS nor DMI" in out:
            return []
        devs = []
        dev = {}
        path = []
        cla = []
        desc = []
        for line in out.splitlines():
            if line.strip() == "Memory Device":
                # new mem device
                if dev:
                    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:
            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 = {}
        for line in out.splitlines():
            if re.match(r"^\w", line):
                # new pci device
                if dev:
                    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:
            devs.append(dev)
        return devs
                
    def get_boot_id(self):
        fpath = "/proc/sys/kernel/random/boot_id"
        if os.path.exists(fpath):
            with open(fpath, "r") as f:
                return f.read().strip()
        fpath = "/proc/stat"
        if os.path.exists(fpath):
            with open(fpath, "r") as f:
                for line in f.readlines():
                    if line.startswith("btime "):
                        return line.split()[-1]
        return super(Asset, self).get_boot_id()

    def get_last_boot(self):
        with open("/proc/uptime", "r") as f:
            s = f.readline().split()[0]
        last = datetime.datetime.now() - datetime.timedelta(seconds=float(s))
        last = last.replace(microsecond=0)
        return {
            "title": "last boot",
            "value": time.mktime(last.timetuple()),
            "source": self.s_probe
        }


if __name__ == "__main__":
    from env import Env
    import json
    print(json.dumps(Asset(Env.nodename).get_asset_dict(), indent=4))
  0707010001f4cf000081a40000000000000000000000016a100daf000028e7000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/asset/windows.py import sys
import platform
import datetime
import time

try:
    import ctypes
    import win32timezone
except ImportError:
    raise

from .asset import BaseAsset

from utilities.converters import convert_size
from utilities.lazy import lazy
from utilities.diskinfo import DiskInfo
from utilities.storage import Storage
from utilities.proc import justcall, which

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
        super(MEMORYSTATUSEX, self).__init__()

class Asset(BaseAsset):
    def __init__(self, node):
        self.node = node
        super(Asset, self).__init__(node)
        self.memstat = MEMORYSTATUSEX()
        ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(self.memstat))
        self.init()

    def init(self):
        self.wmi = self.node.wmi()

    def _get_tz(self):
        """
        return in fmt "+01:00"
        """
        mst = win32timezone.TimeZoneInfo.local()
        utcoff = datetime.datetime.now(mst).strftime("%z")
        return utcoff[:3] + ":" + utcoff[3:]

    def _get_mem_bytes(self):
        return str(self.memstat.ullTotalPhys // 1024 // 1024)

    def _get_mem_banks(self):
        md = len(self.wmi.WIN32_PhysicalMemory())
        return str(md)

    def _get_mem_slots(self):
        n = 0
        for a in self.wmi.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):
        try:
            v = self.wmi.Win32_OperatingSystem()[0]
        except AttributeError:
            return "Unknown"
        s = v.Caption
        s = s.replace('Microsoft', '')
        s = s.replace('Windows', '')
        s = s.strip()
        return s

    def _get_os_kernel(self):
        try:
            v = sys.getwindowsversion()
        except AttributeError:
            return "Unknown"
        return ".".join(map(str, [v.major, v.minor, v.build]))

    def _get_os_arch(self):
        return platform.uname()[4]

    @lazy
    def cpuinfo(self):
        self.init()
        data = self.wmi.Win32_Processor()
        ret = Storage()
        ret.NumberOfCores = 0
        for p in data:
            try:
                ret.NumberOfCores += p.NumberOfCores,
            except Exception:
                ret.NumberOfCores += 1
            if ret.SocketDesignation is None:
                ret.SocketDesignation = p.SocketDesignation
            if ret.Name is None:
                ret.Name = p.Name
            if ret.MaxClockSpeed is None:
                ret.MaxClockSpeed = p.MaxClockSpeed
        return ret

    def _get_cpu_freq(self):
        return str(self.cpuinfo.MaxClockSpeed)

    def _get_cpu_cores(self):
        return str(self.cpuinfo.NumberOfCores)

    def _get_cpu_dies(self):
        return str(self.cpuinfo.SocketDesignation)

    def _get_cpu_model(self):
        return str(self.cpuinfo.Name)

    def _get_enclosure(self):
        for i in self.wmi.Win32_SystemEnclosure():
            name = i.Name
        return name

    def _get_serial(self):
        for i in self.wmi.Win32_ComputerSystemProduct():
            name = i.IdentifyingNumber
        return name

    def _get_model(self):
        for i in self.wmi.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_boot_id(self):
        payload = self.wmi.Win32_PerfFormattedData_PerfOS_System()
        uptime = payload[-1].SystemUpTime
        return str((int(time.time()) - int(uptime)) // 2)

    def get_last_boot(self):
        self.init()
        payload = self.wmi.Win32_PerfFormattedData_PerfOS_System()
        uptime = payload[-1].SystemUpTime
        try:
            last = datetime.datetime.now() - datetime.timedelta(seconds=int(uptime))
        except:
            return
        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.wmi.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)
            if a.Description is not None:
                desc.append(a.Description)
            if a.Manufacturer is not None:
                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.wmi.Win32_PnpSignedDriver()
        unknowncpt = 0
        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.Caption is not None:
                desc.append(a.Caption)
            if a.FriendlyName is not None:
                desc.append(a.FriendlyName)
            if a.Manufacturer is not None:
                desc.append(a.Manufacturer)
            if len(desc) == 0 and a.DeviceID is not None:
                desc.append(a.DeviceID)
                if 'XPS Document Writer' in a.DeviceID:
                    type.append('xps printer')
                if 'Print to PDF' in a.DeviceID:
                    type.append('pdf printer')
            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.PDO is not None and len(path) == 0:
                path.append(a.PDO)
            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 len(type) == 0 and a.DeviceClass is not None:
                type.append(a.DeviceClass.lower())
            if len(type) == 0:
                type.append("unknown [" + str(unknowncpt) + "]")
                unknowncpt+=1
            if len(path) == 0 and a.DeviceID is not None:
                path.append(a.DeviceID)
            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 devs
 0707010001f563000081a40000000000000000000000016a100daf00000083000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/process_title.py try:
    from setproctitle import setproctitle as set_process_title
except ImportError:
    def set_process_title(_):
        pass
 0707010001f4c4000081a40000000000000000000000016a100daf00000947000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/arp.py   from __future__ import print_function
import sys
import struct
import socket

ETH_BROADCAST = 'ff:ff:ff:ff:ff:ff'
ETH_TYPE_ARP = 0x0806

def ether_aton(addr):
    """Convert a ethernet address in form AA:BB:... to a sequence of
    bytes.
    """
    data = [struct.pack("B", int(nn, 16)) for nn in addr.split(':')]
    return b''.join(data)

def send_arp(ifname, address):
    """Send out a gratuitous ARP on interface C{ifname}."""
    # Try to get hold of a socket:
    try:
        ether_socket = socket.socket(socket.AF_PACKET,  # Darwin pylint: disable=no-member
            socket.SOCK_RAW)
        ether_socket.bind((ifname, ETH_TYPE_ARP))
        ether_addr = ether_socket.getsockname()[4]
    except socket.error as ex:
        if ex.errno == 1:
            raise Exception('ARP messages can only be sent by root')
        raise
    # From Wikipedia:
    #
    # ARP may also be used as a simple announcement protocol. This
    # is useful for updating other hosts' mapping of a hardware
    # address when the sender's IP address or MAC address has
    # changed. Such an announcement, also called a gratuitous ARP
    # message, is usually broadcast as an ARP request containing
    # the sender's protocol address (SPA) in the target field
    # (TPA=SPA), with the target hardware address (THA) set to
    # zero. An alternative is to broadcast an ARP reply with the
    # sender's hardware and protocol addresses (SHA and SPA)
    # duplicated in the target fields (TPA=SPA, THA=SHA).
    gratuitous_arp = [
        # HTYPE
        struct.pack("!h", 1),
        # PTYPE (IPv4)
        struct.pack("!h", 0x0800),
        # HLEN
        struct.pack("!B", 6),
        # PLEN
        struct.pack("!B", 4),
        # OPER (reply)
        struct.pack("!h", 2),
        # SHA
        ether_addr,
        # SPA
        socket.inet_aton(address),
        # THA
        ether_addr,
        # TPA
        socket.inet_aton(address)
        ]
    ether_frame = [
        # Destination address:
        ether_aton(ETH_BROADCAST),
        # Source address:
        ether_addr,
        # Protocol
        struct.pack("!h", ETH_TYPE_ARP),
        # Data
        b''.join(gratuitous_arp)
        ]
    ether_socket.send(b''.join(ether_frame))
    ether_socket.close()

if __name__ == "__main__":
    ifname = sys.argv[1]
    address = sys.argv[2]
    send_arp(ifname, address)

 0707010001f501000081a40000000000000000000000016a100daf000002b5000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/fcache.py    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 = '_fcache_' + attr
    if hasattr(self, attr_name):
        delattr(self, attr_name)

   0707010001f4d7000081a40000000000000000000000016a100daf00000175000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/concurrent_futures.py    def get_concurrent_futures():
    """
    helper to get concurrent.futures module
    return concurrent.futures module from concurrent.futures or from
    foreign.concurrent.futures
    """
    try:
        import concurrent.futures as concurrent_futures
    except ImportError:
        import foreign.concurrent.futures as concurrent_futures
    return concurrent_futures
   0707010001f551000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/password 0707010001f555000081a40000000000000000000000016a100daf00000169000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/password/linux.py    from subprocess import *

from utilities.proc 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
   0707010001f552000081a40000000000000000000000016a100daf000000e1000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/password/__init__.py import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
change_root_pw = _os.change_root_pw
   0707010001f556000081a40000000000000000000000016a100daf000007b0000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/password/sunos.py    import shutil
from subprocess import *

from utilities.proc import which

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')
0707010001f553000081a40000000000000000000000016a100daf00000161000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/password/aix.py  from subprocess import *

from utilities.proc 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
   0707010001f554000081a40000000000000000000000016a100daf0000014f000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/password/freebsd.py  from subprocess import *

from utilities.proc 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
 0707010001f4ff000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/edit_file    0707010001f500000081a40000000000000000000000016a100daf0000009e000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/edit_file/__init__.py    import os


def edit_file(editor, fpath):
    # for python 2.7 pylint disable no-member
    os.system(' '.join((editor, fpath)))  # pylint: disable=no-member
  0707010001f4f2000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/diskinfo 0707010001f4f6000081a40000000000000000000000016a100daf000002f9000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/diskinfo.py import core.exceptions as ex

class BaseDiskInfo(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 diskinfo_header(self):
        return self.print_diskinfo_fmt % (
          "hbtl",
          "devname",
          "size",
          "dev",
          "vendor",
          "model",
        )


    def print_diskinfo_header(self):
        print(self.diskinfo_header())


    def scanscsi(self, hba=None, target=None, lun=None):
        raise ex.Error("not implemented")


   0707010001f4f7000081a40000000000000000000000016a100daf00000200000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/freebsd.py  from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):
    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

0707010001f4f5000081a40000000000000000000000016a100daf0000004b000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/darwin.py   from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):
    pass
 0707010001f4fc000081a40000000000000000000000016a100daf00000e2a000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/windows.py  import foreign.wmi as wmi
from utilities.proc import justcall, which
from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):

    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

  0707010001f4fb000081a40000000000000000000000016a100daf00001473000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/sunos.py    import math
import os

import utilities.devtree.veritas
import utilities.devices.sunos
from utilities.proc import justcall
from env import Env
from utilities.subsystems.zone import is_zone
from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):
    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]
        basedev = dev[:-2] + "s2"
        size = 0
        out = utilities.devices.sunos.prtvtoc(basedev)
        if out is None:
            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/")
        out = utilities.devices.sunos.prtvtoc(dev)
        if out is None:
            return size

        """
        *     512 bytes/sector
        *      63 sectors/track
        *     255 tracks/cylinder
        *   16065 sectors/cylinder
        *   19581 cylinders
        *   19579 accessible cylinders
        **  OR:
        *   188743612 accessible sectors
        """
        n1 = 0
        n2 = 0
        n3 = 0
        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 = utilities.devtree.veritas.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 = Env.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, log=None):
        os.system("cfgadm -al")
 0707010001f4fa000081a40000000000000000000000016a100daf00000b46000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/osf1.py from __future__ import print_function

import re

from .diskinfo import BaseDiskInfo
from utilities.proc import justcall


class DiskInfo(BaseDiskInfo):

    def __init__(self):
        self.load_cache()

    def is_id(self, line):
        if re.match(r"^\W*[0-9]*:", 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
        id = None
        dev = None
        wwid = None
        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 re.match(r'\W*[0-9]*\W+', 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())
  0707010001f4f4000081a40000000000000000000000016a100daf00000840000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/diskinfo/aix.py  import re

from utilities.proc import call, justcall
from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):

    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')

0707010001f4f8000081a40000000000000000000000016a100daf00001b02000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/hpux.py import os

from utilities.proc import justcall, which
from .diskinfo import BaseDiskInfo

class DiskInfo(BaseDiskInfo):
    legacy_size_cache = {}
    legacy_wwid_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 not refresh:
            try:
                return getattr(self, "ioscan")
            except AttributeError:
                pass
        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 not self.h:
            self.load_cache()
        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 diskinfo_str(self, info):
        info['size'] = self.disk_size(info['devname'])
        info['hbtl'] = "#:#:#:#"
        return self.print_diskinfo_fmt%(
          info['hbtl'],
          os.path.basename(info['devname']),
          info['size'],
          info['dev'],
          info['vendor'],
          '',
        )

    def print_diskinfo(self, info):
        print(self.diskinfo_str(info))

    def scanscsi(self, hba=None, target=None, lun=None, log=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)

        if log:
            log.info(self.diskinfo_header())
        for info in ioscan_after:
            if info['devname'] not in new_disks:
                continue
            if log:
                log.info(self.diskinfo_str(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

  0707010001f4f3000081a40000000000000000000000016a100daf000000d6000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/__init__.py import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
DiskInfo = _os.DiskInfo

  0707010001f4f9000081a40000000000000000000000016a100daf00003383000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/diskinfo/linux.py    from __future__ import print_function

import glob
import json
import math
import os
import re
import sys
import time

import core.exceptions as ex
import utilities.devtree.veritas
import utilities.devices.linux
from .diskinfo import BaseDiskInfo

from core.capabilities import capabilities
from env import Env
from utilities.lazy import lazy
from utilities.proc import justcall

class DiskInfo(BaseDiskInfo):
    disk_ids = {}

    def __init__(self, deferred=False):
        pass

    def prefix_local(self, id):
        return '.'.join((Env.nodename, id))

    def disk_id(self, dev):
        if 'cciss' in dev:
            id = self.cciss_id(dev)
        elif dev.startswith('/dev/disk/by-id/dm-uuid-mpath-'):
            id = dev.replace('/dev/disk/by-id/dm-uuid-mpath-', '')[1:]
        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 = utilities.devtree.veritas.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

    @lazy
    def gce_instance_data(self):
        cmd = ["gcloud", "compute", "instances", "describe", "-q", "--format", "json", Env.nodename]
        out, err, ret = justcall(cmd)
        return json.loads(out)

    def gce_disk_id(self, dev):
        if "Google_PersistentDisk_" in dev:
            devname = dev.split("Google_PersistentDisk_")[-1]
        else:
            devname = dev.split("google-")[-1]
        for disk in self.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]
        cciss_id = capabilities.get("node.x.cciss.path")
        if not cciss_id:
            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 = [Env.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 capabilities.has("node.x.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=None):
        if args is None:
            args = []
        wwid = self.mpath_id(dev)
        if wwid is not None:
            return wwid
        if dev in self.disk_ids:
            return self.disk_ids[dev]
        scsi_id = capabilities.get("node.x.scsi_id.path")
        if not scsi_id:
            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 diskinfo_str(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'] == '':
                info['device/model'] = 'VirtIO'
        info['hbtl'] = os.path.basename(os.path.realpath(os.path.join(disk, "device")))
        return self.print_diskinfo_fmt%(
          info['hbtl'],
          name,
          int(float(info['size'])/2//1024),
          info['dev'],
          info['device/vendor'],
          info['device/model'],
        )

    def print_diskinfo(self, disk):
        print(self.diskinfo_str(disk))

    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, log=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
            if log:
                log.info("scan %s target%s lun%s", os.path.basename(host), target_num, lun)
            os.system('echo - ' + target_num + ' ' + lun + ' >' + scan_f)

        time.sleep(0.5)
        utilities.devices.linux.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:
            if log:
                log.info("scsi scan found no new disk")
            return 0

        if log:
            log.info(self.diskinfo_header())
        for disk in new_disks:
            if log:
                log.info(self.diskinfo_str(disk))
            self.wait_devpath("/dev/"+disk.replace("/sys/block/", ""))

        return 0

    def wait_devpath(self, dev, tmo=10):
        for retry in range(tmo):
            if os.path.exists(dev):
                return
            time.sleep(1)
        raise ex.Error("time out waiting for %s to appear" % dev)

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))
 0707010001f4dd000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/utilities/devices  0707010001f4df000081a40000000000000000000000016a100daf000001f5000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/devices/__init__.py  import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)

def devs_to_disks(self, devs=None):
    return devs or set()

#
# Override generic definitions by os-specific ones
#
try:
    globals().update({"devs_to_disks": _os.devs_to_disks})
except AttributeError:
    pass
try:
    globals().update({"promote_dev_rw": _os.devs_to_disks})
except AttributeError:
    pass

   0707010001f4e2000081a40000000000000000000000016a100daf00002cd8000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/devices/linux.py import json
import os
import re
import glob
import time

import core.exceptions as ex
from env import Env
from core.capabilities import capabilities
from utilities.cache import cache
from utilities.proc import justcall, call, qcall

label_to_dev_cache = {}


def udevadm_settle():
    if "node.x.udevadm" not in capabilities:
        return
    cmd = ["udevadm", "settle"]
    justcall(cmd)

def udevadm_query_symlink(dev):
    cmd = ["udevadm", "info", "-q", "symlink", dev]
    out, err, ret = justcall(cmd)
    if ret != 0:
        return []
    return ["/dev/"+dev for dev in out.split() if dev]

def dev_to_paths(dev, log=None):
    dev = os.path.realpath(dev)
    if dev.startswith("/dev/sd"):
        return [dev]
    if dev.startswith("/dev/vx"):
        from utilities.subsystems.veritas import vx_dev_to_paths
        return vx_dev_to_paths(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.Error(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_set_rw(dev, log=None):
    cmd = ["blockdev", "--setrw", dev]
    if log:
        log.info(" ".join(cmd))
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error(err)

def dev_is_ro_ioctl(dev):
    cmd = ["blockdev", "--getro", dev]
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error(err)
    if out.strip() == "1":
        return True
    return False

def dev_is_ro_sysfs(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
    return False

def dev_is_ro(dev):
    try:
        return dev_is_ro_ioctl(dev)
    except:
        return dev_is_ro_sysfs(dev)

def need_rescan(dev):
    try:
        fd = os.open(dev, os.O_NONBLOCK|os.O_RDWR)
        os.close(fd)
        return False
    except (OSError, IOError):
        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 dev_delete(dev, log=None):
    dev = dev.replace('/dev/', '')
    sysdev = "/sys/block/%s/device/delete" % 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 = [Env.syspaths.multipath, "-v0", "-r", dev]
    (ret, out, err) = call(cmd, info=True, outlog=True, log=log)
    if ret != 0:
        raise ex.Error

def multipath_flush(dev, log=None):
    """
    Settle udev before running a "multipath -f <dev>" to avoid
    the "in use" error.
    """
    udevadm_settle()
    cmd = [Env.syspaths.multipath, "-f", dev]
    ret, out, err = call(cmd, info=True, outlog=True, log=log)
    if ret != 0:
        dump_device_users(dev, log=log)
        for tick in range(int(5//0.5)):
            time.sleep(0.5)
            ret, out, err = call(cmd, info=True, outlog=True, log=log)
            if ret == 0:
                return
        raise ex.Error

def dump_device_users(dev, log=None):
    log.info("===== dump device users : %s =====", dev)
    call(["lsblk"], info=True, outlog=True, log=log)
    call(["exportfs", "-v"], info=True, outlog=True, log=log)
    call(["zpool", "status"], info=True, outlog=True, log=log)
    call(["lsof", dev], info=True, outlog=True, log=log)
    call(["fuser", "-v", dev], info=True, outlog=True, log=log)
    log.info("========================= %s =====", dev)

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

def promote_dev_rw(dev, log=None):
    count = 0
    for _dev in dev_to_paths(dev, log=log):
       changed = False
       if dev_is_ro(_dev):
           try:
               dev_set_rw(_dev, log=log)
               changed = True
           except:
               pass
       if need_rescan(_dev):
           dev_rescan(_dev, log=log)
           changed = True
       if changed:
           count += 1
           wait_for_dev_ready(_dev, log=log)
    if dev_is_ro(dev):
        try:
            dev_set_rw(dev, log=log)
        except ex.Error:
            pass
    if count > 0:
        try:
            refresh_multipath(dev, log=log)
        except ex.Error:
            pass

def loop_is_deleted(dev):
    if "node.x.losetup" not in capabilities:
        raise ex.Error("losetup must be installed")
    out, err, ret = justcall([Env.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 "node.x.blkid" not in capabilities:
        return
    out, err, ret = justcall([Env.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 utilities.devtree 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.Error("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 lv_exists(self, device):
    lvs_path = capabilities.get("node.x.lvs.path")
    if lvs_path and qcall([lvs_path, device]) == 0:
        return True
    return False

def lv_info(self, device):
    cmd = [
        Env.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))
        raise ex.Error
    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=None):
    """ If PV is a device map, replace by its sysfs name (dm-*)
        If device map has slaves, replace by its slaves
    """
    devs = devs or set()
    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

@cache("losetup.json")
def losetup_data():
    cmd = ["losetup", "-J"]
    out, err, ret = justcall(cmd)
    try:
        return json.loads(out)["loopdevices"]
    except ValueError:
        return

def file_to_loop(f):
    """
    Given a file path, returns the loop device associated. For example,
    /path/to/file => /dev/loop0
    """
    data = losetup_data()
    if data:
        return [_data["name"] for _data in data if _data["back-file"] == f]

    out, err, ret = justcall([Env.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
    """
    data = losetup_data()
    if data:
        for _data in data:
            if _data["name"] == f:
                return _data["back-file"]
        return

    out, err, ret = justcall([Env.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

0707010001f4e1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devices/freebsd.py   0707010001f4e0000081a40000000000000000000000016a100daf0000037e000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/devices/darwin.py    import os
import plistlib

import core.exceptions as ex

from utilities.proc import justcall, which

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 []
    out, err, ret = justcall(['hdiutil', 'info', '-plist'])
    if ret != 0:
        return []

    devs= []
    try:
        pl = plistlib.readPlistFromString(out)
    except AttributeError as exc:
        raise ex.Error(str(exc))
    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
  0707010001f4e3000081a40000000000000000000000016a100daf00000421000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/devices/sunos.py from utilities.cache import cache
from utilities.proc import justcall

@cache("prtvtoc.{args[0]}")
def prtvtoc(dev):
    out, _, ret = justcall(["prtvtoc", dev])
    if ret != 0:
        return
    return out

def lofiadm_data():
    data = {}
    cmd = ["lofiadm"]
    output, err, ret = justcall(cmd)
    for line in output.split('\n'):
        if line.startswith('Block Device'):
            continue
        if line.strip():
            fields = line.strip().split()
            key = fields[0]
            val = fields[1]
            data[key] = val
    return data

def file_to_loop(f):
    """
    Given a file path, returns the loop device associated. For example,
    /path/to/file => ['/dev/lofi/2']
    """
    data = lofiadm_data()
    for key in data.keys():
        if f == data[key]:
            return [key]
    return []

def loop_to_file(l):
    """
    Given a loop dev, returns the loop file associated. For example,
    /dev/loop0 => /path/to/file
    """
    data = lofiadm_data()
    if data[l]:
        return data[l]
    return []

   0707010001f4e5000081a40000000000000000000000016a100daf00000109000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/devices/windows.py   import string
from ctypes import windll

def get_drives():
    drives = []
    bitmask = windll.kernel32.GetLogicalDrives()
    for letter in string.ascii_uppercase:
        if bitmask & 1:
            drives.append(letter)
        bitmask >>= 1
    return drives

   0707010001f576000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/utilities/requester    0707010001f577000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/requester/__init__.py    0707010001f57a000081a40000000000000000000000016a100daf000000cb000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/utilities/requester/url_util_direct.py from utilities.requester.url_util_abstract import UrlUtilAbstract


class UrlUtilDirect(UrlUtilAbstract):
    def get_header(self, url):
        return {}

    def get_url(self, url):
        return url
 0707010001f579000081a40000000000000000000000016a100daf0000009f000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/utilities/requester/url_util_abstract.py   class UrlUtilAbstract(object):
    def get_url(self, url):
        raise NotImplementedError

    def get_header(self, url):
        raise NotImplementedError
 0707010001f578000081a40000000000000000000000016a100daf00000318000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/requester/api_client.py  import requests

import utilities.noop_log as noop_log


class ApiClient(object):
    def __init__(self,
                 auth_factory,
                 url_util,
                 log=noop_log):
        self._auth_factory = auth_factory
        self._url_util = url_util
        self.log = log

    def action(self, method='get', url=None, auth_info=None, headers=None, **kwargs):
        new_header = {}
        new_url = self._url_util.get_url(url)
        new_header.update(self._url_util.get_header(url))
        new_header.update(self._auth_factory.get_headers(auth_info or {}))
        new_header.update(headers or {})

        return getattr(requests, method)(url=new_url,
                                         headers=new_header,
                                         **kwargs)
0707010001f515000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/utilities/kv_store 0707010001f518000081a40000000000000000000000016a100daf00000116000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/kv_store/kv_null.py  from utilities.kv_store.kv_abstract import KvAbstract, NoKey


class KvNull(KvAbstract):
    def create(self, key, value):
        pass

    def read(self, key):
        raise NoKey

    def update(self, key, value):
        raise NoKey

    def delete(self, key):
        pass
  0707010001f517000081a40000000000000000000000016a100daf000002d1000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/utilities/kv_store/kv_abstract.py  class NoKey(Exception):
    pass


class KvAbstract(object):
    def __init__(self, name=None, is_expired=None):
        self.name = name
        self.is_expired = is_expired

    def create(self, key, value):
        raise NotImplementedError

    def read(self, key):
        raise NotImplementedError

    def update(self, key, value):
        raise NotImplementedError

    def delete(self, key):
        raise NotImplementedError

    def read_not_expired(self, key):
        """returns value for non expired key or raise NoKey"""
        try:
            data = self.read(key)
        except NoKey:
            raise
        if self.is_expired and self.is_expired(data):
            raise NoKey
        return data
   0707010001f519000081a40000000000000000000000016a100daf000003cd000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/kv_store/kv_sec.py   import json

from utilities.kv_store.kv_abstract import KvAbstract, NoKey
from utilities.lazy import lazy
from utilities.naming import factory, split_path
import core.exceptions as ex


class KvSec(KvAbstract):
    def __init__(self, secpath, node=None, **kwargs):
        super(KvSec, self).__init__(**kwargs)
        self.secpath = secpath
        self.node = node

    @property  # can't use lazy here: other subprocesses may update sec
    def sec(self):
        name, namespace, kind = split_path(self.secpath)
        return factory('sec')(name=name, namespace=namespace, node=self.node, log_handlers=["file"])

    def create(self, key, value):
        self.sec.add_key(key, json.dumps(value))

    def read(self, key):
        try:
            return json.loads(self.sec.decode_key(key))
        except ex.Error:
            raise NoKey

    def update(self, key, value):
        self.create(key, value)

    def delete(self, key):
        self.sec.remove_key(key)
   0707010001f516000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/kv_store/__init__.py 0707010001f51a000081a40000000000000000000000016a100daf0000031d000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/utilities/kv_store/kv_simple.py    import time

from utilities.kv_store.kv_abstract import KvAbstract, NoKey


class KvSimple(KvAbstract):
    def __init__(self, *args, **kwargs):
        super(KvSimple, self).__init__(*args, **kwargs)
        self.cache = {}

    def create(self, key, value):
        now = time.time()
        self.cache[key] = {
            "value": value,
            "created_at": now,
            "updated_at": now
        }

    def read(self, key):
        if key in self.cache:
            return self.cache[key].get('value', None)
        else:
            raise NoKey

    def update(self, key, value):
        now = time.time()
        self.cache[key]['value'] = value
        self.cache[key]['updated_at'] = now

    def delete(self, key):
        if key in self.cache:
            del self.cache[key]
   0707010001f5a8000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/string   0707010001f5a9000081a40000000000000000000000016a100daf000005bf000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/string/__init__.py   import foreign.six as six
import base64


def base64encode(buff):
    """
    base64.encodestring has been deprecated in Python 3.1 and removed from Python 3.9
    """
    if six.PY3:
        base64string = base64.encodebytes(buff.encode()).decode()  # pylint: disable=no-member
    else:
        # noinspection PyDeprecation
        base64string = base64.encodestring(buff)  # pylint: disable=no-member
    return base64string


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 six.PY2:
        return buff
    if type(buff) == str:
        return buff
    return buff.decode("utf-8", errors="ignore")


def try_decode(string, codecs=None):
    codecs = codecs or ['utf8', 'latin1']
    for i in codecs:
        try:
            return string.decode(i)
        except Exception:
            pass
    return string


def empty_string(buff):
    b = buff.strip(' ').strip('\n')
    if len(b) == 0:
        return True
    return False


def is_string(s):
    """
    python[23] compatible string-type test
    """
    if isinstance(s, six.string_types):
        return True
    return False


def is_glob(text):
    if len(set(text) & set("?*[")) > 0:
        return True
    return False
 0707010001f4d0000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/auth 0707010001f4d2000081a40000000000000000000000016a100daf00000309000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/utilities/auth/auth_factory.py from utilities.kv_store.kv_abstract import NoKey


class AuthFactory(object):
    def __init__(
            self,
            auth_provider,
            kv_store,
    ):
        self.kv_store = kv_store
        self.auth_provider = auth_provider

    def _get_auth(self, **auth_info):
        key = self.auth_provider.auth_info_to_key(**auth_info)
        try:
            data = self.kv_store.read_not_expired(key)
        except NoKey:
            args, kwargs = self.auth_provider.creator_args(**auth_info)
            data = self.auth_provider.creator(*args, **kwargs)
            self.kv_store.create(key, data)
        return data

    def get_headers(self, auth_info):
        data = self._get_auth(**auth_info)
        return self.auth_provider.data_to_header(**data)

   0707010001f4d3000081a40000000000000000000000016a100daf0000010d000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/utilities/auth/auth_provider_abstract.py   class AuthProviderAbstract(object):
    def creator_args(self, **auth_info):
        raise NotImplementedError

    def auth_info_to_key(self, **auth_info):
        raise NotImplementedError

    def creator(self, *args, **auth_info):
        raise NotImplementedError
   0707010001f4d1000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/auth/__init__.py 0707010001f4d4000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/utilities/cache    0707010001f4d5000081a40000000000000000000000016a100daf00001099000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/cache/__init__.py    import json
import os
import shutil
import time
from functools import wraps

import core.exceptions as ex
import utilities.lock
from env import Env
from utilities.files import makedirs


def cache_uuid():
    return os.environ.get("OSVC_CACHE_UUID") or Env.session_uuid


def get_cache_d(sid=None):
    return os.path.join(Env.paths.pathvar, "cache", sid or cache_uuid())

def cache(sig, sid=None, ttl=0):
    """
    Decorator for function that needs cache
    :param sig: signature for cached entries, based on decorated function
                *args and **kwargs
    :param sid: optional subdir name for cache directory,
                default value is OSVC_CACHE_UUID or Env.session_uuid
    """
    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.format(args=args, kwargs=kwargs)

            fpath = cache_fpath(_sig, sid=sid)

            try:
                lfd = utilities.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:
                if ttl > 0 and is_expired(fpath, ttl):
                    raise Exception("cache TTL reached: %s" % fpath)
                data = cache_get(fpath, log=log)
                return data
            except Exception as e:
                if log:
                    log.debug(str(e))
                data = fn(*args, **kwargs)
                cache_put(fpath, data, log=log)
                return data
            finally:
                utilities.lock.unlock(lfd)

        return decorator

    return wrapper


def cache_fpath(sig, sid=None):
    cache_d = get_cache_d(sid=sid)
    makedirs(cache_d)
    sig = sig.replace("/", "(slash)")
    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:
        if log:
            log.debug("cache PUT: %s rollback on error: %s", fpath, str(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.Error("cache read error: %s" % str(e))
    return data


def clear_cache(sig, o=None, sid=None):
    if o and hasattr(o, "cache_sig_prefix"):
        sig = o.cache_sig_prefix + sig
    fpath = cache_fpath(sig, sid=sid)
    if not os.path.exists(fpath):
        return
    if o and hasattr(o, "log"):
        o.log.debug("cache CLEAR: %s" % fpath)
    lfd = utilities.lock.lock(timeout=30, delay=0.1, lockfile=fpath + '.lock')
    try:
        os.unlink(fpath)
    except:
        pass
    utilities.lock.unlock(lfd)


def purge_cache():
    cache_d = get_cache_d()
    try:
        shutil.rmtree(cache_d)
    except:
        pass


def purge_cache_session(sid):
    cache_d = get_cache_d(sid)
    try:
        shutil.rmtree(cache_d)
    except:
        pass


def purge_cache_expired():
    cache_d = os.path.join(Env.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 is_expired(fpath, ttl):
    if os.path.isfile(fpath):
        return time.time() > os.stat(fpath).st_mtime + ttl
    return False
   0707010001f4fd000081a40000000000000000000000016a100daf0000040d000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/utilities/dns.py   import json
import socket

import core.exceptions as ex
from env import Env
from utilities.string import bencode, bdecode

def zone_list(zone, nameservers):
    for nameserver in nameservers:
        try:
            return _zone_list(zone, nameserver)
        except Exception:
            continue

def _zone_list(zone, nameserver):
    request = {
        "method": "list",
        "parameters": { 
            "zonename": zone,
        }
    }
    try:
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(Env.paths.dnsuxsock)
        sock.send(bencode(json.dumps(request)+"\n"))
        response = ""
        while True:
            buff = sock.recv(4096)
            if not buff:
                break
            response += bdecode(buff)
            if response[-1] == "\n":
                break
    finally:
        sock.close()
    if not response:
        return
    try:
        return json.loads(response)["result"]
    except ValueError:
        raise ex.Error("invalid response format")

   0707010001f4d8000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/utilities/configparser 0707010001f4d9000081a40000000000000000000000016a100daf00000805000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/utilities/configparser/__init__.py from __future__ import print_function
import sys

import foreign.six as six
from foreign.six.moves import configparser as ConfigParser

Error = ConfigParser.Error
ParsingError = ConfigParser.ParsingError
NoOptionError = ConfigParser.NoOptionError

if six.PY2:
    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, six.string_types):
                        value = str(value)
                    if not isinstance(value, six.text_type):
                        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, six.string_types):
                        value = str(value)
                    if not isinstance(value, six.text_type):
                        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):
            kwargs["strict"] = False
            ConfigParser.RawConfigParser.__init__(self, *args, **kwargs)
   0707010001f51c000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/utilities/lock 0707010001f51d000081a40000000000000000000000016a100daf00001d92000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/lock/__init__.py """
Implement a node-wide locking mechanism.
File-based, lock with fcntl exclusive open when available.
"""

from __future__ import print_function

import contextlib
import json
import os
import sys
import time

import foreign.six as six
from utilities.files import makedirs


class LockNoLockFile(Exception):
    """ no lockfile specified
    """


class LockCreateError(Exception):
    """ could not create lockfile
    """


class LockAcquire(Exception):
    """ could not acquire lock on lockfile
    """

    # noinspection PyShadowingNames
    def __init__(self, intent="", pid=0, progress=None, path=None):
        Exception.__init__(self)
        self.intent = intent
        self.pid = pid
        self.progress = progress
        self.path = path

    def __str__(self):
        s = "lock %(path)s holder pid %(pid)d, holder intent '%(intent)s'" % dict(pid=self.pid, intent=self.intent, path=self.path)
        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 six.PY2:
        return buff
    if type(buff) == str:
        return buff
    else:
        try:
            return str(buff, "utf-8")
        except:
            return str(buff, "ascii")


@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):
    """
    Attempt to acquire a lock within a timeout period.

    Args:
        timeout (int): Maximum time (in seconds) to wait for the lock.
                       Use 0 to attempt only once. Default is 30.
        delay (int): Interval (in seconds) between retries.
                     Use 0 to attempt only once. Default is 1.
        lockfile (str): Path to the lock file.
        intent (str): The purpose of acquiring the lock.

    Raises:
        LockTimeout: If the lock cannot be acquired within the timeout.

    Returns:
        Lock object from `lock_nowait` if successful.
    """
    last_error = {}
    deadline = time.time() + timeout
    retryable = True

    while retryable:
        try:
            return lock_nowait(lockfile, intent)
        except LockAcquire as exc:
            last_error = {"intent": exc.intent, "pid": exc.pid, "path": exc.path}
        except Exception:
            raise

        retryable = delay > 0 and (delay + time.time()) < deadline
        if retryable:
            time.sleep(delay)

    raise LockTimeout(**last_error)


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}
    lock_dir = os.path.dirname(lockfile)

    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 as exc:
        if hasattr(exc, "errno") and getattr(exc, "errno") == 21:
            raise LockCreateError("lockfile points to a directory")
        prev_data = {"pid": 0, "intent": ""}

    # test if we already own the lock
    if prev_data["pid"] == os.getpid():
        return

    flags = os.O_RDWR | os.O_CREAT
    if os.name == 'nt':
        flags |= os.O_TRUNC
    else:
        flags |= os.O_SYNC

    try:
        lockfd = os.open(lockfile, flags, 0o644)
    except Exception as exc:
        if hasattr(exc, "errno") and getattr(exc, "errno") == 2:
            makedirs(lock_dir)
            try:
                lockfd = os.open(lockfile, flags, 0o644)
            except Exception as exc:
                raise LockCreateError(str(exc))
        else:
            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':
            try:
                # noinspection PyUnresolvedReferences
                import msvcrt
            except ImportError:
                raise
            msvcrt.locking(lockfd, msvcrt.LK_NBRLCK, 1)

        # drop our pid and intent in the lockfile, best effort
        try:
            if os.name == "posix":
                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(path=lockfile, **prev_data)
    except:
        os.close(lockfd)
        raise


def unlock(lockfd):
    """
    The lock release function.
    """
    if lockfd is None:
        return
    try:
        os.ftruncate(lockfd, 0)
        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:
                return
            lock_data["progress"] = data
            ofile.truncate(0)
            ofile.seek(0)
            json.dump(lock_data, ofile)
            os.fsync(_lockfd)
        os.close(_lockfd)
    except Exception:
        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__":
    sys.exit(main())
  0707010001f514000081a40000000000000000000000016a100daf00004386000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/journaled_data.py    from __future__ import print_function

import copy
import json
import time
import threading
import operator

import core.exceptions as ex

from functools import reduce


class JournaledDataView(object):
    def __init__(self, data=None, path=None):
        self.data = data
        self.path = path

    def exists(self, path=None):
        path = self.path + (path or [])
        return self.data.exists(path=path)

    def keys(self, path=None):
        path = self.path + (path or [])
        return self.data.keys(path=path)

    def sorted_keys(self, path=None):
        return self.data.sorted_keys(path=path)

    def keys_safe(self, path=None):
        path = self.path + (path or [])
        return self.data.keys_safe(path=path)

    def get(self, path=None, default=Exception):
        path = self.path + (path or [])
        return self.data.get(path=path, default=default)

    def get_full(self, path=None):
        path = self.path + (path or [])
        return self.data.get_full(path=path)

    def merge(self, path=None, value=None):
        path = self.path + (path or [])
        return self.data.merge(path=path, value=value)

    def setnx(self, path=None, value=None):
        path = self.path + (path or [])
        return self.data.setnx(path=path, value=value)

    def set(self, path=None, value=None):
        path = self.path + (path or [])
        return self.data.set(path=path, value=value)

    def unset_safe(self, path=None):
        path = self.path + (path or [])
        return self.data.unset_safe(path=path)

    def unset(self, path=None):
        path = self.path + (path or [])
        return self.data.unset(path=path)

    def view(self, path=None):
        path = self.path + (path or [])
        return self.data.view(path=path)

    def inc(self, path=None):
        path = self.path + (path or [])
        return self.data.inc(path=path)

    def patch(self, path=None, patchset=None):
        path = self.path + (path or [])
        return self.data.patch(path=path, patchset=patchset)


def debug(m):
    def fn(*args, **kwargs):
        try:
            return m(*args, **kwargs)
        except:
            print(m, args, kwargs)
            import traceback
            traceback.print_stack()
            raise
    return fn


class JournaledData(object):
    def __init__(self, initial_data=None, journal_head=None,
                 journal_exclude=None, journal_condition=None,
                 event_q=None, emit_interval=0.3):
        ok = lambda: True
        self.data = initial_data or {}
        self.journal_head = journal_head
        self.journal_exclude = journal_exclude or []
        self.journal_condition = journal_condition or ok
        self.event_q = event_q
        self.emit_interval = emit_interval
        self.diff = []
        self.patch_id = 0
        self.last_emit = 0
        self.coalesce = []
        self.timer = None
        if journal_head is not None:
            self.journal_head_length = len(self.journal_head)
        # import utilities.dbglock
        # self.lock = utilities.dbglock.Lock()
        self.lock = threading.RLock()

    def view(self, path=None):
        return JournaledDataView(data=self, path=path)

    def keys_safe(self, path=None):
        try:
            return self.keys(path=path)
        except (KeyError, TypeError):
            return []

    def keys(self, path=None):
        path = path or []
        data = self.get_ref(path, self.data)
        return list(data)

    def sorted_keys(self, path=None):
        path = path or []
        keys = list(self.get_ref(path, self.data))
        keys.sort()
        return keys

    # @debug
    def get(self, path=None, default=Exception):
        try:
            return self.get_ref(path or [], self.data)
        except (TypeError, KeyError, IndexError):
            if default == Exception:
                raise
            return default

    def get_full(self, path=None):
        path = path or []
        with self.lock:
            self.diff = []
            return copy.deepcopy(self.get_ref(path, self.data))

    def exists(self, path=None):
        if not path:
            return True
        try:
            self.get_ref(path, self.data)
            return True
        except (KeyError, TypeError, IndexError):
            return False

    def get_copy(self, path=None):
        path = path or []
        with self.lock:
            data = self.get_ref(path, self.data)
            return copy.deepcopy(data)

    @staticmethod
    def get_ref(path, data):
        return reduce(operator.getitem, path, data)

    # @debug
    def merge(self, path=None, value=None):
        with self.lock:
            for k, v in value.items():
                self._set_lk(path + [k], value=v)

    def setnx(self, path=None, value=None):
        with self.lock:
            if self.exists(path):
                return
            self._set_lk(path=path, value=value)

    # @debug
    def set(self, path=None, value=None):
        with self.lock:
            self._set_lk(path=path, value=value)

    def _set_lk(self, path=None, value=None):
        """
        Set data at the specified <path> to <value>.
        If path is omitted, the whole root path value is changed.

        Record the canonical change as a json_delta formatted diff, if
        * the change is not empty
        * the path changed is parented to self.journal_head

        The recorded diff is reparented to self.journal_head.
        """
        value = copy.deepcopy(value)
        path = path or []

        try:
            current = self.get_ref(path, self.data)
        except (KeyError, IndexError, TypeError):
            absolute_diff = [[path, value]]
        else:
            absolute_diff = self._diff(current, value, prefix=path)

        if not absolute_diff:
            return

        journal_diff = self._to_journal_diff(absolute_diff)

        self._set(path, value)

        self.emit(absolute_diff)
        if journal_diff:
            self.push_diff_lk(journal_diff)

    def _set(self, path, value):
        """
        Low-level set. No journaling, no messaging.
        """
        if path:
            cursor = self.get_ref(path[:-1], self.data)
            key = path[-1]
            try:
                cursor[key] = value
            except IndexError:
                if len(cursor) == key:
                    cursor.append(value)
                else:
                    raise
        else:
            self.data = value

    def _to_journal_diff(self, absolute_diff):
        if self.journal_condition() and self.journal_head is not None:
            if not self.journal_head:
                journal_diff = absolute_diff
            else:
                journal_diff = self._filter_diff(absolute_diff)
        else:
            journal_diff = None
        return journal_diff

    # @debug
    def patch(self, path=None, patchset=None):
        """
        No journaling.
        """
        with self.lock:
            self._patch_lk(path=path, patchset=patchset)

    def _patch_lk(self, path=None, patchset=None):
        path = path or []
        patchset = patchset or []
        self._patch(path, patchset)
        for p in patchset:
            p[0] = path + p[0]
        self.emit(patchset)

    def _patch(self, path, patchset):
        def patch_fragment(patch):
            try:
                _path, _value = patch
                self._set(path + _path, _value)
            except ValueError:
                _path, = patch
                self._unset(path + _path)

        for i, patch in enumerate(patchset):
            try:
                patch_fragment(patch)
            except Exception:
                buff = "\n"
                buff += "------------------------------- Patch Error ----------------------------------\n"
                buff += "Path:\n   %s\n" % path
                buff += "Patchset:\n"
                for _i, _patch in enumerate(patchset):
                    if i == _i:
                        buff += "=> %s\n" % _patch
                    else:
                        buff += "   %s\n" % _patch
                buff += "\n"
                buff += "Current data:\n%s\n\n" % json.dumps(self.get_ref(path, self.data), indent=4)
                import traceback
                buff += "Traceback:\n"
                buff += "".join(traceback.format_stack()[:-2])
                buff += "\nException "
                buff += "".join(traceback.format_exc())
                buff += "-" * 78 + "\n"
                raise ex.Error(buff)

    def unset_safe(self, path=None):
        path = path or []
        with self.lock:
            self._unset_safe(path)

    def _unset_safe(self, path):
        try:
            self._unset_lk(path)
        except (KeyError, IndexError, TypeError):
            pass

    # @debug
    def unset(self, path=None):
        path = path or []
        with self.lock:
            self._unset_lk(path)

    def _unset(self, path):
        """
        Low-level unset. No journaling, no messaging.
        """
        data = self.get_ref(path[:-1], self.data)
        del data[path[-1]]

    def _unset_lk(self, path):
        """
        Drop data at the specified path
        """
        self._unset(path)

        diff = [[path]]
        journal_diff = self._to_journal_diff(diff)
        self.emit(diff)
        if journal_diff:
            self.push_diff_lk(journal_diff)

    def inc(self, path=None):
        with self.lock:
            return self._inc(path=path, data=self.data)

    def _inc(self, path=None, data=None):
        path = path or []
        try:
            val = self.get_ref(path, data)
            val += 1
        except (KeyError, IndexError, TypeError):
            val = 1
        self._set_lk(path=path, value=val)
        return val

    def push_diff_lk(self, diff):
        """
        Concat a diff list to the in-flight diff list.
        """
        self.diff += copy.deepcopy(diff)

    def pop_diff(self):
        """
        Return a deep copied image of changes and reset the change log
        """
        with self.lock:
            diff = [] + self.diff
            self.diff = []
        return diff

    def dump_data(self):
        """
        Return a deep copied image of the dataset
        """
        with self.lock:
            return copy.deepcopy(self.data)

    def dump_changes(self):
        """
        Return a deep copied image of the changes
        """
        with self.lock:
            return copy.deepcopy(self.diff)

    def emit(self, diff):
        """
        Emit an event for the data change.
        The "id" event key can be used to very the sequence of event
        is not broken.
        """
        if not self.event_q:
            return
        if not diff:
            return
        now = time.time()
        self.coalesce += copy.deepcopy(diff)
        next_emit = self.emit_interval - (now - self.last_emit)
        if next_emit > 0:
            if not self.timer:
                self.timer = threading.Timer(next_emit, self._emit)
        else:
            self._emit()

    def _emit(self):
        self.patch_id += 1
        now = time.time()
        data = {
            "kind": "patch",
            "id": self.patch_id,
            "ts": now,
            "data": [] + self.coalesce,
        }
        self.event_q.put(data)
        self.last_emit = now
        self.coalesce = []
        self.timer = None

    def _filter_diff(self, diff):
        assert self.journal_head, "unexpected call"
        data = []
        head = self.journal_head
        exclude = self.journal_exclude
        head_len = len(head)

        def recurse(_diff):
            path = _diff[0]
            try:
                value = _diff[1]
            except IndexError:
                # delete diff fragment, no recursion
                if path[:head_len] == head:
                    yield [path[head_len:]]
            else:
                # add diff fragment

                if path == head:
                    # exactly head
                    yield [[], value]
                else:
                    if path[:head_len] == head:
                        # under head
                        yield [path[head_len:], value]
                    elif hasattr(value, "items"):
                        for k, v in value.items():
                            for _ in recurse([path + [k], v]):
                                yield _
                    elif isinstance(value, list):
                        for i, v in enumerate(value):
                            for _ in recurse([path + [i], v]):
                                yield _

        def excluded(p):
            for exc in exclude:
                if p[:len(exc)] == exc:
                    return True
            return False

        for _diff in diff:
            for _ in recurse(_diff):
                p = _[0]
                if excluded(p):
                    continue
                if not p and len(_) == 1:
                    # protect journal head from deletion
                    data.append([p, None])
                    continue
                data.append(_)

        return data

    def _diff(self, src, dst, prefix=None):
        data = []
        prefix = prefix or []
        added = []

        def recurse(d1, d2, path=None, changes=True):
            try:
                ref_v = self.get_ref(path, d2)
            except (KeyError, IndexError, TypeError):
                yield [path, d1]
            else:
                if ref_v is None and d1 is not None:
                    yield [path, d1]
                elif isinstance(d1, dict):
                    for k, v in d1.items():
                        for _ in recurse(v, d2, path=path+[k], changes=changes):
                            yield _
                elif isinstance(d1, list):
                    if prefix+path not in added:
                        if changes:
                            iterator = enumerate(d1)
                        else:
                            iterator = reversed(list(enumerate(d1)))
                        for i, v in iterator:
                            for _ in recurse(v, d2, path=path+[i], changes=changes):
                                yield _
                elif changes and ref_v != d1:
                    yield [path, d1]

        for k, v in recurse(dst, src, [], changes=True):
            data.append([prefix+k, v])
            added.append(prefix+k)

        for k, v in recurse(src, dst, [], changes=False):
            data.append([prefix+k])

        return data


if __name__ == '__main__':
    # noinspection PyUnresolvedReferences
    from foreign.six.moves import queue
    q = queue.Queue()
    tests = [
        ("set", dict(path=None, value={"a": {"b": 0, "c": [1, 2], "d": {"da": ""}}})),
        ("set", dict(path=["a"], value={"b": 1, "c": [1, 2, 3], "e": {"ea": 1, "eb": 2}})),
        ("set", dict(path=["a", "b"], value=2)),
        ("set", dict(path=["a", "c"], value=[1, 2, 3, 4, 5])),
        ("set", dict(path=["a", "c"], value=[1, 3, 2, 5])),
        ("set", dict(path=["a", "c"], value=[1])),
        ("set", dict(path=["a", "d"], value=["f"])),
        ("get", dict(path=["a"])),
        ("inc", dict(path=["a", "d"])),
        ("inc", dict(path=["a", "d"])),
        ("exists", dict(path=["a", "b"])),
        ("unset", dict(path=["a", "b"])),
        ("exists", dict(path=["a", "b"])),
        ("set", dict(path=["array"], value=[["sub-1", "sub-2"], "two", "three"])),
        ("set", dict(path=["array", 1], value="TWO")),
        ("set", dict(path=["array", 0], value=["sub-1", "sub-2"])),
        ("set", dict(path=["array", 0, 1], value="SUB-2")),
        ("unset", dict(path=["array", 0, 1])),
        ("set", dict(path=["array", 0, 1], value=["changed-SUB-2a", "changed-SUB-2b"])),
    ]

    def run(data):
        for fn, kwargs in tests:
            print("* %s(%s)" % (fn, ", ".join(["%s=%s" % (k, v) for k, v in kwargs.items()])))
            ret = getattr(data, fn)(**kwargs)
            if ret is not None:
                print("   => %s" % ret)
            while not q.empty():
                msg = q.get(0)
                print("   => event %s" % msg)
        print("journal: %s" % data.dump_changes())
        print("data:    %s" % data.dump_data())
        rdata = JournaledData()
        rdata.patch(patchset=data.dump_changes())
        print("copy:    %s" % rdata.data)
        print()

    print("no journaling")
    print("-------------")
    data = JournaledData(event_q=q, emit_interval=0)
    run(data)

    print("full journaling")
    print("---------------")
    data = JournaledData(journal_head=[], event_q=q, emit_interval=0)
    run(data)

    print("'a' journaling, 'a.b' excluded")
    print("------------------------------")
    data = JournaledData(journal_head=["a"], event_q=q, emit_interval=0, journal_exclude=[["b"]])
    run(data)

    print("'a.b' journaling")
    print("----------------")
    data = JournaledData(journal_head=["a", "b"], event_q=q, emit_interval=0)
    run(data)

    print("'[array, 0]' journaling")
    print("-----------------------")
    data = JournaledData(journal_head=["array", 0], event_q=q, emit_interval=0)
    run(data)
  0707010001f51e000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/loop_delay   0707010001f51f000081a40000000000000000000000016a100daf0000003a000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/utilities/loop_delay/__init__.py   import time


def delay(delay=0.2):
    time.sleep(delay)
  0707010001f52d000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/utilities/net  0707010001f52f000081a40000000000000000000000016a100daf0000042c000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/utilities/net/converters.py    import re

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 s is None:
        return s
    elif '.' in s:
        return dotted_to_cidr(s)
    elif re.match(r"^(0x)*[0-9a-f]{8}$", s):
        # example: 0xffffff00
        s = hexmask_to_dotted(s)
        return dotted_to_cidr(s)
    return s



0707010001f531000081a40000000000000000000000016a100daf00013837000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/utilities/net/ipaddress.py # pylint: disable-all

# 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
 0707010001f52e000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/utilities/net/__init__.py  0707010001f530000081a40000000000000000000000016a100daf000007e5000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/utilities/net/getaddr.py   import os
import datetime
import socket

from env import Env
from utilities.files import makedirs

def getaddr_cache_set(name, addr):
    cache_d = os.path.join(Env.paths.pathvar, "cache", "addrinfo")
    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(Env.paths.pathvar, "cache", "addrinfo")
    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
   0707010001f4d6000081a40000000000000000000000016a100daf00000095000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/chunker.py   def chunker(buff, n):
    """
    Yield successive n-sized chunks from buff
    """
    for i in range(0, len(buff), n):
        yield buff[i:i + n]
   0707010001f57b000081a40000000000000000000000016a100daf000002e7000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/utilities/rfc3339.py   import logging
import time
from datetime import datetime


class RFC3339(object):
    """
    RFC3339 provides converters to RFC 3339 with timezone (process timezone)
    """
    def __init__(self):
        iso_format = "%Y-%m-%dT%H:%M:%S.%f"
        timezone_offset = time.strftime("%z")
        self.format = iso_format + timezone_offset[0:3]+":"+timezone_offset[3:]

    def from_epoch(self, t):
        return datetime.fromtimestamp(t).strftime(self.format)


class RFC3339Formatter(logging.Formatter):
    def __init__(self, *args, **kwargs):
        logging.Formatter.__init__(self, *args, **kwargs)
        self.rfc3339 = RFC3339()

    def formatTime(self, record, datefmt=None):
        return self.rfc3339.from_epoch(record.created)
 0707010001f5bf000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/utilities/version  0707010001f5c0000081a40000000000000000000000016a100daf000004f6000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/utilities/version/__init__.py  def agent_version():
    try:
        from utilities.version import version
        return version.version
    except ImportError:
        pass

    try:
        import importlib
        importlib.reload(version)
        return version.version
    except (ImportError, AttributeError, UnboundLocalError):
        pass

    try:
        import imp
        imp.reload(version)
        return version.version
    except (ImportError, AttributeError, UnboundLocalError):
        pass

    import os
    from utilities.proc import justcall
    from core.capabilities import capabilities
    from env import Env
    if "node.x.git" in capabilities:
        cmd = ["git", "--git-dir", os.path.join(Env.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(Env.paths.pathsvc, ".git"),
               "describe", "--tags"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return "dev"
        try:
            _release = out.strip().split("-")[1]
        except IndexError:
            _release = "0"
        return "-".join((_version, _release+"dev"))

    return "dev"


  0707010001f5c1000081a40000000000000000000000016a102a8500000015000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/utilities/version/version.py   version = "2.1-2031"
   0707010001f430000081a40000000000000000000000016a100daf00002b2d000000e600010003ffffffffffffffff0000002600000000root/usr/share/opensvc/opensvc/env.py import os
import platform
import socket
import sys
import time
from uuid import uuid4

from utilities.storage import Storage


_sysname, _, _, _, _machine, _ = platform.uname()


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 Exception:
            # unprivileged
            pass


class Paths(object):
    def __init__(self, osvc_root_path=None, detect=False):
        if osvc_root_path:
            self.pathsvc = osvc_root_path
        elif detect:
            self.pathsvc = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
        else:
            self.pathsvc = '/usr/share/opensvc'

        if self.pathsvc == '/usr/share/opensvc':
            self.pathlib = '/usr/share/opensvc/opensvc'
            self.pathbin = '/usr/bin'
            self.pathetc = '/etc/opensvc'
            self.pathetcns = '/etc/opensvc/namespaces'
            self.pathlog = '/var/log/opensvc'
            if _sysname != "SunOS":
                self.pathtmpv = '/var/tmp/opensvc'
            else:
                # Prevent use of /var/tmp (may conflict with /system/filesystem/minimal)
                self.pathtmpv = '/var/lib/opensvc/tmp'
            self.pathvar = '/var/lib/opensvc'
            self.pathdoc = '/usr/share/doc/opensvc'
            self.pathhtml = '/usr/share/opensvc/html'
            self.pathlock = '/var/lib/opensvc/lock'
            self.pathcron = '/usr/share/opensvc'
            self.postinstall = '/usr/share/opensvc/bin/postinstall'
            self.preinstall = '/usr/share/opensvc/bin/preinstall'
        else:
            self.pathlib = os.path.join(self.pathsvc, 'opensvc')
            self.pathbin = os.path.join(self.pathsvc, 'bin')
            self.pathetc = os.path.join(self.pathsvc, 'etc')
            self.pathetcns = os.path.join(self.pathsvc, 'etc', 'namespaces')
            self.pathlog = os.path.join(self.pathsvc, 'log')
            self.pathtmpv = os.path.join(self.pathsvc, 'tmp')
            self.pathvar = os.path.join(self.pathsvc, 'var')
            self.pathdoc = os.path.join(self.pathsvc, 'usr', 'share', 'doc')
            self.pathhtml = os.path.join(self.pathsvc, 'usr', 'share', 'html')
            self.pathlock = os.path.join(self.pathvar, 'lock')
            self.pathcron = self.pathbin
            self.postinstall = os.path.join(self.pathbin, 'postinstall')
            self.preinstall = os.path.join(self.pathbin, 'preinstall')

        if os.name == "nt":
            self.svcmgr = os.path.join(self.pathsvc, "svcmgr.cmd")
            self.nodemgr = os.path.join(self.pathsvc, "nodemgr.cmd")
            self.svcmon = os.path.join(self.pathsvc, "svcmon.cmd")
            self.cron = os.path.join(self.pathsvc, "cron.cmd")
            self.om = os.path.join(self.pathsvc, "om.cmd")
        else:
            self.svcmgr = os.path.join(self.pathbin, "svcmgr")
            self.nodemgr = os.path.join(self.pathbin, "nodemgr")
            self.svcmon = os.path.join(self.pathbin, "svcmon")
            self.cron = os.path.join(self.pathcron, "cron")
            self.om = os.path.join(self.pathbin, "om")

        self.nodeconf = os.path.join(self.pathetc, "node.conf")
        self.clusterconf = os.path.join(self.pathetc, "cluster.conf")

        self.lsnruxsockd = os.path.join(self.pathvar, "lsnr")
        self.lsnruxsock = os.path.join(self.lsnruxsockd, "lsnr.sock")
        self.lsnruxh2sock = os.path.join(self.lsnruxsockd, "h2.sock")
        self.dnsuxsockd = os.path.join(self.pathvar, "dns")
        self.dnsuxsock = os.path.join(self.dnsuxsockd, "pdns.sock")
        self.pathcomp = os.path.join(self.pathvar, "compliance")
        self.pathcomposvc = os.path.join(self.pathcomp, "com.opensvc")
        self.safe = os.path.join(self.pathvar, "safe")
        self.certs = os.path.join(self.pathvar, "certs")
        self.crl = os.path.join(self.pathvar, "certs", "ca_crl")
        self.drp_path = os.path.join(self.pathvar, "cache")
        self.last_shutdown = os.path.join(self.pathvar, "last_shutdown")
        self.nodes_info = os.path.join(self.pathvar, "nodes_info.json")
        self.oc3_version = os.path.join(self.pathvar, "oc3_version.json")
        self.capabilities = os.path.join(self.pathvar, "capabilities.json")

        self.daemon_pid = os.path.join(self.pathvar, "osvcd.pid")
        self.daemon_pid_args = os.path.join(self.pathvar, "osvcd.pid.args")
        self.daemon_lock = os.path.join(self.pathlock, "osvcd.lock")

        self.tmp_prepared = False

    @property
    def pathtmp(self):
        self.prepare_tmp()
        return self.pathtmpv

    def prepare_tmp(self):
        if self.tmp_prepared:
            return
        create_or_update_dir(self.pathtmpv)
        self.tmp_prepared = True


class Env(object):
    """Class to store globals
    """
    package = os.path.basename(os.path.dirname(__file__))
    uuid = ""
    session_uuid = os.environ.get("OSVC_PARENT_SESSION_UUID") or str(uuid4())
    initial_env = os.environ.copy()
    os.environ["OSVC_SESSION_UUID"] = session_uuid
    default_priority = 100

    cluster_roles = [
        "root",
        "blacklistadmin",
        "squatter",
        "prioritizer",
        "heartbeat",
    ]
    ns_roles = [
        "admin",
        "operator",
        "guest",
    ]
    roles = cluster_roles + ns_roles
    roles_equiv = {
        "admin": ["operator", "guest"],
        "operator": ["guest"],
    }
    kinds = [
        "svc",
        "vol",
        "cfg",
        "sec",
        "usr",
        "ccfg",
        "nscfg",
    ]
    allowed_svc_envs = [
        'CERT',
        'DEV',
        'DRP',
        'FOR',
        'INT',
        'MUS',
        'POC',
        'PRA',
        'PRD',
        'PRJ',
        'PPR',
        'PPRD',
        'QUAL',
        'RCT',
        '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",
    ]
    fs_net = [
        "nfs", "nfs4", "smbfs", "cifs", "9pfs", "gpfs",
        "afs", "ncpfs", "glusterfs", "cephfs",
    ]
    _platform = sys.platform
    sysname = _sysname
    module_sysname = sysname.lower().replace("-", "")
    nodename = socket.gethostname().lower()
    fqdn = socket.getfqdn().lower()
    listener_port = 1214
    listener_tls_port = 1215

    # programs 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", "podman", "oci"]
    vt_supported = vt_vm + vt_container
    oci_types = ["podman", "docker"]

    dbopensvc = None
    dbopensvc_host = None
    dbcompliance = None
    dbcompliance_host = None
    paths = Paths(detect=True)

    syspaths = Storage(
        df="/bin/df",
        mount="/bin/mount",
        umount="/bin/umount",
        zfs="/sbin/zfs",
        zpool="/sbin/zpool",
        true="/bin/true",
        false="/bin/false",
    )
    if sysname == "Linux":
        syspaths.ps = "/bin/ps"
        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.vgscan = "/sbin/vgscan"
        syspaths.vgs = "/sbin/vgs"
    elif sysname == "SunOS":
        syspaths.ps = "/usr/bin/ps"
        syspaths.df = "/usr/sbin/df"
        syspaths.ipadm = "/usr/sbin/ipadm"
        syspaths.mount = "/usr/sbin/mount"
        syspaths.umount = "/usr/sbin/umount"
        syspaths.zfs = "/usr/sbin/zfs"
        syspaths.zpool = "/usr/sbin/zpool"
    elif sysname == "AIX":
        syspaths.ps = "/usr/sbin/ps"
        syspaths.df = "/usr/sbin/df"
        syspaths.mount = "/usr/sbin/mount"
        syspaths.umount = "/usr/sbin/umount"
    elif sysname == "Darwin":
        syspaths.true = "/usr/bin/true"
        syspaths.false = "/usr/bin/false"
    elif sysname == "FreeBSD":
        syspaths.true = "/usr/bin/true"
        syspaths.false = "/usr/bin/false"

    if "LD_PRELOAD" in os.environ:
        ld_preload = os.environ["LD_PRELOAD"]
        del os.environ["LD_PRELOAD"]
    else:
        ld_preload = None

    if "OSVC_PYTHON_ARGS" in os.environ:
        pyargs = os.environ["OSVC_PYTHON_ARGS"].split()
        del os.environ["OSVC_PYTHON_ARGS"]
    else:
        pyargs = None

    python_cmd = []
    if ld_preload:
        python_cmd.append("LD_PRELOAD="+ld_preload)
    if os.name == "nt":
        python_cmd.append(sys.executable.replace(
            os.path.join('opensvc', 'site-packages', 'win32', 'PythonService.exe'),
            "Python.exe"))
    else:
        python_cmd.append(sys.executable)
    if pyargs:
        python_cmd += pyargs
    om = python_cmd + ["-m", package]
   0707010001f13f000081a40000000000000000000000016a100daf0000114f000000e600010003ffffffffffffffff0000002b00000000root/usr/share/opensvc/opensvc/__main__.py    from __future__ import print_function

import os
import sys

HELP = """Usage:

  om ns set <namespace>       Set the current namespace
  om ns unset                 Unset the current namespace
  om ns get                   Get the current namespace

  om mon                      Monitor the cluster
  om ctx                      Manage Remote Connections contexts
  om node                     Manage Cluster Nodes
  om cluster                  Manage Cluster Configuration
  om nscfg                    Manage Namespace Configuration
  om svc                      Manage Services
  om vol                      Manage Persistent Data Volumes
  om cfg                      Manage Configurations
  om sec                      Manage Secrets
  om usr                      Manage Users
  om net                      Manage Networks
  om pool                     Manage Storage Pools
  om daemon                   Manage Agent Daemon
  om array                    Manage Storage Arrays

  om <selector> <options>     Manage the selected objects


Selector:
  <selector>[,<selector>,...] Unioned selectors
  <selector>[+<selector>+...] Intersected selectors

Path Selectors:
  <namespace>/<kind>/<name>   Fully qualified path
  <kind>/<name>               Path relative to the current namespace
  <name>                      Service <name> in the current namespace
  **                          All objects
  **/<name>                   All objects named <name>
  <namespace>/**              All objects in the <namespace> namespace
  <namespace>/<kind>/*        All <kind> objects in the <namespace> namespace

Status Selectors:
  <jsonpath>:                 All objects with the <jsonpath> referenced key existing in the instance status data
  <jsonpath>=<val>            All objects with the <jsonpath> referenced key value equals to <val> in the instance status data

Config Selectors:
  <keyword>:                  All objects with the <keyword> existing in the instance config data
  <keyword>=<value>           All objects with the <keyword> value equals to <val> in the instance config data
"""

def main(argv=None):
    cwd = os.environ.get("OSVC_CWD")
    if cwd:
        try:
            os.chdir(cwd)
        except Exception:
            pass
    try:
        arg1 = argv[1]
    except IndexError:
        print(HELP, file=sys.stderr)
        return 1
    if arg1 == "ns":
        print("The 'om' alias must be sourced to handle ns actions", file=sys.stderr)
        return 1
    elif arg1 == "ctx":
        from core.contexts import main
        ret = main(argv[1:])
        return ret
    elif arg1 == "svc":
        from commands.svc import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "vol":
        from commands.vol import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "nscfg":
        from commands.nscfg import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "cfg":
        from commands.cfg import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "sec":
        from commands.sec import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "usr":
        from commands.usr import Mgr
        os.environ["OSVC_KIND"] = arg1
        ret = Mgr()(argv=argv[2:])
        return ret
    elif arg1 == "node":
        from commands.node import main
        ret = main(argv=argv[2:])
        return ret
    elif arg1 == "array":
        from commands.node import main
        ret = main(argv=argv[1:])
        return ret
    elif arg1 in ("net", "network"):
        from commands.network import main
        ret = main(argv=argv[2:])
        return ret
    elif arg1 == "pool":
        from commands.pool import main
        ret = main(argv=argv[2:])
        return ret
    elif arg1 == "daemon":
        from commands.daemon import main
        ret = main(argv=argv[2:])
        return ret
    elif arg1 == "mon":
        from commands.svcmon import main
        ret = main(argv=argv[1:])
        return ret
    else:
        from commands.mgr import Mgr
        ret = Mgr(selector=arg1)(argv=argv[2:])
        return ret

if __name__ == "__main__":
    ret = main(sys.argv)
    sys.exit(ret)
 0707010001f13e000081a40000000000000000000000016a100daf00000056000000e600010003ffffffffffffffff0000002b00000000root/usr/share/opensvc/opensvc/__init__.py    import os
import sys

sys.path.insert(0, os.path.realpath(os.path.dirname(__file__)))
  0707010001f167000041ed00000000000000000000000b6a102a9200000000000000e600010003ffffffffffffffff0000002400000000root/usr/share/opensvc/opensvc/core   0707010001f17d000081a40000000000000000000000016a100daf000020e7000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/core/logger.py import gzip
import logging
import logging.handlers
import os
import sys

import foreign.six as six
from env import Env
from utilities.files import makedirs

min_name_len = 10
namelen = 10
namefmt = "%-"+str(namelen)+"s"

try:
    type(PermissionError)
except:
    PermissionError = IOError

import core.extconfig
from utilities.render.color import colorize, color

DEFAULT_HANDLERS = ["file", "stream", "syslog"]

def namer(name):
    """
    Adds a .gz suffix to the rotated file.
    """
    return name + ".gz"

def rotator(source, dest):
    """
    Compress file upon rotation.
    """
    with open(source, "rb") as sf:
        data = sf.read()
        with gzip.open(dest, "wb") as df:
            df.write(data)
    os.remove(source)

class StreamFilter(logging.Filter):
    """
    Discard from StreamHander records flagged f_stream=False.
    """
    def filter(self, record):
        try:
            if record.args["f_stream"] is False:
                return False
            else:
                return True
        except (KeyError, AttributeError, TypeError):
            return True

class OsvcFormatter(logging.Formatter):
    """
    Add context information embedded in the record via "extra".
    And obfuscate obvious passwords seen in the record message.
    If human is True, factorize the context information, trim the level and colorize.
    """
    def __init__(self, *args, **kwargs):
        logging.Formatter.__init__(self, *args, **kwargs)
        self.sid = False
        self.human = False
        self.last_context = None
        self.attrs = [
            ("sid", "sid"),
            ("node", "n"),
            ("component", "c"),
            ("path", "o"),
            ("subset", "rs"),
            ("rid", "r"),
            ("cron", "sc"),
        ]

    def format(self, record):
        record.message = record.getMessage()
        record.context = ""
        for xattr, key in self.attrs:
            if xattr == "sid" and not self.sid:
                continue
            try:
                val = getattr(record, xattr)
                if val in (None, ""):
                    continue
                if val is True:
                    val = "y"
                elif val is False:
                    val = "n"
                record.context += "%s:%s " % (key, val)
            except AttributeError:
                pass
        record.context = record.context.rstrip()
        for pattern in core.extconfig.SECRETS:
            record.message = record.message.replace(pattern, "xxxx")

        if not self.human:
            return logging.Formatter.format(self, record)

        # Factorize context information, trim the level and colorize.
        buff = ""
        if self.last_context != record.context:
            buff += colorize("@ " + record.context, color.LIGHTBLUE)+"\n"
            self.last_context = record.context
        if record.levelname == "INFO":
            buff += "  " + record.message
        elif record.levelname == "ERROR":
            buff += colorize("E " + record.message, color.RED)
        elif record.levelname == "WARNING":
            buff += colorize("W " + record.message, color.BROWN)
        elif record.levelname == "DEBUG":
            buff += colorize("D " + record.message, color.LIGHTBLUE)
        return buff

class OsvcFileHandler(logging.handlers.RotatingFileHandler):
    """
    Create the hosting directory and setup a RotatingFileHandler.
    """
    def __init__(self, logfile):
        logdir = os.path.dirname(logfile)
        makedirs(logdir)
        logging.handlers.RotatingFileHandler.__init__(self, logfile, maxBytes=1*5242880, backupCount=1)

class LoggerHandler(logging.handlers.SysLogHandler):
    def __init__(self, facility=logging.handlers.SysLogHandler.LOG_USER):
        logging.Handler.__init__(self)
        self.formatter = None
        self.facility = facility.upper()

    def close(self):
        pass

    def emit(self, record):
        """
        Emit a record.

        The record is formatted, and then sent to the syslog server. If
        exception information is present, it is NOT sent to the server.
        """
        try:
            import syslog
            facility = syslog.__dict__["LOG_"+self.facility]
            syslog.openlog("opensvc", 0, facility)
        except Exception as exc:
            self.handleError(record)
        try:
            prio = self.priority_names[self.mapPriority(record.levelname)]
            msg = self.format(record)
            if six.PY2:
                msg = msg.encode("utf8", errors="ignore")
            syslog.syslog(prio, msg)
        except Exception as exc:
            self.handleError(record)
        finally:
            syslog.closelog()

def initLogger(root, logfile, handlers=None, sid=True):
    if handlers is None:
        handlers = DEFAULT_HANDLERS
    log = logging.getLogger(root)
    if log.handlers:
        # already setup
        return log
    log.propagate = False
    log.handlers = []

    if "file" in handlers:
        try:
            fileformatter = OsvcFormatter("%(asctime)s %(levelname)s %(context)s | %(message)s")
            fileformatter.sid = sid
            filehandler = OsvcFileHandler(logfile)
            filehandler.setFormatter(fileformatter)
            filehandler.rotator = rotator
            filehandler.namer = namer
            log.addHandler(filehandler)

            if '--debug' in sys.argv:
                filehandler.setLevel(logging.DEBUG)
            else:
                filehandler.setLevel(logging.INFO)

        except PermissionError:
            pass

    if "stream" in handlers:
        streamformatter = OsvcFormatter("%(levelname)s %(context)s %(message)s")
        streamformatter.human = True
        streamhandler = logging.StreamHandler()
        streamhandler.setFormatter(streamformatter)
        streamfilter = StreamFilter()
        streamhandler.addFilter(streamfilter)
        log.addHandler(streamhandler)

        if '--debug' in sys.argv:
                Env.loglevel = logging.DEBUG
                streamhandler.setLevel(logging.DEBUG)
        else:
                Env.loglevel = logging.INFO
                streamhandler.setLevel(logging.INFO)

    if "syslog" in handlers:
        from foreign.six.moves import configparser as ConfigParser
        config = ConfigParser.RawConfigParser({})
        try:
            config.read(Env.paths.nodeconf)
        except:
            pass
        try:
            facility = config.get("syslog", "facility")
        except:
            facility = "daemon"
        try:
            # get() returns a list if multiple "syslog" sections are found.
            # use the last definition.
            facility = [f for f in facility if f][-1]
        except:
            pass
        try:
            # verify the facility exists
            import syslog
            syslog.__dict__["LOG_"+facility.upper()]
        except Exception as exc:
            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 = OsvcFormatter("%(context)s %(message)s")
        try:
            if Env.sysname == "SunOS" and not isinstance(address, tuple):
                sysloghandler = LoggerHandler(facility=facility)
            else:
                sysloghandler = logging.handlers.SysLogHandler(address=address, facility=facility)
        except Exception as e:
            sysloghandler = None
        if sysloghandler:
            sysloghandler.setLevel(lvl)
            sysloghandler.setFormatter(syslogformatter)
            log.addHandler(sysloghandler)

    log.setLevel(logging.DEBUG)

    return log


 0707010001f19e000081a40000000000000000000000016a100daf00001a17000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/core/pool.py   from __future__ import print_function

import os

import core.exceptions as ex
from utilities.naming import factory
from utilities.lazy import lazy
from utilities.converters import convert_size

class BasePool(object):
    type = None

    def __init__(self, name=None, node=None, **kwargs):
        try:
            name = name.strip(os.sep)
        except Exception:
            pass
        self.name = name
        self.node = node
        self.log = node.log

    @lazy
    def volume_env(self):
        return []

    @lazy
    def optional_volume_env(self):
        return []

    @lazy
    def array(self):
        """
        Implemented by child classes
        """
        return

    @lazy
    def section(self):
        return "pool#" + self.name

    def oget(self, key, **kwargs):
        return self.node.oget(self.section, key, **kwargs)

    def oget_scopes(self, key, **kwargs):
        return self.node.oget_scopes(self.section, key, **kwargs)

    @lazy
    def fs_type(self):
        return self.oget("fs_type")

    @lazy
    def mkfs_opt(self):
        return self.oget("mkfs_opt")

    @lazy
    def mkblk_opt(self):
        return self.oget("mkblk_opt")

    @lazy
    def mnt_opt(self):
        return self.oget("mnt_opt")

    @lazy
    def status_schedule(self):
        return self.oget("status_schedule")

    def mount_point(self, name):
        return os.path.join(os.sep, "srv", name)

    def default_disk_name(self, volume):
        return "%s.%s.vol.%s" % (
            volume.name,
            volume.namespace if volume.namespace else "root",
            self.node.cluster_name,
        )

    def configure_volume(self, volume, size=None, fmt=True, access="rwo", shared=False, nodes=None, env=None):
        name = self.default_disk_name(volume)
        data = self.translate(name=name, size=size, fmt=fmt, shared=shared)
        defaults = {
            "rtype": "DEFAULT",
            "pool": self.name,
            "size": size,
            "access": access,
        }
        if access in ("rox", "rwx"):
            defaults["topology"] = "flex"
            defaults["flex_min"] = 0
        if nodes:
            defaults["nodes"] = nodes
        if self.status_schedule is not None:
            defaults["status_schedule"] = self.status_schedule
        data.append(defaults)
        if env:
            data.append(env)
        volume._update(data)
        self.disable_sync_internal(volume)
        if volume.volatile:
            return volume
        return factory("vol")(name=volume.name, namespace=volume.namespace, node=self.node, volatile=volume.volatile)

    def pool_status(self):
        pass

    def translate(self, name=None, size=None, fmt=True, shared=False):
        return []

    def sep(self):
        return "."

    def create_disk(self, name, size, nodes=None):
        return {}

    def delete_disk(self, name=None, disk_id=None):
        return {}

    def delete_volume(self, name, namespace=None):
        volume = factory("vol")(name=name, namespace=namespace, node=self.node)
        if not volume.exists():
            self.log.info("volume does not exist")
            return
        self.log.info("delete volume %s", volume.path)
        volume.action("delete", options={"wait": True, "unprovision": True, "time": "5m"})
        
    def create_volume(self, name, namespace=None, size=None, access="rwo", fmt=True, nodes=None, shared=False):
        volume = factory("vol")(name=name, namespace=namespace, node=self.node)
        if volume.exists():
            self.log.info("volume %s already exists", name)
            return volume
        if nodes is None:
            nodes = ""
        self.log.info("create volume %s (pool name: %s, pool type: %s, "
                           "access: %s, size: %s, format: %s, nodes: %s, shared: %s)",
                           volume.path, self.name, self.type, access, size,
                           fmt, nodes, shared)
        self.configure_volume(volume,
                              fmt=fmt,
                              size=convert_size(size),
                              access=access,
                              nodes=nodes,
                              shared=shared)
        volume.action("provision", options={"wait": True, "time": "5m"})

    def get_targets(self):
        return []

    def _get_mappings(self, nodes, transport="fc"):
        data = []
        tgts = self.get_targets()
        if self.node.nodes_info is None:
            raise ex.Error("nodes info is not available")
        for nodename, ndata in self.node.nodes_info.items():
            if nodes and nodename not in nodes:
                continue
            for mapping in ndata.get("targets", []):
                if transport == "iscsi" and not mapping["hba_id"].startswith("iqn"):
                    continue
                if mapping["tgt_id"] not in tgts:
                    continue
                data.append(":".join((mapping["hba_id"], mapping["tgt_id"])))
        self.log.info("mappings for nodes %s: %s", ",".join(sorted(list(nodes))), " ".join(data))
        return data

    def disable_sync_internal(self, volume):
        """
        Disable sync#i0 if the volume has no resource contributing files to sync.
        """
        tosync = []
        for res in volume.get_resources():
            tosync += res.files_to_sync()
        if len(tosync):
            return []
        volume._update([{"rid": "sync#i0", "disable": True}])

    def add_fs(self, name, shared=False, dev="disk#1"):
        data = []
        if self.fs_type == "zfs":
            disk = {
                "rtype": "disk",
                "type": "zpool",
                "name": name,
                "vdev": "{%s.exposed_devs[0]}" % dev,
                "shared": shared,
            }
            fs = {
                "rtype": "fs",
                "type": self.fs_type,
                "dev": "%s/root" % name,
                "mnt": self.mount_point(name),
                "shared": shared,
            }
            if self.mkfs_opt:
                fs["mkfs_opt"] = " ".join(self.mkfs_opt)
            if self.mnt_opt:
                fs["mnt_opt"] = self.mnt_opt
            data += [disk, fs]
        else:
            fs = {
                "rtype": "fs",
                "type": self.fs_type,
                "dev": "{%s.exposed_devs[0]}" % dev,
                "mnt": self.mount_point(name),
                "shared": shared,
            }
            if self.mkfs_opt:
                fs["mkfs_opt"] = " ".join(self.mkfs_opt)
            if self.mnt_opt:
                fs["mnt_opt"] = self.mnt_opt
            data += [fs]
        return data

 0707010001f1a0000081a40000000000000000000000016a100daf00003e5d000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/resourceset.py    """
Defines the resource set class, which is a collection of resources.
"""
from __future__ import print_function

import sys
import logging
from multiprocessing import Process

import core.exceptions as ex
import core.status
from utilities.lazy import lazy
from env import Env
from core.resource 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
        try:
            self.driver_group, self.subset_name = self.rid.split(":")
        except ValueError:
            self.driver_group = self.rid
            self.subset_name = ""

    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
            if self.svc.options.dry_run:
                self.log.info("pre_action %s" % resource.type)
                continue
            try:
                resource.pre_action(action)
            except ex.AbortAction:
                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
            if self.svc.options.dry_run:
                self.log.info("post_action %s" % resource.type)
                continue
            try:
                resource.post_action(action)
            except ex.AbortAction:
                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 = core.status.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 = core.status.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 res.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:
            action_rid_before_depends = set(self.svc.action_rid_before_depends)
            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:
                    deps = self.svc.action_rid_dependencies(action, res.rid)
                    if deps and deps < action_rid_before_depends:
                        continue
                    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.is_standby]

        resources = self.sort_resources(resources, action)

        if action == "provision":
            # make sure scsi reservation are taken after their co-resource is
            # provisioned, so the disk list to reserv will not be empty.
            resources = [res for res in resources if res.type != "disk.scsireserv"] + \
                        [res for res in resources if res.type == "disk.scsireserv"]

        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")
        resources = self.action_resources(action, tags, xtags, xtypes, types)
        resources = [r for r in resources if not r.skip and not r.is_disabled() and (action != "rollback" or r.can_rollback)]
        barrier = None

        if self.parallel:
            if Env.sysname == "Windows":
                parallel = False
            else:
                parallel = True
        else:
            parallel = False

        if not self.svc.options.dry_run \
                and parallel \
                and len(resources) > 1 and \
                action not in ["presync", "postsync"]:
            procs = {}
            self.log.info("parallel %s resources %s" % (action, ",".join(sorted([r.rid for r in resources]))))
            from utilities.process_title import set_process_title  # warm up for side effect
            for resource in resources:
                if not resource.can_rollback and action == "rollback":
                    continue
                if resource.skip or resource.is_disabled():
                    continue
                title = "om %s --subset %s --rid %s %s" % (resource.svc.path,
                                                           self.subset_name,
                                                           resource.rid,
                                                           action)
                proc = Process(target=self._action_job, args=(title, 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]
                exitcode = proc.exitcode
                if exitcode == 1 and not resource.optional:
                    err.append(resource.rid)
                elif 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 action == "provision" and resource.type == "volume":
                    # need reset lazy in current process, (subprocess reset lazy has no effect)
                    resource.post_provision_reset_lazy()
                if action in ("stop", "start", "provision", "unprovision") and hasattr(resource, "is_up_clear_cache"):
                    # need reset lazy in current process, (subprocess reset lazy has no effect)
                    # for ex: docker caches container_id, dockerlib caches the ps data, etc...
                    getattr(resource, "is_up_clear_cache")()
            if len(err) > 0:
                raise ex.Error("%s non-optional resources jobs returned "
                               "with error" % ",".join(err))
        else:
            if self.svc.options.dry_run and \
               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.AbortAction as exc:
                    msg = str(exc)
                    if msg != "":
                        resource.log.warning(msg)
                    resource.log.warning("abort action on resource set")
                    break
                except ex.ContinueAction as exc:
                    msg = str(exc)
                    if msg != "":
                        resource.log.info(msg)
                    resource.log.info("continue action on resource set")
                except ex.Error as exc:
                    msg = str(exc)
                    if msg != "":
                        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.EndAction(barrier)

    def _action_job(self, title, resource, action):
        """
        The worker job used for parallel execution of a resource action in
        a resource set.
        """
        from utilities.process_title import set_process_title
        set_process_title(title)
        try:
            getattr(resource, 'action')(action)
        except ex.Error as exc:
            self.log.error(str(exc))
            sys.exit(1)
        except Exception as exc:
            self.log.exception(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):
        extra = {
            "path": self.svc.path,
            "node": Env.nodename,
            "sid": Env.session_uuid,
            "cron": self.svc.options.cron,
            "subset": self.subset_name,
        }
        return logging.LoggerAdapter(self.logger, extra)

    @lazy
    def logger(self):
        """
        Lazy init for the resource logger.
        """
        l = [
            Env.nodename,
            self.svc.name,
            self.rid
        ]
        if self.svc.namespace:
            l.insert(1, self.svc.namespace.lower())
        name = ".".join(l)
        return logging.getLogger(name)


if __name__ == "__main__":
    pass
   0707010001f169000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/core/capabilities  0707010001f16a000081a40000000000000000000000016a100daf00001471000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/core/capabilities/__init__.py  from __future__ import print_function

import importlib
import json
import sys

from env import Env
from utilities.proc import which
from utilities.lazy import lazy
from utilities.subsystems.docker import has_docker

DRIVER_CAP_FN = "driver_capabilities"
DRIVER_CAP_PREFIX = "drivers.resource."


class BaseCapabilities(object):
    def __contains__(self, cap):
        return cap in self.data["tags"]

    def scan_generic(self):
        tags = [
            "node.x.cache.name",
            "node.x.cache.ttl",
        ]
        labels = {}

        for tag, bp in (
            ("node.x.blkid", Env.syspaths.blkid),
            ("node.x.dmsetup", Env.syspaths.dmsetup),
            ("node.x.drbdadm", "drbdadm"),
            ("node.x.exportfs", "exportfs"),
            ("node.x.findfs", "findfs"),
            ("node.x.git", "git"),
            ("node.x.hpvmstart", "/opt/hpvm/bin/hpvmstart"),
            ("node.x.hpvmstatus", "/opt/hpvm/bin/hpvmstatus"),
            ("node.x.hpvmstop", "/opt/hpvm/bin/hpvmstop"),
            ("node.x.ifconfig", "ifconfig"),
            ("node.x.ip", "/sbin/ip"),
            ("node.x.losetup", Env.syspaths.losetup),
            ("node.x.lvs", "/sbin/lvs"),
            ("node.x.multipath", Env.syspaths.multipath),
            ("node.x.netstat", "netstat"),
            ("node.x.podman", "/usr/bin/podman"),
            ("node.x.powermt", "powermt"),
            ("node.x.scsi_id", ("scsi_id", "/lib/udev/scsi_id", "/usr/lib/udev/scsi_id")),
            ("node.x.share", "share"),
            ("node.x.srp", "srp"),
            ("node.x.srp_su", "srp_su"),
            ("node.x.stat", "stat"),
            ("node.x.udevadm", "udevadm"),
            ("node.x.vmware-cmd", "vmware-cmd"),
            ("node.x.vxdmpadm", "vxdmpadm"),
            ("node.x.zfs", "zfs"),
            ("node.x.zpool", "zpool"),
        ):
            if not isinstance(bp, tuple):
                bp = (bp,)
            for bpc in bp:
                p = which(bpc)
                if not p:
                    continue
                tags.append(tag)
                labels[tag+".path"] = p
                break

        if has_docker(["docker"]):
            tags.append("node.x.docker")
        if has_docker(["docker.io"]):
            tags.append("node.x.docker.io")
        if has_docker(["dockerd"]):
            tags.append("node.x.dockerd")

        if "node.x.stat" in tags:
            tags.append("drivers.resource.fs.check_readable")

        return {"tags": tags, "labels": labels}
    
    def need_refresh(self):
        return False

    def scan_os(self):
        return {"tags": [], "labels": {}}

    def scan(self, node=None):
        from core.objects.svcdict import SECTIONS
        from utilities.drivers import iter_drivers
        if node is None:
            from core.node import Node
            node = Node()
        data = self.scan_generic()
        osdata = self.scan_os()
        data["tags"] += osdata["tags"]
        data["labels"].update(osdata["labels"])
        for mod in iter_drivers(SECTIONS):
            if not hasattr(mod, DRIVER_CAP_FN):
                if hasattr(mod, "DRIVER_GROUP") and hasattr(mod, "DRIVER_BASENAME"):
                    # consider the driver active by default
                    data["tags"] += ["%s%s.%s" % (DRIVER_CAP_PREFIX, mod.DRIVER_GROUP, mod.DRIVER_BASENAME)]
                continue
            try:
                for cap in getattr(mod, DRIVER_CAP_FN)(node=node):
                    if isinstance(cap, tuple):
                        cap, val = cap
                        pcap = DRIVER_CAP_PREFIX + cap
                        data["labels"][pcap] = val
                    else:
                        pcap = DRIVER_CAP_PREFIX + cap
                        data["tags"].append(pcap)
            except Exception as exc:
                print(mod, exc, file=sys.stderr)
        self.dump(data)
        return data

    @staticmethod
    def as_list(data):
        l = [] + data["tags"]
        for k, v in data["labels"].items():
            l.append("%s=%s" % (k, v))
        return sorted(l)

    def dump(self, data):
        data = self.as_list(data)
        with open(Env.paths.capabilities, "w") as f:
            json.dump(data, f)
    
    def load(self):
        with open(Env.paths.capabilities, "r") as f:
            l = json.load(f)
        data = {"tags": [], "labels": {}}
        for s in l:
            try:
                label, val = s.split("=", 1)
                data["labels"][label] = val
            except ValueError:
                data["tags"].append(s)
        return data

    @lazy
    def data(self):
        if self.need_refresh():
            return self.scan()
        try:
            return self.load()
        except Exception as exc:
            return self.scan()
    
    def has(self, cap):
        return cap in self.data["tags"]

    def get(self, cap):
        return self.data["labels"].get(cap)

try:
    _package = __package__ or __spec__.name # pylint: disable=undefined-variable
    _os = importlib.import_module("." + Env.module_sysname, package=_package)
    capabilities = _os.Capabilities()
except (ImportError, AttributeError):
    capabilities = BaseCapabilities()


   0707010001f16b000081a40000000000000000000000016a100daf0000022a000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/core/capabilities/linux.py import os
from . import BaseCapabilities
from env import Env

REFRESH_FLAGS = [
   "/var/log/dpkg.log",
   "/var/lib/rpm/Basenames",
]

class Capabilities(BaseCapabilities):
    def need_refresh(self):
        try:
            last = os.path.getmtime(Env.paths.capabilities)
        except OSError:
            return True
        for flag in REFRESH_FLAGS:
            try:
                mtime = os.path.getmtime(flag)
            except OSError:
                continue
            if last < mtime:
                return True
        return False

  0707010001f16c000081a40000000000000000000000016a100daf00000216000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/core/capabilities/sunos.py import os
from . import BaseCapabilities
from env import Env

REFRESH_FLAGS = [
   "/var/sadm/install/contents",
]

class Capabilities(BaseCapabilities):
    def need_refresh(self):
        try:
            last = os.path.getmtime(Env.paths.capabilities)
        except OSError:
            return True
        for flag in REFRESH_FLAGS:
            try:
                mtime = os.path.getmtime(flag)
            except OSError:
                continue
            if last < mtime:
                return True
        return False

  0707010001f17a000081a40000000000000000000000016a100daf0000fcf0000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/core/extconfig.py  from __future__ import print_function

import ast
import codecs
import copy
import operator as op
import os
import re
import sys

import foreign.six as six

import core.exceptions as ex
from core.configfile import move_config_file
from env import Env
from utilities.naming import factory
from utilities.files import makedirs
from utilities.configparser import RawConfigParser, NoOptionError
from utilities.converters import *
from utilities.lazy import lazy
from utilities.string import is_string, try_decode

SECRETS = []
MAX_RECURSION = 20

DEFER = [
    (None, "exposed_devs"),
    (None, "sub_devs"),
    (None, "base_devs"),
    ("volume", "mnt"),
]

# 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, getattr(ast, "NameConstant")):
            return node.value
        else:
            raise TypeError("unsupported node type %s" % type(node))

    return eval_(ast.parse(expr, mode='eval').body)


def read_cf(fpaths, defaults=None):
    """
    Read and parse an arbitrary ini-formatted config file, and return
    the RawConfigParser object.
    """
    try:
        from collections import OrderedDict
        config = RawConfigParser(dict_type=OrderedDict)
    except ImportError:
        config = RawConfigParser()

    if defaults is None:
        defaults = {}
    config = RawConfigParser(defaults)
    config.optionxform = str
    if not isinstance(fpaths, (list, tuple)):
        fpaths = [fpaths]
    for fpath in fpaths:
        if not os.path.exists(fpath):
            continue
        with codecs.open(fpath, "r", "utf8") as ofile:
            try:
                if six.PY3:
                    config.read_file(ofile)
                else:
                    config.readfp(ofile)
            except AttributeError:
                raise
    return config


def read_cf_comments(fpath):
    data = {}
    if isinstance(fpath, list):
        return data
    if not os.path.exists(fpath):
        return data
    section = ".header"
    current = []

    with codecs.open(fpath, "r", "utf8") as ofile:
        buff = ofile.read()

    for line in buff.splitlines():
        line = line.strip()
        if not line:
            continue
        if re.match(r"\[.+\]", line):
            if current:
                data[section] = current
                current = []
            section = line[1:-1]
            continue
        if line[0] in (";", "#"):
            stripped = line.lstrip("#;").strip()
            if re.match(r"\[.+\]", stripped):
                # add an empty line before a commented section
                current.append("")
            current.append(stripped)
    if current:
        data[section] = current
        current = []
    return data


class ExtConfigMixin(object):
    def __init__(self, default_status_groups=None):
        self.ref_cache = {}
        self.default_status_groups = default_status_groups

    def clear_ref_cache(self):
        self.ref_cache = {}

    @lazy
    def has_default_section(self):
        if hasattr(self, "path"):
            return True
        else:
            return False

    def delete_sections(self, sections=None):
        """
        Delete config file sections.
        """
        self.cd_clear_caches()
        if sections is None:
            sections = []
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        deleted = 0
        for section in sections:
            try:
                del cd[section]
                deleted += 1
            except KeyError:
                self.log.info("skip delete %s: not found", section)
        if deleted:
            self.commit()

    def unset(self):
        """
        The 'unset' action entrypoint.
        Verifies the --param is set, and finally call the _unset
        internal method.
        """
        if self.options.kw:
            kws = self.options.kw
        elif self.options.param:
            kws = [self.options.param]
        else:
            print("no keyword specified. set --kw <keyword>", file=sys.stderr)
            return 1
        self.unset_multi(kws)

    def unset_multi(self, kws):
        self.cd_clear_caches()
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        if not kws:
            return
        deleted = 0
        for kw in kws:
            try:
                section, option = kw.split(".", 1)
            except Exception:
                section = "DEFAULT"
                option = kw
            if "#" not in section and section in self.default_status_groups:
                # <group>.keyword[@<scope>] format => loop over all rids in group
                for rid in [rid for rid in cd if rid.startswith(section+"#")]:
                    try:
                        del cd[rid][option]
                        deleted += 1
                    except KeyError:
                        continue
            else:
                try:
                    del cd[section][option]
                    deleted += 1
                except KeyError:
                    continue
        if not deleted:
            return 0
        try:
            self.commit(cd=cd)
        except (IOError, OSError) as exc:
            raise ex.Error(str(exc))
        return deleted

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

    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, evaluate=self.options.eval, impersonate=self.options.impersonate))
        except ex.OptNotFound as exc:
            print(exc.default)
        except ex.RequiredOptNotFound as exc:
            return 1
        except ex.Error as exc:
            print(exc, file=sys.stderr)
            return 1
        except Exception:
            return 1
        return 0

    def _get(self, param=None, evaluate=False, impersonate=None):
        """
        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.Error("no parameter. set --param")
        elements = param.split(".", 1)
        if self.has_default_section and len(elements) == 1:
            elements.insert(0, "DEFAULT")
        elif len(elements) != 2:
            raise ex.Error("malformed parameter. format as 'section.key'")
        section, option = elements
        if section == "DEFAULT" and not self.has_default_section:
            raise ex.Error("the DEFAULT section is not allowed in %s" % self.paths.cf)
        if section not in self.cd:
            if section != 'DEFAULT' and self.has_default_section:
                raise ex.OptNotFound("section [%s] not found" % section)
            if not self.has_default_section:
                raise ex.OptNotFound("section [%s] not found" % section)
        if evaluate:
            if self.options.format is None:
                self.options.format = "json"
            return self.conf_get(section, option, scope=True, impersonate=impersonate)
        else:
            try:
                return self.cd[section][option]
            except Exception:
                raise ex.OptNotFound()

    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.eval:
            eval = self.options.eval
        else:
            eval = False
        if self.options.kw is not None:
            self.set_multi(self.options.kw, eval=eval)
        else:
            self.set_mono(eval=eval)

    def set_multi(self, kws, eval=False, validation=True):
        self.cd_clear_caches()
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        changes = []
        self.set_multi_cache = {}
        for kw in kws:
            if "=" not in kw:
                raise ex.Error("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]
            elif keyword[-1] == "+":
                op = "insert"
                keyword = keyword[:-1]
            else:
                op = "set"
            index = None
            if "[" in keyword:
                keyword, right = keyword.split("[", 1)
                if not right.endswith("]"):
                    raise ex.Error("malformed kw expression: %s: no trailing"
                                   " ']' at the end of keyword" % kw)
                try:
                    index = int(right[:-1])
                except ValueError:
                    raise ex.Error("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(".", 1)[0]
                if group in self.default_status_groups:
                    for rid in [rid for rid in cd if rid.startswith(group+"#")]:
                        keyword = rid + keyword[keyword.index("."):]
                        changes.append(self.set_mangle(keyword, op, value, index, eval))
                else:
                    # <section>.keyword[@<scope>]
                    changes.append(self.set_mangle(keyword, op, value, index, eval))
            else:
                # <rid>.keyword[@<scope>]
                changes.append(self.set_mangle(keyword, op, value, index, eval))
        return self._set_multi(changes, validation=validation)

    def set_mono(self, eval=False):
        self.cd_clear_caches()
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        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(".", 1)[0]
            if group in self.default_status_groups:
                for rid in [rid for rid in cd if rid.startswith(group+"#")]:
                    keyword = rid + keyword[keyword.index("."):]
                    changes.append(self.set_mangle(keyword, op, value, index, eval))
            else:
                # <section>.keyword[@<scope>]
                changes.append(self.set_mangle(keyword, op, value, index, eval))
        else:
            # <rid>.keyword[@<scope>]
            changes.append(self.set_mangle(keyword, op, value, index, eval))

        return self._set_multi(changes)

    def set_mangle(self, keyword, op, value, index, eval):
        def list_value(keyword):
            if keyword in self.set_multi_cache:
                return self.set_multi_cache[keyword].split()
            try:
                _value = self._get(keyword, eval).split()
            except (ex.Error, NoOptionError) as exc:
                _value = []
            except ex.OptNotFound as exc:
                _value = copy.copy(exc.default)
            if _value is None:
                _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 == "insert":
            _value = list_value(keyword)
            if index is None:
                i = len(_value)
            else:
                i = index
            _value.insert(i, value)
            _value = " ".join(_value)
            self.set_multi_cache[keyword] = _value
        elif op == "add":
            _value = list_value(keyword)
            for v in value.split():
                if v in _value:
                    continue
                if index is None:
                    i = len(_value)
                else:
                    i = index
                    index += 1
                _value.insert(i, v)
            _value = " ".join(_value)
            self.set_multi_cache[keyword] = _value
        else:
            _value = value
        elements = keyword.split(".", 1)
        if self.has_default_section and len(elements) == 1:
            elements.insert(0, "DEFAULT")
        elif len(elements) != 2:
            raise ex.Error("malformed kw: format as 'section.key'")
        return elements[0], elements[1], _value, eval

    def _set(self, section, option, value, validation=True):
        changes = [(section, option, value, False)]
        return self._set_multi(changes, validation=validation)

    def _set_one(self, section, option, value):
        value = try_decode(value)
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        changed = False
        if section not in cd:
            cd[section] = {}
            changed = True
        try:
            current = cd[section][option]
            if current != value:
                cd[section][option] = value
                changed = True
        except KeyError:
            cd[section][option] = value
            changed = True
        return changed

    def _set_multi(self, changes, validation=True):
        applied = []
        for change in changes:
            if change is None:
                continue
            section, option, value, eval = change
            changed = self._set_one(section, option, value)
            if changed:
                applied.append(change)
        if len(applied) == 0:
            return applied
        try:
            self.commit(validation=validation)
        except (IOError, OSError) as exc:
            raise ex.Error(str(exc))
        return applied

    #########################################################################
    #
    # config helpers
    #
    #########################################################################
    def handle_reference(self, ref, scope=False, impersonate=None, cd=None,
                         section=None, stack=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,
                                               section=section, stack=stack))
        else:
            index = None

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

        is_svc = hasattr(self, "path")
        has_node = hasattr(self, "node")

        modifier = None
        if _ref.startswith("upper:"):
            _ref = _ref[6:]
            modifier = lambda x: x.upper()
        elif _ref.startswith("lower:"):
            _ref = _ref[6:]
            modifier = lambda x: x.lower()
        elif _ref.startswith("capitalize:"):
            _ref = _ref[11:]
            modifier = lambda x: x.capitalize()
        elif _ref.startswith("title:"):
            _ref = _ref[6:]
            modifier = lambda x: x.title()
        elif _ref.startswith("swapcase:"):
            _ref = _ref[9:]
            modifier = lambda x: x.swapcase()
        elif _ref.startswith("uri_ip:"):
            _ref = _ref[7:]
            modifier = lambda x: "[%s]" % x if ":" in str(x) else x

        # hardcoded references
        val = None
        if _ref == "nodename":
            val = Env.nodename
        elif _ref == "short_nodename":
            val = Env.nodename.split(".")[0]
        elif _ref == "namespace" and is_svc:
            val = self.namespace if self.namespace else "root"
        elif _ref == "kind" and is_svc:
            val = self.kind
        elif _ref == "id" and is_svc:
            val = self.id
        elif _ref in ("path", "svcpath") and is_svc:
            val = self.path
        elif _ref in ("name", "svcname") and is_svc:
            val = self.name
        elif _ref in ("short_name", "short_svcname") and is_svc:
            val = self.name.split(".")[0]
        elif _ref in ("scaler_name", "scaler_svcname") and is_svc:
            val = re.sub(r"[0-9]+\.", "", self.name)
        elif _ref in ("scaler_short_name", "scaler_short_svcname") and is_svc:
            val = re.sub(r"[0-9]+\.", "", self.name.split(".")[0])
        elif _ref == "rid" and is_svc:
            val = section
        elif _ref == "rindex" and is_svc:
            val = section.split("#")[-1]
        elif _ref == "clusterid":
            if has_node:
                val = self.node.cluster_id
            else:
                val = self.cluster_id
        elif _ref == "clustername":
            if has_node:
                val = self.node.cluster_name
            else:
                val = self.cluster_name
        elif _ref == "dns_janitor_major":
            val = "1"
        elif _ref == "fqdn":
            if has_node:
                ns = "root" if self.namespace is None else self.namespace
                val = "%s.%s.%s.%s" % (self.name, ns, self.kind, self.node.cluster_name)
        elif _ref == "domain":
            if has_node:
                val = "%s.%s.%s" % (self.namespace, self.kind, self.node.cluster_name)
        elif _ref == "clusternodes":
            if has_node:
                val = " ".join(self.node.cluster_nodes)
            else:
                val = " ".join(self.cluster_nodes)
        elif _ref == "clusterdrpnodes":
            if has_node:
                val = " ".join(self.node.cluster_drpnodes)
            else:
                val = " ".join(self.cluster_drpnodes)
        elif _ref == "dns":
            if has_node:
                val = " ".join(self.node.dns)
            else:
                val = " ".join(self.dns)
        elif _ref == "dnsnodes":
            if has_node:
                val = " ".join(self.node.dnsnodes)
            else:
                val = " ".join(self.dnsnodes)
        elif _ref == "svcmgr":
            val = Env.paths.svcmgr
        elif _ref == "nodemgr":
            val = Env.paths.nodemgr
        elif _ref == "etc":
            val = Env.paths.pathetc
        elif _ref == "var":
            val = Env.paths.pathvar
        elif _ref == "private_var":
            val = self.var_d
        elif _ref == "initd":
            val = self.paths.initd
        elif _ref == "collector_api":
            if has_node:
                url = self.node.collector_env.dbopensvc
            else:
                url = self.collector_env.dbopensvc
            if url:
                val = url.replace("/feed/default/call/xmlrpc", "/init/rest/api") if url else ""
            else:
                val = ""
        elif _ref == "dnsuxsockd":
            val = Env.paths.dnsuxsockd
        elif _ref == "dnsuxsock":
            val = Env.paths.dnsuxsock
        elif _ref.startswith("safe://"):
            try:
                if has_node:
                    val = self.node.download_from_safe(_ref, path=self.path)
                else:
                    val = self.download_from_safe(_ref)
                val = val.decode()
                SECRETS.append(val)
            except ex.Error as exc:
                val = ""

        _v = None
        if val is None:
            # use DEFAULT as the implicit section
            n_dots = _ref.count(".")
            _section = ""
            if n_dots == 0 and section and section != "DEFAULT":
                _section = section
                _v = _ref
            elif n_dots == 0 and self.has_default_section:
                _section = "DEFAULT"
                _v = _ref
            elif n_dots >= 1:
                _section, _v = _ref.split(".", 1)

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

            try:
                val = self._handle_reference(_ref, _section, _v, scope=scope,
                                             impersonate=impersonate,
                                             cd=cd, return_length=return_length,
                                             stack=stack)
            except Exception:
                val = None

            if val is None and _section != "DEFAULT" and n_dots == 0 and self.has_default_section:
                val = self._handle_reference(ref, "DEFAULT", _v, scope=scope,
                                             impersonate=impersonate, cd=cd,
                                             return_length=return_length,
                                             stack=stack)

            if val is None:
                if self.is_deferred(_section, _v):
                    raise ex.NotAvailable
                else:
                    # not deferrable and unknown reference
                    # return it unchanged
                    raise ex.NotSupported

        if return_length or index is not None:
            if is_string(val):
                val = val.split()
            if return_length:
                val = str(len(val))
            elif index is not None:
                try:
                    val = val[index]
                except IndexError:
                    if self.is_deferred(section, _v):
                        raise ex.NotAvailable
                    else:
                        # not deferrable and unknown reference
                        # return it unchanged
                        raise ex.NotSupported

        if modifier and val:
            try:
                return modifier(val)
            except Exception as exc:
                pass
        return val

    def is_deferred(self, section, option):
        if not hasattr(self, "path"):
            return False
        drv_group = section.split("#", 1)[0]
        return (drv_group, option) in DEFER or (None, option) in DEFER

    def _handle_reference(self, ref, _section, _v, scope=False,
                          impersonate=None, cd=None, return_length=False, stack=None):
        if cd is None:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd
        # 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, "path"):
            # go fetch the reference in the node.conf [node] section
            try:
                # set BaseSvc::node if not already set
                self.get_node()
                if "." in _v:
                    __section, __v = _v.split(".", 1)
                    if __section in ("env", "labels"):
                        # allowed explicit section
                        return self.node.conf_get(__section, __v)
                # use "node" as the implicit section
                return self.node.conf_get("node", _v)
            except Exception as exc:
                raise ex.Error("%s: unresolved reference (%s)" % (ref, str(exc)))

        if _section != "DEFAULT" and _section not in cd:
            raise ex.Error("%s: section %s does not exist" % (ref, _section))

        if self.is_deferred(_section, _v):
            try:
                self.init_resources()
                res = self.resources_by_id[_section]
                fn = getattr(res, _v)
                result = fn()
                if isinstance(result, set):
                    return list(result)
                else:
                    return result
            except Exception as exc:
                return

        try:
            t = None if return_length else "string"
            return self.conf_get(_section, _v, t, scope=scope,
                                 impersonate=impersonate, cd=cd, stack=stack)
        except ex.OptNotFound as exc:
            return copy.copy(exc.default)
        except ex.RequiredOptNotFound as exc:
            raise ex.Error("%s: unresolved reference (%s)" % (ref, str(exc)))

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

    def _handle_references(self, s, scope=False, impersonate=None, cd=None,
                           section=None, first_step=None, stack=None):
        if not is_string(s):
            return s
        done = ""
        OPEN = "@@O1@@"
        CLOSE = "@@C1@@"
        while True:
            m = re.search(r'{\w*[\w#][\w\.\[\]:\/]*}', s)
            if m is None:
                ret = done + s
                ret = ret.replace(OPEN, "{")
                ret = ret.replace(CLOSE, "}")
                return ret
            ref = m.group(0)[1:-1]
            if first_step and ref.startswith("safe://"):
                # do safe references after expressions only
                done += s[:m.end()]
                s = s[m.end():]
                continue
            try:
                val = self.handle_reference(ref, scope=scope,
                                            impersonate=impersonate,
                                            cd=cd, section=section,
                                            stack=stack)
            except ex.NotSupported:
                val = OPEN + ref + CLOSE
            except ex.NotAvailable:
                # deferred
                return
            if ref.startswith("safe://"):
                # disallow new refs in val
                done += s[:m.start()] + str(val)
                s = s[m.end():]
            else:
                # allow new refs in val
                s = s[:m.start()] + str(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.Error("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, cd=None,
                          section=None, stack=None):
        if s is None:
            # avoid cache key collision with "None" values (typically node.dbopensvc)
            return
        key = self.ref_cache_key(s, scope, impersonate)
        if key and key in self.ref_cache:
            return self.ref_cache[key]
        try:
            val = self._handle_references(s, scope=scope,
                                          impersonate=impersonate,
                                          cd=cd, section=section,
                                          first_step=True, stack=stack)
            val = self._handle_expressions(val)
            val = self._handle_references(val, scope=scope,
                                          impersonate=impersonate,
                                          cd=cd, section=section, stack=stack)
        except Exception as e:
            raise
            raise ex.Error("%s: reference evaluation failed: %s" % (s, str(e)))
        if key and val is not None:
            self.ref_cache[key] = val
        return val

    def ref_cache_key(self, s, scope, impersonate):
        if not self.cacheable(s):
            return
        return (str(s), scope, impersonate)

    @staticmethod
    def cacheable(s):
        try:
            s = str(s)
        except:
            return False
        if "{rid}" in s or "{rindex}" in s:
            # those can take different values in the same service,
            # being section-dependent
            return False
        return True

    def oget_scopes(self, *args, **kwargs):
        data = {}
        for node in self.cluster_nodes:
            kwargs["impersonate"] = node
            data[node] = self.oget(*args, **kwargs)
        return data

    def oget(self, *args, **kwargs):
        """
        A wrapper around conf_get() that returns the keyword default
        instead of raising OptNotFound.
        """
        try:
            return self.conf_get(*args, **kwargs)
        except ex.OptNotFound as exc:
            return exc.default
        except ex.RequiredOptNotFound as exc:
            raise ex.Error(str(exc))

    def get_rtype(self, s, section, cd):
        if section == "DEFAULT":
            return
        try:
            return cd[s]["type"]
        except Exception as exc:
            pass
        try:
            return self.kwstore[section].getkey("type").default
        except AttributeError:
            pass

    def conf_get(self, s, o, t=None, scope=None, impersonate=None,
                 use_default=True, cd=None, verbose=True, rtype=None, stack=None):
        """
        Handle keyword and section deprecation.
        """
        stack = stack or []
        if len(stack) > MAX_RECURSION:
            raise ex.Error("recursion exceeds %d: %s" % (MAX_RECURSION, " => ".join(["%s.%s" % e for e in stack])))
        stack.append((s, o))
        if cd is None:
            cd = self.cd
        section = s.split("#")[0]
        if rtype:
            pass
        elif section in self.kwstore.deprecated_sections:
            section, rtype = self.kwstore.deprecated_sections[section]
        else:
            rtype = self.get_rtype(s, section, cd)

        if rtype:
            fkey = ".".join((section, rtype, o))
        else:
            fkey = ".".join((section, o))

        deprecated_keywords = self.kwstore.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:
            _val = self._conf_get(s, o, t=t, scope=scope,
                                  impersonate=impersonate,
                                  use_default=use_default, cd=cd,
                                  section=section, rtype=rtype, stack=stack)
            if _val is not None:
                # allow x repeats of valid ref
                stack.pop()
            return _val
        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 = False
        for deprecated_keyword in deprecated_keywords:
            try:
                _val = self._conf_get(s, deprecated_keyword, t=t, scope=scope,
                                      impersonate=impersonate,
                                      use_default=use_default, cd=cd,
                                      section=section, rtype=rtype, stack=stack)
                if _val is not None:
                    # allow x repeats of valid ref
                    stack.pop()
                return _val
            except ex.RequiredOptNotFound:
                exc = True
        if exc:
            self.log.error("%s.%s is mandatory" % (s, o))
            raise ex.RequiredOptNotFound

    def _conf_get(self, s, o, t=None, scope=None, impersonate=None,
                  use_default=True, cd=None, section=None, rtype=None, stack=None):
        """
        Get keyword properties and handle inheritance.
        """
        inheritance = "leaf"
        kwargs = {
            "default": None,
            "required": False,
            "deprecated": False,
            "impersonate": impersonate,
            "use_default": use_default,
            "cd": cd,
            "stack": stack,
        }
        if s not in ("labels", "env", "data"):
            key = self.kwstore[section].getkey(o, rtype)
            if key is None:
                if s == "DEFAULT":
                    # default values don't need a kw definition
                    pass
                elif 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
                kwargs["default_keyword"] = key.default_keyword
                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.Error("unknown inheritance value: %s" % str(inheritance))

    def convert(self, converter, val):
        if converter == "nodes_selector":
            if hasattr(self, "path"):
                data = self.node.listener.nodes_info() if self.node.listener else None
                return self.node.nodes_selector(val, data)
            else:
                data = self.listener.nodes_info() if self.listener else None
                return self.nodes_selector(val, data)
        return globals()["convert_"+converter](val)

    def __conf_get(self, s, o, t=None, scope=None, impersonate=None,
                   use_default=None, cd=None, default=None, required=None,
                   deprecated=None, default_keyword=None, stack=None):
        try:
            if not scope:
                val = self.conf_get_val_unscoped(s, o, use_default=use_default,
                                                 cd=cd,
                                                 default_keyword=default_keyword)
            else:
                val = self.conf_get_val_scoped(s, o, use_default=use_default,
                                               cd=cd,
                                               impersonate=impersonate,
                                               default_keyword=default_keyword)
        except ex.OptNotFound as exc:
            if required:
                raise ex.RequiredOptNotFound
            else:
                exc.default = copy.copy(self.handle_references(default, scope=scope,
                                                               impersonate=impersonate,
                                                               cd=cd, section=s))
                if t not in (None, "string"):
                    exc.default = self.convert(t, exc.default)
                raise exc
        try:
            val = self.handle_references(val, scope=scope,
                                         impersonate=impersonate,
                                         cd=cd, section=s, stack=stack)
        except ex.Error 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 self.convert(t, val)

    def conf_get_val_unscoped(self, s, o, use_default=True, cd=None, default_keyword=None):
        if cd is None:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd
        try:
            return cd[s][o]
        except KeyError:
            pass
        if s != "DEFAULT" and use_default and self.has_default_section:
            # fallback to default
            return self.conf_get_val_unscoped("DEFAULT", default_keyword,
                                              cd=cd)
        raise ex.OptNotFound("unscoped keyword %s.%s not found." % (s, o))

    def conf_has_option_scoped(self, s, o, nodename=None, cd=None, scope_order=None):
        """
        Handles the keyword scope_order property, at and impersonate
        """
        if s != "DEFAULT":
            try:
                options = cd[s].keys()
            except KeyError:
                return
        else:
            try:
                options = cd["DEFAULT"].keys()
            except KeyError:
                options = []

        prefix = o + "@"
        options = [option for option in options if o == option or option.startswith(prefix)]
        if not options:
            return

        candidates = [
            (o+"@"+nodename, True),
        ]
        if not hasattr(self, "path"):
            if o != "nodes":
                candidates.append((o+"@nodes", nodename in self.cluster_nodes))
            if o != "drpnodes":
                candidates.append((o+"@drpnodes", nodename in self.cluster_drpnodes))
        elif self.path == "cluster":
            if o != "nodes":
                candidates.append((o+"@nodes", nodename in self.node.cluster_nodes))
            if o != "drpnodes":
                candidates.append((o+"@drpnodes", nodename in self.node.cluster_drpnodes))
        else:
            if o != "nodes":
                candidates.append((o+"@nodes", nodename in self.nodes))
            if o != "drpnodes":
                candidates.append((o+"@drpnodes", nodename in self.drpnodes))
            if o != "encapnodes":
                candidates.append((o+"@encapnodes", nodename in self.encapnodes))
            if o != "flex_primary":
                candidates.append((o+"@flex_primary", nodename == self.flex_primary))
            if o != "drp_flex_primary":
                candidates.append((o+"@drp_flex_primary", nodename == self.drp_flex_primary))
        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, cd=None, scope_order=None,
                            default_keyword=None):
        if cd is None:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd
        if impersonate is None:
            nodename = Env.nodename
        else:
            nodename = impersonate

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

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

        try:
            val = cd[s][option]
        except KeyError:
            raise ex.Error("param %s.%s is not set" % (s, o))

        return val

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

    def _validate_config(self, cd=None, path=None):
        """
        The validate config core method.
        Returns a dict with the list of syntax warnings and errors.
        """
        if path:
            cd = self.parse_config_file(path)
        elif not cd:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd

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

        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 = cd.get(section, {}).get(option)
            if not is_string(value) \
                    or ".exposed_devs" in value \
                    or ".base_devs" in value \
                    or ".sub_devs" in value \
                    or re.match(r"volume#.*\.mnt", value):
                return 0
            try:
                deref = self.handle_references(value, scope=True, cd=cd,
                                               section=section)
            except ex.Error 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
            if deref is None:
                self.log.warning("broken reference: %s.%s", section, option)
            return 0

        def get_val(key, section, option, verbose=True, impersonate=None):
            """
            Fetch the value and convert it to the expected type.
            """
            _option = option.split("@")[0]
            value = self.conf_get(section, _option, cd=cd, verbose=verbose, impersonate=impersonate)
            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
            impersonate = None
            if hasattr(self, "encap"):
                if self.encap and section not in self.resources_by_id:
                    # encap node does not validate global resource values
                    return err
                elif not self.encap and section in self.encap_resources:
                    impersonate = list(self.encapnodes)[0]
            try:
                value = get_val(key, section, option, verbose=False, impersonate=impersonate)
            except ValueError as exc:
                self.log.warning(str(exc))
                return 0
            except ex.OptNotFound:
                return 0
            except ex.RequiredOptNotFound:
                if hasattr(self, "path"):
                    # no need to err here: already caught by svc build
                    return err
                else:
                    self.log.error("%s.%s is mandatory" % (section, option))
                    err += 1
                    return err
            err += check_candidates(key, section, option, value)
            return err

        def validate_default_options(ret):
            """
            Validate DEFAULT section options.
            """
            if not self.has_default_section:
                return ret
            for option in cd.get("DEFAULT", {}):
                if option == "comment":
                    continue
                key = self.kwstore.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 cd:
                        family = section.split("#")[0]
                        rtype = self.get_rtype(section, family, cd)
                        if family not in list(self.kwstore.sections.keys()) + \
                           list(self.kwstore.deprecated_sections.keys()):
                            continue
                        if family in self.kwstore.deprecated_sections:
                            results = self.kwstore.deprecated_sections[family]
                            family = results[0]
                        if self.kwstore.sections[family].getkey(option, rtype) is not None:
                            found = True
                            break
                    if not found:
                        self.log.warning("ignored option DEFAULT.%s", option)
                        ret["warnings"] += 1
                elif "DEFAULT."+option in self.kwstore.deprecated_keywords:
                        newkw = self.kwstore.deprecated_keywords["DEFAULT."+option]
                        if newkw is None:
                            self.log.warning("deprecated option DEFAULT.%s", option)
                        else:
                            self.log.warning("deprecated option DEFAULT.%s, use %s", option, newkw)
                        ret["warnings"] += 1
                else:
                    # here we know its a native DEFAULT option
                    ret["errors"] += check_known_option(key, "DEFAULT", option)
            return ret

        def validate_env_references(ret):
            """
            Validate env section options.
            """
            section = "env"
            for option in cd.get(section, {}):
                try:
                    self.conf_get(section, option, cd=cd, stack=[])
                except ex.Error as error:
                    value = cd.get(section, {}).get(option)
                    self.log.error('unable to resolv %s.%s = %s, error: %s', section, option, value, str(error)[:20])
                    ret["errors"] += 1
            return ret

        def validate_resources_options(ret):
            """
            Validate resource sections options.
            """
            for section in cd:
                if section in ("labels", "env", "data"):
                    # the "env" section is not handled by a resource driver, and is
                    # unknown to the kwstore. Just ignore it.
                    continue
                family = section.split("#")[0]
                rtype = self.get_rtype(section, family, cd)
                if family not in ("DEFAULT", "labels", "env", "data", "subset"):
                    try:
                        loader = self.load_driver
                    except AttributeError:
                        pass
                    else:
                        try:
                            loader(family, rtype)
                        except Exception:
                            pass
                if family not in list(self.kwstore.sections.keys()) + list(self.kwstore.deprecated_sections.keys()):
                    self.log.warning("ignored section %s", section)
                    ret["warnings"] += 1
                    continue
                if family in self.kwstore.deprecated_sections:
                    self.log.warning("deprecated section prefix %s", family)
                    ret["warnings"] += 1
                    family, rtype = self.kwstore.deprecated_sections[family]
                for option in cd.get(section, {}):
                    if option == "comment":
                        continue
                    if option in cd.get("DEFAULT", {}):
                        continue
                    key = self.kwstore.sections[family].getkey(option, rtype=rtype)
                    if key is None:
                        key = self.kwstore.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(ret):
            """
            Try a service build to catch errors missed in other tests.
            """
            if not hasattr(self, "path"):
                return ret
            svc = None
            try:
                svc = factory(self.kind)(self.name, namespace=self.namespace,
                                         cd=cd, node=self.node, volatile=True)
            except Exception as exc:
                self.log.error("the new configuration causes the following "
                               "build error: %s", str(exc))
                ret["errors"] += 1
            if svc:
                try:
                    ret["errors"] += svc.init_resources()
                except Exception as exc:
                    self.log.error(exc)
                    ret["errors"] += 1
            return ret

        ret = validate_build(ret)
        ret = validate_default_options(ret)
        ret = validate_env_references(ret)
        ret = validate_resources_options(ret)

        return ret

    def _read_cf(self):
        """
        Return the service config file content.
        """
        if not os.path.exists(self.paths.cf):
            return ""
        with codecs.open(self.paths.cf, "r", "utf8") as ofile:
            buff = ofile.read()
        return buff

    def skip_config_section(self, section):
        return False

    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
        """
        if src_config:
            data = self.parse_config_file(src_config)
        else:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd
            data = type(cd)(cd)
        meta = {}
        if hasattr(self, "namespace"):
            meta.update({
                "name": self.name,
                "kind": self.kind,
                "namespace": self.namespace,
            })
        else:
            meta.update({
                "kind": "node",
            })

        if not evaluate:
            data["metadata"] = meta
            return data
        edata = {}
        for section, _data in data.items():
            if self.skip_config_section(section):
                continue
            edata[section] = {}
            keys = []
            for key in _data:
                key = key.split("@")[0]
                if key in edata[section]:
                    continue
                try:
                    val = self.conf_get(section, key, impersonate=impersonate)
                except (ex.RequiredOptNotFound, ex.OptNotFound):
                    continue
                except ValueError:
                    raise
                # ensure the data is json-exportable
                if isinstance(val, set):
                    val = list(val)
                edata[section][key] = val
        edata["metadata"] = meta
        return edata

    @lazy
    def labels(self):
        try:
            cd = self.private_cd
        except AttributeError:
            cd = self.cd
        data = {}
        try:
            for label, value in cd.get("labels", {}).items():
                try:
                    cd["DEFAULT"][label]
                    continue
                except KeyError:
                    pass
                data[label] = value
        except Exception:
            pass
        return data

    def section_kwargs(self, section, rtype=None):
        kwargs = {}
        try:
            cat = section.split("#")[0]
        except ValueError:
            return kwargs
        for keyword in self.kwstore.all_keys(cat, rtype):
            try:
                kwargs[keyword.protoname] = self.conf_get(section, keyword.keyword, rtype=rtype, verbose=False)
            except ex.RequiredOptNotFound:
                try:
                    if keyword.provisioning and (self.running_action != "provision" or self.oget(section, "provision") == False):
                        continue
                except AttributeError:
                    # not a BaseSvc
                    pass
                self.log.error("%s.%s is mandatory" % (section, keyword.keyword))
                raise
            except ex.OptNotFound as exc:
                kwargs[keyword.protoname] = exc.default
        return kwargs

    def conf_sections(self, cat=None, cd=None):
        if cd is None:
            cd = self.cd
        for section in cd:
            if cat is None or section.startswith(cat+"#"):
                yield section

    def parse_config_file(self, cf=None):
        self.clear_ref_cache()
        if cf is None:
            cf = self.paths.cf
        try:
            config = read_cf(cf)
        except Exception as exc:
            #import traceback
            #traceback.print_stack()
            raise ex.Error("error parsing %s: %s" % (cf, exc))
        try:
            from collections import OrderedDict
            best_dict = OrderedDict
        except ImportError:
            best_dict = dict
        data = best_dict()
        tmp = best_dict()
        defaults = config.defaults()
        for key in defaults.keys():
            tmp[key] = defaults[key]

        if tmp:
            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

        comments = read_cf_comments(cf)
        for section, comments in comments.items():
            if section in data:
                if "comment" not in data[section]:
                    data[section]["comment"] = ""
                else:
                    data[section]["comment"] += "\n"
                data[section]["comment"] += "\n".join(comments)
            else:
                if "DEFAULT" not in data:
                    data["DEFAULT"] = {}
                if "comment" not in data["DEFAULT"]:
                    data["DEFAULT"]["comment"] = ""
                else:
                    data["DEFAULT"]["comment"] += "\n"
                data["DEFAULT"]["comment"] += "\n".join(comments)

        return data

    def is_volatile(self):
        try:
            if self.volatile:
                return True
        except AttributeError:
            pass
        return False

    def commit(self, cd=None, cf=None, validation=True):
        """
        Installs a service configuration file from section, keys and values
        fed from a data structure.
        """
        if cd is None:
            try:
                cd = self.private_cd
            except AttributeError:
                cd = self.cd
        if cf is None:
            cf = self.paths.cf
        if not isinstance(cd, dict):
            return
        if "metadata" in cd:
            del cd["metadata"]
        if hasattr(self, "new_id") and "id" not in cd.get("DEFAULT", {}):
            if "DEFAULT" not in cd:
                cd["DEFAULT"] = {}
            if self.is_volatile():
                cd["DEFAULT"]["id"] = self.new_id()
            else:
                current_id = self.parse_config_file(cf).get("DEFAULT", {}).get("id")
                if current_id:
                    cd["DEFAULT"]["id"] = current_id
                else:
                    cd["DEFAULT"]["id"] = self.new_id()
        if validation:
            ret = self._validate_config()
            if ret["errors"]:
                raise ex.Error

        if not self.is_volatile():
            self.dump_config_data(cd=cd, cf=cf)

        self.clear_ref_cache()
        self.post_commit()

    def post_commit(self):
        """
        Place holder for things to do on the child class instance after a commit.
        """
        pass

    def dump_config_data(self, cd=None, cf=None):
        import tempfile
        import shutil
        if cf is None:
            cf = self.paths.cf
        dirpath = os.path.dirname(cf)
        makedirs(dirpath)
        tmpf = tempfile.NamedTemporaryFile(delete=False, dir=dirpath, prefix=os.path.basename(cf)+".")
        tmpfpath = tmpf.name
        tmpf.close()
        os.chmod(tmpfpath, 0o0600)
        lines = []

        for section_name, section in cd.items():
            lines.append("[%s]" % section_name)
            for key, value in section.items():
                if value is None:
                    continue
                if key != "comment":
                    lines.append("%s = %s" % (key, str(value).replace("\n", "\n\t")))
                else:
                    lines += map(lambda x: "# "+x if x else "", value.split("\n"))
            lines.append("")

        try:
            buff = "\n".join(lines)
            if six.PY2:
                with codecs.open(tmpfpath, "w", "utf-8") as ofile:
                    ofile.write(buff)
            else:
                with open(tmpfpath, "w") as ofile:
                    ofile.write(buff)
            move_config_file(tmpfpath, cf)
        except Exception as exc:
            raise ex.Error("failed to write %s: %s" % (cf, exc))
        finally:
            try:
                os.unlink(tmpfpath)
            except Exception:
                pass
0707010001f1a2000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002b00000000root/usr/share/opensvc/opensvc/core/status    0707010001f1a3000081a40000000000000000000000016a100daf00001930000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/status/__init__.py    """
This module defines the Status class and the functions
to convert a Status to its printable form or integer form.
"""

from utilities.render.color import colorize, color

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, agg_status=None):
    """
    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 == "down" or status in ("err", "error"):
        if agg_status == "up":
            return colorize(fmt % status, color.LIGHTBLUE)
        else:
            return colorize(fmt % status, color.RED)
    elif status == "up" or status == "ok":
        return colorize(fmt % status, color.GREEN)
    elif status == "stdby up":
        if agg_status == "up":
            return colorize(fmt % status, color.LIGHTBLUE)
        else:
            return colorize(fmt % status, color.RED)
    elif status == "stdby down":
        return colorize(fmt % status, color.RED)
    elif status == "n/a":
        return colorize(fmt % status, color.LIGHTBLUE)
    else:
        return colorize(fmt % status, color.LIGHTBLUE)


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)]
0707010001f19c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/core/oc3path   0707010001f19d000081a40000000000000000000000016a100daf00000216000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/core/oc3path/__init__.py   # Defines the API endpoints for OC3

# Server api paths
SERVER_NODE_REGISTER = "/api/auth/node"

# Feeder api paths
FEED_DAEMON_PING = "/api/daemon/ping"
FEED_DAEMON_STATUS = "/api/daemon/status"

FEED_INSTANCE_ACTION = "/api/instance/action"
FEED_INSTANCE_RESINFO = "/api/instance/resource_info"
FEED_INSTANCE_STATUS = "/api/instance/status"

FEED_NODE_DISK = "/api/node/disk"
FEED_NODE_SYSREPORT = "/api/node/sysreport"
FEED_NODE_SYSTEM = "/api/node/system"

FEED_OBJECT_CONFIG = "/api/object/config"

FEED_VERSION = "/api/version"
  0707010001f173000081a40000000000000000000000016a100daf000093d9000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/core/comm.py   """
The daemon encrypted communications primitives.
"""

from __future__ import print_function
import base64
import json
import os
import socket
import threading
import zlib
import time
import select
import sys
from errno import ECONNREFUSED, EPIPE, EBUSY, EALREADY, EAGAIN, ETIMEDOUT


DEFAULT_DAEMON_TIMEOUT = 5


class DummyException(Exception):
    pass

try:
    import ssl
    import foreign.h2.connection
    import foreign.hyper as hyper
    from foreign.hyper.common.headers import HTTPHeaderMap
    SSLWantReadError = ssl.SSLWantReadError
    SSLError = ssl.SSLError
    ssl.HAS_ALPN # stack on Attribute error on py <3.5 and <2.7.10
    has_ssl = True
except Exception:
    # consider py <2.7.9 and <3.4.0 does not have ssl (h2 disabled)
    SSLWantReadError = DummyException
    SSLError = DummyException
    has_ssl = False

import foreign.six as six
import foreign.pyaes as pyaes
from env import Env
from utilities.storage import Storage
from utilities.lazy import lazy
from core.contexts import get_context, want_context
from utilities.string import bdecode
import core.exceptions as ex

if six.PY3:
    def to_bytes(x):
        return bytes(x, "utf-8") if not isinstance(x, bytes) else x
else:
    def to_bytes(x):
        return bytes(x) if not isinstance(x, bytes) else x
    ConnectionResetError = DummyException
    ConnectionRefusedError = DummyException

# add ECONNRESET, ENOTFOUND, ETIMEDOUT, EHOSTUNREACH, ECONNREFUSED, ?
RETRYABLE = (
    EAGAIN,
    EBUSY,
    EPIPE,
    EALREADY,
)
SOCK_TMO_REQUEST = 1.5
SOCK_TMO_STREAM = 6.2
PAUSE = 0.2
PING = ".".encode()

# Number of received misencrypted data messages by senders
BLACKLIST = {}

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

class Headers(object):
    node = "o-node"
    secret = "o-secret"
    multiplexed = "o-multiplexed"

class SockReset(Exception):
    pass


try:
    try:
        from Crypto.Cipher import AES
        from Crypto import __version__ as version
        CRYPTO_MODULE = "pycrypto %s" % version
    except ImportError:
        from Cryptodome.Cipher import AES
        from Cryptodome import __version__ as version
        CRYPTO_MODULE = "pycryptodome %s" % version

    def _encrypt(message, key, _iv):
        """
        Low level encrypter.
        """
        message = pyaes.util.append_PKCS7_padding(
            zlib.compress(message)
        )
        obj = AES.new(to_bytes(key), AES.MODE_CBC, _iv)
        ciphertext = obj.encrypt(message)
        return ciphertext

    def _decrypt(ciphertext, key, _iv):
        """
        Low level decrypter.
        """
        obj = AES.new(to_bytes(key), AES.MODE_CBC, _iv)
        message = obj.decrypt(ciphertext)
        return zlib.decompress(pyaes.util.strip_PKCS7_padding(message))
except ImportError:
    CRYPTO_MODULE = "fallback"

    def _encrypt(message, key, _iv):
        """
        Low level encrypter.
        """
        obj = pyaes.Encrypter(
            pyaes.AESModeOfOperationCBC(to_bytes(key), iv=_iv)
        )
        ciphertext = obj.feed(zlib.compress(message))
        ciphertext += obj.feed()
        return ciphertext

    def _decrypt(ciphertext, key, _iv):
        """
        Low level decrypter.
        """
        obj = pyaes.Decrypter(
           pyaes.AESModeOfOperationCBC(to_bytes(key), iv=_iv)
        )
        message = obj.feed(ciphertext)
        message += obj.feed()
        return zlib.decompress(message)

def get_http2_client_ssl_context(cafile=None, keyfile=None, certfile=None):
    """
    This function creates an SSLContext object that is suitably configured for
    HTTP/2. If you're working with Python TLS directly, you'll want to do the
    exact same setup as this function does.
    """
    # Get the basic context from the standard library.
    ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=cafile)
    if keyfile and certfile:
        ctx.load_cert_chain(keyfile=keyfile, certfile=certfile)
    else:
        ctx.load_default_certs()
    ctx.check_hostname = False

    # RFC 7540 Section 9.2: Implementations of HTTP/2 MUST use TLS version 1.2
    # or higher. Disable TLS 1.1 and lower.
    ctx.options |= (
        ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
    )

    # RFC 7540 Section 9.2.1: A deployment of HTTP/2 over TLS 1.2 MUST disable
    # compression.
    ctx.options |= ssl.OP_NO_COMPRESSION

    # RFC 7540 Section 9.2.2: "deployments of HTTP/2 that use TLS 1.2 MUST
    # support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256". In practice, the
    # blacklist defined in this section allows only the AES GCM and ChaCha20
    # cipher suites with ephemeral key negotiation.
    ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20")

    # We want to negotiate using NPN and ALPN. ALPN is mandatory, but NPN may
    # be absent, so allow that. This setup allows for negotiation of HTTP/1.1.
    ctx.set_alpn_protocols(["h2", "http/1.1"])

    try:
        ctx.set_npn_protocols(["h2", "http/1.1"])
    except (NotImplementedError, AttributeError):
        pass

    return ctx

class Crypt(object):
    """
    A class implement AES encrypt, decrypt and message padding.
    Used by child classes to authenticate senders on data receive.
    """
    def __init__(self):
        self.log = None

    def get_node(self):
        """
        To be redefined by Crypt child classes
        """
        return Storage()

    @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=None, locker=None):
        """
        This is 4x faster than calling os.urandom(16) and prevents
        the "too many files open" issue with concurrent access to os.urandom()
        """
        if urandom is None:
            urandom = []
        try:
            return urandom.pop()
        except IndexError:
            locker = locker or threading.RLock()
            try:
                locker.acquire()
                ur = os.urandom(1024)
                urandom += [ur[i:i + 16] for i in range(16, 1024, 16)]
                return ur[0:16]
            finally:
                locker.release()

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

    @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.
        """
        node = self.get_node()
        if want_context():
            return node._daemon_status()["cluster"]["nodes"]
        nodes = node.oget("cluster", "nodes")

        if nodes:
            if Env.nodename in nodes:
                return nodes
            else:
                nodes.append(Env.nodename)
        else:
            nodes = [Env.nodename]

        from core.objects.ccfg import Ccfg
        svc = Ccfg()
        svc.set_multi(["cluster.nodes=" + " ".join(nodes)], validation=False)
        node.unset_multi(["cluster.nodes"])
        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.
        """
        node = self.get_node()
        if want_context():
            return node._daemon_status()["cluster"].get("drpnodes", [])
        nodes = node.oget("cluster", "drpnodes")
        return nodes

    @lazy
    def cluster_name(self):
        """
        Return the cluster name, read from cluster.name in the node
        configuration. If not set, return "default".
        """
        node = self.get_node()
        try:
            return node.oget("cluster", "name").lower()
        except Exception as exc:
            pass
        name = "default"
        from core.objects.ccfg import Ccfg
        svc = Ccfg()
        svc.set_multi(["cluster.name=" + name], validation=False)
        return name

    @lazy
    def cluster_names(self):
        node = self.get_node()
        names = set([self.cluster_name])
        for nodename in self.cluster_drpnodes:
            names.add(node.oget("cluster", "name", impersonate=nodename))
        return names

    @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.
        """
        node = self.get_node()
        try:
            key = node.oget("cluster", "secret")
            return self.prepare_key(key)
        except Exception as exc:
            pass
        import uuid
        from core.objects.ccfg import Ccfg
        key = uuid.uuid1().hex
        svc = Ccfg()
        svc.set_multi(["cluster.secret="+key], validation=False)
        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.
        """
        node = self.get_node()
        try:
            return node.conf_get("cluster", "id")
        except Exception as exc:
            pass
        import uuid
        from core.objects.ccfg import Ccfg
        cluster_id = str(uuid.uuid1())
        svc = Ccfg()
        svc.set_multi(["cluster.id="+cluster_id], validation=False)
        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, structured=True):
        """
        Validate the message meta, decrypt and return the data.
        """
        if cluster_name is None:
            cluster_names = self.cluster_names
        else:
            cluster_names = [cluster_name]
        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, None
        msg_clustername = message.get("clustername")
        msg_nodename = message.get("nodename")
        if secret is None:
            if msg_nodename in self.cluster_drpnodes:
                cluster_key = self.get_secret(Storage(server=msg_nodename), None)
            else:
                cluster_key = self.cluster_key
        else:
            cluster_key = secret
        if cluster_name != "join" and \
           msg_clustername not in set(["join"]) | self.cluster_names:
            self.log.warning("discard message from cluster %s, sender %s",
                             msg_clustername, sender_id)
            return None, None, None
        if cluster_key is None:
            return None, None, None
        if msg_nodename is None:
            return None, None, None
        iv = message.get("iv")
        if iv is None:
            return None, None, None
        if self.blacklisted(sender_id):
            return None, None, None
        iv = base64.urlsafe_b64decode(to_bytes(iv))
        data = base64.urlsafe_b64decode(to_bytes(message["data"]))
        try:
            data = self._decrypt(data, cluster_key, iv)
        except Exception as exc:
            self.log.error("decrypt message from %s: %s", msg_nodename, str(exc))
            self.blacklist(sender_id)
            return None, None, None
        if sender_id:
            self.blacklist_clear(sender_id)
        if not structured:
            try:
                loaded = json.loads(bdecode(data))
            except ValueError as exc:
                loaded = data
            if not isinstance(loaded, six.text_type):
                loaded = data
            return msg_clustername, msg_nodename, loaded
        try:
            return msg_clustername, msg_nodename, json.loads(bdecode(data))
        except ValueError as exc:
            return msg_clustername, msg_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()
        try:
            data = json.dumps(data).encode()
        except (UnicodeDecodeError, TypeError):
            # already binary data
            pass
        message = {
            "clustername": cluster_name,
            "nodename": Env.nodename,
            "iv": bdecode(base64.urlsafe_b64encode(iv)),
            "data": bdecode(
                base64.urlsafe_b64encode(
                    self._encrypt(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)
        try:
            count = BLACKLIST[sender_id]
        except Exception:
            count = 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)
        try:
            count = BLACKLIST[sender_id]
        except Exception:
            count = 0
        BLACKLIST[sender_id] = count + 1
        if count == BLACKLIST_THRESHOLD:
            getattr(self, "event")(
                "blacklist_add",
                level="warning",
                data={
                    "sender": sender_id,
                }
            )

    def blacklist_clear(self, sender_id=None):
        """
        Clear the senders blacklist.
        """
        global BLACKLIST
        if sender_id is None and BLACKLIST == {}:
            return
        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" % \
                          sender_id)

    @staticmethod
    def get_blacklist():
        """
        Return a copy of the senders blacklist.
        """
        data = {}
        for key in list(BLACKLIST):
            try:
                data[key] = BLACKLIST[key]
            except Exception:
                pass
        return data

    def get_listener_info(self, nodename):
        """
        Get the listener address and port from node.conf.
        """
        node = self.get_node()
        addr = node.oget("listener", "tls_addr", impersonate=nodename)
        port = node.oget("listener", "tls_port", impersonate=nodename)
        if nodename != Env.nodename and addr in ("0.0.0.0", "::", ""):
            addr = nodename
            port = 1214
        return addr, port

    def recv_message(self, *args, **kwargs):
        data = self.recv_messages(*args, **kwargs)
        if data is None:
            return
        return data[0]

    @staticmethod
    def sock_recv(sock, bufsize):
        while True:
            try:
                buff = sock.recv(bufsize)
            except SSLError as exc:
                if exc.errno == ssl.SSL_ERROR_WANT_READ:
                    time.sleep(PAUSE)
                    continue
                raise
            break
        return buff

    def recv_messages(self, sock, cluster_name=None, secret=None,
                      use_select=True, encrypted=True, bufsize=65536,
                      stream=False):
        """
        Receive, decrypt and return a message from a socket.
        """
        sock.setblocking(0)
        messages = []
        chunks = []
        sep = b"\x00"
        while True:
            if use_select:
                ready = select.select([sock], [], [sock], 1)
                if ready[0]:
                    chunk = self.sock_recv(sock, bufsize)
                else:
                    raise socket.timeout
                if ready[2]:
                    break
            else:
                chunk = self.sock_recv(sock, bufsize)
            if not chunk:
                if stream:
                    raise SockReset
                break
            if chunk == sep:
                break
            chunks.append(chunk)
        if six.PY3:
            data = b"".join(chunks)
        else:
            data = "".join(chunks)
        if len(data) == 0:
            return
        for message in data.split(sep):
            if encrypted:
                _, _, message = self.decrypt(
                    data, cluster_name=cluster_name, secret=secret
                )
            else:
                message = self.msg_decode(data)
            messages.append(message)
        return messages

    def get_cluster_context(self):
        context = {}
        context["secret"] = True
        cafile = os.path.join(Env.paths.certs, "ca_certificate_chain")
        if os.path.exists(cafile):
            context["cluster"] = {
                "certificate_authority": cafile,
            }
        keyfile = os.path.join(Env.paths.certs, "private_key")
        certfile = os.path.join(Env.paths.certs, "certificate_chain")
        if os.path.exists(keyfile) and os.path.exists(certfile):
            context["user"] = {
                "client_key": keyfile,
                "client_certificate": certfile,
            }
            #context["secret"] = False
        return context

    def socket_parms_ux(self, server):
        if has_ssl:
            return self.socket_parms_ux_h2(server)
        else:
            return self.socket_parms_ux_raw(server)

    def socket_parms_ux_h2(self, server):
        data = Storage()
        data.scheme = "h2"
        data.af = socket.AF_UNIX
        data.to = Env.paths.lsnruxh2sock
        data.encrypted = False
        data.server = server
        data.context = None
        return data

    def socket_parms_ux_raw(self, server):
        data = Storage()
        data.scheme = "raw"
        data.af = socket.AF_UNIX
        data.to = Env.paths.lsnruxsock
        data.encrypted = False
        data.server = server
        data.context = None
        return data

    def socket_parms_from_context(self, server):
        if not has_ssl:
            raise ex.Error("tls1.2 capable ssl module is required but not available")
        data = Storage()
        context = get_context()
        addr = context["cluster"]["addr"]
        port = context["cluster"]["port"]
        data.context = context
        data.scheme = "h2"
        data.af = socket.AF_INET
        data.to = (addr, port)
        data.encrypted = False
        data.tls = True
        data.server = server
        return data

    def socket_parms_parser(self, server):
        data = Storage()

        # defaults
        port = Env.listener_tls_port
        data.scheme = "h2"
        data.tls = True
        data.encrypted = False
        data.server = server
        data.af = socket.AF_INET
        host = ""

        if server.startswith("https://"):
            host = server[8:]
            data.context = self.get_cluster_context()
        elif server.startswith("raw://"):
            data.tls = False
            data.scheme = "raw"
            data.encrypted = True
            port = Env.listener_port
            host = server[6:]
        elif "://" in server:
            scheme, host = server.split("://", 1)
            raise ex.Error("unknown scheme '%s'. use 'raw' or 'https'" % scheme)

        if not host:
            addr = "localhost"
            data.to = (addr, port)
        elif host[0] == "[":
            try:
                # ipv6 notation
                addr, port = host[1:].split("]:", 1)
                port = int(port)
            except:
                addr = host[1:-1]
            data.to = (addr, port)
        else:
            try:
                addr, port = host.split(":", 1)
                port = int(port)
            except:
                addr = host
            data.to = (addr, port)
        return data

    def socket_parms_inet_raw(self, server):
        data = Storage()
        data.server = server
        addr, port = self.get_listener_info(server)
        data.scheme = "raw"
        data.af = socket.AF_INET
        data.to = (addr, port)
        data.encrypted = True
        data.tls = False
        return data

    def socket_parms(self, server=None):
        if os.environ.get("OSVC_ACTION_ORIGIN") != "daemon" and want_context():
            return self.socket_parms_from_context(server)
        if server is None or server == "":
            return self.socket_parms_ux(server)
        if server == Env.nodename and os.name != "nt":
            # Local comms
            return self.socket_parms_ux(server)
        if server == Env.paths.lsnruxsock:
            return self.socket_parms_ux_raw(server)
        if server == Env.paths.lsnruxh2sock:
            return self.socket_parms_ux_h2(server)
        if ":" in server:
            # Explicit server uri (ex: --server https://1.2.3.4:1215)
            return self.socket_parms_parser(server)
        else:
            # relay, arbitrator, node-to-node
            return self.socket_parms_inet_raw(server)

    def get_http2_client_context(self, sp):
        if not sp.tls:
            return
        try:
            cafile = sp.context["cluster"]["certificate_authority"]
        except:
            cafile = None
        try:
            keyfile = sp.context["user"]["client_key"]
            certfile = sp.context["user"]["client_certificate"]
        except:
            keyfile = None
            certfile = None
        return get_http2_client_ssl_context(
            cafile=cafile,
            keyfile=keyfile,
            certfile=certfile,
        )

    def h2c(self, sp=None, **kwargs):
        context = self.get_http2_client_context(sp)
        if isinstance(sp.to, tuple):
            host = sp.to[0]
            port = sp.to[1]
        else:
            host = sp.to
            port = 0
        conn = hyper.HTTP20Connection(host, port=port, ssl_context=context, secure=sp.tls, **kwargs)
        return conn

    def daemon_get(self, *args, **kwargs):
        kwargs["method"] = "GET"
        return self.daemon_request(*args, **kwargs)

    def daemon_post(self, *args, **kwargs):
        kwargs["method"] = "POST"
        return self.daemon_request(*args, **kwargs)

    def daemon_request(self, *args, **kwargs):
        #print("req", args, kwargs)
        #import traceback
        #traceback.print_stack()
        sp = self.socket_parms(kwargs.get("server"))
        if sp.scheme == "raw" and not want_context():
            return self.raw_daemon_request(*args, sp=sp, **kwargs)
        else:
            return self.h2_daemon_request(*args, sp=sp, **kwargs)

    def get_cluster_name(self, sp, cluster_name):
        if want_context():
            return
        if sp.context and not sp.context.get("clustername"):
            return
        elif cluster_name:
            return cluster_name
        elif sp.server in self.cluster_drpnodes:
            node = self.get_node()
            return node.oget("cluster", "name", impersonate=sp.server)
        else:
            return self.cluster_name

    def get_secret(self, sp, secret):
        if want_context():
            return
        if sp.context and not sp.context.get("secret"):
            return
        elif secret:
            return bdecode(secret)
        elif sp.server in self.cluster_drpnodes:
            node = self.get_node()
            return bdecode(self.prepare_key(node.oget("cluster", "secret", impersonate=sp.server)))
        else:
            return bdecode(self.cluster_key)

    def h2_daemon_request(self, data, server=None, node=None, with_result=True, silent=False,
                          cluster_name=None, secret=None, timeout=None, sp=None, method="GET"):
        secret = self.get_secret(sp, secret)
        path = self.h2_path_from_data(data)
        headers = self.h2_headers(node=node, secret=secret, multiplexed=data.get("multiplexed"), af=sp.af)
        body = self.h2_body_from_data(data)
        headers.update({"Content-Length": str(len(body))})
        conn = self.h2c(sp=sp, timeout=timeout)
        elapsed = 0
        while True:
            try:
                conn.request(method, path, headers=headers, body=body)
                break
            except AssertionError as exc:
                raise ex.Error(str(exc))
            except ConnectionResetError:
                return {"status": 1, "error": "%s %s connection reset"%(method, path)}
            except (ConnectionRefusedError, ssl.SSLError, socket.error) as exc:
                try:
                    errno = exc.errno
                except AttributeError:
                    errno = None
                if errno in RETRYABLE and \
                   (timeout == 0 or (timeout and elapsed < timeout)):
                    # Resource temporarily unavailable (busy, overflow)
                    # Retry after a delay, if the daemon is still
                    # running and timeout is not exhausted
                    time.sleep(PAUSE)
                    elapsed += PAUSE
                    continue
                return {"status": 1, "error": "%s"%exc, "errno": errno}
        resp = conn.get_response()
        data = resp.read()
        data = json.loads(bdecode(data))
        return data

    def raw_daemon_request(self, data, server=None, node=None, with_result=True, silent=False,
                           cluster_name=None, secret=None, timeout=None, sp=None, method="GET"):
        """
        Send a request to the daemon running on server and return the result
        fetched if with_result is set.
        """
        elapsed = 0
        sock = None
        if server is None or server == "":
            server = Env.nodename
        if node:
            data["node"] = node
        data["method"] = method
        progress = "connecting"
        try:
            while True:
                try:
                    sp = self.socket_parms(server)
                    secret = self.get_secret(sp, secret)
                    cluster_name = self.get_cluster_name(sp, cluster_name)
                    if sp.af == socket.AF_UNIX:
                        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                        sock.settimeout(SOCK_TMO_REQUEST)
                        sock.connect(sp.to)
                    else:
                        sock = socket.create_connection(sp.to, SOCK_TMO_REQUEST)
                    break
                except socket.timeout:
                    elapsed += SOCK_TMO_REQUEST + PAUSE
                    if timeout == 0 or (timeout and elapsed >= timeout):
                        if with_result:
                            return {
                                "status": 1,
                                "err": "timeout daemon request (connect error)",
                            }
                        else:
                            raise
                    time.sleep(PAUSE)
                    continue
                except socket.error as exc:
                    if exc.errno == ETIMEDOUT:
                        elapsed += SOCK_TMO_REQUEST + PAUSE
                        if timeout == 0 or (timeout and elapsed >= timeout):
                            if with_result:
                                return {
                                    "status": 1,
                                    "err": "timeout daemon request (connect error)",
                                }
                            else:
                                raise
                        time.sleep(PAUSE)
                        continue
                    elif exc.errno in RETRYABLE and \
                       (timeout == 0 or (timeout and elapsed < timeout)):
                        # Resource temporarily unavailable (busy, overflow)
                        # Retry after a delay, if the daemon is still
                        # running and timeout is not exhausted
                        sock.close()
                        time.sleep(PAUSE)
                        elapsed += PAUSE
                        continue
                    raise

            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",
                }

            progress = "sending"
            sock.sendall(message)

            if with_result:
                progress = "receiving"
                elapsed = 0
                while True:
                    try:
                        return self.recv_message(
                            sock, cluster_name=cluster_name, secret=secret,
                            encrypted=sp.encrypted
                        )
                    except socket.timeout:
                        elapsed += SOCK_TMO_REQUEST + PAUSE
                        if timeout == 0 or (timeout and elapsed >= timeout):
                            return {
                                "status": 1,
                                "err": "timeout daemon request (recv_message error)",
                            }
                        time.sleep(PAUSE)
        except socket.error as exc:
            if not silent:
                self.log.error("%s comm error while %s: %s",
                               sp.to, progress, str(exc))
            return {
                "status": 1,
                "error": str(exc),
                "errno": exc.errno,
                "retryable": exc.errno in RETRYABLE,
            }
        finally:
            if sock:
                sock.close()
        return {"status": 0}

    @staticmethod
    def h2_path_from_data(data):
        return "/" + data.get("action", "").lstrip("/")

    def h2_headers(self, node=None, secret=None, multiplexed=None, af=None):
        headers = HTTPHeaderMap()
        if node:
            if isinstance(node, (tuple, list, set)):
                for n in node:
                    headers.update({Headers.node: n})
            else:
                headers.update({Headers.node: node})
        if secret and af != socket.AF_UNIX:
            headers.update({Headers.secret: secret})
        if multiplexed:
            headers.update({Headers.multiplexed: "true"})
        return headers

    @staticmethod
    def h2_body_from_data(data):
        return json.dumps(data.get("options", {})).encode()

    def daemon_stream(self, *args, **kwargs):
        sp = self.socket_parms(kwargs.get("server"))
        if sp.scheme == "h2":
            iterator = self.h2_daemon_stream
        else:
            iterator = self.raw_daemon_stream
        for e in iterator(*args, sp=sp, **kwargs):
            yield e

    def h2_daemon_stream(self, *args, **kwargs):
        while True:
            try:
                for msg in self._h2_daemon_stream(*args, **kwargs):
                    yield msg
            except hyper.common.exceptions.ConnectionResetError:
                time.sleep(PAUSE)
            except socket.error as exc:
                if exc.errno == ECONNREFUSED:
                    time.sleep(PAUSE)
                else:
                    raise

    def _h2_daemon_stream(self, *args, **kwargs):
        stream_id, conn, resp = self.h2_daemon_stream_conn(*args, **kwargs)
        while True:
            for msg in self.h2_daemon_stream_fetch(stream_id, conn):
                yield msg
            conn._recv_cb(stream_id=stream_id)

    def h2_daemon_stream_conn(self, data, server=None, node=None, cluster_name=None, secret=None, sp=None):
        secret = self.get_secret(sp, secret)
        path = self.h2_path_from_data(data)
        headers = self.h2_headers(node=node, secret=secret, multiplexed=data.get("multiplexed"), af=sp.af)
        body = self.h2_body_from_data(data)
        headers.update({"Content-Length": str(len(body))})
        conn = self.h2c(sp=sp, enable_push=True)
        stream_id = conn.request("GET", path, headers=headers, body=body) 
        #data = resp.read()
        resp = conn.get_response(stream_id)
        return stream_id, conn, resp

    def h2_daemon_stream_fetch(self, stream_id, conn):
        resps = []
        for push in conn.get_pushes(stream_id):
            resps.append(push.get_response())
        for resp in resps:
            # resp.read() can modify push.promises_headers, which get_pushes iterates
            # causing a RuntimeError => keep in a separate loop
            evt = resp.read()
            evt = json.loads(bdecode(evt))
            yield evt

    def raw_daemon_stream(self, data, server=None, node=None, cluster_name=None,
                              secret=None, sp=None):
        """
        Send a request to the daemon running on server and yield the results
        fetched if with_result is set.
        """
        if node:
            data["node"] = node
        data["method"] = "GET"
        sp = self.socket_parms(server)
        try:
            if sp.af == socket.AF_UNIX:
                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                sock.settimeout(SOCK_TMO_STREAM)
                sock.connect(sp.to)
            else:
                sock = socket.create_connection(sp.to, SOCK_TMO_STREAM)
            if sp.encrypted:
                message = self.encrypt(data, cluster_name=cluster_name,
                                       secret=secret)
            else:
                message = self.msg_encode(data)
            if message is None:
                raise StopIteration()
            sock.sendall(message)
            while True:
                try:
                    data = self.recv_messages(
                        sock, cluster_name=cluster_name, secret=secret,
                        encrypted=sp.encrypted, bufsize=1
                    )
                    if data is None:
                        raise StopIteration()
                    for message in data:
                        yield message
                except socket.timeout:
                    time.sleep(PAUSE)
        except socket.error as exc:
            self.log.error("daemon send to %s error: %s", sp.to, str(exc))
        finally:
            sock.close()

    @staticmethod
    def parse_result(data):
        """
        Extract status and formatted errors from a daemon_get() result.
        Return a (<status>, <error string>) tuple.

        * data format 1:

        {
            "status": 1,
            "error": "foo"
        }

        * data format 2:

        {
            "status": 1,
            "error": ["foo", "bar"]
        }

        * data format 3: multiplexed request

        {
            "status": 1,
            "nodes": {
                "node1": {
                    "error": ["foo", "bar"]
                }
            }
        }
        """
        def _fmt(_data, key, node=None):
            _buff = ""
            if not _data or not isinstance(_data, dict):
                return _buff
            entries = _data.get(key, [])
            if not isinstance(entries, (list, tuple, set)):
                entries = [entries]
            for entry in entries:
                if node:
                    _buff += "%s: %s\n" % (node, entry)
                else:
                    _buff += "%s\n" % entry
            return _buff

        if data is None:
            return 1, "no data in response", ""
        if not isinstance(data, dict):
            return 1, "unstructured data", ""
        status = data.get("status", 0)
        if "nodes" in data:
            error = ""
            info = ""
            for node, ndata in data["nodes"].items():
                error += _fmt(ndata, "error", node)
                info += _fmt(ndata, "info", node)
            return status, error.rstrip(), info.rstrip()
        error = _fmt(data, "error")
        info = _fmt(data, "info")
        traceback = data.get("traceback")
        if traceback:
            print("Server "+traceback, file=sys.stderr)
        return status, error.rstrip(), info.rstrip()
   0707010001f178000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/core/exceptions    0707010001f179000081a40000000000000000000000016a100daf00001066000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/core/exceptions/__init__.py    class OsvcException(Exception):
    pass

class HTTP(OsvcException):
    def __init__(self, status, msg=""):
        self.status = status
        self.msg = msg
    def __str__(self):
        return "status %s: %s" % (self.status, self.msg)

class EncapUnjoinable(OsvcException):
    pass

class Error(OsvcException):
    """ Failed action
    """
    def __init__(self, value=""):
        self.value = value
    def __str__(self):
        return str(self.value)

class ExecError(Error):
    """ Failed command exec
    """
    def __init__(self, value="", exitcode=0):
        self.value = value
        self.exitcode = exitcode
    def __str__(self):
        return "%s [%d]" % (self.value, self.exitcode)

class Version(OsvcException):
    """ propagate the version string
    """
    def __init__(self, value=""):
        self.value = value
    def __str__(self):
        return str(self.value)

class TimeOut(OsvcException):
    pass

class AlreadyDone(OsvcException):
    pass

class OptNotFound(OsvcException):
    """
    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(OsvcException):
    """
    Required service config file option not found.
    Raised by Svc::conf_get().
    """
    pass

class Signal(OsvcException):
    """ Termination signal received
    """

class Undefined(OsvcException):
    """ 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(OsvcException):
    """ No nodes to sync => abort stacked syncs resource actions
    """

class syncNoFilesToSync(OsvcException):
    """ No files to sync => move on to the next stacked syncs resource actions
    """

class syncConfigSyntaxError(OsvcException):
    """ Bogus configuration syntax => abort all
    """

class syncNotSnapable(OsvcException):
    """ A dir/file specified as source of a sync with snap is not included in a
        snapable resource mount => abort all
    """

class syncSnapExists(OsvcException):
    """ The snapshot already exists
    """

class syncSnapCreateError(OsvcException):
    """ Error in snapshot creation => clean up
    """

class syncSnapDestroyError(OsvcException):
    """ Error in snapshot destroy => clean up
    """

class syncSnapMountError(OsvcException):
    """ Error mounting fs => clean up
    """

class EndAction(OsvcException):
    """ End multi-resource action. Not an error.
    """

class ContinueAction(OsvcException):
    """ Abort current resource action, but proceed anyway
    """

class AbortAction(OsvcException):
    """ Abort multi-resource action
    """

class InitError(OsvcException):
    """ Resource initialisation error
    """

class ScsiPrNotsupported(OsvcException):
    """ Scsi persistent reservation is not supported
    """

class NotAvailable(OsvcException):
    """ Not available
    """

class NotSupported(OsvcException):
    """ Not supported
    """

class MissImpl(OsvcException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpDevDown(OsvcException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpConflict(OsvcException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpAlreadyUp(OsvcException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)

class IpNoActions(OsvcException):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)
  0707010001f174000081a40000000000000000000000016a100daf00007355000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/core/compliance.py from __future__ import print_function

import os
import sys
import re
import datetime
import json
from stat import *
from subprocess import *

import foreign.six as six
import core.exceptions as ex
from env import Env
from utilities.storage import Storage
from utilities.naming import ANSI_ESCAPE
from utilities.fcache import fcache
from utilities.render.banner import banner
from utilities.render.color import color, colorize, formatter
from utilities.proc import is_exe
from utilities.string import is_string

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

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
        self.rset_md5 = ""
        self.ruleset = None
        self.context = None
        self.options = Storage()

        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.InitError('module %s not found in %s'%(name, comp_dir))
        if len(match) > 1:
            raise ex.Error('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.Error('%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 = ANSI_ESCAPE.sub('', s)
        if six.PY3:
            return s
        else:
            return s.decode('utf8', 'ignore')

    def log_action(self, out, ret, action):
        vals = [Env.nodename,
                self.name,
                str(ret),
                self.strip_unprintable(out),
                action,
                self.rset_md5]
        if self.context.svc:
            vals.append(self.context.svc.path)
        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 Env.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 Env.paths.pathbin != "/usr/bin":
            os.environ["PATH"] = os.environ["PATH"] + ":" + Env.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": Env.paths.pathetc,
          "OSVC_PATH_VAR": Env.paths.pathvar,
          "OSVC_PATH_COMP": Env.paths.pathcomp,
          "OSVC_PATH_TMP": Env.paths.pathtmp,
          "OSVC_PATH_LOG": Env.paths.pathlog,
          "OSVC_NODEMGR": Env.paths.nodemgr,
          "OSVC_SVCMGR": Env.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:
            os.environ[self.context.format_rule_var("SVC_NAME")] = self.context.format_rule_val(self.context.svc.name)
            os.environ[self.context.format_rule_var("SVC_PATH")] = self.context.format_rule_val(self.context.svc.path)
            if self.context.svc.namespace:
                os.environ[self.context.format_rule_var("SVC_NAMESPACE")] = self.context.format_rule_val(self.context.svc.namespace)
            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 = ''
        rets = set()

        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, Env.python_cmd + [obj, self.context.format_rule_var(var)])
                rets.add(_ret)
                log += _log
                if action == "fix" and _ret not in (0, 2):
                    # stop at frist error in a 'fix' action
                    break

        self.reset_env()
        if rets == set([0]) or rets == set():
            ret = 0
        elif rets == set([0, 2]):
            ret = 0
        elif rets == set([2]):
            ret = 2
        else:
            ret = 1
        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.Error("%s execution error (File not found or bad interpreter)"%cmd[0])
            elif e.errno == 8:
                raise ex.Error("%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, "path"):
            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.Error('--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.Error(str(e))
            if self.data is None:
                raise ex.Error("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.Error(self.data["msg"])
                raise ex.Error("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.Error("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.InitError 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 six.PY2:
                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.path)
        else:
            moduleset = self.node.collector.call('comp_get_data_moduleset')
        if moduleset is None:
            raise ex.Error('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.path)
        else:
            ruleset = self.node.collector.call('comp_get_ruleset')
        if ruleset is None:
            raise ex.Error('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.Error('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.path,
                                            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.Error('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.Error('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.path, 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.Error()

    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.path, 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.Error()

    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.path, 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.Error()

    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.path, 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.Error()

    @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.path)
        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
        if isinstance(l, dict) and l.get("ret", 0) != 0:
            raise ex.Error(l.get("msg", ""))
        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


   0707010001f17e000081a40000000000000000000000016a100daf00004a46000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/core/network.py    from __future__ import print_function
import json
import os
import re
import socket

import core.exceptions as ex
import foreign.six as six

from env import Env
from utilities.files import makedirs
from utilities.lazy import lazy
from utilities.net.ipaddress import ip_network, ip_address, summarize_address_range
from utilities.render.color import formatter


DRIVERS = {
    "routed_bridge": "RoutedBridge",
    "bridge": "Bridge",
    "loopback": "Loopback",
}

class NetworksMixin(object):

    ##########################################################################
    #
    # Exposed actions
    #
    ##########################################################################

    @formatter
    def network_ls(self):
        nets = self.networks_data()
        if self.options.format in ("json", "flat_json"):
            return nets
        print("\n".join([net for net in nets]))

    @formatter
    def network_show(self):
        data = {}
        for name, netdata in self.networks_data().items():
            if self.options.name and name != self.options.name:
                continue
            data[name] = netdata
        if self.options.format in ("json", "flat_json"):
            return data
        if not data:
            return
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        tree.load(data, title="networks")
        print(tree)

    def network_setup(self):
        data = self.networks_data()
        names = [name for name in data]
        if "default" not in names:
            names.append("default")
        for name in names:
            try:
                self.network_overlaps(name, data)
            except ex.Error as exc:
                self.log.warning("skip setup: %s", exc)
                continue
            self.network_create_config(name)
            self.network_create_bridge(name)
            self.network_create_routes(name)
            self.network_create_fwrules(name)

    @formatter
    def network_status(self):
        data = self.network_status_data(self.options.name)
        if self.options.format in ("json", "flat_json"):
            return data
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        head = tree.add_node()
        head.add_column("name", color.BOLD)
        head.add_column("type", color.BOLD)
        head.add_column("network", color.BOLD)
        head.add_column("size", color.BOLD)
        head.add_column("used", color.BOLD)
        head.add_column("free", color.BOLD)
        head.add_column("pct", color.BOLD)
        for name in sorted(data):
            ndata = data[name]
            net_node = head.add_node()
            net_node.add_column(name, color.BROWN)
            net_node.add_column(data[name]["type"])
            net_node.add_column(data[name]["network"])
            net_node.add_column("%d" % data[name]["size"])
            net_node.add_column("%d" % data[name]["used"])
            net_node.add_column("%d" % data[name]["free"])
            net_node.add_column("%.2f%%" % data[name]["pct"])
            if not self.options.verbose:
                continue
            ips_node = net_node.add_node()
            ips_node.add_column("ip", color.BOLD)
            ips_node.add_column("node", color.BOLD)
            ips_node.add_column("service", color.BOLD)
            ips_node.add_column("resource", color.BOLD)
            for ip in sorted(ndata.get("ips", []), key=lambda x: (x["ip"], x["node"], x["path"], x["rid"])):
                ip_node = ips_node.add_node()
                ip_node.add_column(ip["ip"])
                ip_node.add_column(ip["node"])
                ip_node.add_column(ip["path"])
                ip_node.add_column(ip["rid"])
        print(tree)

    ##########################################################################
    #
    # Internal methods
    #
    ##########################################################################

    @lazy
    def cni_config(self):
        try:
            return self.conf_get("cni", "config").rstrip("/")
        except ex.OptNotFound as exc:
            return exc.default

    def network_data(self, id, nets=None):
        if nets is None:
            nets = self.networks_data()
        if id:
            return nets[id]
        else:
            return nets

    def networks_data_from_cni_confs(self):
        import glob
        nets = {}
        for cf in glob.glob(self.cni_config+"/*.conf"):
            try:
                with open(cf, "r") as ofile:
                    data = json.load(ofile)
            except ValueError:
                data = {}
            if data.get("type") == "portmap":
                continue
            name = os.path.basename(cf)
            name = re.sub(".conf$", "", name)
            nets[name] = {
                "cni": {
                    "cf": cf,
                    "mtime": os.path.getmtime(cf),
                    "data": data,
                },
                "config": {
                    "type": "undef",
                    "network": "undef",
                },
            }
        return nets

    def networks_data(self):
        nets = self.networks_data_from_cni_confs()
        sections = list(self.conf_sections("network"))
        if "network#default" not in sections:
            sections.append("network#default")
        for section in sections:
            _, name = section.split("#", 1)
            config = {}
            config["type"] = self.oget(section, "type")
            for key in self.section_kwargs(section, config["type"]):
                config[key] = self.oget(section, key, rtype=config["type"])
                if config["type"] == "routed_bridge":
                    config["subnets"] = self.oget_scopes(section, "subnet", rtype=config["type"])
                    config["gateway"] = self.oget_scopes(section, "gateway", rtype=config["type"])
                    config["tunnel_mode"] = self.oget(section, "tunnel_mode", rtype=config["type"])
            if not config:
                continue
            if config.get("network") in ("None", "none", None):
                continue
            routes = self.routes(name, config)
            if config["type"] == "routed_bridge" and not any(config["subnets"][n] for n in config["subnets"]):
                self.log.info("initial %s routed_bridge network subnets assignment:", name)
                kws = []
                for route in routes:
                    kw = "network#%s.subnet@%s=%s" % (name, route["node"], route["dst"])
                    kws.append(kw)
                    self.log.info(" %s", kw)
                from core.objects.ccfg import Ccfg
                svc = Ccfg()
                svc.set_multi(kws, validation=False)
                self.unset_lazy("cd")
                config["subnets"] = self.oget_scopes(section, "subnet", rtype="routed_bridge")
                config["gateway"] = self.oget_scopes(section, "gateway", rtype="routed_bridge")
            if name not in nets:
                nets[name] = {}
            nets[name]["config"] = config
            nets[name]["routes"] = routes
            nets[name]["tables"] = self.tables(name)
        nets["lo"] = {
            "config": {
                "type": "loopback",
                "network": "127.0.0.1/32",
                "tables": ["main"],
            },
        }
        return nets

    def node_subnet(self, name, nodename=None, config=None):
        if nodename is None:
            nodename = Env.nodename
        if not config:
            config = self.network_data(name)["config"]
        persistent_subnet = config.get("subnets", {}).get(nodename)
        if persistent_subnet:
            return ip_network(six.text_type(persistent_subnet))
        idx = self.cluster_nodes.index(nodename)
        network = config["network"]
        ips_per_node = config["ips_per_node"]
        ips_per_node = 1 << (ips_per_node - 1).bit_length()
        net = ip_network(six.text_type(network))
        first = net[0] + (idx * ips_per_node)
        last = first + ips_per_node - 1
        subnet = next(summarize_address_range(first, last))
        return subnet

    def tables(self, name):
        try:
            return self.oget("network#"+name, "tables")
        except:
            return

    def find_node_ip(self, nodename, af=socket.AF_INET):
        try:
            data = socket.getaddrinfo(nodename, None)
        except socket.gaierror:
            raise ex.Error("node %s is not resolvable" % nodename)
        for d in data:
            _af, _, _, _, addr = d
            if _af != af:
                continue
            addr = addr[0]
            if addr in ("127.0.0.1", "127.0.1.1", "::1") or addr.startswith("fe80:"):
                continue
            return addr
        if af == socket.AF_INET:
            afs = "ipv4"
        elif af == socket.AF_INET6:
            afs = "ipv6"
        else:
            afs = "%s" % af
        raise ex.Error("node %s has no %s address" % (nodename, afs))

    def routes(self, name, config=None):
        routes = []
        if not config:
            config = self.network_data(name)["config"]
        ntype = config["type"]
        if ntype != "routed_bridge":
            return routes
        network = config.get("network")
        if not network:
            return []
        if ":" in network:
            af = socket.AF_INET6
        else:
            af = socket.AF_INET
        try:
            local_ip = self.oget("network#"+name, "addr")
        except ValueError:
            local_ip = None
        if local_ip is None:
            try:
                local_ip = self.find_node_ip(Env.nodename, af=af)
            except ex.Error as exc:
                self.log.warning("%s", exc)
                return routes

        def get_gw(nodename, af=socket.AF_INET):
            gw = config.get("gateway", {}).get(nodename)
            if gw:
                return gw
            gw = self.find_node_ip(nodename, af=af)
            return gw

        for nodename in self.cluster_nodes:
            for table in config["tables"]:
                if nodename == Env.nodename:
                    routes.append({
                        "node": nodename,
                        "dst": str(self.node_subnet(name, nodename, config=config)),
                        "dev": "obr_"+name,
                        "brdev": "obr_"+name,
                        "table": table,
                    })
                    continue
                try:
                    gw = get_gw(nodename, af=af)
                except socket.gaierror:
                    self.log.warning("node %s is not resolvable", nodename)
                    continue
                routes.append({
                    "local_ip": local_ip,
                    "node": nodename,
                    "dst": str(self.node_subnet(name, nodename, config=config)),
                    "gw": gw,
                    "brdev": "obr_"+name,
                    "brip": self.network_bridge_ip(name, config=config),
                    "table": table,
                    "tunnel": config["tunnel"],
                    "tunnel_mode": config.get("tunnel_mode", ""),
                })
        return routes

    def network_overlaps(self, name, nets=None):
        def get_val(key, net):
            try:
                return net["config"][key]
            except KeyError:
                try:
                    return net["cni"]["data"][key]
                except KeyError:
                    return
        if nets is None:
            nets = self.networks_data()
        net = nets.get(name)
        if not net:
            return
        try:
            network = ip_network(six.text_type(get_val("network", net)))
        except Exception:
            return
        for other_name, other in nets.items():
            if name == other_name:
                continue
            try:
                other_network = ip_network(six.text_type(get_val("network", other)))
            except Exception:
                continue
            if other_network and network.overlaps(other_network):
                raise ex.Error("network %s %s overlaps with %s %s" % \
                                  (name, network, other_name, other_network))

    def network_create_fwrules(self, name):
        """
        OS specific
        """
        pass

    def network_bridge_ip(self, name, config=None):
        net = self.node_subnet(name, config=config)
        ip = str(net[1])+"/"+str(net.prefixlen)
        return ip

    def network_create_bridge(self, name, nets=None):
        data = self.network_data(name, nets=nets)
        ntype = data["config"]["type"]
        if ntype != "routed_bridge":
            return
        ip = self.network_bridge_ip(name, config=data["config"])
        self.network_bridge_add("obr_"+name, ip)

    def network_bridge_add(self, *args, **kwargs):
        """
        OS specific
        """
        pass

    def network_create_routes(self, name):
        routes = self.routes(name)
        for route in routes:
            self.network_route_add(**route)

    def network_route_add(self, *args, **kwargs):
        """
        OS specific
        """
        pass

    def network_create_config(self, name="default", nets=None):
        try:
            data = self.network_data(name, nets=nets)
        except KeyError:
            raise ex.Error("network %s does not exist" % name)
        ntype = data["config"]["type"]
        fn = "network_create_%s_config" % ntype
        if hasattr(self, fn):
            getattr(self, fn)(name, nets=nets)

    def network_create_weave_config(self, name="default", nets=None):
        cf = os.path.join(self.cni_config, name+".conf")
        if os.path.exists(cf):
            return
        self.log.info("create %s", cf)
        data = self.network_data(name, nets=nets)
        network = data["config"]["network"]
        conf = {
            "cniVersion": "0.3.0",
            "name": name,
            "type": "weave-net",
            "ipam": {
                "subnet": network,
            },
        }
        makedirs(self.cni_config)
        with open(cf, "w") as ofile:
            json.dump(conf, ofile, indent=4)

    def network_create_routed_bridge_config(self, name="default", nets=None):
        config = self.network_data(name, nets=nets)["config"]
        subnet = str(self.node_subnet(name, config=config))
        network = config["network"]
        brip = self.network_bridge_ip(name, config=config).split("/")[0]
        cf = os.path.join(self.cni_config, name+".conf")
        if os.path.exists(cf):
            return
        if ":" in network:
            default = "::/0"
        else:
            default = "0.0.0.0/0"
        self.log.info("create %s", cf)
        conf = {
            "cniVersion": "0.3.0",
            "name": name,
            "type": "bridge",
            "bridge": "obr_"+name,
            "isGateway": True,
            "ipMasq": False,
            "ipam": {
                "type": "host-local",
                "subnet": subnet,
                "routes": [
                    { "dst": default },
                    { "dst": network, "gw": brip },
                ]
            }
        }
        makedirs(self.cni_config)
        with open(cf, "w") as ofile:
            json.dump(conf, ofile, indent=4)

    def network_create_bridge_config(self, name="default", nets=None):
        cf = os.path.join(self.cni_config, name+".conf")
        if os.path.exists(cf):
            return
        self.log.info("create %s", cf)
        conf = {
            "cniVersion": "0.3.0",
            "name": name,
            "type": "bridge",
            "bridge": "obr_"+name,
            "isGateway": True,
            "ipMasq": True,
            "ipam": {
                "type": "host-local",
                "routes": [
                    { "dst": "0.0.0.0/0" }
                ]
            }
        }
        makedirs(self.cni_config)
        data = self.network_data(name, nets=nets)
        network = data["config"]["network"]
        conf["ipam"]["subnet"] = network
        with open(cf, "w") as ofile:
            json.dump(conf, ofile, indent=4)

    def network_create_loopback_config(self, name="lo", nets=None):
        cf = os.path.join(self.cni_config, name+".conf")
        if os.path.exists(cf):
            return
        self.log.info("create %s", cf)
        conf = {
            "cniVersion": "0.3.0",
            "name": name,
            "type": "loopback",
        }
        makedirs(self.cni_config)
        data = self.network_data(name, nets=nets)
        try:
            network = data["config"]["network"]
            conf["ipam"]["subnet"] = network
        except KeyError:
            pass
        with open(cf, "w") as ofile:
            json.dump(conf, ofile, indent=4)

    def network_ip_data(self):
        data = []
        try:
            cdata = self._daemon_status(silent=True).get("monitor", {}).get("nodes", {})
        except Exception:
            cdata = {}
        for nodename, node in cdata.items():
            for path, sdata in node.get("services", {}).get("status", {}).items():
                for rid, rdata in sdata.get("resources", {}).items():
                    ip = rdata.get("info", {}).get("ipaddr")
                    if not ip:
                        continue
                    data.append({
                        "ip": ip,
                        "node": nodename,
                        "path": path,
                        "rid": rid,
                    })
        return data

    def network_status_data(self, name=None):
        data = {}
        nets = self.networks_data()
        ipdata = self.network_ip_data()
        for _name, ndata in nets.items():
            if name and name != _name:
                continue
            try:
                network = ip_network(six.text_type(ndata["config"]["network"]))
                _data = {
                    "type": ndata["config"]["type"],
                    "network": ndata["config"]["network"],
                    "size": network.num_addresses,
                    "ips": [],
                }
            except Exception:
                network = None
                _data = {
                    "type": ndata["config"]["type"],
                    "network": ndata["config"]["network"],
                    "size": 1,
                    "ips": [],
                }
            for idata in ipdata:
                ip = ip_address(idata["ip"])
                if not network or ip not in network:
                    continue
                _data["ips"].append(idata)
            _data["used"] = len(_data["ips"])
            _data["free"] = _data["size"] - _data["used"]
            _data["pct"] = 100 * _data["used"] / _data["size"]
            data[_name] = _data
        return data


  0707010001f1a1000081a40000000000000000000000016a100daf00009c15000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/core/scheduler.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 core.exceptions as ex
from env import Env
from utilities.storage import Storage
from utilities.render.color import formatter, color
from utilities.string import is_string
from utilities.converters import convert_duration

# ISO-8601 weeks. week one is the first week with thursday in year

SCHED_FMT = "%s: %s"
ALL_MONTHS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
ALL_WEEKDAYS = [1, 2, 3, 4, 5, 6, 7]
ALL_DAYS = [{"weekday": wd} for wd in ALL_WEEKDAYS]
ALL_WEEKS = list(range(1, 54))
DAY_SECONDS = 24 * 60 * 60
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": 1,
    "tue": 2,
    "wed": 3,
    "thu": 4,
    "fri": 5,
    "sat": 6,
    "sun": 7,
    "monday": 1,
    "tuesday": 2,
    "wednesday": 3,
    "thursday": 4,
    "friday": 5,
    "saturday": 6,
    "sunday": 7,
}

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.
    """
    def __init__(self, message="", until=0):
        self.message = message
        self.until = until

class SchedOpts(object):
    """
    The class storing a task schedule options.
    """
    def __init__(self, section,
                 fname=None,
                 schedule_option="push_schedule",
                 req_collector=False):
        self.section = section
        self.fname = fname
        self.req_collector = req_collector
        if self.fname is None:
            self.fname = "last_"+section+"_push"
        self.schedule_option = schedule_option

    def __str__(self):
        return "<SchedOpts section=%s fname=%s schedule_option=%s>" % (
            self.section,
            self.fname,
            self.schedule_option,
        )

    __repr__ = __str__

def to_time(dt):
    return time.mktime(dt.timetuple()) + dt.microsecond / 1E6


def trace_sched_action():
    """
    Create a trace flag from OSVC_SCHED_FLAG env var
    This trace flag can be used from daemon scheduler to ensure crm task has been launched
    """
    flag_launched = os.environ.get("OSVC_SCHED_FLAG")
    if not flag_launched:
        return
    try:
        last = datetime.datetime.fromtimestamp(float(os.environ["OSVC_SCHED_TIME"]))
    except Exception:
        last = str(datetime.datetime.now())
    timestamp(flag_launched, last=last)


def timestamp(timestamp_f, last=None):
    """
    Update the timestamp file <timestamp_f>.
    Create missing parent directories if needed.
    """
    if last is None:
        last = time.time()
    elif type(last) == datetime.datetime:
        last = to_time(last)
    buff = repr(last)
    try:
        with open(timestamp_f, 'w') as ofile:
            ofile.write(buff+os.linesep)
    except (OSError, IOError):
        timestamp_d = os.path.dirname(timestamp_f)
        try:
            os.makedirs(timestamp_d, 0o755)
        except (OSError, IOError):
            # another // call may have created this dir
            # ignore error, and try again write file
            pass
        with open(timestamp_f, 'w') as ofile:
            ofile.write(buff+os.linesep)


def sched_action(func):
    """
    A decorator in charge of updating the scheduler tasks and subtasks
    timestamps.
    """
    def _func(self, action, options=None):
        if options is None:
            options = Storage()
        self.sched.configure(action=action)
        if action in self.sched.actions:
            self.sched.action_timestamps(action, options.rid)
            trace_sched_action()
        try:
            ret = func(self, action, options)
        except ex.AbortAction:
            # finer-grained locking can raise that to cancel the task
            return 0
        if ret == 0 and action in self.sched.actions:
            self.sched.action_timestamps(action, options.rid, success=True)
        return ret
    return _func

def contextualize_days(year, month, days):
    monthdays = days_in_month(year, month)
    for i, day in enumerate(days):
        monthday = day.get("monthday")
        if monthday is None:
            continue
        if monthday > 0:
            continue
        if abs(monthday) > monthdays:
            continue
        days[i]["monthday"] = monthdays + monthday + 1
    return days

def modulo_filter(l, 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 l if (m + shift) % modulo == 0])

def resolve_calendar_name(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 parse_calendar_expression(spec, max):
    """
    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[1:].count("-")
        if n_dash > 1:
            raise SchedSyntaxError("only one '-' allowed in range '%s'" % spec)
        elif n_dash == 0:
            elements.add(resolve_calendar_name(subspec))
            continue
        begin, end = subspec.split("-")
        begin = resolve_calendar_name(begin)
        end = resolve_calendar_name(end)
        if begin > end:
            elements |= set(range(begin, max+1))
            elements |= set(range(1, end+1))
        else:
            elements |= set(range(begin, end+1))
    return elements

def days_in_month(year, month):
    first_day_this_month = datetime.date(year=year, month=month, day=1)
    first_day_next_month = datetime.date(year if month<12 else year+1, month % 12 + 1, 1)
    return (first_day_next_month - first_day_this_month).days

def time_to_seconds(dt_spec):
    """
    Convert a datetime or a %H:%M[:%S] formatted string to seconds.
    """
    if isinstance(dt_spec, datetime.datetime):
        dtm = dt_spec
        dt_spec = dtm.hour * 60 * 60 + dtm.minute * 60 + dtm.second
    else:
        try:
            if dt_spec.count(":") == 1:
                dt_spec += ":00"
            dtm = time.strptime(dt_spec, "%H:%M:%S")
        except:
            raise SchedSyntaxError("malformed time string: %s"%str(dt_spec))
        dt_spec = dtm.tm_hour * 3600 + dtm.tm_min * 60 + dtm.tm_sec
    return dt_spec

def seconds_to_hms(s):
    return (
        s // 3600,
        (s % 3600) // 60,
        s % 60
    )

def time_to_hms(s):
    return seconds_to_hms(time_to_seconds(s))

def seconds_to_time(s):
    return "%02d:%02d:%02d" % seconds_to_hms(s)

def interval_from_timerange(timerange):
    """
    Return a default interval from a timerange data structure.
    This interval is the timerange length in minute, plus one.
    """
    begin_s = time_to_seconds(timerange['begin'])
    end_s = time_to_seconds(timerange['end'])
    if begin_s > end_s:
        return DAY_SECONDS - begin_s + end_s + 1
    return end_s - begin_s + 1

def in_timerange_safe(timerange, now=None):
    try:
        in_timerange(timerange, now)
        return True
    except SchedNotAllowed:
        return False

def in_timerange(timerange, now=None):
    """
    Validate if <now> is in <timerange>.
    """
    try:
        begin = time_to_seconds(timerange["begin"])
        end = time_to_seconds(timerange["end"])
        now = time_to_seconds(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 <= DAY_SECONDS) or \
           (now >= 0 and now <= end):
            return
    raise SchedNotAllowed("not in timerange %s-%s" % \
                          (timerange["begin"], timerange["end"]))


def timerange_delay(timerange, d):
    """
    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 second on every nodes.
    """
    if not timerange.get("probabilistic", False):
        return 0

    try:
        begin = time_to_seconds(timerange["begin"])
        end = time_to_seconds(timerange["end"])
        second = time_to_seconds(d)
    except:
        raise SchedNotAllowed("time conversion error delay eval")

    # day change in the timerange
    if begin > end:
        end += DAY_SECONDS
    if second < begin:
        second += DAY_SECONDS

    length = end - begin
    remaining = end - second - 1

    if remaining < 1:
        # 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*rnd)


class Schedule(object):
    def __init__(self, schedule, last=None):
        self.schedule = self.normalize_schedule(schedule)
        self.last = last

    @staticmethod
    def normalize_schedule(schedules):
        try:
            schedules = json.loads(schedules)
        except:
            pass
        if schedules in (None, "@0", ""):
            return []
        if isinstance(schedules, (list, tuple, set)):
            return schedules
        return [schedules]

    @property
    def data(self):
        data = []
        for schedule in self.schedule:
            try:
                one = self.data_one(schedule)
                data.append(one)
            except Exception as exc:
                pass
        return data

    def data_one(self, schedule):
        schedule_orig = schedule
        schedule = schedule.strip()
        if len(schedule) == 0:
            return {}
        if schedule.startswith("!"):
            exclude = True
            schedule = schedule[1:].strip()
        else:
            exclude = False
        if len(schedule) == 0:
            return {}
        elements = schedule.split()
        ecount = len(elements)
        if ecount == 1:
            _data = {
                "timeranges": self.parse_timerange(elements[0]),
                "day": ALL_DAYS,
                "week": ALL_WEEKS,
                "month": ALL_MONTHS,
            }
        elif ecount == 2:
            _tr, _day = elements
            _data = {
                "timeranges": self.parse_timerange(_tr),
                "day": self.parse_day(_day),
                "week": ALL_WEEKS,
                "month": ALL_MONTHS,
            }
        elif ecount == 3:
            _tr, _day, _week = elements
            _data = {
                "timeranges": self.parse_timerange(_tr),
                "day": self.parse_day(_day),
                "week": self.parse_week(_week),
                "month": ALL_MONTHS,
            }
        elif ecount == 4:
            _tr, _day, _week, _month = elements
            _data = {
                "timeranges": self.parse_timerange(_tr),
                "day": self.parse_day(_day),
                "week": self.parse_week(_week),
                "month": self.parse_month(_month),
            }
        else:
            raise SchedSyntaxError("invalid number of element, '%d' not in "
                                   "(1, 2, 3, 4)" % ecount)
        _data["raw"] = schedule_orig
        _data["exclude"] = exclude
        return _data

    def parse_day(self, day):
        """
        Convert to a list of <integer day of week>:<integer day of month>
        """
        l = []
        for e in day.split(","):
            l += self._parse_day(e)
        return l

    @staticmethod
    def _parse_day(day):
        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(":")
            from_head = True
            if len(day_of_month) == 0:
                raise SchedSyntaxError("day_of_month specifier is empty")
            if day_of_month in ("first", "1st"):
                day_of_month = 1
            elif day_of_month in ("second", "2nd"):
                day_of_month = 2
            elif day_of_month in ("third", "3rd"):
                day_of_month = 3
            elif day_of_month in ("fourth", "4th"):
                day_of_month = 4
            elif day_of_month in ("fifth", "5th"):
                day_of_month = 5
            elif day_of_month == "last":
                day_of_month = -1
            try:
                day_of_month = int(day_of_month)
            except ValueError:
                raise SchedSyntaxError("day_of_month %s is not a number" % day_of_month)

        day = parse_calendar_expression(day, 7)
        if day in ("*", ""):
            day = ALL_WEEKDAYS
        allowed_days = [{"weekday": d, "monthday": day_of_month} for d in day if d in ALL_WEEKDAYS]
        return allowed_days

    @staticmethod
    def parse_week(week):
        """
        Convert to a list of integer weeks
        """
        week = parse_calendar_expression(week, 53)
        if week == "*":
            return ALL_WEEKS
        return sorted([w for w in week if 1 <= w <= 53])

    @staticmethod
    def parse_month(month):
        """
        Convert to a list of integer months
        """
        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 = ALL_MONTHS
            else:
                _allowed_months = parse_calendar_expression(month_s, 12)
            if modulo_s is not None:
                _allowed_months = modulo_filter(_allowed_months, modulo_s)
            allowed_months |= _allowed_months
        return sorted(list(allowed_months))

    def parse_timerange(self, spec):
        """
        Return the list of timerange data structure parsed from the <spec>
        definition string.
        """
        min_tr_len = 1

        def parse_timerange(spec):
            if spec == "*" or spec == "":
                return {"begin": "00:00:00", "end": "23:59:59"}
            if "-" not in spec:
                spec = "-".join((spec, spec))
            try:
                begin, end = spec.split("-")
            except:
                raise SchedSyntaxError("split '%s' error" % spec)
            begin_s = time_to_seconds(begin)
            end_s = time_to_seconds(end)
            if begin_s == end_s:
                end_s += min_tr_len
                end = seconds_to_time(end_s)
            return {"begin": begin, "end": end}

        tr_list = []
        for _spec in spec.split(","):
            tr_data = {
                "probabilistic": False,
                "begin": "00:00",
                "end": "23:59",
                "interval": DAY_SECONDS,
            }
            if len(_spec) == 0:
                tr_list.append(tr_data)
                continue
            if _spec[0] == "~":
                tr_data["probabilistic"] = True
                _spec = _spec[1:]
            if _spec == "*":
                tr_list.append(tr_data)
                continue
            ecount = _spec.count("@")
            if ecount == 0:
                tr_data.update(parse_timerange(_spec))
                tr_data["interval"] = interval_from_timerange(tr_data)
                if tr_data["interval"] <= min_tr_len + 1:
                    tr_data["probabilistic"] = False
                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.update(parse_timerange(elements[0]))
            try:
                tr_data["interval"] = convert_duration(elements[1], _from="m", _to="s")
            except ValueError as exc:
                raise SchedSyntaxError("interval '%s' is not a valid duration expression: %s" % (elements[1], exc))
            tr_len = interval_from_timerange(tr_data)
            if tr_len <= min_tr_len + 1 or tr_data["interval"] < tr_len:
                tr_data["probabilistic"] = False
            tr_list.append(tr_data)
        return tr_list

    def validate(self, now=None, last=None, schedules=None):
        """
        Validate if <now> pass the constraints of a set of schedules,
        iterating over each non-excluded one.
        """
        def _validate(schedule):
            """
            Validate if <now> is in the allowed days and in the allowed timranges.
            """
            _in_days(schedule)
            return _in_timeranges(schedule)

        def _in_days(schedule):
            _validate_month(schedule["month"])
            _validate_week(schedule["week"])
            _validate_day(schedule["day"])

        def _validate_day(day):
            """
            Split the allowed <day> spec and for each element,
            validate if <now> is in allowed <day> of week and of month.
            """
            now_weekday = now.isoweekday()
            now_monthday = now.day
            for _day in contextualize_days(now.year, now.month, day):
                try:
                    __validate_day(_day["weekday"], _day.get("monthday"), now_weekday=now_weekday, now_monthday=now_monthday)
                    return
                except SchedNotAllowed:
                    pass
            raise SchedNotAllowed("not in allowed days")

        def __validate_day(weekday, monthday, now_weekday, now_monthday):
            """
            Validate if <now> is in allowed <day> of week and of month.
            """
            if now_weekday != weekday:
                raise SchedNotAllowed
            if monthday is None:
                return
            if now_monthday != monthday:
                raise SchedNotAllowed
            return

        def _validate_week(week):
            """
            Validate if <now> is in allowed <week>.
            """
            if now.isocalendar()[1] not in week:
                raise SchedNotAllowed("not in allowed weeks")
            return

        def _validate_month(month):
            """
            Validate if <now> is in allowed <month>.
            """
            if now.month not in month:
                raise SchedNotAllowed("not in allowed months")
            return

        def _in_timeranges(schedule):
            """
            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 = []
            for timerange in schedule["timeranges"]:
                try:
                    in_timerange(timerange, now=now)
                    if schedule.get("exclude"):
                        return _timerange_remaining(timerange)
                    _in_timerange_interval(timerange)
                    return timerange_delay(timerange, now)
                except SchedNotAllowed as exc:
                    errors.append(str(exc))
            raise SchedNotAllowed(", ".join(errors))

        def _timerange_remaining(timerange):
            try:
                begin = time_to_seconds(timerange["begin"])
                end = time_to_seconds(timerange["end"])
                second = time_to_seconds(now)
            except:
                raise SchedNotAllowed("time conversion error delay eval")
            # day change in the timerange
            if begin > end:
                end += DAY_SECONDS
            if second < begin:
                second += DAY_SECONDS

            length = end - begin
            remaining = end - second

            return remaining

        def _in_timerange_interval(timerange):
            """
            Validate if the last task run is old enough to allow running again.
            """
            if timerange["interval"] == 0:
                raise SchedNotAllowed("interval set to 0")
            if last is None:
                return
            if _skip_action_interval(timerange["interval"]):
                raise SchedNotAllowed("last run is too soon")
            return

        def _skip_action_interval(interval):
            """
            Return the negation of _need_action_interval()
            """
            return not _need_action_interval(interval)

        def _need_action_interval(delay=10):
            """
            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
            limit = last + datetime.timedelta(seconds=delay)
            return now >= limit


        schedules = schedules or self.data
        if len(schedules) == 0:
            raise SchedNotAllowed("no schedule")
        errors = []
        for schedule in schedules:
            try:
                delay = _validate(schedule)
                if schedule["exclude"]:
                    raise SchedExcluded('excluded by schedule member "%s"' % schedule["raw"], until=delay)
                else:
                    return delay
            except SchedNotAllowed as exc:
                errors.append(str(exc))
        raise SchedNotAllowed(", ".join(errors))


    def get_next(self, now=None, last=None):
        if not now:
            now = time.time()
        if isinstance(now, (int, float)):
            now = datetime.datetime.fromtimestamp(now)
        if isinstance(last, (int, float)):
            last = datetime.datetime.fromtimestamp(last)
        _next = None
        _interval = None
        last = last or self.last
        sdata = self.data
        excludes = [sd for sd in sdata if sd.get("exclude")]
        includes = [sd for sd in sdata if not sd.get("exclude")]
        for s in includes:
            __next, __interval = self._get_next(s, now, last, excludes)
            if _next is None or _next > __next:
                _next = __next
                _interval = __interval
        return _next, _interval

    def _get_next(self, s, now, last, excludes):
        def valid_day(weekday, monthday, days):
            return {"weekday": weekday, "monthday": None} in days or \
                   {"weekday": weekday, "monthday": monthday} in days or \
                   {"weekday": weekday} in days

        def exclude(d):
            try:
                self.validate(d, last=last, schedules=excludes)
                return 0
            except SchedExcluded as exc:
                return exc.until
            except SchedNotAllowed as exc:
                return 0

        class NextDay(Exception):
            pass

        class Drift(Exception):
            def __init__(self, hour=0, minute=0, second=0):
                self.hour = hour
                self.minute = minute
                self.second = second

        def daily(year, month, day, hour, minute, second):
            d = datetime.datetime(year=year, month=month, day=monthday, hour=hour, minute=minute, second=second)
            week = d.isocalendar()[1]
            if week not in s["week"]:
                raise NextDay
            weekday = d.isoweekday()
            if not valid_day(weekday, monthday, days):
                raise NextDay
            try:
                d, interval = self.get_timerange(s, d, last)
            except SchedNotAllowed:
                raise NextDay
            try:
                self.validate(d, last=last, schedules=excludes)
            except SchedNotAllowed as exc:
                pass
            except SchedExcluded as exc:
                if exc.until > 0:
                    d += datetime.timedelta(seconds=exc.until)
                    if year == d.year and month == d.month and monthday == d.day:
                        raise Drift(hour=d.hour, minute=d.minute, second=d.second)
                    raise NextDay
            return d, interval

        hour = now.hour
        minute = now.minute
        second = now.second
        for year in [now.year, now.year+1]:
            for month in s["month"]:
                if year == now.year and month < now.month:
                    continue
                days = contextualize_days(year, month, s["day"])
                if year == now.year and month == now.month:
                    first_day = now.day
                else:
                    first_day = 1
                for monthday in range(first_day, days_in_month(year, month) + 1):
                    while True:
                        try:
                            return daily(year, month, monthday, hour, minute, second)
                        except NextDay:
                            hour = 0
                            minute = 0
                            second = 0
                            break
                        except Drift as exc:
                            hour = exc.hour
                            minute = exc.minute
                            second = exc.second

        return None, None

    @staticmethod
    def get_timerange(s, d, last):
        def valid_interval(d, last, interval):
            if not last:
                return True
            if d - last >= datetime.timedelta(seconds=interval):
                return True
            return False

        # if the candidate date is inside timeranges, return (candidate, smallest interval)
        ranges = [tr for tr in s["timeranges"] if in_timerange_safe(tr, d)]
        for tr in sorted(ranges, key=lambda x: (x.get("interval"), x.get("begin", 0))):
            interval = tr.get("interval")
            if not valid_interval(d, last, interval):
                di = last + datetime.timedelta(seconds=interval)
                if in_timerange_safe(tr, di):
                    return di, interval
                raise SchedNotAllowed
            if tr["probabilistic"]:
                delay = timerange_delay(tr, d)
                pd = d + datetime.timedelta(seconds=delay)
                pinterval = interval - delay
                return pd, pinterval
            return d, interval

        # the candidate date is outside timeranges, return the closest range's (begin, interval)
        ref = "%02d:%02d:%02d" % (d.hour, d.minute, d.second)
        ranges = [tr for tr in s["timeranges"] if ref < tr.get("begin", 0)]
        for tr in sorted(ranges, key=lambda x: (x.get("begin", 0), x.get("interval"))):
            interval = tr.get("interval")
            hour, minute, second = time_to_hms(tr["begin"])
            d = d.replace(hour=hour, minute=minute, second=second)
            if not valid_interval(d, last, interval):
                di = last + datetime.timedelta(seconds=interval)
                if in_timerange_safe(tr, di):
                    return di, interval
                continue
            if tr["probabilistic"]:
                delay = timerange_delay(tr, d)
                pd = d + datetime.timedelta(seconds=delay)
                pinterval = interval - delay
                return pd, pinterval
            return d, interval

        raise SchedNotAllowed

    def __iter__(self):
        return ScheduleIterator(self)

class ScheduleIterator:
    def __init__(self, schedule):
        self._schedule = schedule
        self._index = 0
        self._now = datetime.datetime.now()

    def __next__(self):
        while True:
            _next, interval = self._schedule.get_next(self._now)
            if not _next:
                raise StopIteration
            self._now = _next + datetime.timedelta(seconds=interval)
            return _next

    next = __next__

class Scheduler(object):
    """
    The scheduler class.

    The node and each service inherit an independent scheduler through
    this class.
    """
    def __init__(self, config_defaults=None, node=None, options=None,
                 scheduler_actions=None, log=None, svc=None,
                 configure_method=None):
        self.config_defaults = config_defaults
        self.configured = False
        self.configure_method = configure_method
        self.scheduler_actions = scheduler_actions or {}
        self.options = Storage(options or {})
        self.svc = svc
        self.node = node
        if svc:
            self.obj = svc
            self.log = svc.log
            if node is None:
                self.node = svc.node
        else:
            self.obj = node
            self.log = node.log
            if node is None:
                self.node = node

    def update(self, data):
        self.scheduler_actions.update(data)

    def reconfigure(self):
        self.configured = False

    def configure(self, *args, **kwargs):
        """
        Placeholder for post-instanciation configuration.
        """
        if self.configured:
            return
        if self.configure_method:
            getattr(self.obj, self.configure_method)(*args, **kwargs)
        self.configured = True

    def get_last(self, fname, success=False):
        """
        Return the last task run timestamp, fetched from the on-disk cache.
        """
        timestamp_f = self.get_timestamp_f(fname, success=success)
        if not os.path.exists(timestamp_f):
            return
        try:
            with open(timestamp_f, 'r') as ofile:
                buff = ofile.read()
        except (OSError, IOError, ValueError):
            return
        try:
            last = datetime.datetime.fromtimestamp(float(buff))
            return to_time(last)
        except ValueError:
            pass
        try:
            last = datetime.datetime.strptime(buff, "%Y-%m-%d %H:%M:%S.%f"+os.linesep)
            return to_time(last)
        except ValueError:
            pass
        return 0

    def 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

        try:
            schedule_s = self.obj.oget(section, option if section == "DEFAULT" else "schedule")
        except ValueError:
            # keyword not found
            schedule_s = None
        if schedule_s is not None:
            # explicit schedule in config data
            return schedule_s

        if self.svc and section in self.svc.resources_by_id and \
             hasattr(self.svc.resources_by_id[section], "default_schedule"):
            # driver default
            schedule_s = self.svc.resources_by_id[section].default_schedule
        elif option in self.config_defaults:
            # scheduler action default
            if section == "sync#i0":
                schedule_s = self.config_defaults["sync#i0_schedule"]
            else:
                schedule_s = self.config_defaults[option]
        else:
            raise SchedNoDefault

        return schedule_s

    def 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.get_schedule_raw(section, option)
        return Schedule(schedules)

    def get_timestamp_f(self, fname, success=False):
        """
        Return the full path of the last run timestamp file with the <fname>
        basename.
        """
        if self.svc:
            timestamp_d = os.path.join(self.svc.var_d, "scheduler")
        else:
            timestamp_d = os.path.join(Env.paths.pathvar, "node", "scheduler")
        fpath = os.path.join(timestamp_d, fname)
        if success:
            fpath += ".success"
        return fpath

    @property
    def actions(self):
        return self.scheduler_actions

    def action_timestamps(self, action, rids=None, success=False):
        sched_options = self.scheduler_actions[action]
        tsfiles = []
        for _so in sched_options:
            if rids and not _so.section in rids:
                continue
            tsfile = self.get_timestamp_f(_so.fname, success=success)
            tsfiles.append(tsfile)

        if len(tsfiles) == 0:
            return

        try:
            last = datetime.datetime.fromtimestamp(float(os.environ["OSVC_SCHED_TIME"]))
        except Exception as exc:
            last = datetime.datetime.now()

        for tsfile in tsfiles:
            timestamp(tsfile, last=last)

    def print_schedule(self):
        """
        The 'print schedule' node and service action entrypoint.
        """
        if self.obj.cd is None:
            print("you are not allowed to print schedules", file=sys.stderr)
            raise ex.Error()
        data = self.print_schedule_data()
        if self.options.format is None:
            self.format_schedule(data)
            return
        if self.svc and not self.svc.options.single_service:
            # let the Node object do the formatting (for aggregation)
            return data
        # format ourself
        return self.format_schedule_data(data)

    @formatter
    def format_schedule_data(self, data):
        """
        Display the scheduling table using the formatter specified in
        command line --format option.
        """
        return data

    @staticmethod
    def format_schedule(data):
        """
        Print the scheduling table as a tree.
        """
        from utilities.render.forest import Forest
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column("Action", color.BOLD)
        head_node.add_column("Last Run", color.BOLD)
        head_node.add_column("Next Run", color.BOLD)
        head_node.add_column("Config Parameter", color.BOLD)
        head_node.add_column("Schedule Definition", color.BOLD)

        def add_column_date(timestamp):
            if timestamp is None:
                node.add_column("-")
            else:
                node.add_column(datetime.datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S"))

        for _data in data:
            node = head_node.add_node()
            node.add_column(_data["action"], color.LIGHTBLUE)
            add_column_date(_data["last_run"])
            add_column_date(_data["next_run"])
            node.add_column(_data["config_parameter"])
            node.add_column(_data["schedule_definition"])

        tree.out()

    def print_schedule_data(self, with_next=True):
        """
        Return a list of dict of schedule information for all tasks.
        """
        self.configure()
        data = []
        now = datetime.datetime.now()
        for action in self.scheduler_actions:
            data += self._print_schedule_data(action, now, with_next=with_next)
        return data

    def _print_schedule_data(self, action, now, with_next=True):
        """
        Return a dict of a scheduled task, or list of dict of a task-set,
        containing schedule information.
        """
        data = []
        for sopt in self.scheduler_actions[action]:
            data += [self.__print_schedule_data(action, sopt, now, with_next=with_next)]
        return sorted(data, key=lambda x: x["config_parameter"])

    def __print_schedule_data(self, action, sopt, now, with_next=True):
        """
        Return a dict of a scheduled task information.
        """
        section = sopt.section
        schedule_option = sopt.schedule_option
        fname = sopt.fname

        try:
            schedule = self.get_schedule(section, schedule_option)
            schedule_s = " ".join(schedule.schedule)
        except SchedNoDefault:
            schedule_s = "anytime"
        except SchedSyntaxError:
            schedule_s = "malformed"
        if len(schedule_s) == 0:
            schedule_s = "-"

        try:
            last = self.get_last(fname)
        except (AttributeError, IOError, OSError):
            last = None

        if section != "DEFAULT":
            param = "schedule"
        else:
            param = schedule_option
        param = '.'.join((section, param))

        data = dict(
            action=action,
            last_run=last,
            config_parameter=param,
            schedule_definition=schedule_s,
        )

        if with_next:
            if schedule_s in ("-", "@0", "malformed"):
                nxt = 0
            else:
                nxt = to_time(schedule.get_next(now, last=last)[0])
            data["next_run"] = nxt

        return data

if __name__ == '__main__':
    now = datetime.datetime.now()
    last_value = datetime.datetime.now()
    # s = Schedule("00:10-01:00@1,03:10-04:00@10,@1h sun-mon:last,fri:first,wed 10-40 2,jun-aug")
    # Schedule("18:00-19:00@10").validate(now.replace(hour=18, minute=51), last=now.replace(hour=18, minute=51))
    # Schedule("18:00-19:00@10").validate(now.replace(hour=15, minute=01))
    # s = Schedule(["14:00-21:00@10", "!12:00-18:55", "!19:10-20:00"])
    s = Schedule("@11s mon:last")
    # s = Schedule("")
    print(json.dumps(s.data, indent=4))
    for _ in range(10):
        last_value, interval = s.get_next(now=last_value, last=last_value)
        if not last_value:
            break
        print(last_value, last_value.strftime("%c"), interval)
   0707010001f1a4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/core/sysreport 0707010001f1aa000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/sysreport/linux.py    from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1ab000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/core/sysreport/osf1.py from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1a7000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/core/sysreport/darwin.py   from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1ae000081a40000000000000000000000016a100daf0000483e000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/core/sysreport/sysreport.py    from __future__ import print_function

import hashlib
import os
import sys
import shutil
import shlex
import glob
import json
from stat import *
from subprocess import *

import foreign.six as six
import core.exceptions as ex
from env import Env
from utilities.proc import which

POSIX = os.name == "posix"


class BaseSysReport(object):
    def __init__(self, node=None, collect_d=None, compress=False):
        self.todo = [
            ("INC", os.path.join(Env.paths.pathetc, "*.conf")),
            ("INC", os.path.join(Env.paths.pathetc, "namespaces", "*", "*", "*.conf")),
            ("INC", os.path.join(Env.paths.pathetc, "sysreport.conf.d")),
        ]

        self.changed = []
        self.deleted = []
        self.sysreport_conf_d = os.path.join(Env.paths.pathetc, "sysreport.conf.d")
        if collect_d is None:
            self.sysreport_d = os.path.join(Env.paths.pathvar, "sysreport")
            self.collect_d = os.path.join(self.sysreport_d, Env.nodename)
        else:
            self.sysreport_d = collect_d
            self.collect_d = collect_d
        self.collect_cmd_d = os.path.join(self.collect_d, "cmd")
        self.collect_file_d = os.path.join(self.collect_d, "file")

        if collect_d is None:
            self.collect_stat = os.path.join(self.collect_file_d, "stat")
            self.full = [self.collect_stat]
        else:
            self.collect_stat = None
            self.full = []
        self.stat_changed = False
        self.root_uid = 0
        self.root_gid = 0
        self.node = node
        if compress:
            self.archive_extension = '.tar.gz'
            self.tarmode = "w:gz"
        else:
            self.archive_extension = '.tar'
            self.tarmode = "w"
        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, force=False):
        if not force and 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 check_cf_perms(self, fpath):
        s = os.stat(fpath)
        mode = s[ST_MODE]
        if mode & S_IWOTH:
            raise ValueError("skip %s config file: file mode is insecure ('other' has write permission)" % fpath)
        if s.st_uid != self.root_uid or s.st_gid != self.root_gid:
            raise ValueError("skip %s config file: file ownership is insecure (must be owned by root)" % fpath)

    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)
                try:
                    self.check_cf_perms(fpath)
                except ValueError as exc:
                    print(exc, file=sys.stderr)
                    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

        if self.collect_stat is None:
            return

        # find deleted
        dst_files = self.find_files(self.collect_file_d)
        dst_files = self.rel_paths(self.collect_file_d, dst_files, posix=False)
        files = self.rel_paths("", self.files, posix=False)
        self.deleted = set(dst_files) - set(files) - set([os.sep + "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)
        self.deleted = self.rel_paths("", self.deleted, posix=True)

    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)')
        fname = fname.replace("\\", '(bslash)')
        return fname

    def write(self, fpath, buff):
        try:
            with open(fpath, 'r') as f:
                pbuff = f.read()
        except IOError as exc:
            pbuff = None
        if POSIX:
            diff = pbuff != buff
        else:
            diff = pbuff != buff.replace("\r", "\n")
        if diff:
            self.changed.append(fpath)
            with open(fpath, 'w') as f:
                f.write(buff)
            self.full.append(fpath)
        elif pbuff is not None:
            self.full.append(fpath)

    def get_exe(self, fpath):
        if not os.path.exists(fpath):
            raise ValueError("not found (%s)" % fpath)
        if which(fpath) is None:
            raise ValueError("not executable (%s)" % fpath)
        return fpath

    def collect_cmd(self, cmd):
        l = shlex.split(cmd, posix=POSIX)
        if len(l) == 0:
            print(" err: syntax error", file=sys.stderr)
            return
        if os.sep not in l[0]:
            print(" err: full path required for commands (%s)" % l[0], file=sys.stderr)
        try:
            self.get_exe(l[0])
        except ValueError as exc:
            print(" err: %s" % str(exc), file=sys.stderr)
            return
        fname = self.cmdlist2fname(l)
        cmd_d = os.path.join(self.collect_cmd_d, fname)
        p = Popen(l, stdout=PIPE, stderr=STDOUT, close_fds=POSIX)
        out, err = p.communicate()
        if six.PY3:
            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):
        if self.collect_stat is None:
            return
        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 dst_d(self, base_d, fpath):
        """
        Return the full path of the collect dir that will host
        fpath
        """
        return base_d + os.path.dirname(fpath)

    def _digest_secret(self, name, data):
        obfuscate = {}
        if name in ['cluster.conf']:
            rid_secrets = ['cluster']
            rid_secrets.extend(self.node.conf_sections(cat='hb'))
            rid_secrets.extend(self.node.conf_sections(cat='arbitrator'))
            for rid in rid_secrets:
                try:
                    value = self.node.oget(rid, 'secret')
                    obfuscate[value] = 'hexdigest-%s' % hashlib.md5(value.encode()).hexdigest()
                except:
                    pass
        for secret, secret_md5 in obfuscate.items():
            data = data.replace(secret, secret_md5)
        return data

    def collect_file(self, fpath):
        if not os.path.exists(fpath):
            return
        if os.path.islink(fpath):
            return
        dst_d = self.dst_d(self.collect_file_d, 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()
                if fpath.endswith('cluster.conf'):
                    buff = self._digest_secret('cluster.conf', buff)
            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)
        if dst_f.endswith('cluster.conf'):
            with open(dst_f, 'r') as src:
                cluster_conf = src.read()
            with open(dst_f, 'w') as dst:
                dst.write(self._digest_secret('cluster.conf', cluster_conf))
        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.collect()
        self.delete_collected(self.deleted)
        self.write_stat(force=force)
        self.send(force)

    def collect(self):
        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)

    def deleted_report(self):
        print("files deleted:")
        for fpath in sorted(self.deleted):
            print("  " + fpath)

    def collected(self):
        return self.rel_paths(self.collect_d, self.full)

    def rel_paths(self, base, fpaths, posix=True):
        if base:
            n = len(base)
        else:
            n = 0
        return [x[n:] for x in fpaths]

    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.Error("can not get lstree from collector")
            lstree_data = self.rel_paths("", lstree_data, posix=True)
            collected = self.rel_paths("", self.collected(), posix=True)
            self.deleted = sorted(list(set(lstree_data) - set(os.sep + "stat") - set(collected)))
        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")
        from utilities.semver import Semver
        if self.node.oc3_version() >= Semver(3, 0, 1):
            self._oc3_send(tmpf, self.deleted)
        else:
            self.node.collector.call(self.send_rpc, tmpf, self.deleted)

        if tmpf is not None:
            os.unlink(tmpf)

    def _oc3_send(self, fpath, deleted):
        import uuid
        import core.oc3path as oc3path

        boundary = uuid.uuid4().hex
        boundary_bytes = boundary.encode('utf-8')
        parts = []

        if fpath is not None:
            with open(fpath, 'rb') as f:
                fdata = f.read()
            parts.append(
                b'--' + boundary_bytes + b'\r\n' +
                b'Content-Disposition: form-data; name="file"; filename="' +
                os.path.basename(fpath).encode('utf-8') + b'"\r\n' +
                b'Content-Type: application/octet-stream\r\n\r\n' +
                fdata + b'\r\n'
            )

        for d in deleted:
            parts.append(
                b'--' + boundary_bytes + b'\r\n' +
                b'Content-Disposition: form-data; name="deleted"\r\n\r\n' +
                d.encode('utf-8') + b'\r\n'
            )

        body = b''.join(parts) + b'--' + boundary_bytes + b'--\r\n'
        headers = {
            "Accept": "application/json",
            "Content-Type": "multipart/form-data; boundary=" + boundary,
        }

        try:
            status_code, resp = self.node.oc3_request_feed("POST", oc3path.FEED_NODE_SYSREPORT, headers=headers, data=body)
            self.node.oc3_assert_status_code("POST", oc3path.FEED_NODE_SYSREPORT, status_code, resp, expected=[202])
        except Exception as exc:
            raise ex.Error(str(exc))

    @staticmethod
    def mangle_sep(fpath):
        return fpath

    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=self.tarmode)
        for fpath in l:
            if len(fpath) < n:
                print(" err: can not archive", fpath, "(fpath too short)", file=sys.stderr)
                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)
  0707010001f1af000081a40000000000000000000000016a100daf000005dc000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/core/sysreport/windows.py  import os

from core.sysreport.sysreport import BaseSysReport
from utilities.proc import which


class SysReport(BaseSysReport):
    @staticmethod
    def mangle_drive(fpath):
        try:
            if fpath[1] == ":":
                fpath = fpath[0].upper() + fpath[2:]
        except IndexError:
            pass
        return os.sep + fpath

    @staticmethod
    def mangle_sep(fpath):
        def mangle(_fp):
            return _fp.replace("\\", "/")

        if isinstance(fpath, list):
            return [mangle(_fp) for _fp in fpath]
        return mangle(fpath)

    def rel_paths(self, base, fpaths, posix=True):
        if base:
            n = len(base) + 1
        else:
            n = 0
        if posix:
            return [self.mangle_drive(x[n:]).replace("\\", "/")[1:] for x in fpaths]
        else:
            return [self.mangle_drive(x[n:]) for x in fpaths]

    def dst_d(self, base_d, fpath):
        """
        Return the full path of the collect dir that will host
        fpath
        """
        dst_d = os.path.dirname(fpath)
        return base_d + self.mangle_drive(dst_d)

    def check_cf_perms(self, fpath):
        pass

    def get_exe(self, fpath):
        for suffix in ("", ".exe", ".bat", ".cmd", ".lnk"):
            candidate = fpath + suffix
            if not os.path.exists(candidate):
                continue
            if which(candidate):
                return candidate
        raise ValueError("not found or not executable (%s)" % fpath)
0707010001f1ac000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/sysreport/sunos.py    from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1a5000081a40000000000000000000000016a100daf000000d7000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/core/sysreport/__init__.py import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
SysReport = _os.SysReport
 0707010001f1a8000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/core/sysreport/freebsd.py  from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1a9000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/core/sysreport/hpux.py from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f1a6000081a40000000000000000000000016a100daf0000005e000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/sysreport/aix.py  from core.sysreport.sysreport import BaseSysReport


class SysReport(BaseSysReport):
    pass
  0707010001f17b000081a40000000000000000000000016a100daf00000b88000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/core/freezer.py    """
Define the Freezer class, instantiated as a Svc lazy attribute,
providing the methods to freeze, thaw a service and to test if
a service is frozen.
"""
import os
from env import Env
from utilities.naming import split_path
from utilities.files import unlink_and_sync


class Freezer(object):
    """
    The freezer class, instantiated 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):
        """
        If strict, return the mtime if the service frozen file flag is present.

        If not strict, return the mtime of either, in turn, the service frozen
        file if present, or the node frozen file mtime if present.

        Return 0 if the service is not frozen.
        """
        try:
            return os.path.getmtime(self.flag)
        except (OSError, IOError):
            pass
        if not strict:
            try:
                return os.path.getmtime(self.node_flag)
            except (OSError, IOError):
                pass
        return 0

    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)
        fd = open(self.flag, 'w')
        os.fsync(fd)
        fd.close()

    def thaw(self):
        """
        Remove the service frozen file flag.
        """
        if self.flag != self.node_flag and os.path.exists(self.flag):
            unlink_and_sync(self.flag)

    def node_frozen(self):
        """
        Return the mtime of the node frozen file if present.

        Return 0 if the node is not frozen.
        """
        try:
            return os.path.getmtime(self.node_flag)
        except (OSError, IOError):
            pass
        return 0

    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)
        fd = open(self.node_flag, 'w')
        os.fsync(fd)
        fd.close()

    def node_thaw(self):
        """
        Remove the node frozen file flag.
        """
        if not os.path.exists(self.node_flag):
            return
        unlink_and_sync(self.node_flag)

    def __init__(self, name):
        self.node_flag = os.path.join(Env.paths.pathvar, "node", "frozen")
        if name == "node":
            self.flag = self.node_flag
        else:
            name, namespace, kind = split_path(name)
            if namespace:
                self.flag = os.path.join(Env.paths.pathvar, "namespaces", namespace, kind, name, "frozen")
            else:
                self.flag = os.path.join(Env.paths.pathvar, kind, name, "frozen")
0707010001f177000081a40000000000000000000000016a100daf0000305f000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/core/contexts.py   from __future__ import print_function

import os
import json
import sys

import core.exceptions as ex
from env import Env
from utilities.optparser import OptParser, Option
from utilities.storage import Storage

def split_context(buff):
    user, buff = buff.rsplit("@", 1)
    cluster, namespace = buff.split("/", 1)
    return user, cluster, namespace

def contexts_config_path():
    return os.path.join(os.path.expanduser("~"), ".opensvc", "config")

def get_context(context=None):
    """
    Get the listener address and port from ~/.opensvc/config
    """
    if context is None:
        try:
            context = os.environ["OSVC_CONTEXT"]
        except KeyError:
            return

    info = {}
    fpath = contexts_config_path()
    try:
        with open(fpath, "r") as ofile:
            data = json.load(ofile)
    except ValueError as exc:
        raise ex.Error("invalid context: %s: %s" % (fpath, str(exc)))
    except (IOError, OSError):
        data = {}

    # context => user, cluster, namespace
    context_data = data.get("contexts", {}).get(context)
    if context_data:
        try:
            user = context_data["user"]
            cluster = context_data["cluster"]
        except KeyError as exc:
            raise ex.Error("invalid context: %s: key %s not found" % (context, str(exc)))
        namespace = context_data.get("namespace")
    else:
        try:
            user, cluster, namespace = split_context(context)
        except Exception:
            raise ex.Error("invalid context '%s'. should be <user>/<cluster>[/<namespace>] or the name of a context defined in %s" % (context, fpath))

    # cluster data
    cdata = data.get("clusters", {}).get(cluster)
    if cdata is None:
        raise ex.Error("invalid context '%s'. cluster not found in %s" % (context, fpath))
    info["cluster"] = cdata
    
    certificate_authority = cdata.get("certificate_authority")

    server = cdata.get("server")
    if server is None:
        raise ex.Error("invalid context '%s'. cluster.%s.server not found in %s" % (context, cluster, fpath))

    server = server.replace("tls://", "").strip("/")
    server = server.replace("https://", "").strip("/")
    if ":" in server:
        addr, port = server.split(":", 1)
    else:
        addr = server
        port = Env.listener_tls_port
    info["cluster"]["addr"] = addr
    try:
        info["cluster"]["port"] = int(port)
    except Exception:
        raise ex.Error("invalid context '%s'. port %s number is not integer" % (context, port))

    # user data
    udata = data.get("users", {}).get(user)
    if udata is None:
        raise ex.Error("invalid context '%s'. user not found in %s" % (context, fpath))
    info["user"] = udata
    info["namespace"] = namespace
    
    cert = info.get("user", {}).get("client_certificate")
    if cert is None:
        raise ex.Error("invalid context '%s'. user.%s.client_certificate not found in %s" % (context, user, fpath))
    if not os.path.exists(cert):
        raise ex.Error("invalid context '%s'. user.%s.client_certificate %s not found" % (context, user, cert))

    key = info.get("user", {}).get("client_key")
    if key is None:
        # consider 'client_certificate' points to a full pem
        info["user"]["client_key"] = cert
    elif not os.path.exists(key):
        raise ex.Error("invalid context '%s'. user.%s.client_key %s not found" % (context, user, key))
    #print(json.dumps(info, indent=4))
    return info

def want_context():
    return "OSVC_CONTEXT" in os.environ

def write_context(data):
    fpath = contexts_config_path()
    dpath = os.path.dirname(fpath)
    try:
        os.makedirs(dpath, 0o0700)
    except OSError as exc:
        if exc.errno == 17:
            pass
        else:
            raise
    with open(fpath, "w") as ofile:
        json.dump(data, ofile, indent=4)

def load_context():
    fpath = contexts_config_path()
    try:
        with open(fpath, "r") as ofile:
            data = json.load(ofile)
    except ValueError as exc:
        raise ex.Error("invalid context: %s: %s" % (fpath, str(exc)))
    except (IOError, OSError):
        data = {}
    return data

def user_create(name=None, client_certificate=None, client_key=None, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if name in cdata.get("users", {}):
        # preload current values for incremental changes
        data = cdata["users"][name]
    else:
        data = {}
    if client_certificate:
        data["client_certificate"] = client_certificate
    if client_key:
        data["client_key"] = client_key
    if "users" not in cdata:
        cdata["users"] = {}
    cdata["users"][name] = data
    write_context(cdata)

def user_delete(name, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if name not in cdata.get("users", {}):
        return
    del cdata["users"][name]
    write_context(cdata)

def user_list(**kwargs):
    cdata = load_context()
    for name in cdata.get("users", {}):
        print(name)

def user_show(name=None, **kwargs):
    cdata = load_context()
    if name is None:
        print(json.dumps(cdata.get("users", {}), indent=4))
    else:
        print(json.dumps(cdata.get("users", {}).get(name, {}), indent=4))

def cluster_create(name=None, server=None, certificate_authority=None, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if name in cdata.get("clusters", {}):
        # preload current values for incremental changes
        data = cdata["clusters"][name]
    else:
        data = {}
    if server:
        data["server"] = server
    if certificate_authority:
        data["certificate_authority"] = certificate_authority
    if "clusters" not in cdata:
        cdata["clusters"] = {}
    cdata["clusters"][name] = data
    write_context(cdata)

def cluster_delete(name=None, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if name not in cdata.get("clusters", {}):
        return
    del cdata["clusters"][name]
    write_context(cdata)

def cluster_list(**kwargs):
    cdata = load_context()
    for name in cdata.get("clusters", {}):
        print(name)

def cluster_show(name=None, **kwargs):
    cdata = load_context()
    if name is None:
        print(json.dumps(cdata.get("clusters", {}), indent=4))
    else:
        print(json.dumps(cdata.get("clusters", {}).get(name, {}), indent=4))

def get(**kwargs):
    raise ex.Error("The 'om' alias must be sourced to handle ctx get")

def set(**kwargs):
    raise ex.Error("The 'om' alias must be sourced to handle ctx set")

def unset(**kwargs):
    raise ex.Error("The 'om' alias must be sourced to handle ctx unset")

def create(name=None, cluster=None, user=None, namespace=None, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if not cluster:
        raise ex.Error("cluster is mandatory")
    if cluster not in cdata.get("clusters", {}):
        raise ex.Error("unknown cluster %s" % cluster)
    if not user:
        raise ex.Error("user is mandatory")
    if user not in cdata.get("users", {}):
        raise ex.Error("unknown user %s" % user)
    if name in cdata.get("contexts", {}):
        # preload current values for incremental changes
        data = cdata["contexts"][name]
    else:
        data = {}
    if cluster:
        data["cluster"] = cluster
    if user:
        data["user"] = user
    if namespace:
        data["namespace"] = namespace
    if "contexts" not in cdata:
        cdata["contexts"] = {}
    cdata["contexts"][name] = data
    write_context(cdata)

def delete(name=None, **kwargs):
    if name is None:
        raise ex.Error("name is mandatory")
    cdata = load_context()
    if name not in cdata.get("contexts", {}):
        return
    del cdata["contexts"][name]
    write_context(cdata)

def list(**kwargs):
    cdata = load_context()
    for name in cdata.get("contexts", {}):
        print(name)

def show(name=None, **kwargs):
    cdata = load_context()
    if name is None:
        print(json.dumps(cdata.get("contexts", {}), indent=4))
    else:
        print(json.dumps(cdata.get("contexts", {}).get(name, {}), indent=4))

PROG = "om ctx"
OPT = Storage({
    "help": Option(
        "-h", "--help", action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "user": Option(
        "--user", action="store", dest="user",
        help="User name."),
    "cluster": Option(
        "--cluster", action="store", dest="cluster",
        help="Cluster name."),
    "namespace": Option(
        "--namespace", action="store", dest="namespace",
        help="Namespace name or glob pattern."),
    "name": Option(
        "--name", action="store", dest="name",
        help="The name of the object to create or delete."),
    "certificate_authority": Option(
        "--certificate-authority", action="store", dest="certificate_authority",
        help="The certificate authority pem file path."),
    "server": Option(
        "--server", action="store", dest="server",
        help="The uri where to contact the cluster. ex: tls://1.2.3.4:1215."),
    "client_certificate": Option(
        "--client-certificate", action="store", dest="client_certificate",
        help="The client certificate pem file path."),
    "client_key": Option(
        "--client-key", action="store", dest="client_key",
        help="The client key pem file path."),
})

ACTIONS = {
    "Users": {
        "user_create": {
            "msg": "Create or update a user.",
            "options": [
                OPT.name,
                OPT.client_certificate,
                OPT.client_key,
            ],
        },
        "user_show": {
            "msg": "Show a user configuration.",
            "options": [
                OPT.name,
            ],
        },
        "user_delete": {
            "msg": "Delete a user.",
            "options": [
                OPT.name,
            ],
        },
        "user_list": {
            "msg": "List defined users.",
        },
    },
    "Clusters": {
        "cluster_create": {
            "msg": "Create or update a cluster.",
            "options": [
                OPT.name,
                OPT.server,
                OPT.certificate_authority,
            ],
        },
        "cluster_delete": {
            "msg": "Delete a cluster.",
            "options": [
                OPT.name,
            ],
        },
        "cluster_show": {
            "msg": "Show a cluster configuration.",
            "options": [
                OPT.name,
            ],
        },
        "cluster_list": {
            "msg": "List defined clusters.",
        },
    },
    "Contexts": {
        "get": {
            "msg": "Show the current context. The context is valid for this shell session only.",
        },
        "set": {
            "msg": "Switch the current context. The context is valid for this shell session only.",
        },
        "unset": {
            "msg": "Unset the current context.",
        },
        "create": {
            "msg": "Create or update a context.",
            "options": [
                OPT.name,
                OPT.namespace,
                OPT.cluster,
                OPT.user,
            ],
        },
        "delete": {
            "msg": "Delete a context.",
            "options": [
                OPT.name,
            ],
        },
        "show": {
            "msg": "Show a context configuration.",
            "options": [
                OPT.name,
            ],
        },
        "list": {
            "msg": "List defined contexts.",
        },
    },
}

DEPRECATED_ACTIONS = {}
GLOBAL_OPTS = {}

def main(argv):
    parser = OptParser(prog=PROG, options=OPT, actions=ACTIONS,
                       deprecated_actions=DEPRECATED_ACTIONS,
                       global_options=GLOBAL_OPTS)
    try:
        options, action = parser.parse_args(argv[1:])
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        return 1
    kwargs = vars(options)
    globals()[action](**kwargs)

if __name__ == "__main__":
    try:
        ret = main(sys.argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)

 0707010001f168000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/core/__init__.py   0707010001f17c000081a40000000000000000000000016a100daf00006d1c000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/core/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

import core.exceptions as ex
from env import Env

class MissKeyNoDefault(Exception):
    pass

class KeyInvalidValue(Exception):
    pass

class Keyword(object):
    def __init__(self, section, keyword,
                 rtype=None,
                 protoname=None,
                 required=False,
                 generic=False,
                 at=False,
                 inheritance="leaf > head",
                 scope_order="specific > generic",
                 default=None,
                 default_text=None,
                 default_keyword=None,
                 candidates=None,
                 strict_candidates=True,
                 convert=None,
                 depends=None,
                 text="",
                 example=None,
                 provisioning=False):
        if depends is None:
            depends = []
        self.protoname = protoname or keyword
        self.section = section
        self.keyword = keyword
        self.default_keyword = default_keyword or keyword
        if rtype is None or isinstance(rtype, list):
            self.rtype = rtype
        else:
            self.rtype = [rtype]
        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.provisioning = provisioning
        self.convert = convert
        self.inheritance = inheritance
        self.scope_order = scope_order
        if example is not None:
            self.example = example
        elif self.convert == "size":
            self.example = "100m"
        elif self.convert == "duration":
            self.example = "1h"
        elif self.convert in ("boolean", "tristate"):
            self.example = "false"
        else:
            self.example = "foo"

        if self.default_text is None:
            self.default_text = self.default

    def __repr__(self):
        return "<Keyword %s>" % self.keyword

    def __str__(self):
        return "<Keyword %s>" % self.keyword

    def __lt__(self, o):
        return self.section + self.keyword < o.section + o.keyword

    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 rtype is None:
                if self.section+"."+self.keyword in self.top.deprecated_keywords:
                    return True
            elif 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)
            if self.keyword != self.default_keyword:
                s += "#  default keyword: %s\n" % self.default_keyword
        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:
            fill=""
            if "template.node" in self.top.template_prefix:
                fill="node."
            if "template.cluster" in self.top.template_prefix:
                fill="cluster."
            if "template.secret" in self.top.template_prefix:
                fill="secret."
            if "template.cfg" in self.top.template_prefix:
                fill="cfg."
            s += ".. _%s%s.%s:\n\n" % (fill, 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)
            if self.keyword != self.default_keyword:
                s += "**default keyword** %s\n" % self.default_keyword
        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

    def dump(self):
        data = {"keyword": self.keyword}
        if self.rtype:
            data["type"] = self.rtype
        if self.at:
            data["at"] = self.at
        if self.required:
            data["required"] = self.required
        if self.candidates:
            data["candidates"] = self.candidates
            data["strict_candidates"] = self.strict_candidates
        if self.default:
            data["default"] = self.default
        if self.default_text:
            data["default_text"] = self.default_text
        if self.default_keyword != self.keyword:
            data["default_keyword"] = self.default_keyword
        if self.inheritance:
            data["inheritance"] = self.inheritance
        if self.scope_order:
            data["scope_order"] = self.scope_order
        if self.provisioning:
            data["provisioning"] = self.provisioning
        if self.depends:
            data["depends"] = self.depends
        if self.convert:
            data["convert"] = self.convert
        else:
            data["convert"] = "string"
        if self.text:
            data["text"] = self.text
        return data

class Section(object):
    def __init__(self, section, top=None):
        self.section = section
        self.top = top
        self.data = {}
        self.sigs = []
        self.rtypes = set()

    def __repr__(self):
        return "<Section %s keywords:%d>" % (self.section, len(self.data))

    def __str__(self):
        return "<Section %s keywords:%d>" % (self.section, len(self.data))

    def __iadd__(self, o):
        if not isinstance(o, Keyword):
            return self
        if o.rtype is None:
            sig = (None, o.keyword)
            if sig in self.data:
                return
            self.data[sig] = o
        else:
            for rt in o.rtype:
                sig = (rt, o.keyword)
                if sig in self.data:
                    continue
                self.data[sig] = o
                if rt is not None:
                    self.rtypes.add(rt)
        return self

    @property
    def keywords(self):
        return self.data.values()

    def dump(self):
        data = []
        for kw in self.keywords:
            data.append(kw.dump())
        return data

    def template(self, fmt="text", write=False):
        k = self.getkey("type")
        if k is None:
            return self._template(fmt=fmt, write=write)
        if k.candidates is None:
            return self._template(fmt=fmt, write=write)
        s = ""
        if not k.strict_candidates:
            s += self._template(fmt=fmt, write=write)
        for t in k.candidates:
            s += self._template(t, fmt=fmt, write=write)
        return s

    def _template(self, rtype=None, fmt="text", write=False):
        section = self.section
        if self.section in self.top.deprecated_sections:
            return ""
        if rtype and self.top and self.section+"."+rtype in self.top.deprecated_sections:
            return ""
        if fmt == "text":
            return self._template_text(rtype, section, write=write)
        elif fmt == "rst":
            return self._template_rst(rtype, section, write=write)
        else:
            return ""

    def _template_text(self, rtype, section, write=False):
        fpath = os.path.join(Env.paths.pathdoc, self.top.template_prefix+section+".conf")
        if rtype:
            section += ", type "+rtype
            fpath = os.path.join(Env.paths.pathdoc, 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#rindex]\n" % self.section
        done = []
        if rtype is not None:
            s += ";type = " + rtype + "\n\n"
            for keyword in sorted(self.getkeys(rtype)):
                s += keyword.template(fmt="text")
                done.append(keyword.keyword)
        for keyword in sorted(self.getprovkeys(rtype)):
            if keyword.keyword in done:
                continue
            s += keyword.template(fmt="text")
        for keyword in sorted(self.getkeys()):
            if keyword.keyword in done:
                continue
            if keyword.keyword == "type":
                continue
            s += keyword.template(fmt="text")
        if write:
            print("write", fpath)
            with open(fpath, "w") as f:
                f.write(s)
        return s

    def _template_rst(self, rtype, section, write=False):
        dpath = os.path.join(Env.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 self.top.template_prefix != "template.cluster." 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)
        if write:
            print("write", fpath)
            with open(fpath, "w") as f:
                f.write(s)
        return s

    def getallkeys(self, rtype=None):
        if rtype is None:
            return [k for k in self.keywords if k.rtype is None]
        elif rtype in self.rtypes:
            return [k for k in self.keywords if k.rtype and rtype in k.rtype]
        else:
            # non-strict rtype candidates. ex: fs.vfat falls back to fs
            return [k for k in self.keywords if k.rtype == ""]

    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]
        elif rtype in self.rtypes:
            return [k for k in self.keywords if k.rtype and rtype in k.rtype and not k.provisioning]
        else:
            return [k for k in self.keywords if 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]
        elif rtype in self.rtypes:
            return [k for k in self.keywords if k.rtype and rtype in k.rtype and k.provisioning]
        else:
            return [k for k in self.keywords if k.rtype == "" and k.provisioning]

    def getkey(self, keyword, rtype=None):
        try:
            keyword, _ = keyword.split("@", 1)
        except ValueError:
            pass
        k = None
        if rtype:
            if rtype not in self.rtypes:
                # unknown rtype, allowed by non-strict candidates,
                # are routed to special type ''.
                rtype = ""
                fkey = ".".join((self.section, keyword))
            else:
                fkey = ".".join((self.section, rtype, keyword))
            if fkey in self.top.deprecated_keywords:
                keyword = self.top.deprecated_keywords[fkey]
                new_keyword = self.top.deprecated_keywords[fkey]
                if new_keyword:
                    keyword = new_keyword
            k = self.data.get((rtype, keyword))
            if not k:
                k = self.data.get((None, keyword))
        else:
            fkey = ".".join((self.section, keyword))
            if fkey in self.top.deprecated_keywords:
                new_keyword = self.top.deprecated_keywords[fkey]
                if new_keyword:
                    keyword = new_keyword
            k = self.data.get((None, keyword))
        return k

class KeywordStore(dict):
    def __init__(self, name=None, provision=False, keywords=None, deprecated_keywords=None,
                 reverse_deprecated_keywords=None,
                 deprecated_sections=None, template_prefix="template.",
                 base_sections=None, has_default_section=True):
        dict.__init__(self)
        self.name = name
        self.sections = {}
        self.deprecated_sections = deprecated_sections or {}
        self.deprecated_keywords = deprecated_keywords or {}
        self.reverse_deprecated_keywords = reverse_deprecated_keywords or {}
        self.template_prefix = template_prefix
        self.base_sections = base_sections or ["DEFAULT"]
        self.provision = provision
        self.has_default_section = has_default_section
        self.modules = set()

        for keyword in keywords or []:
            sections = keyword.get("sections", [keyword.get("section")])
            prefixes = keyword.get("prefixes", [""])
            for section in sections:
                for prefix in prefixes:
                    data = dict((key, val) for (key, val) in keyword.items() if key not in ("sections", "prefixes"))
                    try:
                        data.update({
                            "section": section,
                            "keyword": prefix+keyword["keyword"],
                            "text": keyword["text"].replace("{prefix}", prefix),
                        })
                        self += Keyword(**data)
                    except KeyError as exc:
                        raise ex.Error("misformatted keyword definition: %s: %s" % (exc, data))

    def __str__(self):
        return "<KeywordStore name:%s sections:%d keywords:%d>" % (self.name, len(self.sections), self.keywords_count())

    def __getstate__(self):
        return self.__dict__

    def __setstate__(self, d):
        self.__dict__ = d

    def keywords_count(self):
        n = 0
        for section in self.sections.values():
            n += len(section.keywords)
        return n

    def register_driver(self, driver_group, driver_basename, keywords=None, driver_basename_aliases=None, **kwargs):
        keywords = [
            dict(k, section=driver_group, rtype=driver_basename) for k in keywords
        ]
        self += KeywordStore(
            keywords=keywords,
            **kwargs
        )
        driver_basename_aliases = driver_basename_aliases or []
        for alias in driver_basename_aliases:
            keywords = [
                dict(k, section=driver_group, rtype=alias) for k in keywords
            ]
            self += KeywordStore(
                keywords=keywords,
                **kwargs
            )

    def driver_kwstore(self, mod=None, modname=None):
        if mod is None:
            try:
                mod = __import__(modname)
            except Exception:
                return
        if modname is None:
            modname = mod.__name__
        if modname in self.modules:
            # already merged
            return
        self.modules.add(modname)
        kwargs = {
            "name": modname,
            "provision": True,
            "base_sections": ["env", "DEFAULT"],
            "template_prefix": "template.service.",
        }
        # mandatory attributes:
        # - DRIVER_GROUP
        # - DRIVER_BASENAME
        # - KEYWORDS
        try:
            kwargs["keywords"] = [
                dict(k, section=mod.DRIVER_GROUP, rtype=mod.DRIVER_BASENAME)
                for k in getattr(mod, "KEYWORDS")
            ]
        except AttributeError:
            return

        # optional attributes:
        # - DEPRECATED_SECTIONS
        # - DEPRECATED_KEYWORDS
        # - REVERSE_DEPRECATED_KEYWORDS
        # - DRIVER_BASENAME_ALIASES
        for kwarg in ("deprecated_sections", "deprecated_keywords", "reverse_deprecated_keywords"):
            try:
                kwargs[kwarg] = getattr(mod, kwarg.upper())
            except:
                pass
        try:
            aliases = getattr(mod, "DRIVER_BASENAME_ALIASES")
        except AttributeError:
            aliases = []
        for alias in aliases:
            kwargs["keywords"] += [
                dict(k, section=mod.DRIVER_GROUP, rtype=alias)
                for k in getattr(mod, "KEYWORDS")
            ]

        kwstore = KeywordStore(**kwargs)
        return kwstore

    def __iadd__(self, o):
        if o is None:
            return self
        if isinstance(o, Keyword):
            return self.__iadd_keyword__(o)
        if isinstance(o, Section):
            return self.__iadd_section__(o)
        if isinstance(o, KeywordStore):
            return self.__iadd_keyword_store__(o)
        if isinstance(o, str):
            return self.__iadd_drivername__(o)
        if hasattr(o, "KEYWORDS"):
            return self.__iadd_driver__(o)
        return self

    def __iadd_driver__(self, other):
        self += self.driver_kwstore(mod=other)
        return self

    def __iadd_drivername__(self, other):
        self += self.driver_kwstore(modname=other)
        return self

    def __iadd_keyword_store__(self, other):
        for section in other.sections.values():
            self += section
        self.deprecated_sections.update(other.deprecated_sections)
        self.deprecated_keywords.update(other.deprecated_keywords)
        self.reverse_deprecated_keywords.update(other.reverse_deprecated_keywords)
        return self

    def __iadd_section__(self, section):
        #print("   ", section.section)
        if section.section not in self.sections:
            self.sections[section.section] = Section(section.section, top=self)
        rtype_key = self[section.section].getkey("type")
        for kw in section.keywords:
            #print("    ", kw.keyword)
            self.sections[section.section] += kw
            if rtype_key \
               and isinstance(rtype_key.candidates, list):
                rtypes = kw.rtype if kw.rtype is not None else [None]
                for rtype in rtypes:
                    if rtype not in rtype_key.candidates:
                        rtype_key.candidates.append(rtype)
        return self

    def __iadd_keyword__(self, o):
        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, top=self)
        return self.sections[k]

    def dump(self):
        data = {}
        for section in sorted(self.sections):
            data[section] = self.sections[section].dump()
        return data

    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 write_templates(self, fmt="text"):
        """
        Write templates in the spectified format (text by default, or rst).
        """
        for section in sorted(self.sections):
            self.sections[section].template(fmt=fmt, write=True)

    def required_keys(self, section, rtype=None):
        """
        Return the list of required keywords in the section for the resource
        type specified by <rtype>.
        """
        try:
            return [k for k in sorted(self.sections[section].getkeys(rtype)) if k.required is True]
        except KeyError:
            return []

    def optional_keys(self, section, rtype=None):
        """
        Return the list of optional keywords in the section for the resource
        type specified by <rtype>.
        """
        try:
            return [k for k in sorted(self.sections[section].getkeys(rtype)) if k.required is False]
        except KeyError:
            return []

    def all_keys(self, section, rtype=None):
        """
        Return the list of optional keywords in the section for the resource
        type specified by <rtype>.
        """
        try:
            return sorted(self.sections[section].getallkeys(rtype))
        except KeyError:
            return []

    def section_kwargs(self, cat, rtype=None):
        kwargs = {}
        for keyword in self.all_keys(cat, rtype):
            try:
                kwargs[keyword.name] = self.conf_get(cat, keyword.name)
            except ex.RequiredOptNotFound:
                raise
            except ex.OptNotFound as exc:
                kwargs[keyword.name] = exc.default
        return kwargs

    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
0707010001f16e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/core/collector 0707010001f16f000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/core/collector/__init__.py 0707010001f172000081a40000000000000000000000016a100daf0000d5e4000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/collector/rpc.py  from __future__ import print_function

import logging
import logging.handlers
import os
import random
import socket
import sys
import time
import json
import shutil
import tempfile
from datetime import datetime

import core.exceptions as ex
import foreign.six as six
from env import Env
from utilities.naming import split_path
from utilities.converters import print_duration
from utilities.uri import ssl_context_kwargs

socket.setdefaulttimeout(5)

kwargs = ssl_context_kwargs()
kwargs["allow_none"] = True

try:
    import xmlrpclib
except ImportError:
    import xmlrpc.client as xmlrpclib


def get_proxy(uri):
    try:
        return xmlrpclib.ServerProxy(uri, **kwargs)
    except Exception as e:
        if "__init__" in str(e):
            return xmlrpclib.ServerProxy(uri)


Env.warned = False

logfile = os.path.join(Env.paths.pathlog, 'xmlrpc.log')
log = logging.getLogger("xmlrpc")
log.setLevel(logging.INFO)

LO_ADDR = "127.0.0.1"
DUMMY_URL = "https://" + LO_ADDR

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
    s = ""
    for i in range(tries):
        try:
            return _do_call(fn, args, kwargs, log, proxy, mode=mode)
        except socket.timeout:
            max_wait = 1
        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, s))
    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))
    _b = datetime.now()
    try:
        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 socket.timeout:
        raise
    except (OSError, Exception) as exc:
        # socket.gaierror (name resolution failure) is a subclass of OSError in py3.3+
        _e = datetime.now()
        _d = _e - _b
        log.error("call %s error after %d.%03d seconds: %s" % (fn, _d.seconds, _d.microseconds//1000, exc))
        if hasattr(exc, "faultString"):
            raise ex.Error(getattr(exc, "faultString").split(":", 1)[-1])
        else:
            raise ex.Error(str(exc))


class CollectorRpc(object):
    def call(self, *args, **kwargs):
        fn = args[0]
        self.init(fn)
        if self.node.collector_env.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 self.node.collector_env.uuid == "" and \
           self.node.collector_env.dbopensvc is not None and \
           not Env.warned and \
           fn != "register_node":
            print("this node is not registered. try 'om node register'", file=sys.stderr)
            print("to disable this warning, set 'dbopensvc = None' in node.conf", file=sys.stderr)
            Env.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.log = logging.getLogger("xmlrpc")

    def list_methods(self, t):
        if self.list_methods_outdated(t):
            try:
                l = self.remote_list_methods(t)
                self.dump_list_methods(t, l)
                return l
            except Exception:
                pass
        l = self.load_list_methods(t)
        return l

    def remote_list_methods(self, t):
        self.log.info("refresh %s methods", t)
        if t == "proxy":
            return self.proxy.system.listMethods()
        else:
            return self.comp_proxy.system.listMethods()

    @staticmethod
    def file_list_methods(t):
        return os.path.join(Env.paths.pathvar, "node", "rpc_list_methods."+t)

    def dump_list_methods(self, t, l):
        p = self.file_list_methods(t)
        f = tempfile.NamedTemporaryFile(prefix="rpc_list_methods."+t+".", dir=os.path.dirname(p))
        tmpp = f.name
        f.close()
        with open(tmpp, "w") as f:
            json.dump(l, f)
        shutil.move(tmpp, p)
        return l

    def list_methods_outdated(self, t):
        p = self.file_list_methods(t)
        try:
            mtime = os.path.getmtime(p)
        except Exception:
            self.log.info("no %s method list cache", t)
            return True
        now = time.time()
        threshold = mtime + 86400 + random.random()*3600  # 1d + random(1h)
        if now > threshold:
            self.log.info("%s method list cache too old (%s)", t, print_duration(now-mtime))
            return True
        return False

    def load_list_methods(self, t):
        self.log.debug("get %s method list from cache", t)
        p = self.file_list_methods(t)
        with open(p, "r") as f:
            l = json.load(f)
            return l

    def get_methods_dbopensvc(self):
        if self.node.collector_env.dbopensvc is None:
            self.proxy_methods = []
            return
        try:
            if self.proxy is None:
                self.proxy = get_proxy(self.node.collector_env.dbopensvc)
            self.proxy_methods = self.list_methods("proxy")
        except Exception as exc:
            self.log.error("get dbopensvc methods: %s", exc)
            self.proxy = get_proxy(DUMMY_URL)
            self.proxy_methods = []
        self.log.debug("%d feed methods" % len(self.proxy_methods))

    def get_methods_dbcompliance(self):
        if self.node.collector_env.dbcompliance is None:
            self.comp_proxy_methods = []
            return
        try:
            if self.comp_proxy is None:
                self.comp_proxy = get_proxy(self.node.collector_env.dbcompliance)
            self.comp_proxy_methods = self.list_methods("comp_proxy")
        except Exception as exc:
            self.log.error("get dbcompliance methods: %s", exc)
            self.comp_proxy = get_proxy(DUMMY_URL)
            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 self.node.collector_env.dbopensvc is None:
            return

        self.init_feed_proxy()
        if fn in self.comp_fns:
            self.init_comp_proxy()

    def init_feed_proxy(self):
        try:
            a = socket.getaddrinfo(self.node.collector_env.dbopensvc_host, None)
            if len(a) == 0:
                raise Exception
            dbopensvc_ip = a[0][-1][0]
        except Exception:
            self.log.error("could not resolve %s to an ip address. disable collector updates.",
                           self.node.collector_env.dbopensvc_host)
            self.proxy = get_proxy(DUMMY_URL)
            return
        try:
            self.proxy = get_proxy(self.node.collector_env.dbopensvc)
            self.get_methods_dbopensvc()
        except Exception as exc:
            self.log.error("init dbopensvc: %s", exc)
            self.proxy = get_proxy(DUMMY_URL)
            return
        self.log.info("feed proxy %s", str(self.proxy))

    def init_comp_proxy(self):
        try:
            a = socket.getaddrinfo(self.node.collector_env.dbcompliance_host, None)
            if len(a) == 0:
                raise Exception
            dbcompliance_ip = a[0][-1][0]
        except Exception:
            self.log.error("could not resolve %s to an ip address. disable collector updates.",
                           self.node.collector_env.dbcompliance_host)
            self.comp_proxy = get_proxy(DUMMY_URL)
            return
        try:
            self.comp_proxy = get_proxy(self.node.collector_env.dbcompliance)
            self.get_methods_dbcompliance()
        except Exception:
            self.comp_proxy = get_proxy(DUMMY_URL)
            return
        self.log.info("compliance proxy %s", str(self.comp_proxy))

    def disable(self):
        self.proxy = None

    def disabled(self):
        """
        Example repr():
            <ServerProxy for 127.0.0.1/RPC2>
        """
        return self.proxy is None or LO_ADDR in repr(self.proxy)

    def reinit(self):
        if self.disabled():
            self.log.info("disabled")
            self.init()
            if not self.disabled():
                self.log.info("the collector is available. proxies reinitialized")
                return True
        return False

    def begin_action(self, svcname, action, version, begin, cron, sid, argv):
        from subprocess import list2cmdline
        args = [
            ['svcname',
             'action',
             'hostname',
             'sid',
             'version',
             'begin',
             'status_log',
             'cron'],
            [str(svcname),
             str(action),
             str(Env.nodename),
             sid,
             str(version),
             str(datetime.fromtimestamp(begin).strftime("%Y-%m-%d %H:%M:%S")),
             list2cmdline(argv),
             '1' if cron else '0']
        ]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.begin_action(*args)

    def end_action(self, path, action, begin, end, cron, sid, alogfile, err):
        if err == 0:
            err = "ok"
        else:
            err = "err"
        rid = None
        rid_err = None
        pid = None
        msg = None
        name, namespace, kind = split_path(path)
        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",     # 1
                "hostname",
                "sid",
                "pid",        # 4
                "rid",        # 5
                "subset",
                "begin",
                "end",
                "status_log", # 9
                "status",     # 10
                "cron"]
        vals = []
        last = []
        for line in lines.split(";;EOL\n"):
            if line.count(";;") != 4:
                continue
            if ";;status_history;;" in line:
                continue
            rid_err = "ok"
            date, rid, lvl, msg, pid = line.split(";;")
            rid = rid.lower()
            rid = rid.replace(Env.nodename+"."+kind+"."+name, "")
            rid = rid.replace(Env.nodename+"."+name, "")
            rid = rid.replace(Env.nodename, "")
            rid = rid.lstrip(".")
            subset = ""

            # container:front#nginx
            # container#nginx
            # task
            if ":" in rid:
                rgrp, rid = rid.split(":")
                if "#" in rid:
                    subset, rname = rid.split("#")
                    rid = rgrp + "#" + rname

            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 |= {pid}
            if lvl is None or lvl == "DEBUG":
                continue
            elif lvl == "ERROR":
                rid_err = "err"
            elif lvl == "WARNING":
                rid_err = "warn"

            try:
                if last:
                    if last[1] == action and last[4] == pid and last[5] == rid and last[10] == rid_err:
                        last[9] += "\n"+msg
                        continue
                    else:
                        vals.append(last)
            except Exception as exc:
                print(exc)
                continue
            
            last = [
                path,
                action,
                Env.nodename,
                sid,
                pid,
                rid,
                subset,
                date,
                "",
                msg,
                rid_err,
                "1" if cron else "0"
            ]

        if last:
            vals.append(last)

        if len(vals) > 0:
            args = [vars, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.res_action_batch(*args)

        """Complete the wrap-up database entry
        """

        duration_in_second = int(round(end - begin))
        args = [
            ['svcname',
             'action',
             'hostname',
             'begin',
             'end',
             'time',
             'status',
             'cron'],
            [str(path),
             str(action),
             str(Env.nodename),
             datetime.fromtimestamp(begin).strftime("%Y-%m-%d %H:%M:%S"),
             datetime.fromtimestamp(end).strftime("%Y-%m-%d %H:%M:%S"),
             str(duration_in_second),
             str(err),
             '1' if cron else '0']
        ]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.end_action(*args)

    def svcmon_update_combo(self, g_vars, g_vals, r_vars, r_vals):
        args = [g_vars, g_vals, r_vars, r_vals]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.svcmon_update_combo(*args)

    def push_resinfo(self, vals, sync=False):
        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]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        if sync:
            self.proxy.update_resinfo_sync(*args)
        else:
            self.proxy.update_resinfo(*args)

    def push_config(self, svc, sync=True):
        if svc.encap:
            self.log.info("skip push config for encap object %s", svc.path)
            return

        def repr_config(svc):
            import codecs
            cfg_file = svc.path_cf
            if not os.path.exists(cfg_file):
                return
            with codecs.open(cfg_file, 'r', encoding="utf8") as f:
                buff = f.read()
                return buff

        vars = ['svc_name',
                'cluster_id',
                'svc_topology',
                'svc_flex_min_nodes',
                'svc_flex_max_nodes',
                'svc_flex_target',
                '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.path,
                svc.cluster_id,
                svc.topology,
                svc.flex_min,
                svc.flex_max,
                svc.flex_target,
                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]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.update_service(*args)

    def push_containerinfo(self, path, containers, sync=True):
        vars = ['mon_svcname',
                'mon_nodname',
                'mon_vmname',
                'mon_guestos',
                'mon_vmem',
                'mon_vcpus',
                'mon_containerpath']
        vals = []

        for container in containers:
            vals += [[
                path,
                Env.nodename,
                container.vm_hostname,
                container.guestos,
                container.vmem,
                container.vcpus,
                container.zonepath
            ]]

        if len(vals) > 0:
            args = [vars, vals]
            args += [(self.node.collector_env.uuid, Env.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 = []

        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"],
                 disk["dg"],
                 Env.nodename,
                 service["region"],
                ])
            if disk["used"] < disk["size"]:
                vals.append([
                 disk_id,
                 "",
                 disk["size"],
                 disk["size"] - disk["used"],
                 disk["vendor"],
                 disk["model"],
                 disk["dg"],
                 Env.nodename,
                 0,
                ])

        args = [vars, vals]
        args += [(self.node.collector_env.uuid, Env.nodename)]

        # Avoid "read timeout" error for big datasets: add 1s every 50 disks
        oldtmo = socket.getdefaulttimeout()
        socket.setdefaulttimeout(oldtmo+len(vals)//50+1)
        proxy = get_proxy(self.node.collector_env.dbopensvc)

        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]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        proxy.register_diskinfo(*args)
        socket.setdefaulttimeout(oldtmo)

    def push_stats_fs_u(self, l, sync=True):
        args = [l[0], l[1]]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.insert_stats_fs_u(*args)

    def push_pkg(self, pkgs, sync=True):
        vars = ['pkg_nodename',
                'pkg_name',
                'pkg_version',
                'pkg_arch']
        n = len(pkgs)
        if n == 0:
            return
        n_fields = len(pkgs[0])
        if n_fields >= 5:
            vars.append('pkg_type')
        if n_fields >= 6:
            vars.append('pkg_install_date')
            for i, pkg in enumerate(pkgs):
                if len(pkg) > 5:
                    pkgs[i][5] = from_epoch(pkg[5]) if pkg[5] is not None else None
                else:
                    pkgs[i].append(None)
        if n_fields >= 7:
            vars.append('pkg_sig')
        args = [vars, pkgs]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        self.proxy.insert_pkg(*args)

    def push_patch(self, sync=True):
        import utilities.packages.list as p
        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 = [vars, vals]
        args += [(self.node.collector_env.uuid, Env.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:
            from utilities.stats.provider import StatsProvider
        except ImportError:
            return

        try:
            sp = 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])))
        args = [json.dumps(h)]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        print("pushing")
        self.proxy.insert_stats(*args)

    def sysreport_lstree(self, sync=True):
        args = []
        args += [(self.node.collector_env.uuid, Env.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]
        args += [(self.node.collector_env.uuid, Env.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 = [(Env.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]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.insert_generic(*args)

        _vars = []
        _vals = []
        for key, _d in d.items():
            _vars.append(key)
            if _d["value"] is None:
                _d["value"] = ""
            if key == "last_boot":
                _vals.append(from_epoch(_d["value"]))
            else:
                _vals.append(_d["value"])
        args = [_vars, _vals]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        if node.options.syncrpc:
            self.proxy.update_asset_sync(*args)
        else:
            self.proxy.update_asset(*args)

    def daemon_ping(self, sync=True):
        args = [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.daemon_ping(*args)

    def push_status(self, svcname, data, sync=True):
        if data.get("encap", False) is True:
            self.log.info("skip push status for encap object %s", svcname)
            return
        try:
            # push cluster_id to allow reconcile node cluster_id after join
            data["cluster_id"] = self.node.cluster_id
        except:
            pass
        args = [svcname, json.dumps(data), (self.node.collector_env.uuid, Env.nodename)]
        self.proxy.push_status(*args)

    def push_daemon_status(self, data, changes=None, sync=True):
        args = [json.dumps(data), json.dumps(changes), (self.node.collector_env.uuid, Env.nodename)]
        self.proxy.push_daemon_status(*args)

    def push_brocade(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_brocade' not in self.proxy_methods:
            print("'update_brocade' method is not exported by the collector")
            return
        import drivers.sanswitch.brocade as m
        try:
            brocades = m.Brocades(objects)
        except Exception:
            return
        for brocade in brocades:
            vals = []
            for key in brocade.keys:
                try:
                    vals.append(getattr(brocade, 'get_'+key)())
                except Exception:
                    print("error fetching", key)
                    continue
            args = [brocade.name, brocade.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_brocade(*args)

    def push_vioserver(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_vioserver' not in self.proxy_methods:
            print("'update_vioserver' method is not exported by the collector")
            return
        import drivers.array.vioserver as m
        try:
            vioservers = m.VioServers(objects)
        except Exception:
            return
        for vioserver in vioservers:
            vals = []
            for key in vioserver.keys:
                vals.append(getattr(vioserver, 'get_'+key)())
            args = [vioserver.name, vioserver.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_vioserver(*args)

    def push_hds(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_hds' not in self.proxy_methods:
            print("'update_hds' method is not exported by the collector")
            return
        import drivers.array.hds as m
        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]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_hds(*args)

    def push_necism(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_necism' not in self.proxy_methods:
            print("'update_necism' method is not exported by the collector")
            return
        import drivers.array.necism as m
        try:
            necisms = m.NecIsms(objects)
        except Exception:
            return
        for necism in necisms:
            vals = []
            for key in necism.keys:
                vals.append(getattr(necism, 'get_'+key)())
            args = [necism.name, necism.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_necism(*args)

    def push_hp3par(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_hp3par' not in self.proxy_methods:
            print("'update_hp3par' method is not exported by the collector")
            return
        import drivers.array.hp3par as m
        try:
            hp3pars = m.Hp3pars(objects)
        except Exception:
            return
        for hp3par in hp3pars:
            vals = []
            for key in hp3par.keys:
                vals.append(getattr(hp3par, 'get_'+key)())
            args = [hp3par.name, hp3par.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_hp3par(*args)

    def push_centera(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_centera' not in self.proxy_methods:
            print("'update_centera' method is not exported by the collector")
            return
        import drivers.array.centera as m
        try:
            centeras = m.Centeras(objects)
        except Exception:
            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]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_centera(*args)

    def push_emcvnx(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_emcvnx' not in self.proxy_methods:
            print("'update_emcvnx' method is not exported by the collector")
            return
        import drivers.array.emcvnx as m
        try:
            emcvnxs = m.EmcVnxs(objects)
        except Exception:
            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]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_emcvnx(*args)

    def push_netapp(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_netapp' not in self.proxy_methods:
            print("'update_netapp' method is not exported by the collector")
            return
        import drivers.array.netapp as m
        try:
            netapps = m.Netapps(objects)
        except Exception:
            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]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_netapp(*args)

    def push_ibmsvc(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_ibmsvc' not in self.proxy_methods:
            print("'update_ibmsvc' method is not exported by the collector")
            return
        import drivers.array.ibmsvc as m
        try:
            ibmsvcs = m.IbmSvcs(objects)
        except Exception:
            return
        for ibmsvc in ibmsvcs:
            vals = []
            for key in ibmsvc.keys:
                vals.append(getattr(ibmsvc, 'get_'+key)())
            args = [ibmsvc.name, ibmsvc.keys, vals]
            args += [(self.node.collector_env.uuid, Env.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
        import drivers.backupsrv.networker as m
        try:
            nsr = m.Nsr()
        except Exception:
            return
        vals = []
        for key in nsr.keys:
            vals.append(getattr(nsr, 'get_'+key)())
        args = [Env.nodename, nsr.keys, vals]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        try:
            self.proxy.update_nsr(*args)
        except Exception:
            print("error pushing nsr index")

    def push_ibmds(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_ibmds' not in self.proxy_methods:
            print("'update_ibmds' method is not exported by the collector")
            return
        import drivers.array.ibmds as m
        try:
            ibmdss = m.IbmDss(objects)
        except Exception:
            return
        for ibmds in ibmdss:
            vals = []
            for key in ibmds.keys:
                vals.append(getattr(ibmds, 'get_'+key)())
            args = [ibmds.name, ibmds.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            try:
                self.proxy.update_ibmds(*args)
            except Exception:
                print("error pushing", ibmds.name)

    def push_gcedisks(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_gcedisks' not in self.proxy_methods:
            print("'update_gcedisks' method is not exported by the collector")
            return
        import drivers.array.gce as m
        try:
            arrays = m.GceDiskss(objects)
        except Exception:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            args += [(self.node.collector_env.uuid, Env.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=None, sync=True):
        if objects is None:
            objects = []
        if 'update_freenas' not in self.proxy_methods:
            print("'update_freenas' method is not exported by the collector")
            return
        import drivers.array.freenas as m
        try:
            arrays = m.Freenass(objects)
        except Exception:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            try:
                self.proxy.update_freenas(*args)
            except Exception:
                print("error pushing", array.name)

    def push_pure(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_pure' not in self.proxy_methods:
           print("'update_pure' method is not exported by the collector")
           return
        import drivers.array.pure as m
        try:
            arrays = m.Arrays(objects)
        except:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(json.dumps(getattr(array, 'get_'+key)()))
            args = [array.name, array.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            try:
                self.proxy.update_pure(*args)
            except Exception as exc:
                print("error pushing", array.name, file=sys.stderr)
                print(exc, file=sys.stderr)
                raise ex.Error

    def push_xtremio(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_xtremio' not in self.proxy_methods:
            print("'update_xtremio' method is not exported by the collector")
            return
        import drivers.array.xtremio as m
        try:
            arrays = m.Arrays(objects)
        except Exception:
            return
        for array in arrays:
            vals = []
            for key in array.keys:
                vals.append(getattr(array, 'get_'+key)())
            args = [array.name, array.keys, vals]
            args += [(self.node.collector_env.uuid, Env.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.Error

    def push_eva(self, objects=None, sync=True):
        if objects is None:
            objects = []
        if 'update_eva_xml' not in self.proxy_methods:
            print("'update_eva_xml' method is not exported by the collector")
            return
        import drivers.array.eva as m
        try:
            evas = m.Evas(objects)
        except Exception:
            return
        for eva in evas:
            vals = []
            for key in eva.keys:
                vals.append(getattr(eva, 'get_'+key)())
            args = [eva.name, eva.keys, vals]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_eva_xml(*args)

    def push_hcs(self, objects=None, sync=True):
        if objects is None:
            objects = []
        import drivers.array.hcs as m
        try:
            arrays = m.Hcss(objects)
        except Exception as e:
            print(e)
            return 1
        r = 0
        try:
            for array in arrays:
                # can be too big for a single rpc
                print(array.name)
                for key in array.keys:
                    print(" extract", key)
                    vars = [key]
                    try:
                        data = getattr(array, 'get_'+key)()
                        vals = [json.dumps(data)]
                    except Exception as e:
                        print(e)
                        continue
                    args = [array.name, vars, vals]
                    args += [(self.node.collector_env.uuid, Env.nodename)]
                    try:
                        print(" send   ", key)
                        self.proxy.update_hcs(*args)
                    except Exception as e:
                        print(array.name, key, ":", e)
                        r = 1
                        continue
                # signal all files are received
                args = [array.name, [], []]
                args += [(self.node.collector_env.uuid, Env.nodename)]
                self.proxy.update_hcs(*args)
        finally:
            for array in arrays:
                array.close_session()
        return r

    def push_dorado(self, objects=None, sync=True):
        if objects is None:
            objects = []
        import drivers.array.dorado as m
        try:
            arrays = m.Dorados(objects)
        except Exception as e:
            print(e)
            return 1
        r = 0
        try:
            for array in arrays:
                # can be too big for a single rpc
                print(array.name)
                for key in array.keys:
                    print(" extract", key)
                    vars = [key]
                    try:
                        data = getattr(array, 'get_'+key)()
                        vals = [json.dumps(data)]
                    except Exception as e:
                        print(e)
                        continue
                    args = [array.name, vars, vals]
                    args += [(self.node.collector_env.uuid, Env.nodename)]
                    try:
                        print(" send   ", key)
                        self.proxy.update_dorado_xml(*args)
                    except Exception as e:
                        print(array.name, key, ":", e)
                        r = 1
                        continue
                # signal all files are received
                args = [array.name, [], []]
                args += [(self.node.collector_env.uuid, Env.nodename)]
                self.proxy.update_dorado_xml(*args)
        finally:
            for array in arrays:
                array.close_session()
        return r

    def push_sym(self, objects=None, sync=True):
        if objects is None:
            objects = []
        import zlib
        if 'update_sym_xml' not in self.proxy_methods:
            print("'update_sym_xml' method is not exported by the collector")
            return 1
        import drivers.array.symmetrix as m
        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:
                    data = getattr(sym, 'get_'+key)()
                    if six.PY3:
                        data = bytes(data, "utf-8")
                    vals = [xmlrpclib.Binary(zlib.compress(data))]
                except Exception as e:
                    print(e)
                    continue
                args = [sym.sid, vars, vals]
                args += [(self.node.collector_env.uuid, Env.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, [], []]
            args += [(self.node.collector_env.uuid, Env.nodename)]
            self.proxy.update_sym_xml(*args)
        return r

    def push_checks(self, data, sync=True):
        if "push_checks" not in self.proxy_methods:
            print("'push_checks' method is not exported by the collector")
            return
        vars = [
            "chk_nodename",
            "chk_svcname",
            "chk_type",
            "chk_instance",
            "chk_value",
            "chk_updated"]
        vals = []
        now = str(datetime.now())
        for chk_type, d in data.items():
            for instance in d:
                vals.append([
                    Env.nodename,
                    instance["path"],
                    chk_type,
                    instance['instance'],
                    str(instance['value']).replace("%", ""),
                    now]
                )
        self.proxy.push_checks(vars, vals, (self.node.collector_env.uuid, Env.nodename))

    def register_node(self, sync=True):
        return self.proxy.register_node(Env.nodename)

    def comp_get_data(self, modulesets=None, sync=True):
        if modulesets is None:
            modulesets = []
        args = [Env.nodename, modulesets]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_data_v2(*args)

    def comp_get_svc_data(self, svcname, modulesets=None, sync=True):
        if modulesets is None:
            modulesets = []
        args = [Env.nodename, svcname, modulesets]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_svc_data_v2(*args)

    def comp_get_data_moduleset(self, sync=True):
        args = [Env.nodename]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_data_moduleset(*args)

    def comp_get_svc_data_moduleset(self, svc, sync=True):
        args = [svc]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_svc_data_moduleset(*args)

    def comp_attach_moduleset(self, moduleset, sync=True):
        args = [Env.nodename, moduleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_attach_moduleset(*args)

    def comp_attach_svc_moduleset(self, svc, moduleset, sync=True):
        args = [svc, moduleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_attach_svc_moduleset(*args)

    def comp_detach_svc_moduleset(self, svcname, moduleset, sync=True):
        args = [svcname, moduleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_detach_svc_moduleset(*args)

    def comp_detach_moduleset(self, moduleset, sync=True):
        args = [Env.nodename, moduleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_detach_moduleset(*args)

    def comp_get_svc_ruleset(self, svcname, sync=True):
        args = [svcname]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_svc_ruleset(*args)

    def comp_get_ruleset(self, sync=True):
        args = [Env.nodename]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_ruleset(*args)

    def comp_get_ruleset_md5(self, rset_md5, sync=True):
        args = [rset_md5]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_get_ruleset_md5(*args)

    def comp_attach_ruleset(self, ruleset, sync=True):
        args = [Env.nodename, ruleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_attach_ruleset(*args)

    def comp_detach_svc_ruleset(self, svcname, ruleset, sync=True):
        args = [svcname, ruleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_detach_svc_ruleset(*args)

    def comp_attach_svc_ruleset(self, svcname, ruleset, sync=True):
        args = [svcname, ruleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_attach_svc_ruleset(*args)

    def comp_detach_ruleset(self, ruleset, sync=True):
        args = [Env.nodename, ruleset]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_detach_ruleset(*args)

    def comp_list_ruleset(self, pattern='%', sync=True):
        args = [pattern, Env.nodename]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_list_rulesets(*args)

    def comp_list_moduleset(self, pattern='%', sync=True):
        args = [pattern]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_list_modulesets(*args)

    def comp_log_actions(self, vars, vals, sync=True):
        args = [vars, vals]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_log_actions(*args)

    def comp_show_status(self, svcname, pattern='%', sync=True):
        args = [svcname, pattern]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.comp_proxy.comp_show_status(*args)

    def collector_update_root_pw(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_update_root_pw(*args)

    def collector_ack_unavailability(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_ack_unavailability(*args)

    def collector_list_unavailability_ack(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_unavailability_ack(*args)

    def collector_list_actions(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_actions(*args)

    def collector_ack_action(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_ack_action(*args)

    def collector_status(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_status(*args)

    def collector_asset(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_asset(*args)

    def collector_networks(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_networks(*args)

    def collector_checks(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_checks(*args)

    def collector_disks(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_disks(*args)

    def collector_alerts(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_alerts(*args)

    def collector_show_actions(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_show_actions(*args)

    def collector_events(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_events(*args)

    def collector_tag(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_tag(*args)

    def collector_untag(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_untag(*args)

    def collector_create_tag(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_create_tag(*args)

    def collector_show_tags(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_show_tags(*args)

    def collector_list_tags(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_tags(*args)

    def collector_list_nodes(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_nodes(*args)

    def collector_list_services(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_services(*args)

    def collector_list_filtersets(self, opts, sync=True):
        args = [opts]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_list_filtersets(*args)

    def collector_get_action_queue(self, sync=True):
        args = [Env.nodename]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_get_action_queue(*args)

    def collector_update_action_queue(self, data, sync=True):
        args = [data]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_update_action_queue(*args)

    def collector_update_action_queue_received(self, data, sync=True):
        args = [data]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_update_action_queue_received(*args)

    def collector_get_action_queue_v2(self, sync=True):
        args = [Env.nodename]
        args += [(self.node.collector_env.uuid, Env.nodename)]
        return self.proxy.collector_get_action_queue_v2(*args)


def from_epoch(t):
    return datetime.fromtimestamp(t).strftime("%Y-%m-%d %H:%M:%S")


if __name__ == "__main__":
    x = CollectorRpc()
    x.init()
    print(x.proxy_methods)
    print(x.comp_proxy_methods)
0707010001f171000081a40000000000000000000000016a100daf0001810e000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/collector/cli.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
import textwrap

# issue19884 workaround (spurious heading '\033[1034h')
TERM = os.environ.get("TERM")
if TERM:
    del os.environ["TERM"]
    import readline
    os.environ["TERM"] = TERM
else:
    import readline

import atexit
import fnmatch

from foreign.six.moves import configparser as ConfigParser
import core.exceptions as ex
from foreign.six.moves import input
from utilities.storage import Storage
from utilities.proc import find_editor
from utilities.render.color import formatter
from utilities.string import bdecode, is_glob

try:
    import requests
except ImportError:
    raise ex.Error("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
try:
    import docutils.utils
    import docutils.parsers
    import docutils.parsers.rst
    has_docutils = True
except ImportError:
    has_docutils = False

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 AttributeError:
    pass

class Cmd(object):
    command = "undef"
    desc = ""
    parser = None
    candidates_path = {}
    api_candidates = False
    parser_options = []

    # 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()
                try:
                    data = data.encode("utf-8")
                except:
                    pass
                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(r"^\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 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 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 'filters' in options.__dict__ and options.filters:
            params["filters"] = options.filters
        if 'query' in options.__dict__ and options.query is not None:
            params["query"] = options.query
        if 'limit' in options.__dict__ and options.limit:
            params["limit"] = int(options.limit[0])
        _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 'filters' in options.__dict__ and options.filters:
            params["filters"] = options.filters
        if 'query' in options.__dict__ and options.query is not None:
            params["query"] = options.query
        if 'limit' in options.__dict__ and options.limit:
            params["limit"] = int(options.limit[0])
        _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.")
    parser.add_option("--id", default=None, action="store", dest="id", type="int",
                      help="An optional safe file integer id. If specified the safe id will point to the new uploaded version of the file. The previous version is still referenced by the same uuid.")
    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)

        if options.id is not None:
            path = "/safe/%d/upload" % options.id
        else:
            path = "/safe/upload"

        files = {
          "file": (os.path.realpath(options.file), open(options.file, 'rb')),
        }

        r = requests.post(self.cli.api+path, 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.RED))
            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"
        CmdLs(self.cli).cmd(p)

    def show_filter(self, options):
        if options.show is None or options.filter is None:
            return
        _path = "/filters/"+options.filter
        r = requests.get(self.cli.api+_path, auth=self.cli.auth, verify=not self.cli.insecure)
        validate_response(r)
        data = json.loads(bdecode(r.content))
        self.print_filter(data["data"][0])

    def print_filter(self, data):
        print(ls_info.get("filters").get("fmt") % 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"
        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"
        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
        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
        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(find_editor()+" "+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"
        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"
        else:
            # impossible, keep for pylint
            t = ""
        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(bdecode(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("/")]
        pattern2 = pattern + "[/]*$"
        if re.match(pattern2, 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:
            try:
                requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
            except AttributeError:
                pass

        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.Error(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 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.Error("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.Error(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 = 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)

  0707010001f170000081a40000000000000000000000016a100daf00002cba000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/core/collector/actions.py  from __future__ import print_function

import sys
from stat import *

import core.exceptions as ex
from utilities.storage import Storage
from utilities.converters import convert_duration
from subprocess import *

class CollectorActions(object):
    def __init__(self, options=None, node=None, path=None):
        if isinstance(options, dict):
            self.options = Storage(options)
        else:
            self.options = options
        self.node = node
        self.collector = node.collector
        self.path = path

    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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

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

        opts = {}
        opts['svcname'] = self.path
        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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

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

        opts = {}
        opts['svcname'] = self.path
        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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

        return d['data']

    def collector_list_actions(self):
        opts = {}
        if self.path is not None:
            opts['svcname'] = self.path
        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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

        return d['data']

    def collector_ack_action(self):
        opts = {}
        if self.path is not None:
            opts['svcname'] = self.path
        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.Error("--id is not set")
        else:
            opts['id'] = self.options.id

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

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

        return d['data']

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

        return d['data']

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

        return d['data']

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

        return d['data']

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

        return d['data']

    def collector_events(self):
        opts = {}
        if self.path is not None:
            opts['svcname'] = self.path
        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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

        return d['data']

    def collector_show_actions(self):
        opts = {}
        if self.path is not None:
            opts['svcname'] = self.path
        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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])

        return d['data']

    def collector_tag_detach(self):
        opts = {}
        opts['tag_name'] = self.options.name
        if self.path:
            opts['svcname'] = self.path
        d = self.collector.call('collector_untag', opts)
        if d is None:
            raise ex.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])
        elif d.get("msg"):
            print(d.get("msg"))

    def collector_tag_attach(self):
        opts = {}
        opts['tag_name'] = self.options.name
        opts['tag_attach_data'] = self.options.attach_data
        if self.path:
            opts['svcname'] = self.path
        d = self.collector.call('collector_tag', opts)
        if d is None:
            raise ex.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(d['msg'])
        elif d.get("msg"):
            print(d.get("msg"))

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

    def collector_tag_list(self):
        d = self._collector_tag_list()
        for tag in d:
            print(tag)

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

    def collector_tag_show(self):
        opts = {}
        if self.path:
            opts['svcname'] = self.path
        if self.options.verbose:
            opts['full'] = True

        d = self.collector.call('collector_show_tags', opts)
        if d is None:
            raise ex.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(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.Error("xmlrpc unknown failure")
        if d['ret'] != 0:
            raise ex.Error(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.Error("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):
        rpath = "/logs"
        data = {
          "log_fmt": self.options.message,
        }
        d = self.node.collector_rest_post(rpath, data, path=self.path)
        if "error" in d:
            raise ex.Error(d["error"])
        print("logged")

  0707010001f16d000081a40000000000000000000000016a100daf00000273000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/core/cloud.py  """
Defines the BaseCloud class, parent class of cloud drivers.
"""
import socket
import core.exceptions as ex

class BaseCloud(object):
    """
    Parent class of cloud drivers hosted in the drivers.cloud package.
    """
    def __init__(self, s, auth):
        self.cid = s
        self.auth = auth
        self.driver = None

    def list_names(self):
         print("todo")
         return []

    def list_nodes(self):
        try:
            nodes = self.driver.list_nodes()
        except socket.error as e:
            raise ex.Error("error connecting to %s cloud url (%s)"%(self.cid, str(e)))
        return nodes

 0707010001f19f000081a40000000000000000000000016a100daf0000c0dd000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/core/resource.py   """
Defines the resource class, which is the parent class of every
resource driver.
"""
from __future__ import print_function

import json
import logging
import os
import sys
import time

import core.status
import utilities.lock
import core.exceptions as ex
import utilities.devices
import utilities.render.color
from env import Env
from core.capabilities import capabilities
from utilities.naming import factory
from utilities.cache import clear_cache
from utilities.lazy import lazy, set_lazy, unset_lazy
from utilities.storage import Storage
from utilities.proc import action_triggers, call, lcall, vcall

ALLOW_ACTION_WITH_NOACTION = [
    "presync",
    "set_provisioned",
    "set_unprovisioned",
]

LOCKER_TYPES = [
    "disk.scsireserv",
    "disk.radoslock",
]

MAX_STATUS_HISTORY = 100

class Resource(object):
    """
    Resource drivers parent class
    """
    default_optional = False
    refresh_provisioned_on_provision = False
    refresh_provisioned_on_unprovision = False

    def __init__(self,
                 rid=None,
                 type=None,
                 subset=None,
                 optional=None,
                 disabled=False,
                 monitor=False,
                 restart=0,
                 restart_delay=0,
                 tags=None,
                 standby=None,
                 enable_provision=False,
                 enable_unprovision=False,
                 shared=False,
                 promote_rw=False,
                 encap=False,
                 always_on=None,
                 **ignored):
        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 = self.mangle_optional(optional, self.tags)
        self.standby = standby
        self.always_on = always_on or []
        self.disabled = disabled
        self.skip = False
        self.monitor = monitor
        self.nb_restart = restart
        self.restart_delay = restart_delay
        self.rstatus = None
        self.skip_provision = not enable_provision
        self.skip_unprovision = not enable_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
        self.always_pg = False
        try:
            self.label = type
        except AttributeError:
            # label is a lazy prop of the child class
            pass
        self.status_logs = []
        self.can_rollback = False
        self.rollback_even_if_standby = False
        self.skip_triggers = set()
        self.driver_group = self.format_driver_group()
        self.driver_basename = self.format_driver_basename()
        self.rset_id = self.format_rset_id()
        self.last_status_info = {}

    def on_add(self):
        """
        Placeholder for a method run when adding the Resource to a BaseSvc,
        after the Resource::svc attribute is set.
        """
        pass

    @lazy
    def is_standby(self):
        if self.standby is not None:
            return self.standby
        if Env.nodename in self.always_on:
            return True
        if not hasattr(self, "svc"):
            return False
        if "nodes" in self.always_on and Env.nodename in self.svc.nodes:
            return True
        if "drpnodes" in self.always_on and Env.nodename in self.svc.drpnodes:
            return True
        return False

    def mangle_optional(self, optional, tags):
        if "noaction" in tags:
            return True
        if optional is None:
            return self.default_optional
        return optional

    @lazy
    def log(self):
        """
        Lazy init for the resource logger.
        """
        extra = {
            "path": self.svc.path,
            "node": Env.nodename,
            "sid": Env.session_uuid,
            "cron": self.svc.options.cron,
            "rid": self.rid,
            "subset": self.subset,
        }
        return logging.LoggerAdapter(logging.getLogger(self.log_label()), extra)

    @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

    @lazy
    def stopped_flag(self):
        """ 
        transcient stopped state file path
        """
        return os.path.join(self.var_d, "stopped")

    def stopped(self):
        """
        Return True if the resource has been stopped.
        """
        return os.path.exists(self.stopped_flag)

    def stopped_info(self):
        """
        status info for the stopped state
        warning if resource beeing previously stopped becomes up without been started
        """
        if self.stopped() and self.rstatus in (core.status.UP, core.status.STDBY_UP):
            self.log.debug("resource %s is stopped", self.rid)
            self.status_log("unmanaged start", "warn")

    def clear_stopped(self):
        """
        Remove the stopped state file of the resource
        """
        try:
            os.unlink(self.stopped_flag)
        except:
            pass

    def clear_stopped_if_not_scoped(self):
        """
        Clear the stopped state file of the resource if the command is not scoped (no --rid xxx)
        """
        try:
            if not self.svc.command_is_scoped():
                os.unlink(self.stopped_flag)
        except:
            pass

    def set_stopped_if_scoped(self):
        """
        Set the stopped state file of the resource if the command is scoped (eg. --rid xxx)
        """
        if self.svc.command_is_scoped():
            try:
                open(self.stopped_flag, "w").close()
            except:
                pass

    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):
            count = len(key)
            if count and key[-1] is None:
                key[-1] = ""
            if count == 2:
                keys[idx] = [
                    self.svc.path,
                    self.svc.node.nodename,
                    self.svc.topology,
                    self.rid
                ] + key
            elif count == 3:
                keys[idx] = [
                    self.svc.path,
                    self.svc.node.nodename,
                    self.svc.topology
                ] + key
        return keys

    def log_label(self):
        """
        Return the resource label used in logs entries.
        """
        if hasattr(self, "svc"):
            label = self.svc.loggerpath + '.'
        else:
            label = Env.nodename + "."

        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.driver_group, 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.type in LOCKER_TYPES and other.type not in LOCKER_TYPES:
            if other.type == "disk.disk" and self.rid == other.rid + "pr":
                ret = False
            else:
                ret = True
        elif self.type not in LOCKER_TYPES and other.type in LOCKER_TYPES:
            if self.type == "disk.disk" and self.rid + "pr" == other.rid:
                ret = True
            else:
                ret = False
        elif self.type == "sync.zfssnap" and other.type == "sync.zfs":
            ret = True
        elif self.type == "sync.zfs" and other.type == "sync.zfssnap":
            ret = False
        else:
            ret = self.sort_key < other.sort_key
        return ret

    def save_exc(self):
        """
        A helper method to save stacks in the service log.
        """
        self.log.error("", 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.
        """
        env = kwargs.get("env")
        if env is None:
            env = dict(os.environ)
        env["OPENSVC_RID"] = self.rid
        kwargs["env"] = env
        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
        getattr(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.is_standby and not self.svc.options.force:
            standby_action = action+'standby'
            if hasattr(self, standby_action):
                self.set_stopped_if_scoped()
                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()

        if action in ["start", "boot"]:
            self.clear_stopped()
        elif action in ["shutdown", "stop"]:
            self.clear_stopped_if_not_scoped()

        self.action_triggers("pre", action)
        self.action_triggers("blocking_pre", action, blocking=True)

        if action in ["stop", "shutdown", "_pg_kill"]:
            self.set_stopped_if_scoped()

        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 = (
            "boot",
            "shutdown",
            "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 = (
            "shutdown",
            "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.Undefined as exc:
            print(exc)
            return False
        except ex.ContinueAction as exc:
            if self.svc.options.cron:
                # no need to flood the logs for scheduled tasks
                self.log.debug(str(exc))
            else:
                self.log.info(str(exc))
        except ex.Error 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.is_standby:
            return status
        if status == core.status.UP:
            return core.status.STDBY_UP
        elif status == core.status.DOWN:
            return core.status.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 core.status.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 core.status.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 core.status.NA

        if not ignore_nostatus and "nostatus" in self.tags:
            self.status_log("nostatus tag", "info")
            return core.status.NA

        if self.rstatus is not None and not refresh:
            return self.rstatus

        last_status = self.load_status_last(refresh)

        if refresh:
            self.purge_status_last()
        else:
            self.rstatus = last_status

        # now the rstatus can no longer be None
        if self.rstatus == core.status.UNDEF or refresh:
            self.status_logs = []
            self.rstatus = self.try_status(verbose)
            self.rstatus = self.status_stdby(self.rstatus)
            self.stopped_info()
            self.last_status_info = self.status_info()
            self.log.debug("refresh status: %s => %s",
                           core.status.Status(last_status),
                           core.status.Status(self.rstatus))
            self.write_status()

        if self.rstatus in (core.status.UP, core.status.STDBY_UP) and \
           not self._is_provisioned_flag():
            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 purge_var_d(self, keep_provisioned=True):
        import glob
        import shutil
        paths = glob.glob(os.path.join(self.var_d, "*"))
        for path in paths:
            if keep_provisioned and path == os.path.join(self.var_d, "provisioned"):
                # Keep the provisioned flag to remember that the
                # resource was unprovisioned, even if the driver
                # says it is always provisioned.
                # This is necessary because the orchestrated
                # unprovision would retry the CRM action if a
                # resource reports it is still provisioned after
                # the first unprovision.
                continue
            try:
                if os.path.isdir(path):
                    shutil.rmtree(path)
                else:
                    os.unlink(path)
            except OSError:
                # errno 39: not empty (racing with a writer)
                pass

    def has_status_last(self):
        return os.path.exists(self.fpath_status_last)

    def load_status_last(self, refresh=False):
        """
        Fetch the resource status from the on-disk cache.
        """
        try:
            with open(self.fpath_status_last, 'r') as ofile:
                data = json.load(ofile)
        except ValueError:
            return core.status.UNDEF
        except (OSError, IOError) as exc:
            if exc.errno != 2:
                # not EEXISTS
                self.log.debug(exc)
            return core.status.UNDEF

        try:
            status = core.status.Status(data["status"])
        except (IndexError, AttributeError, ValueError) as exc:
            self.log.debug(exc)
            return core.status.UNDEF

        if not refresh and hasattr(self, "set_label"):
            if hasattr(self, "_lazy_label"):
                attr = "_lazy_label"
            else:
                attr = "label"
            try:
                setattr(self, attr, data["label"])
            except (IndexError, AttributeError, ValueError):
                pass

        self.status_logs = data.get("log", [])

        if "info" in data:
            self.last_status_info = data["info"]

        return status

    def write_status_last(self):
        """
        Write the in-memory resource status to the on-disk cache.
        """
        data = {
            "status": str(core.status.Status(self.rstatus)),
            "label": self.label,
            "log": self.status_logs,
            "info": self.last_status_info,
        }
        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:
            json.dump(data, ofile)
            ofile.flush()

    def load_status_history(self):
        try:
            with open(self.fpath_status_history, "r") as fp:
                return json.load(fp)
        except Exception:
            return []

    def write_status_history(self):
        """
        Log a change to the resource status history file.
        """
        tmpfpath = self.fpath_status_history + ".tmp"
        data = self.load_status_history()
        try:
            last = data[-1]["status"]
        except IndexError:
            last = None
        except KeyError:
            # corrupted => reset
            self.log.warning("reset corrupted status history. last entry: %s", data[-1])
            data = []
            last = None

        current = core.status.Status(self.rstatus)
        if current == last:
            # no change
            return

        # purge history
        trim = len(data) - MAX_STATUS_HISTORY - 1
        if trim > 0:
            data = data[trim:]

        data.append({
            "ts": time.time(),
            "status": str(current),
        })
        try:
            with open(tmpfpath, "w") as fp:
                json.dump(data, fp)
            import shutil
            shutil.move(tmpfpath, self.fpath_status_history)
        except Exception as exc:
            self.log.error("write_status_history: %s", exc)

    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
        for line in text.splitlines():
            self.status_logs.append((level, line))

    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 = utilities.render.color.color.BROWN
                elif level == "error":
                    color = utilities.render.color.color.RED
                else:
                    color = utilities.render.color.color.LIGHTBLUE
                status_str += utilities.render.color.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.
        """
        if func():
            return
        for tick in range(int(tmo//delay)):
            time.sleep(delay)
            if func():
                return
        raise ex.Error(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 top_dev in top_devs:
                base_devs.add(os.path.realpath(top_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:
            disks = utilities.devices.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:
            disks = utilities.devices.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:
            disks = utilities.devices.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.is_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.is_standby:
            self.start()

    def start(self):
        """
        The resource start action entrypoint.
        """
        pass

    def boot(self):
        """
        Clean up actions to do on node boot before the daemon starts.
        """
        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 and not self.always_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 and not self.always_pg:
            return False
        if self.svc.pg is None:
            return False
        return self.svc.pg.frozen(self)

    def create_pg(self, **kwargs):
        """
        Create a process group if this service asks for it and if possible.
        """
        if not self.svc.create_pg and not self.always_pg:
            return
        if self.svc.pg is None:
            return
        self.svc.pg.create_pg(self, **kwargs)

    def check_requires(self, action, cluster_data=None):
        """
        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, cluster_data=cluster_data)

    def _check_requires(self, element, cluster_data=None):
        """
        Validate a requires element, raising Error if the requirement is
        not met.
        """
        if element is None:
            return
        if element == "impossible":
            raise ex.ContinueAction("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
        if cluster_data:
            try:
                current_state = cluster_data[Env.nodename]["services"]["status"][self.svc.path]["resources"][rid]["status"]
            except KeyError:
                current_state = "undef"
        else:
            resource = self.svc.resources_by_id[rid]
            current_state = core.status.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.ContinueAction(msg)
            else:
                raise ex.Error(msg)

    def dns_update(self):
        """
        Placeholder for resource specific implementation of the dns update.
        """
        pass

    def oget(self, o, **kwargs):
        return self.svc.oget(self.rid, o, **kwargs)

    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):
        """
        Placeholder for driver implementation
        """
        return {}

    def status_info(self):
        data = self._status_info()
        if not self.shared:
            self.last_status_info = data
            return data
        sopts = self.schedule_options()
        if not sopts:
            self.last_status_info = data
            return data
        data["sched"] = {}
        for saction, sopt in sopts.items():
            data["sched"][saction] = self.schedule_info(sopt)
        self.last_status_info = data
        return data

    def info(self):
        data = [
          ["driver", self.type],
          ["standby", str(self.is_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)],
          ["restart_delay", str(self.restart_delay)],
        ]
        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 += getattr(self, "_info")()
            except AttributeError:
                pass
            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.
        """
        if not hasattr(self, "provisioner"):
            return
        try:
            isprov = self.is_provisioned()
        except Exception as exc:
            self.status_log("provisioned: %s" % str(exc), "error")
            isprov = False
        data = {}
        if isprov is not None:
            data["state"] = isprov
        mtime = self.provisioned_flag_mtime()
        if mtime is not None:
            data["mtime"] = mtime
        return data

    def is_provisioned_flag(self):
        """
        Return the boolean provisioned state cached on disk.
        Return None if the file does not exist or is corrupted.
        """
        if not hasattr(self, "provisioner"):
            return
        return self._is_provisioned_flag()

    def _is_provisioned_flag(self):
        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 not hasattr(self, "provisioner"):
            return
        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 has_provisioned_flag(self):
        return os.path.exists(self.provisioned_flag)

    def remove_is_provisioned_flag(self):
        """
        Remove the provisioned state cache file. Used in the Svc::delete_resource()
        code path.
        """
        if not self.has_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)

    def format_driver_group(self):
        try:
            return self.type.split(".", 1)[0]
        except ValueError:
            return self.type
        except AttributeError:
            return ""

    def format_driver_basename(self):
        try:
            return self.type.split(".", 1)[1]
        except (ValueError, IndexError, AttributeError):
            return ""

    def format_rset_id(self):
        if self.subset is not None:
            return "%s:%s" % (self.driver_group, self.subset)
        else:
            return self.driver_group

    def provision_shared_non_leader(self):
        self.log.info("non leader shared resource provisioning")
        self.write_is_provisioned_flag(True, mtime=1)

        # do not execute post_provision triggers
        self.skip_triggers.add("post_provision")
        self.skip_triggers.add("blocking_post_provision")

        if self.skip_provision:
            self.log.info("provision skipped (configuration directive)")
            return
        if hasattr(self, "provisioner_shared_non_leader"):
            getattr(self, "provisioner_shared_non_leader")()

    def provision(self):
        if self.shared and not self.svc.options.leader:
            self.provision_shared_non_leader()
            return
        self._provision()
        try:
            self.post_provision_start()
        except Exception:
            if self.skip_provision:
                # best effort
                pass
            else:
                raise

    def _provision(self):
        """
        Unimplemented is_provisioned() trusts provisioner() to do the right
        thing.
        """
        if self.skip_provision:
            self.log.info("provision skipped (configuration directive)")
            self.write_is_provisioned_flag(True)
            return
        if not hasattr(self, "provisioner"):
            return
        if self.is_provisioned(refresh=self.refresh_provisioned_on_provision) is True:
            self.log.info("%s already provisioned", self.label)
        else:
            getattr(self, "provisioner")()
        self.write_is_provisioned_flag(True)

    def unprovision(self):
        try:
            self.pre_provision_stop()
        except Exception:
            if self.skip_unprovision:
                # best effort
                pass
            else:
                raise
        if self.shared and not self.svc.options.leader:
            self.unprovision_shared_non_leader()
            return
        self._unprovision()

    def unprovision_shared_non_leader(self):
        self.log.info("non leader shared resource unprovisioning")

        # do not execute post_unprovision triggers
        self.skip_triggers.add("post_unprovision")
        self.skip_triggers.add("blocking_post_unprovision")

        if self.skip_unprovision:
            self.log.info("unprovision skipped (configuration directive)")
            return
        if not hasattr(self, "unprovisioner") and not hasattr(self, "unprovisioner_shared_non_leader"):
            return
        if hasattr(self, "unprovisioner_shared_non_leader"):
            getattr(self, "unprovisioner_shared_non_leader")()
        self.write_is_provisioned_flag(False)

    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 not hasattr(self, "unprovisioner") and not hasattr(self, "unprovisioner_shared_non_leader"):
            return
        if self.is_provisioned(refresh=self.refresh_provisioned_on_unprovision) is False:
            self.log.info("%s already unprovisioned", self.label)
        else:
            getattr(self, "unprovisioner")()
        self.write_is_provisioned_flag(False)

    def post_provision_start(self):
        self.start()

    def pre_provision_stop(self):
        self.stop()

    def is_provisioned(self, refresh=False):
        if not hasattr(self, "provisioner") and not hasattr(self, "provisioner_shared_non_leader"):
            return True
        if "noaction" in self.tags:
            # can not determine state if we can't run an action to toggle it
            return
        if not refresh:
            flag = self.is_provisioned_flag()
            if flag is not None:
                return flag
        if hasattr(self, "provisioned"):
            value = getattr(self, "provisioned")()
        elif hasattr(self, "provisioner") and not self.has_provisioned_flag():
            value = False
        else:
            return
        if not self.shared or self.svc.options.leader 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
        try:
            from utilities.devices import promote_dev_rw
        except ImportError:
            self.log.warning("promote_rw is not supported on this operating system")
            return
        for dev in self.base_devs():
            promote_dev_rw(dev, log=self.log)

    def progress(self):
        utilities.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(self.var_d, "lock")
        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 = utilities.lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=lockfile,
                intent=action
            )
        except utilities.lock.LockTimeout as exc:
            raise ex.Error("timed out waiting for lock %s: %s" % (details, str(exc)))
        except utilities.lock.LockNoLockFile:
            raise ex.Error("lock_nowait: set the 'lockfile' param %s" % details)
        except utilities.lock.LockCreateError:
            raise ex.Error("can not create lock file %s" % details)
        except utilities.lock.LockAcquire as exc:
            raise ex.Error("another action is currently running %s: %s" % (details, str(exc)))
        except ex.Signal:
            raise ex.Error("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.Error("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def resunlock(self):
        """
        Release the service action lock.
        """
        utilities.lock.unlock(self.lockfd)
        self.lockfd = None

    def section_kwargs(self):
        stype = self.driver_basename if self.driver_basename else self.driver_group
        return self.svc.section_kwargs(self.rid, stype)

    def replace_volname(self, *args, **kwargs):
        path, vol = self.svc.replace_volname(*args, **kwargs)
        if not vol:
            return path, vol
        volrid = self.svc.get_volume_rid(vol.name)
        if volrid:
            self.svc.register_dependency("stop", volrid, self.rid)
            self.svc.register_dependency("start", self.rid, volrid)
        return path, vol

    def direct_environment_env(self, mappings):
        env = {}
        if not mappings:
            return env
        for mapping in mappings:
            try:
                var, val = mapping.split("=", 1)
            except Exception as exc:
                self.log.info("ignored environment mapping %s: %s", mapping, exc)
                continue
            var = var.upper()
            env[var] = val
        return env

    def kind_environment_env(self, kind, mappings):
        env = {}
        if mappings is None:
            return env
        for mapping in mappings:
            try:
                var, val = mapping.split("=", 1)
            except Exception as exc:
                var = None
                val = mapping
            try:
                name, key = val.split("/", 1)
            except Exception as exc:
                self.log.info("ignored %s environment mapping %s: %s", kind, mapping, exc)
                continue
            obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node)
            if not obj.exists():
                self.log.info("ignored %s environment mapping %s: config %s does not exist", kind, mapping, name)
                continue
            keys = obj.data_keys(key)
            if not keys:
                self.log.info("ignored %s environment mapping %s: key %s does not exist", kind, mapping, key)
                continue
            elif var and len(keys) > 1:
                self.log.info("ignored %s environment mapping %s: %s match multiple keys, can not map to a single variable", kind, mapping, key)
                continue
            for key in keys:
                if var is None:
                    _var = key
                else:
                    _var = var
                val = obj.decode_key(key)
                env[_var] = val
        return env

    def schedule_info(self, sopt):
        try:
            last = self.svc.sched.get_last(sopt.fname)
        except Exception:
            return {}
        data = {
            "last": last,
        }
        return data

    def schedule_options(self):
        """
        Placeholder for driver implementation.
        Must return a dict of scheduler options indexed by scheduler action.
        Used by Svc::configure_scheduler() and Resource::status_info().
        """
        return {}

    def has_capability(self, cap):
        return capabilities.has("drivers.resource.%s" % cap)

    def clear_status_cache(self):
        self.rstatus = None
        self.status_logs = []
        self.last_status_info = {}

class DataResource(Resource):
    def __init__(self, rid, type="data", **kwargs):
        Resource.__init__(self, rid, type=type)
        self.options = Storage(kwargs)

    def _status_info(self):
        data = {}
        for key, val in self.section_kwargs().items():
            if val not in (None, []):
                data[key] = val
        return data

    def _status(self, verbose=False):
        return core.status.NA
   0707010001f17f000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002900000000root/usr/share/opensvc/opensvc/core/node  0707010001f181000081a40000000000000000000000016a100daf0000010b000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/node/darwin.py    from .node import Node as BaseNode


class Node(BaseNode):
    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)
 0707010001f185000081a40000000000000000000000016a100daf00002fa6000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/core/node/linux.py import os
import socket
import time
from itertools import islice

import core.exceptions as ex
import utilities.os.linux
import utilities.ping
from utilities.lazy import lazy
from utilities.proc import justcall

from .node import Node as BaseNode


class Node(BaseNode):
    def still_alive(self, action):
        try:
            with open("/proc/sys/kernel/sysrq", "r") as ofile:
                buff = ofile.read()
        except Exception:
            buff = "<unknown>"
        self.log.error("still alive ... maybe %s is ignored by kernel.sysrq=%s "
                       "(check dmesg)" % (action, buff.strip()))
        time.sleep(1)
        self.freeze()

    def sys_reboot(self, delay=0):
        if delay:
            self.log.info("sysrq reboot in %s seconds", delay)
            time.sleep(delay)
        with open("/proc/sysrq-trigger", "w") as ofile:
            ofile.write("b")
        self.still_alive("reboot")

    def sys_crash(self, delay=0):
        if delay:
            self.log.info("sysrq crash in %s seconds", delay)
            time.sleep(delay)
        with open("/proc/sysrq-trigger", "w") as ofile:
            ofile.write("c")
        self.still_alive("crash")

    def shutdown(self):
        cmd = ["shutdown", "-h", "now"]
        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
        data["mem_avail"] = 100 * raw_data.get("MemAvailable", 0) // raw_data["MemTotal"]
        data["swap_total"] = raw_data["SwapTotal"] // 1024
        try:
            data["swap_avail"] = 100 * raw_data["SwapFree"] // raw_data["SwapTotal"]
        except:
            data["swap_avail"] = 0
        return data

    @lazy
    def user_hz(self):
        return os.sysconf(os.sysconf_names['SC_CLK_TCK'])

    @staticmethod
    def get_tid():
        return utilities.os.linux.get_tid()

    def cpu_time(self, stat_path='/proc/stat'):
        with open(stat_path) as stat_file:
            stat_line = next(stat_file)
        return sum(float(time) for time in
                islice(stat_line.split(), 1, None)) / self.user_hz

    def tid_cpu_time(self, tid):
        stat_path = "/proc/%d/task/%d/stat" % (tid, tid)
        with open(stat_path) as stat_file:
            stat_line = next(stat_file)
        return sum(float(time) for time in
                islice(stat_line.split(), 13, 14)) / self.user_hz

    def pid_cpu_time(self, pid):
        stat_path = "/proc/%d/stat" % pid
        with open(stat_path) as stat_file:
            stat_line = next(stat_file)
        return sum(float(time) for time in
                islice(stat_line.split(), 13, 14)) / self.user_hz

    def tid_mem_total(self, tid):
        stat_path = "/proc/%d/task/%d/statm" % (tid, tid)
        with open(stat_path) as stat_file:
            stat_line = next(stat_file)
        return sum(float(time) for time in
                islice(stat_line.split(), 2, 5))

    def pid_mem_total(self, pid):
        stat_path = "/proc/%d/statm" % pid
        with open(stat_path) as stat_file:
            stat_line = next(stat_file)
        return sum(float(time) for time in
                islice(stat_line.split(), 2, 5))

    def network_route_add(self, dst=None, gw=None, dev=None, local_ip=None, brdev=None, brip=None, table=None, tunnel="auto", node=None, **kwargs):
        if dst is None:
            return
        if ":" in dst:
            ipcmd = ["ip", "-6"]
        else:
            ipcmd = ["ip"]
        if tunnel in ("auto", "never"):
            if gw is not None:
                cmd = ipcmd + ["route", "replace", dst, "via", gw, "table", table]
            elif dev is not None:
                cmd = ipcmd + ["route", "replace", dst, "dev", dev, "table", table]
            else:
                raise ex.Error("network_route_add needs a gw or a dev kwarg")
            out, err, ret = justcall(cmd)
        else:
            err = ""
            out = ""
        if tunnel == "never":
            self.log.info(" ".join(cmd))
            for line in out.splitlines():
                self.log.info(line)
        elif tunnel == "always" or "invalid gateway" in err or "is unreachable" in err:
            name = self.network_tunnel_ipip_name(node, gw)
            tun = self.network_tunnel_ipip_add(name, local_ip, gw, kwargs.get("tunnel_mode", ""))
            self.network_tunnel_ipip_route_add(ipcmd, dst, tun["dev"], brip.split("/", 1)[0], table)
        else:
            self.log.info(" ".join(cmd))
            for line in out.splitlines():
                self.log.info(line)

    def network_tunnel_ipip_route_add(self, ipcmd, dst, dev, src, table):
        utilities.ping.check_ping(src, timeout=5, count=1)
        cmd = ipcmd + ["route", "replace", dst, "dev", dev, "src", src, "table", table]
        self.vcall(cmd)

    def mac_from_ip4(self, ip):
        """
        When the device with the lowest mac is removed from the bridge or when
        a new device with the lowest mac is added to the bridge, all containers
        can experience tcp hangs while the arp table resynchronizes.

        Setting a mac address to the bridge explicitely avoids these mac address
        changes.

        Forge the mac address using a 0a:58 prefix followed by the bridge ipv4
        address converted to hexa (same algorithm used in k8s).
        """
        mac = "0a:58"
        for i in ip.split("/", 1)[0].split("."):
            mac += ":%.2x" % int(i)
        return mac

    def network_bridge_add(self, name, ip):
        cmd = ["ip", "link", "show", name]
        _, _, ret = justcall(cmd)
        if ret != 0:
            cmd = ["ip", "link", "add", "name", name, "type", "bridge"]
            self.vcall(cmd)
        cmd = ["ip", "addr", "show", "dev", name]
        out, _, _ = justcall(cmd)
        if " "+ip+" " not in out:
            if ":" in ip:
                cmd = ["ip", "-6", "addr", "add", "dev", name, "scope", "global", ip]
            else:
                cmd = ["ip", "addr", "add", ip, "dev", name]
            self.vcall(cmd)
        cmd = ["ip", "link", "show", "dev", name]
        out, _, _ = justcall(cmd)
        if ":" not in ip:
            mac = self.mac_from_ip4(ip)
            if mac not in out:
                cmd = ["ip", "link", "set", "dev", name, "address", mac]
                self.vcall(cmd)
        if "DOWN" in out:
            cmd = ["ip", "link", "set", "dev", name, "up"]
            self.vcall(cmd)

    def network_ip_intf(self, addr):
        cmd = ["ip", "addr"]
        out, _, _ = justcall(cmd)
        marker = "inet %s/" % addr
        for line in out.splitlines():
            if marker in line:
                return line.split()[-1]

    def network_tunnel_ipip_name(self, nodename, dst):
        if ":" in dst:
            name = "otun%d" % self.cluster_nodes.index(nodename)
        else:
            name = "tun" + dst.split("/", 1)[0].replace(".", "")
        return name

    def network_tunnel_ipip_add(self, name, src, dst, mode):
        src_dev = self.network_ip_intf(src)
        ipcmd = ["ip"]
        if ":" in dst:
            ipcmd += ["-6"]
            if mode == "ipip":
                # ipip tunnel don't support ipv6, upgrade to ip6ip6
                mode = "ip6ip6"
        cmd = ipcmd + ["tunnel", "show", name]
        out, err, ret = justcall(cmd)
        if out:
            action = "change"
        else:
            action = "add"

        # detect a relayout causing tunnel renames
        cmd = ["ip", "tunnel", "show", "mode", mode, "local", src, "remote", dst]
        out, err, ret = justcall(cmd)
        if ret == 0:
            try:
                current_name = out.splitlines()[0].split(":")[0]
                if current_name not in ("gre0", "tunl0") and current_name != name:
                    self.vcall(["ip", "tunnel", "delete", current_name])
            except IndexError:
                pass

        cmd = ["ip", "tunnel", action, name, "mode", mode,
               "local", src, "remote", dst]
        if src_dev:
            cmd += ["dev", src_dev]
        self.vcall(cmd)
        if action == "add":
            cmd = ["ip", "link", "set", "dev", name, "up"]
            self.vcall(cmd)
        return {
            "dev": name,
            "local": src,
            "remote": dst,
        }

    def network_create_fwrules(self, name):
        nets = self.networks_data()
        data = nets[name]
        ntype = data["config"]["type"]
        if ntype not in ("bridge", "routed_bridge"):
            return
        chain = "osvc-" + name
        comment = "name: %s" % name
        src = data.get("cni", {}).get("data", {}).get("ipam", {}).get("subnet")
        if not src:
            src = data.get("cni", {}).get("data", {}).get("network")
        if src and ":" in src:
            af = socket.AF_INET6
        else:
            af = socket.AF_INET

        self.network_ipt_add_chain(chain, nat=True, af=af)

        for net in nets.values():
            if net["config"]["network"] == "undef":
                continue
            dst = net["config"]["network"]
            _af = socket.AF_INET6 if ":" in dst else socket.AF_INET
            if af != _af:
                continue
            self.network_ipt_add_rule(chain, nat=True, dst=net["config"]["network"], act="RETURN", comment=comment, where="head", af=af)

        if af == socket.AF_INET6:
            self.network_ipt_add_rule(chain=chain, nat=True, dst="::/0", act="MASQUERADE", comment=comment, where="tail", af=af)
        else:
            self.network_ipt_add_rule(chain=chain, nat=True, dst="!224.0.0.0/4", act="MASQUERADE", comment=comment, where="tail", af=af)
        self.network_ipt_add_rule(chain="POSTROUTING", nat=True, src=src, act=chain, comment=comment, af=af)
        self.network_ipt_add_rule(chain="FORWARD", nat=False, indev="obr_"+name, act="ACCEPT", comment=comment, af=af)
        self.network_ipt_add_rule(chain="FORWARD", nat=False, outdev="obr_"+name, act="ACCEPT", comment=comment, af=af)

    def network_ipt_add_rule(self, chain=None, nat=False, dst=None, src=None, act="RETURN", comment=None, where="tail", indev=None, outdev=None, af=socket.AF_INET):
        if nat:
            nat = ["-t", "nat"]
        else:
            nat = []
        if where == "head":
            where = "-I"
        else:
            where = "-A"
        if af == socket.AF_INET6:
            cmd1 = ["ip6tables"] + nat
        else:
            cmd1 = ["iptables"] + nat
        cmd2 = [chain]
        if indev:
            cmd2 += ["-i", indev]
        if outdev:
            cmd2 += ["-o", outdev]
        if src:
            if src[0] == "!":
                cmd2 += ["!"]
                src = src[1:]
            cmd2 += ["-s", src]
        if dst:
            if dst[0] == "!":
                cmd2 += ["!"]
                dst = dst[1:]
            cmd2 += ["-d", dst]
        cmd2 += ["-j", act]
        if comment:
            cmd2 += ["-m", "comment", "--comment", comment]
        cmd = cmd1 + ["-C"] + cmd2
        out, err, ret = justcall(cmd)
        if ret == 0:
            return
        cmd = cmd1 + [where] + cmd2
        self.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd)
        if ret != 0 and err:
            self.log.error(err)

    def network_ipt_add_chain(self, chain, nat=False, af=socket.AF_INET):
        if af == socket.AF_INET6:
            cmd = ["ip6tables"]
        else:
            cmd = ["iptables"]
        if nat:
            cmd += ["-t", "nat"]
        cmd += ["-N", chain]
        out, err, ret = justcall(cmd)
        if "already exist" in err:
            return
        self.log.info(" ".join(cmd))
        if ret != 0:
            self.log.error(err)
  0707010001f182000081a40000000000000000000000016a100daf000007c5000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/core/node/freebsd.py   from utilities.proc import justcall

from .node import Node as BaseNode


class Node(BaseNode):
    def shutdown(self):
        cmd = ["halt"]
        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.
        """
        cmd = [
            "sysctl",
            "hw.realmem",
            "hw.pagesize",
            "vm.stats.vm.v_page_count",
            "vm.stats.vm.v_wire_count",
            "vm.stats.vm.v_active_count",
            "vm.stats.vm.v_inactive_count",
            "vm.stats.vm.v_cache_count",
            "vm.stats.vm.v_free_count",
            "vm.swap_total",
        ]
        raw_data = {}
        data = {}
        out, err, ret = justcall(cmd)
        for line in out.splitlines():
            key, val = line.split(":", 1)
            val = val.strip()
            raw_data[key] = int(val)

        data["mem_total"] = raw_data["hw.realmem"] // 1024 // 1024
        data["mem_avail"] = (raw_data["vm.stats.vm.v_inactive_count"] + raw_data["vm.stats.vm.v_cache_count"] + raw_data["vm.stats.vm.v_free_count"]) * raw_data["hw.pagesize"] // 1024 // 1024
        if data["mem_total"]:
            data["mem_avail"] = 100 * data["mem_avail"] // data["mem_total"]
        else:
            data["mem_avail"] = 0

        data["swap_total"] = raw_data["vm.swap_total"] // 1024 // 1024

        cmd = ["pstat", "-T"]
        out, err, ret = justcall(cmd)
        for line in out.splitlines():
            if "swap" in line:
                swap_used = int(line.split("M/")[0])
                break
        if data["swap_total"]:
            swap_avail = data["swap_total"] - swap_used
            data["swap_avail"] = 100 * swap_avail // data["swap_total"]
        else:
            data["swap_avail"] = 0

        return data

if __name__ == "__main__":
    print(Node().stats_meminfo())
   0707010001f188000081a40000000000000000000000016a100daf0000105d000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/core/node/sunos.py import os
import threading
import time

from utilities.proc import justcall

from .node import Node as BaseNode


class Node(BaseNode):
    @staticmethod
    def get_tid():
        return "%s.%s" % (os.getpid(), threading.current_thread().ident)

    def sys_reboot(self, delay=0):
        if delay:
            self.log.info("sysrq reboot in %s seconds", delay)
            time.sleep(delay)
        justcall(['reboot', '-q'])

    def sys_crash(self, delay=0):
        if delay:
            self.log.info("sysrq crash in %s seconds", delay)
            time.sleep(delay)
        justcall(['halt', '-q'])

    def shutdown(self):
        cmd = ["init", "5"]
        self.vcall(cmd)

    def _reboot(self):
        cmd = ["init", "6"]
        self.vcall(cmd)

    def stats_meminfo(self):
        """
        return {
            "swap_total": int value in MB
            "swap_avail":
            "mem_total": int value in MB
            "mem_avail": %
        }
        """
        swap_total, swap_avail = self._get_swap()
        mem_total, mem_avail = self._get_mem()
        return {
            "swap_total": swap_total,
            "swap_avail": swap_avail,
            "mem_total": mem_total,
            "mem_avail": mem_avail
        }

    @staticmethod
    def _get_swap():
        """:returns swap_total, swap_avail or 0, 0 if errors

        where swap_avail is percentages, swap_total is MB
        Those value are computed from command 'swap -l' output

        swapfile             dev  swaplo blocs   libres
        /dev/dsk/c0d0s1     102,1       8 2425800 2425800
        """
        out, err, ret = justcall(["swap", "-l"])
        if ret != 0:
            return 0, 0
        swap_avail = 0
        swap_total = 0
        for line in out.splitlines():
            if line[0] != "/":
                continue
            elem = line.split()
            swap_avail += int(elem[4]) // 2
            swap_total += int(elem[3]) // 2
        if swap_total > 0:
            return swap_total // 1024, (100 * swap_avail) // swap_total
        else:
            return 0, 0

    @staticmethod
    def _get_mem():
        """
        :returns mem_total, mem_avail or 0, 0 if errors

        where mem_avail is percentages, mem_total is MB
        Those value are computed from command: 'kstat -n system_pages -p' output,

        unix:0:system_pages:availrmem   186832
        unix:0:system_pages:class       pages
        unix:0:system_pages:crtime      35,300879878
        unix:0:system_pages:desfree     8191
        unix:0:system_pages:desscan     25
        unix:0:system_pages:econtig     18446744073646514176
        unix:0:system_pages:fastscan    524231
        unix:0:system_pages:freemem     51757
        unix:0:system_pages:kernelbase  18446604435732824064
        unix:0:system_pages:lotsfree    16382
        unix:0:system_pages:minfree     4095
        unix:0:system_pages:nalloc      2007716475
        unix:0:system_pages:nalloc_calls        77545
        unix:0:system_pages:nfree       2003516163
        unix:0:system_pages:nfree_calls 67462
        unix:0:system_pages:nscan       0
        unix:0:system_pages:pagesfree   51757
        unix:0:system_pages:pageslocked 823743
        unix:0:system_pages:pagestotal  1048463
        unix:0:system_pages:physmem     1048463
        unix:0:system_pages:pp_kernel   861352
        unix:0:system_pages:slowscan    100
        unix:0:system_pages:snaptime    15173,890295243
        """
        import mmap

        out, err, ret = justcall(["kstat", "-n", "system_pages", "-p"])
        if ret != 0:
            return 0, 0
        raw_data = {}

        for line in out.splitlines():
            elem = line.split()
            raw_data[elem[0]] = elem[1]

        try:
            phys_mem = int(raw_data.get("unix:0:system_pages:physmem", "0"))
            avail_r_mem = int(raw_data.get("unix:0:system_pages:availrmem", "0"))
        except Exception:
            phys_mem = 0
            avail_r_mem = 0

        if phys_mem <= 0:
            return 0, 0
        mem_avail = 100 * avail_r_mem // phys_mem
        mem_total = phys_mem * mmap.PAGESIZE // 1024 // 1024

        return mem_total, mem_avail
   0707010001f187000081a40000000000000000000000016a100daf00010304000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/node/nodedict.py  import sys
from env import Env
from core.keywords import 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",
    "hds",
    "necism",
    "eva",
    "ibmsvc",
    "vioserver",
    "brocade",
    "disks",
    "sym",
    "rotate_root_pw",
    "listener",
    "syslog",
    "stats_collection",
    "reboot",
    "cluster",
]

PRIVATE_KEYWORDS = [
    {
        "section": "node",
        "keyword": "oci",
        "text": "The default micro-container driver. If not set, prefer podman if installed, fallback to docker."
    },
    {
        "section": "node",
        "keyword": "uuid",
        "text": "The auth token provided by the collector on :cmd:`om node register`."
    },
    {
        "section": "node",
        "keyword": "prkey",
        "default_text": "<autogenerated>",
        "text": "The scsi3 persistent reservation key used by the pr resources."
    },
    {
        "section": "node",
        "keyword": "connect_to",
        "example": "1.2.3.4",
        "text": "An asset information to push to the collector on pushasset, overriding the currently discovered value. On GCE instances, defaults to the instance ip address."
    },
    {
        "section": "node",
        "keyword": "mem_bytes",
        "example": "256mb",
        "convert": "size",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "mem_banks",
        "example": "4",
        "convert": "integer",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "mem_slots",
        "example": "4",
        "convert": "integer",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "os_vendor",
        "example": "Digital",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "os_release",
        "example": "5",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "os_kernel",
        "example": "5.1234",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "os_arch",
        "example": "5.1234",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "cpu_freq",
        "example": "3.2 Ghz",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "cpu_threads",
        "example": "4",
        "convert": "integer",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "cpu_cores",
        "example": "2",
        "convert": "integer",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "cpu_dies",
        "example": "1",
        "convert": "integer",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "cpu_model",
        "example": "Alpha EV5",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "serial",
        "example": "abcdef0123456",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "bios_version",
        "example": "1.025",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "sp_version",
        "example": "1.026",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "enclosure",
        "example": "1",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "tz",
        "example": "+0200",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "manufacturer",
        "example": "Digital",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "node",
        "keyword": "model",
        "example": "ds20e",
        "text": "Override for the corresponding pushasset discovery probe."
    },
    {
        "section": "centera",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushcentera` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "xtremio",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushxtremio` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "array",
        "rtype": "xtremio",
        "keyword": "name",
        "example": "array1",
        "text": "The name of the array. If not provided, fallback to the section name suffix."
    },
    {
        "section": "hp3par",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushhp3par` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "emcvnx",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushvnx` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "freenas",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushfreenas` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "hcs",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushhcs` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "dorado",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushdorado` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "gcedisks",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushgcedisks` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "sym",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushsym` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "nsr",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushnsr` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "hds",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushhds` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "necism",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushnecism` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "eva",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pusheva` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "ibmds",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushibmds` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "ibmsvc",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushibmsvc` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "vioserver",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushvioserver` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "netapp",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushnetapp` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "brocade",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushbrocade` node action. See usr/share/doc/schedule for the schedule syntax."
    },
]

DEFAULT_COLLECTOR_DB_UPDATE_INTERVAL = 300
DEFAULT_COLLECTOR_DB_MIN_UPDATE_INTERVAL = 10
DEFAULT_COLLECTOR_DB_MIN_PING_INTERVAL = 60

KEYWORDS = [
    {
        "section": "node",
        "keyword": "secure_fetch",
        "default": True,
        "convert": "boolean",
        "text": "If set to false, disable ssl authentication checks on all uri fetches."
    },
    {
        "section": "node",
        "keyword": "min_avail_mem",
        "default": "2%",
        "convert": "size",
        "text": "The minimum required available memory to allow orchestration."
    },
    {
        "section": "node",
        "keyword": "min_avail_swap",
        "default": "10%",
        "convert": "size",
        "text": "The minimum required available swap to allow orchestration."
    },
    {
        "section": "node",
        "keyword": "env",
        "default": "TST",
        "candidates": Env.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)."
    },
    {
        "section": "node",
        "keyword": "max_parallel",
        "default": 10,
        "convert": "integer",
        "text": "Allow a maximum of :kw:`max_parallel` subprocesses to run simultaneously on :cmd:`om <selector> --parallel <action>` commands."
    },
    {
        "section": "node",
        "keyword": "allowed_networks",
        "default": ["10.0.0.0/8", "172.16.0.0/24", "192.168.0.0/16"],
        "default_text": "10.0.0.0/8 172.16.0.0/24 192.168.0.0/16",
        "convert": "list",
        "text": "The list of cidr blocks the agents allows creation of backend network into. Should be restricted to match your site constraints."
    },
    {
        "section": "node",
        "keyword": "loc_country",
        "example": "fr",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_city",
        "example": "Paris",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_zip",
        "example": "75017",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_addr",
        "example": "7 rue blanche",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_building",
        "example": "Crystal",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_floor",
        "example": "21",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_room",
        "example": "102",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "loc_rack",
        "example": "R42",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "sec_zone",
        "example": "dmz1",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "team_integ",
        "example": "TINT",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "team_support",
        "example": "TSUP",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "asset_env",
        "example": "Production",
        "text": "An asset information to push to the collector on pushasset, overriding the currently stored value."
    },
    {
        "section": "node",
        "keyword": "dbopensvc",
        "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."
    },
    {
        "section": "node",
        "keyword": "collector",
        "example": "https://collector.opensvc.com",
        "text": "The system enables OpenSVC Collector 3 calls conditional upon the detection of the Collector 3 environment. If collector_feeder or collector_server are not explicitly defined, they are derived from this value."
    },
    {
        "section": "node",
        "keyword": "collector_feeder",
        "example": "https://collector.opensvc.com/feeder",
        "text": "OpenSVC enables Collector v3 feeder calls upon detection of a collector v3 instance. When the feeder path is undefined, the system automatically constructs it using the format: node.collector + /feeder."
    },
    {
        "section": "node",
        "keyword": "collector_server",
        "example": "https://collector.opensvc.com/server",
        "text": "OpenSVC enables Collector v3 server calls upon detection of a collector v3 instance. When the server path is undefined, the system automatically constructs it using the format: node.collector + /server."
    },
    {
        "section": "node",
        "keyword": "collector_timeout",
        "convert": "duration",
        "at": True,
        "default": 5,
        "text": "The maximum time to wait for a collector v3 call. Maximum allowed value 20."
    },
    {
        "section": "node",
        "keyword": "dbcompliance",
        "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 collector's main xmlrpc server. The path part of the uri can be left unspecified."
    },
    {
        "section": "node",
        "keyword": "db_update_interval",
        "convert": "duration",
        "default": DEFAULT_COLLECTOR_DB_UPDATE_INTERVAL,
        "text": "Set the maximum wait time before next collector thread iteration."
                " Minimum value: %d" % DEFAULT_COLLECTOR_DB_UPDATE_INTERVAL,
    },
    {
        "section": "node",
        "keyword": "db_min_update_interval",
        "convert": "duration",
        "default": DEFAULT_COLLECTOR_DB_MIN_UPDATE_INTERVAL,
        "text": "Set the minimum delay before next push daemon status changes to collector."
                " Minimum value: %d" % DEFAULT_COLLECTOR_DB_MIN_UPDATE_INTERVAL,
    },
    {
        "section": "node",
        "keyword": "db_min_ping_interval",
        "convert": "duration",
        "default": DEFAULT_COLLECTOR_DB_MIN_PING_INTERVAL,
        "text": "Set the minimum interval between 2 push daemon ping to collector."
                " push daemon ping are called when there are no daemon status changes, but we want to send alive status to collector."
                " Minimum value: %d" % DEFAULT_COLLECTOR_DB_MIN_PING_INTERVAL,
    },
    {
        "section": "node",
        "keyword": "dblog",
        "convert": "boolean",
        "default": True,
        "text": "If true and dbopensvc is set, the objects action logs are reported to the collector. Set to false to disable log reporting to the collector, even if dbopensvc is set."
    },
    {
        "section": "node",
        "keyword": "dblogcron",
        "convert": "boolean",
        "default": True,
        "text": "If true and dbopensvc is set, the objects croned action logs are reported to the collector. Set to false to disable croned actions log reporting to the collector."
    },
    {
        "section": "node",
        "keyword": "branch",
        "example": "1.9",
        "text": "Set the targeted opensvc agent branch. The downloaded upgrades will honor that branch. If not set, the :kw:`repopkg` imposes the target branch, which is not recommended with a public repopkg."
    },
    {
        "section": "node",
        "keyword": "pkg_format",
        "example": "tar",
        "candidates": ["tar", "auto"],
        "default": "auto",
        "text": "Force usage of a package format. Only the tar format can be forced."
    },
    {
        "section": "node",
        "keyword": "repo",
        "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 :cmd:`om node updatepkg` and :cmd:`om node 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 -> 2.0/current
	  +- 1.9
	   +- current -> opensvc-1.9-50.rpm
	   +- opensvc-1.9-49.rpm
	   +- opensvc-1.9-50.rpm
	  +- 2.0
	   +- current -> opensvc-2.0-90.rpm
	   +- opensvc-2.0-90.rpm
	 +- tbz

"""
    },
    {
        "section": "node",
        "keyword": "repopkg",
        "example": "http://repo.opensvc.com",
        "text": """Set the uri of the opensvc agent package repository. This parameter is used by the :cmd:`om node updatepkg` command.

Expected repository structure::

	ROOT
	+- deb
	+- depot
	+- pkg
	+- sunos-pkg
	+- rpms
	 +- current -> 2.0/current
	 +- 1.9
	  +- current -> opensvc-1.9-50.rpm
	  +- opensvc-1.9-49.rpm
	  +- opensvc-1.9-50.rpm
	 +- 2.0
	  +- current -> opensvc-2.0-90.rpm
	  +- opensvc-2.0-90.rpm
	+- tbz

"""
    },
    {
        "section": "node",
        "keyword": "repocomp",
        "example": "http://compliance.repo.corp",
        "text": """Set the uri of the opensvc compliance modules gzipped tarball repository. This parameter is used by the :cmd:`om node updatecomp` command.

Expected repository structure::

	ROOT
	+- compliance-100.tar.gz
	+- compliance-101.tar.gz
	+- current -> compliance-101.tar.gz

"""
    },
    {
        "section": "node",
        "keyword": "ruser",
        "default": "root",
        "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:
* om
* 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.
"""
    },
    {
        "section": "node",
        "keyword": "maintenance_grace_period",
        "convert": "duration",
        "default": 60,
        "text": "A duration expression, like ``1m30s``, defining how long the daemon retains a"
                " remote in-maintenance (daemon restart or upgrade) node data."
                " The maintenance state is announced to peers on daemon stop and daemon restart."
                " The upgrade state is announced to peers on daemon stop for upgrade."
                " The maintenance grace period does not apply to shutdown state."
                " As long as the remote node data are retained, the local daemon won't try to takeover"
                " its running instances."
                " This parameter should be adjusted to span the daemon restart time."
    },
    {
        "section": "node",
        "keyword": "rejoin_grace_period",
        "convert": "duration",
        "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. This should be adjusted to the maximum delay you can afford to give a chance to services to start on their placement leader after a simultaneous node reboot."
    },
    {
        "section": "node",
        "keyword": "ready_period",
        "convert": "duration",
        "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. Usually set to allow at least a couple of heartbeats to be received."
    },
    {
        "section": "dequeue_actions",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`dequeue actions` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "sysreport",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`sysreport` node action, which check all modules and fix only modules flagged ``autofix``. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "compliance",
        "keyword": "schedule",
        "default": "~02:00-06:00",
        "text": "Schedule parameter for the :c-action:`compliance auto` node action, which check all modules and fix only modules flagged ``autofix``. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "compliance",
        "keyword": "auto_update",
        "convert": "boolean",
        "default": False,
        "text": "If set to ``true``, and if the execution context indicates a scheduled run, execute :cmd:`om node updatecomp` upon :cmd:`om node 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."
    },
    {
        "section": "stats",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushstats` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "stats",
        "keyword": "disable",
        "convert": "list",
        "example": "blockdev, mem_u",
        "text": "Disable push for a stats group (mem_u, cpu, proc, swap, netdev, netdev_err, block, blockdev, fs_u)."
    },
    {
        "section": "checks",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushchecks` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "packages",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushpkg` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "patches",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushpatch` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "asset",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushasset` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "disks",
        "keyword": "schedule",
        "default": "~00:00-06:00",
        "text": "Schedule parameter for the :c-action:`pushdisks` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "rotate_root_pw",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`rotate root pw` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "stats_collection",
        "keyword": "schedule",
        "default": "@10",
        "text": "Schedule parameter for the :c-action:`collect stats` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "reboot",
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`auto reboot node` action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "reboot",
        "keyword": "once",
        "convert": "boolean",
        "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 :cmd:`om node schedule reboot` is needed to rearm.
"""
    },
    {
        "section": "reboot",
        "keyword": "pre",
        "example": "yum upgrade -y",
        "text": "A command to execute before reboot. Errors are ignored."
    },
    {
        "section": "reboot",
        "keyword": "blocking_pre",
        "example": "yum upgrade -y",
        "text": "A command to execute before reboot. Abort the reboot on error."
    },
    {
        "section": "listener",
        "keyword": "crl",
        "example": "https://crl.opensvc.com",
        "default": Env.paths.crl,
        "text": "The url serving the certificate revocation list. The default points to the path of the cluster ca crl in ``{var}/certs/ca_crl``."
    },
    {
        "section": "listener",
        "keyword": "tls_addr",
        "default": "",
        "example": "1.2.3.4",
        "text": "The ip addr the daemon tls listener must listen on. The empty value used as default means: all ip4 and ip6."
    },
    {
        "section": "listener",
        "keyword": "tls_port",
        "convert": "integer",
        "default": 1215,
        "text": """The port the daemon tls listener must listen on."""
    },
    {
        "section": "listener",
        "keyword": "addr",
        "default": "",
        "example": "1.2.3.4",
        "text": "The ip addr the daemon raw listener must listen on. The empty value used as default means: all ip4 and ip6."
    },
    {
        "section": "listener",
        "keyword": "dns_sock_uid",
        "default": "953",
        "text": "The uid owning the unix socket serving the remote backend to the pdns authoritative server."
    },
    {
        "section": "listener",
        "keyword": "dns_sock_gid",
        "default": "953",
        "text": "The gid owning the unix socket serving the remote backend to the pdns authoritative server."
    },
    {
        "section": "listener",
        "keyword": "port",
        "convert": "integer",
        "default": 1214,
        "text": """The port the daemon raw 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 :c-action:`dequeue actions` node action upon receive. The :kw:`listener.port` parameter is sent to the collector upon :c-action:`pushasset`. The collector uses this port to notify the node."""
    },
    {
        "section": "listener",
        "keyword": "openid_well_known",
        "example": "https://keycloak.opensvc.com/auth/realms/clusters/.well-known/openid-configuration",
        "text": "The url serving the well-known configuration of an openid provider. If set, the h2 listener will try to validate the Bearer token provided in the requests. If valid the user name is fetched from the 'preferred_username' claim (fallback on 'name'), and the user grants are fetched from the 'grant' claim. Grant can be a list, in which case a proper grant value is formatted via concatenation of the list elements."
    },
    {
        "section": "listener",
        "keyword": "ui",
        "convert": "boolean",
        "default": True,
        "text": "Serve the ui webapp to browsers getting the api / path.",
    },
    {
        "section": "syslog",
        "keyword": "facility",
        "default": "daemon",
        "text": """The syslog facility to log to."""
    },
    {
        "section": "syslog",
        "keyword": "level",
        "default": "info",
        "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."
    },
    {
        "section": "syslog",
        "keyword": "host",
        "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."
    },
    {
        "section": "syslog",
        "keyword": "port",
        "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."
    },
    {
        "section": "cluster",
        "keyword": "vip",
        "example": "192.168.99.12/24@eth0",
        "at": True,
        "text": "The cluster virtual ip. If configured, the daemon creates a ``system/svc/vip`` failover service to manage this ip."
    },
    {
        "section": "cluster",
        "keyword": "default_mon_format",
        "candidates": ["compact", "matrix"],
        "example": "compact",
        "at": True,
        "text": "The renderer to use with monitor commands, if not explicitely set by the --format option. The compact renderer is optimized for large clusters."
    },
    {
        "section": "cluster",
        "keyword": "dns",
        "convert": "list",
        "default": [],
        "default_text": "",
        "at": True,
        "text": "The list of nodes to set as dns in the containers resolvers. If set, the search will also be set to :c-dns-domain:`<name>.<namespace>.svc.<clustername>`, :c-dns-domain:`<namespace>.svc.<clustername>` and :c-dns-domain:`<clustername>`."
    },
    {
        "section": "cluster",
        "keyword": "ca",
        "convert": "list",
        "default_text": "system/sec/ca-<clustername>",
        "text": "A whitespace-separated list of paths of the secrets hosting the ca certificates that the listener use to validate clients certificates."
    },
    {
        "section": "cluster",
        "keyword": "cert",
        "default_text": "system/sec/cert-<clustername>",
        "text": "The path of the secret hosting the certificate that the listener use for its tls socket."
    },
    {
        "section": "cluster",
        "keyword": "id",
        "at": True,
        "default_text": "<auto-generated>",
        "text": "This information is fetched from the join command payload received from the joined node."
    },
    {
        "section": "cluster",
        "keyword": "name",
        "at": True,
        "default": "default",
        "text": "The cluster name is used as the zone name in the cluster dns records, in the {fqdn} configuration reference, in the aes secret encryption metadata, in the default name of the secret storing the listener certificate authority (system/sec/ca-<clustername>), in the default name of the secret storing the listener certificate and private key (system/sec/cert-<clustername>). The cluster name should be unique site-wide and be set right before populating secrets. It is always lowercased, so better to set it to a lowercase value to avoid confusion. This information is fetched from the join command payload received from the joined node."
    },
    {
        "section": "cluster",
        "keyword": "secret",
        "at": True,
        "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."
    },
    {
        "section": "cluster",
        "keyword": "nodes",
        "convert": "list",
        "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."
    },
    {
        "section": "cluster",
        "keyword": "drpnodes",
        "convert": "list",
        "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."
    },
    {
        "section": "cluster",
        "keyword": "quorum",
        "convert": "boolean",
        "default": False,
        "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."
    },
    {
        "section": "node",
        "keyword": "split_action",
        "candidates": ["crash", "reboot"],
        "default": "crash",
        "text": "Commit suicide method when cluster split occur."
                " Default is crash."
                " reboot method may be used instead of crash when it is not"
                " simple to poweron node after crash."
    },
    {
        "section": "arbitrator",
        "keyword": "name",
        "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 :cmd:`om node ping --node <arbitrator name>`.
"""
    },
    {
        "section": "arbitrator",
        "keyword": "secret",
        "required": True,
        "text": "The secret to use to encrypt/decrypt data exchanged with the arbitrator (AES256)."
    },
    {
        "section": "arbitrator",
        "keyword": "timeout",
        "convert": "duration",
        "at": True,
        "default": 5,
        "text": "The maximum time to wait for the arbitrator vote during a quorum election. "
                "Upon expiration, the vote is considered lost for the querying node."
    },
    {
        "section": "stonith",
        "keyword": "cmd",
        "at": True,
        "convert": "shlex",
        "required": True,
        "example": "/bin/true",
        "text": "The command to use to STONITH a peer. Usually comes from a fencing utilities collection. The section rindex is the name of the node to stonith, so one section per node must be added."
    },
    {
        "section": "hb",
        "keyword": "type",
        "candidates": ["unicast", "multicast", "disk", "relay"],
        "required": True,
        "text": "The heartbeat driver name."
    },
    {
        "section": "hb",
        "keyword": "addr",
        "rtype": "unicast",
        "at": True,
        "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."
    },
    {
        "section": "hb",
        "keyword": "intf",
        "rtype": "unicast",
        "at": True,
        "default_text": "The natural interface for <addr>",
        "example": "eth0",
        "text": "The interface to bind."
    },
    {
        "section": "hb",
        "keyword": "port",
        "rtype": "unicast",
        "convert": "integer",
        "at": True,
        "default": 10000,
        "text": "The port for each node to send to or listen on."
    },
    {
        "section": "hb",
        "keyword": "timeout",
        "convert": "duration",
        "at": True,
        "default": 15,
        "text": "The delay since the last received heartbeat from a node before considering this node is gone."
    },
    {
        "section": "hb",
        "keyword": "interval",
        "convert": "duration",
        "at": True,
        "default": 5,
        "text": "The interval between tx threads data sends."
    },
    {
        "section": "hb",
        "keyword": "addr",
        "rtype": "multicast",
        "at": True,
        "default": "224.3.29.71",
        "text": "The multicast address to send to and listen on."
    },
    {
        "section": "hb",
        "keyword": "intf",
        "rtype": "multicast",
        "at": True,
        "default_text": "The natural interface for <addr>",
        "example": "eth0",
        "text": "The interface to bind."
    },
    {
        "section": "hb",
        "keyword": "port",
        "rtype": "multicast",
        "convert": "integer",
        "at": True,
        "default": 10000,
        "text": "The port for each node to send to or listen on."
    },
    {
        "section": "hb",
        "keyword": "nodes",
        "at": True,
        "convert": "list",
        "default_text": "All nodes.",
        "text": "The nodes participating to the heartbeat.",
    },
    {
        "section": "hb",
        "keyword": "dev",
        "rtype": "disk",
        "at": True,
        "required": True,
        "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."
    },
    {
        "section": "hb",
        "keyword": "relay",
        "rtype": "relay",
        "required": True,
        "example": "relaynode1",
        "text": "The relay resolvable node name."
    },
    {
        "section": "hb",
        "keyword": "secret",
        "rtype": "relay",
        "required": True,
        "example": "123123123124325543565",
        "text": "The secret to use to encrypt/decrypt data exchanged with the relay (AES256)."
    },
    {
        "section": "cni",
        "keyword": "plugins",
        "default": "/opt/cni/bin",
        "text": "The directory hosting the CNI plugins.",
        "example": "/var/lib/opensvc/cni/bin"
    },
    {
        "section": "cni",
        "keyword": "config",
        "default": "/opt/cni/net.d",
        "text": "The directory hosting the CNI network configuration files.",
        "example": "/var/lib/opensvc/cni/net.d"
    },
    {
        "section": "pool",
        "keyword": "type",
        "default": "directory",
        "candidates": ["directory", "loop", "vg", "zpool", "freenas", "share", "shm", "symmetrix", "virtual", "dorado", "hcs", "drbd", "pure"],
        "text": "The pool type."
    },
    {
        "section": "pool",
        "keyword": "status_schedule",
        "text": "The value to set to the status_schedule keyword of the volume objects allocated from the pool. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "pool",
        "keyword": "mnt_opt",
        "at": True,
        "text": "The mount options of the fs created over the pool devices."
    },
    {
        "section": "pool",
        "rtype": ["freenas", "symmetrix", "dorado", "hcs", "pure"],
        "keyword": "array",
        "at": True,
        "required": True,
        "text": "The name of the array, known as :c-array:`array#<name>` in the node or cluster configuration."
    },
    {
        "section": "pool",
        "rtype": ["hcs", "pure"],
        "keyword": "label_prefix",
        "text": "The prefix to add to the label assigned to the created disks."
    },
    {
        "section": "pool",
        "rtype": "hcs",
        "keyword": "start_ldev_id",
        "text": "The start of the range of ldev ids to allocate from."
    },
    {
        "section": "pool",
        "rtype": "hcs",
        "keyword": "end_ldev_id",
        "text": "The end of the range of ldev ids to allocate from."
    },
    {
        "section": "pool",
        "rtype": "pure",
        "keyword": "pod",
        "text": "The pod to create volume into."
    },
    {
        "section": "pool",
        "rtype": "pure",
        "keyword": "volumegroup",
        "text": "The volumegroup to create volume into."
    },
    {
        "section": "pool",
        "rtype": "pure",
        "keyword": "delete_now",
        "text": "If set to false the pure volumes are not immediately deleted on unprovision, so a following provision action could fail.",
        "default": True,
    },
    {
        "section": "pool",
        "rtype": "hcs",
        "keyword": "resource_group",
        "default": 0,
        "convert": "integer",
        "text": "The resource group to assign ldevs to."
    },
    {
        "section": "pool",
        "rtype": "symmetrix",
        "keyword": "srp",
        "required": True,
        "text": "The name of the array resource pool to allocate volumes from."
    },
    {
        "section": "pool",
        "rtype": "symmetrix",
        "keyword": "slo",
        "default": None,
        "text": "The name of the Service Level Agreement of the selected Storage Group."
    },
    {
        "section": "pool",
        "rtype": "symmetrix",
        "keyword": "srdf",
        "default": False,
        "text": "Use SRDF replication."
    },
    {
        "section": "pool",
        "rtype": "symmetrix",
        "keyword": "rdfg",
        "default": None,
        "text": "Replication Group to use for SRDF."
    },
    {
        "section": "pool",
        "rtype": ["freenas", "dorado", "hcs"],
        "keyword": "diskgroup",
        "required": True,
        "text": "The name of the array disk group to allocate volumes from."
    },
    {
        "section": "pool",
        "rtype": "freenas",
        "keyword": "insecure_tpc",
        "convert": "boolean",
        "default": False,
        "text": "Allow initiators to xcopy without authenticating to foreign targets."
    },
    {
        "section": "pool",
        "rtype": "freenas",
        "keyword": "compression",
        "default": "inherit",
        "candidates": ["inherit", "none", "lz4", "gzip-1", "gzip-2", "gzip-3", "gzip-4", "gzip-5", "gzip-6", "gzip-7", "gzip-8", "gzip-9", "zle", "lzjb"],
        "text": "Compression level.",
    },
    {
        "section": "pool",
        "rtype": "freenas",
        "keyword": "sparse",
        "convert": "boolean",
        "default": False,
        "text": "Create zvol in sparse mode."
    },
    {
        "section": "pool",
        "rtype": "freenas",
        "keyword": "blocksize",
        "default": 512,
        "convert": "size",
        "text": "Allow initiators to xcopy without authenticating to foreign targets."
    },
    {
        "section": "pool",
        "rtype": "vg",
        "keyword": "name",
        "required": True,
        "text": "The name of the volume group to allocate the pool volumes logical volumes into."
    },
    {
        "section": "pool",
        "rtype": "drbd",
        "keyword": "network",
        "text": "The name of the backend network to use for drbd trafic. Set this keyword if some node names are resolved to NATed addresses."
    },
    {
        "section": "pool",
        "rtype": "drbd",
        "keyword": "vg",
        "text": "The name of the volume group to allocate the pool volumes logical volumes into."
    },
    {
        "section": "pool",
        "rtype": "drbd",
        "keyword": "max_peers",
        "convert": "integer",
        "default": 0,
        "text": "The number of drbd peer nodes to configure in the metadata. The default value allows growing x2."
    },
    {
        "section": "pool",
        "rtype": "zpool",
        "keyword": "name",
        "required": True,
        "text": "The name of the zpool to allocate the pool volumes zvol or datasets into."
    },
    {
        "section": "pool",
        "rtype": "drbd",
        "keyword": "zpool",
        "text": "The name of the zpool to allocate the pool volumes zvol into."
    },
    {
        "section": "pool",
        "keyword": "path",
        "rtype": "drbd",
        "text": "The fullpath of the directory hosting the pool volumes loop files."
    },
    {
        "section": "pool",
        "keyword": "addr",
        "text": "The addr to use to connect a peer. Use scoping to define "
                "each non-default address.",
        "at": True,
        "rtype": "drbd",
        "default_text": "The ipaddr resolved for the nodename.",
    },
    {
        "section": "pool",
        "keyword": "path",
        "rtype": "share",
        "default": "{var}/pool/share",
        "text": "The fullpath of the shared directory hosting the pool volumes directories or loop files."
    },
    {
        "section": "pool",
        "keyword": "path",
        "rtype": "directory",
        "default": "{var}/pool/directory",
        "text": "The fullpath of the directory hosting the pool volumes directories or loop files."
    },
    {
        "section": "pool",
        "keyword": "template",
        "rtype": ["virtual"],
        "text": "The service path ``<namespace>/<kind>/<name>`` of a volume to use as a template for new volumes.",
        "example": "templates/vol/mpool-over-loop"
    },
    {
        "section": "pool",
        "keyword": "volume_env",
        "rtype": ["virtual"],
        "convert": "list",
        "example": "container#1.name:container_name env.foo:foo",
        "text": "The list of the volume consumer service config keywords which values are mapped as env keys in the allocated volume service. If the keyword is not set at the source, an error is raised.",
    },
    {
        "section": "pool",
        "keyword": "optional_volume_env",
        "rtype": ["virtual"],
        "convert": "list",
        "example": "container#1.name:container_name env.foo:foo",
        "text": "The list of the volume consumer service config keywords which values are mapped as env keys in the allocated volume service. If the keyword is not set at the source, the default value in the template env section applies.",
    },
    {
        "section": "pool",
        "keyword": "capabilities",
        "rtype": ["virtual"],
        "convert": "list",
        "default": ["roo", "rwo", "rox", "rwx"],
        "default_text": "roo rwo rox rwx",
        "text": "The capabilities exposed by the virtual pool. Supported capabilities: ``shared``, ``roo``, ``rox``, ``rwo``, ``rwx``, ``blk``",
    },
    {
        "section": "pool",
        "keyword": "path",
        "rtype": "loop",
        "default": "{var}/pool/loop",
        "text": "The path to create the pool loop files in."
    },
    {
        "section": "pool",
        "keyword": "fs_type",
        "default": "xfs",
        "text": "The filesystem to format the pool devices with."
    },
    {
        "section": "pool",
        "keyword": "mkfs_opt",
        "default": [],
        "convert": "shlex",
        "example": "-O largefile",
        "text": "The mkfs command options to use to format the pool devices."
    },
    {
        "section": "pool",
        "keyword": "mkblk_opt",
        "default": [],
        "convert": "shlex",
        "text": "The zvol, lv, and other block device creation command options to use to prepare the pool devices."
    },
    {
        "section": "hook",
        "keyword": "events",
        "default": [],
        "default_text": "",
        "convert": "list",
        "text": "The list of events to execute the hook command on. The special value ``all`` is also supported."
    },
    {
        "section": "hook",
        "keyword": "command",
        "convert": "shlex",
        "text": "The command to execute on selected events. The program is fed the json-formatted event data through stdin."
    },
    {
        "section": "network",
        "keyword": "type",
        "candidates": ["bridge", "routed_bridge", "weave"],
        "default": "bridge",
        "text": "The type of network.",
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "subnet",
        "at": True,
        "text": "The cidr subnet handled by this node. This parameter must be scoped for each node. Usually, the subnets are allocated automatically upon initial network setup, each node being attributed a subnet based on its index in the cluster.nodes list."
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "gateway",
        "at": True,
        "text": "The gateway to use to reach the network segment of the node specified as scope."
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "ips_per_node",
        "convert": "integer",
        "default": "1024",
        "text": "The number of allocatable ips per node on the network. Converted to the closest power of two."
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "tables",
        "default": ["main"],
        "default_text": "main",
        "convert": "list",
        "text": "The list of routing tables to add the backend network routes to. The list of available tables is in ``/etc/iproute2/rt_tables``.",
        "example": "main custom1 custom2"
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "addr",
        "at": True,
        "default_text": "Detect using a name resolution of <nodename>. Beware, if the nodename resolves to 127.0.1.1 or 127.0.0.1 the ipip tunnel can not work.",
        "text": "The ip address used as local endpoint for the ipip tunnel configured by network setup to access the backend subnet of peer nodes not on the same subnet."
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "tunnel",
        "default": "auto",
        "candidates": ["auto", "always", "never"],
        "text": "Create and route trafic through tunnels to peer nodes policy. ``auto`` tunnel if the peer is not in the same subnet, ``always`` tunnel even if the peer seems to be in the same subnet (some hosting providers require this as traffic goes through router even between adjacent nodes."
    },
    {
        "section": "network",
        "rtype": "routed_bridge",
        "keyword": "tunnel_mode",
        "default": "ipip",
        "candidates": ["ipip", "gre"],
        "text": "The ip tunnel mode. gre can tunnel mcast ip and ipv6 at the price of a 24B header, ipip can only tunnel ipv4 but with a 20B header. Note, some OVH servers combinations don't support ipip but work with gre."
    },
    {
        "section": "network",
        "rtype": ["bridge", "routed_bridge"],
        "keyword": "network",
        "default": "10.22.0.0/16",
        "text": "The cluster backend network. The routed_bridge driver fragments this network into :kw:`ips_per_nodes` blocks subnets."
    },
    {
        "section": "network",
        "rtype": "weave",
        "keyword": "network",
        "default": "10.32.0.0/12",
        "text": "The cluster backend network."
    },
    {
        "section": "switch",
        "keyword": "type",
        "candidates": ["brocade"],
        "required": True,
        "text": "The network switch driver name."
    },
    {
        "section": "switch",
        "rtype": "brocade",
        "keyword": "name",
        "example": "sansw1.my.corp",
        "text": "The name connect to the switch (dns name or ip address). If not set, fallback to the section name suffix."
    },
    {
        "section": "switch",
        "rtype": "brocade",
        "keyword": "method",
        "default": "ssh",
        "candidates": ["telnet", "ssh"],
        "example": "ssh",
        "text": "The method to use to connect to the switch. ssh without a key set needs sshpass the binary."
    },
    {
        "section": "switch",
        "rtype": "brocade",
        "keyword": "username",
        "required": True,
        "example": "admin",
        "text": "The username to use to log in the switch."
    },
    {
        "section": "switch",
        "rtype": "brocade",
        "keyword": "password",
        "example": "mysec/password",
        "text": "The password to use to log in, expressed as a <secname> reference to a secret. The secret must be in the ``system`` namespace and must have the ``password`` key. Either username or key must be specified."
    },
    {
        "section": "switch",
        "rtype": "brocade",
        "keyword": "key",
        "example": "/path/to/key",
        "text": "The path to the private key to use to log in the switch."
    },
    {
        "section": "array",
        "keyword": "type",
        "candidates": ["freenas", "hds", "eva", "nexenta", "vioserver", "centera", "symmetrix", "emcvnx", "netapp", "hp3par", "ibmds", "ibmsvc", "xtremio", "dorado", "hcs", "pure"],
        "required": True,
        "text": "The storage array driver name."
    },
    {
        "section": "pool",
        "rtype": ["dorado", "hcs"],
        "keyword": "compression",
        "convert": "boolean",
        "default": False,
        "text": "Activate compression on created luns.",
    },
    {
        "section": "pool",
        "rtype": ["dorado", "hcs"],
        "keyword": "dedup",
        "convert": "boolean",
        "default": False,
        "text": "Activate data deduplcation on created luns.",
    },
    {
        "section": "pool",
        "rtype": "dorado",
        "keyword": "hypermetrodomain",
        "required": False,
        "example": "HyperMetroDomain_000",
        "text": "Create LUN as HyperMetro replicated pairs, using this domain."
    },
    {
        "section": "array",
        "rtype": ["pure"],
        "keyword": "schedule",
        "text": "Schedule parameter for the :c-action:`pushpure` node action. See usr/share/doc/schedule for the schedule syntax."
    },
    {
        "section": "array",
        "rtype": ["freenas", "xtremio", "dorado", "hcs", "pure"],
        "keyword": "api",
        "required": True,
        "example": "https://array.opensvc.com/api/v1.0",
        "text": "The array rest api url."
    },
    {
        "section": "array",
        "rtype": ["hcs"],
        "keyword": "http_proxy",
        "example": "http://proxy.mycorp:3158",
        "text": "The proxy server to use for http requests to the api."
    },
    {
        "section": "array",
        "rtype": ["hcs"],
        "keyword": "https_proxy",
        "example": "https://proxy.mycorp:3158",
        "text": "The proxy server to use for https requests to the api."
    },
    {
        "section": "array",
        "rtype": ["hcs"],
        "keyword": "retry",
        "default": 30,
        "convert": "integer",
        "text": "The number of request attemps on retryable errors."
    },
    {
        "section": "array",
        "rtype": ["hcs"],
        "keyword": "delay",
        "default": 10,
        "convert": "duration",
        "text": "The delay between request attemps on retryable errors."
    },
    {
        "section": "array",
        "rtype": ["hcs"],
        "keyword": "model",
        "required": True,
        "example": "VSP G350",
        "candidates": ['VSP G370', 'VSP G700', 'VSP G900', 'VSP F370', 'VSP F700', 'VSP F900', 'VSP G350', 'VSP F350', 'VSP G800', 'VSP F800', 'VSP G400', 'VSP G600', 'VSP F400', 'VSP F600', 'VSP G200', 'VSP G1000', 'VSP G1500', 'VSP F1500', 'Virtual Storage Platform', 'HUS VM', 'VSP 5100', 'VSP 5100H', 'VSP 5200', 'VSP 5200H' 'VSP 5500', 'VSP 5500H', 'VSP 5600', 'VSP 5600H'],
        "text": "The array model"
    },
    {
        "section": "array",
        "rtype": ["centera", "eva", "hds", "ibmds", "ibmsvc", "freenas", "netapp", "nexenta", "vioserver", "xtremio", "dorado", "hcs"],
        "keyword": "username",
        "required": True,
        "example": "root",
        "text": "The username to use to log in."
    },
    {
        "section": "array",
        "rtype": ["centera", "eva", "hds", "freenas", "nexenta", "xtremio", "dorado", "hcs"],
        "keyword": "password",
        "example": "system/sec/array1",
        "required": True,
        "text": "The password to use to log in, expressed as a <path> reference to a secret. The secret must be in the ``system`` namespace and must have the ``password`` key."
    },
    {
        "section": "array",
        "rtype": ["pure"],
        "keyword": "secret",
        "example": "system/sec/array1",
        "required": True,
        "text": "The secret to use to store the information required to create the login jwt, expressed as a <path> reference to a secret. The secret must be in the ``system`` namespace and must have the following keys: ``private_key``."
    },
    {
        "section": "array",
        "rtype": ["freenas", "dorado", "hcs"],
        "keyword": "timeout",
        "convert": "duration",
        "example": "10s",
        "default": 120,
        "text": "The api request timeout."
    },
    {
        "section": "array",
        "rtype": ["dorado", "hcs"],
        "keyword": "name",
        "example": "a09",
        "text": "The name of the array. If not provided, fallback to the section name suffix."
    },
    {
        "section": "array",
        "rtype": "symmetrix",
        "keyword": "name",
        "example": "00012345",
        "text": "The name of the array. If not provided, fallback to the section name suffix."
    },
    {
        "section": "array",
        "rtype": "symmetrix",
        "keyword": "symcli_path",
        "example": "/opt/symcli",
        "text": "Force use of a symcli programs installation, pointing the path of its head directory. For the case multiple symcli versions are installed and the default selector does not select the version preferred for the array."
    },
    {
        "section": "array",
        "rtype": "symmetrix",
        "keyword": "symcli_connect",
        "example": "MY_SYMAPI_SERVER",
        "text": "Set the ``SYMCLI_CONNECT`` environment variable to this value, if set. If not set, the scsi communication channels are used. The value set must be declared in the ``/var/symapi/config/netcnfg`` file."
    },
    {
        "section": "array",
        "rtype": "pure",
        "keyword": "username",
        "example": "pureuser",
        "text": "The username to use as the ``sub`` key in the payload of the login jwt.",
        "required": True,
    },
    {
        "section": "array",
        "rtype": "pure",
        "keyword": "insecure",
        "convert": "boolean",
        "default": False,
        "text": "Disable secure socket verification",
    },
    {
        "section": "array",
        "rtype": "pure",
        "keyword": "issuer",
        "example": "pureuser",
        "default_text": "<same as username>",
        "text": "The issuer to use as the ``iss`` key in the payload of the login jwtusername.",
    },
    {
        "section": "array",
        "rtype": "pure",
        "keyword": "key_id",
        "example": "4a4ba128-7a45-434a-a06f-d61b1fadfe3c",
        "text": "The key id to use as the ``kid`` key in the header of the login jwt.",
        "required": True,
    },
    {
        "section": "array",
        "rtype": "pure",
        "keyword": "client_id",
        "example": "fb4ed5d5-fa47-4ae1-a6b9-e435595e0b2b",
        "text": "The client id to use as the ``aud`` key in the payload of the login jwt.",
        "required": True,
    },
    {
        "section": "array",
        "rtype": ["emcvnx", "hp3par", "symmetrix"],
        "keyword": "username",
        "example": "root",
        "text": "The username to use to log in, if configured."
    },
    {
        "section": "array",
        "rtype": ["emcvnx", "symmetrix"],
        "keyword": "password",
        "example": "system/sec/array1",
        "text": "The password to use to log in, if configured, expressed as a <path> reference to a secret. The secret must be in the ``system`` namespace and must have the ``password`` key."
    },
    {
        "section": "array",
        "rtype": ["centera", "netapp"],
        "keyword": "server",
        "required": True,
        "example": "centera1",
        "text": "The storage server to connect."
    },
    {
        "section": "array",
        "rtype": "centera",
        "keyword": "java_bin",
        "required": True,
        "example": "/opt/java/bin/java",
        "text": "The path to the java executable to use to run the Centera management program."
    },
    {
        "section": "array",
        "rtype": "centera",
        "keyword": "jcass_dir",
        "required": True,
        "example": "/opt/centera/LIB",
        "text": "The path of the directory hosting the JCASScript.jar."
    },
    {
        "section": "array",
        "rtype": "emcvnx",
        "keyword": "method",
        "default": "secfile",
        "candidates": ["secfile", "credentials"],
        "example": "secfile",
        "text": "The authentication method to use."
    },
    {
        "section": "array",
        "rtype": "emcvnx",
        "keyword": "spa",
        "required": True,
        "example": "array1-a",
        "text": "The name of the Service Processor A."
    },
    {
        "section": "array",
        "rtype": "emcvnx",
        "keyword": "spb",
        "required": True,
        "example": "array1-b",
        "text": "The name of the Service Processor B."
    },
    {
        "section": "array",
        "rtype": "emcvnx",
        "keyword": "scope",
        "default": "0",
        "example": "1",
        "text": "The VNC scope to work in."
    },
    {
        "section": "array",
        "rtype": "eva",
        "keyword": "manager",
        "required": True,
        "example": "evamanager.mycorp",
        "text": "The EVA manager to connect."
    },
    {
        "section": "array",
        "rtype": "eva",
        "keyword": "bin",
        "example": "/opt/sssu/bin/sssu",
        "text": "The EVA manager executable to use."
    },
    {
        "section": "array",
        "rtype": "hds",
        "keyword": "bin",
        "example": "/opt/hds/bin/HiCommandCLI",
        "text": "The HDS manager executable to use."
    },
    {
        "section": "array",
        "rtype": "hds",
        "keyword": "jre_path",
        "example": "/opt/java",
        "text": "The path hosting the java installation to use to execute the HiCommandCLI."
    },
    {
        "section": "array",
        "rtype": "hds",
        "keyword": "name",
        "example": "HUSVM.1234",
        "text": "The name of the array. If not provided, fallback to the section name suffix."
    },
    {
        "section": "array",
        "rtype": "hds",
        "keyword": "url",
        "required": True,
        "example": "https://hdsmanager/",
        "text": "The url passed to HiCommandCli, pointing the manager in charge of the array."
    },
    {
        "section": "array",
        "rtype": "hp3par",
        "keyword": "method",
        "default": "ssh",
        "candidates": ["proxy", "cli", "ssh"],
        "example": "ssh",
        "text": "The connection method to use."
    },
    {
        "section": "array",
        "rtype": "hp3par",
        "keyword": "manager",
        "default_text": "wthe name of the array>",
        "example": "mymanager.mycorp",
        "text": "The array manager host name."
    },
    {
        "section": "array",
        "rtype": "hp3par",
        "keyword": "key",
        "example": "/path/to/key",
        "text": "The path to the private key to use to log in."
    },
    {
        "section": "array",
        "rtype": "hp3par",
        "keyword": "pwf",
        "example": "/path/to/pwf",
        "text": "The path to the 3par password file to use to log in."
    },
    {
        "section": "array",
        "rtype": "hp3par",
        "keyword": "cli",
        "default": "3parcli",
        "example": "/path/to/pwf",
        "text": "The path to the 3par password file to use to log in."
    },
    {
        "section": "array",
        "rtype": "ibmds",
        "keyword": "hmc1",
        "required": True,
        "example": "hmc1.mycorp",
        "text": "The host name of the primary HMC."
    },
    {
        "section": "array",
        "rtype": "ibmds",
        "keyword": "hmc2",
        "required": True,
        "example": "hmc2.mycorp",
        "text": "The host name of the secondary HMC."
    },
    {
        "section": "array",
        "rtype": ["netapp", "ibmsvc", "vioserver"],
        "keyword": "key",
        "required": True,
        "example": "/path/to/key",
        "text": "The path to the private key to use to log in."
    },
    {
        "section": "array",
        "rtype": "nexenta",
        "keyword": "port",
        "default": 2000,
        "convert": "integer",
        "example": "2000",
        "text": "The nexenta administration listener port."
    },
]


KEYS = KeywordStore(
    name="node",
    keywords=PRIVATE_KEYWORDS+KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.node.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
 )

0707010001f186000081a40000000000000000000000016a100daf000310f8000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/core/node/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 absolute_import, division, print_function

import datetime
import fnmatch
import json
import logging
import os
import re
import shlex

import sys
import time
from errno import ECONNREFUSED, EPIPE
from multiprocessing import Process

import foreign.six as six

import core.exceptions as ex
import core.logger
import core.objects.builder
import core.oc3path as oc3path
from core.capabilities import capabilities
from core.comm import Crypt, DEFAULT_DAEMON_TIMEOUT
from core.configfile import move_config_file
from core.contexts import want_context
from core.extconfig import ExtConfigMixin
from core.freezer import Freezer
from core.network import NetworksMixin
from core.scheduler import SchedOpts, Scheduler, sched_action
from env import Env
from utilities.loop_delay import delay
from utilities.naming import (ANSI_ESCAPE, factory, fmt_path, glob_services_config,
                              is_service, new_id, paths_data,
                              resolve_path, split_path, strip_path, svc_pathetc,
                              validate_kind, validate_name, validate_ns_name,
                              object_path_glob)
from utilities.render.command import format_command
from utilities.selector import selector_config_match, selector_parse_fragment, selector_parse_op_fragment
from utilities.cache import purge_cache_expired
from utilities.converters import print_duration, print_size, convert_duration
from utilities.drivers import driver_import
from utilities.lazy import (lazy, lazy_initialized, set_lazy, unset_all_lazy,
                            unset_lazy)
from utilities.lock import LOCK_EXCEPTIONS
from utilities.proc import call, justcall, vcall, which, check_privs, daemon_process_running, drop_option, find_editor, \
    init_locale, does_call_cmd_need_shell, get_call_cmd_from_str
from utilities.files import assert_file_exists, assert_file_is_root_only_writeable, makedirs
from utilities.render.color import formatter
from utilities.semver import Semver
from utilities.storage import Storage
from utilities.string import bdecode, base64encode

try:
    from foreign.six.moves.urllib.request import Request, urlopen
    from foreign.six.moves.urllib.error import HTTPError
    from foreign.six.moves.urllib.parse import urlencode
except ImportError:
    # pylint false positive
    pass


if six.PY2:
    BrokenPipeError = IOError

init_locale()

DEFAULT_STATUS_GROUPS = [
    "hb",
    "arbitrator",
]

ACTION_ANY_NODE = (
    "collector_cli",
    "delete",
    "eval",
    "get",
    "set",
    "unset",
    "wait",
)
ACTION_ASYNC = {
    "freeze": {
        "target": "frozen",
        "progress": "freezing",
    },
    "thaw": {
        "target": "thawed",
        "progress": "thawing",
    },
}
ACTIONS_CUSTOM_REMOTE = (
    "drain",
    "ls",
    "logs",
    "ping",
    "events",
    "daemon_mutex_status",
    "daemon_stats",
    "daemon_status",
    "daemon_blacklist_status",
    "daemon_join",
    "daemon_rejoin",
    "daemon_relay_status",
    "stonith",
)
ACTIONS_NOWAIT_RESULT = (
    "delete",
    "eval",
    "get",
    "set",
    "unset",
    "reboot",
    "shutdown",
    "updatepkg",
    "updateclumgr",
    "updatecomp",
    "daemon_restart",
)
ACTIONS_NO_PARALLEL = [
    "edit_config",
    "get",
    "print_config",
    "print_resource_status",
    "print_schedule",
    "print_status",
]

ACTIONS_NO_MULTIPLE_SERVICES = [
    "print_resource_status",
]

CONFIG_DEFAULTS = {
    "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",
]

STATS_INTERVAL = 30


class Node(Crypt, ExtConfigMixin, NetworksMixin):
    """
    Defines a cluster node.  It contain list of Svc.
    Implements node-level actions and checks.
    """
    def __str__(self):
        return self.nodename

    def __init__(self, log_handlers=None):
        ExtConfigMixin.__init__(self, default_status_groups=DEFAULT_STATUS_GROUPS)
        self.listener = None
        self.clouds = None
        self.paths = Storage(
            reboot_flag=os.path.join(Env.paths.pathvar, "REBOOT_FLAG"),
            last_boot_id=os.path.join(Env.paths.pathvar, "node", "last_boot_id"),
            tmp_cf=os.path.join(Env.paths.pathvar, "node.conf.tmp"),
            cf=Env.paths.nodeconf,
        )
        self.services = None
        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.stats_data = {}
        self.stats_updated = 0
        log_file = os.path.join(Env.paths.pathlog, "node.log")
        self.logger = core.logger.initLogger(Env.nodename, log_file, handlers=log_handlers)
        extra = {"node": Env.nodename, "sid": Env.session_uuid}
        self.log = logging.LoggerAdapter(self.logger, extra)

    def get_node(self):
        """
        helper for the comm module to find the Node(), for accessing
        its configuration.
        """
        return self

    @lazy
    def cd(self):
        configs = []
        if os.path.exists(Env.paths.clusterconf):
            configs.append(Env.paths.clusterconf)
        if os.path.exists(Env.paths.nodeconf):
            configs.append(Env.paths.nodeconf)
        return self.parse_config_file(configs)

    def cd_clear_caches(self):
        self.raw_cd = None
        self.unset_lazy("cd")

    @lazy
    def private_cd(self):
        return self.parse_config_file(self.paths.cf)

    @lazy
    def kwstore(self):
        from .nodedict import KEYS
        return KEYS

    @lazy
    def devnull(self):
        return os.open(os.devnull, os.O_RDWR)

    @lazy
    def var_d(self):
        var_d = os.path.join(Env.paths.pathvar, "node")
        makedirs(var_d)
        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,
            node=self,
            scheduler_actions={
                "checks": [SchedOpts(
                    "checks",
                    req_collector=True,
                )],
                "dequeue_actions": [SchedOpts(
                    "dequeue_actions",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushstats": [SchedOpts(
                    "stats",
                    req_collector=True,
                )],
                "collect_stats": [SchedOpts(
                    "stats_collection",
                    schedule_option="collect_stats_schedule"
                )],
                "pushpkg": [SchedOpts(
                    "packages",
                    req_collector=True,
                )],
                "pushpatch": [SchedOpts(
                    "patches",
                    req_collector=True,
                )],
                "pushasset": [SchedOpts(
                    "asset",
                    req_collector=True,
                )],
                "pushnsr": [SchedOpts(
                    "nsr",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushhp3par": [SchedOpts(
                    "hp3par",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushemcvnx": [SchedOpts(
                    "emcvnx",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushcentera": [SchedOpts(
                    "centera",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushnetapp": [SchedOpts(
                    "netapp",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushibmds": [SchedOpts(
                    "ibmds",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushfreenas": [SchedOpts(
                    "freenas",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushxtremio": [SchedOpts(
                    "xtremio",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushpure": [SchedOpts(
                    "pure",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushgcedisks": [SchedOpts(
                    "gcedisks",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushhcs": [SchedOpts(
                    "hcs",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushhds": [SchedOpts(
                    "hds",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushnecism": [SchedOpts(
                    "necism",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pusheva": [SchedOpts(
                    "eva",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushibmsvc": [SchedOpts(
                    "ibmsvc",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushvioserver": [SchedOpts(
                    "vioserver",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushsym": [SchedOpts(
                    "sym",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushbrocade": [SchedOpts(
                    "brocade", schedule_option="no_schedule",
                    req_collector=True,
                )],
                "pushdisks": [SchedOpts(
                    "disks",
                    req_collector=True,
                )],
                "sysreport": [SchedOpts(
                    "sysreport",
                    req_collector=True,
                )],
                "compliance_auto": [SchedOpts(
                    "compliance",
                    fname="last_comp_check",
                    schedule_option="comp_schedule",
                    req_collector=True,
                )],
                "rotate_root_pw": [SchedOpts(
                    "rotate_root_pw",
                    fname="last_rotate_root_pw",
                    schedule_option="no_schedule",
                    req_collector=True,
                )],
                "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")
        from core.collector.rpc import CollectorRpc
        return CollectorRpc(node=self)

    @lazy
    def nodename(self):
        """
        Lazy initialization of the node name.
        """
        return Env.nodename

    @lazy
    def compliance(self):
        from core.compliance import Compliance
        comp = Compliance(self)
        return comp

    def check_privs(self, 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 quorum(self):
        try:
            return self.conf_get("cluster", "quorum")
        except ex.OptNotFound as exc:
            return exc.default

    @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):
        total = self.stats().get(metric)  # mb
        if total in (0, None):
            return 0
        try:
            val = self.conf_get("node", keyword)
        except ex.OptNotFound as exc:
            val = exc.default
        if str(val).endswith("%"):
            val = int(val.rstrip("%"))
        else:
            val = val // 1024 // 1024  # b > mb
            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):
        if not self.dns:
            return []
        from socket import gethostbyaddr
        nodes = []
        for ip in self.dns:
            try:
                data = gethostbyaddr(ip)
                names = [data[0]] + data[1]
            except Exception as exc:
                names = []
            for node in names:
                if node in self.cluster_nodes:
                    nodes.append(node)
                    break
        return nodes

    @lazy
    def arbitrators(self):
        arbitrators = []
        for section in self.conf_sections("arbitrator"):
            data = {
                "id": section,
            }
            try:
                data["name"] = self.conf_get(section, "name")
            except Exception:
                continue
            try:
                data["secret"] = self.conf_get(section, "secret")
            except Exception:
                continue
            try:
                data["timeout"] = self.conf_get(section, "timeout")
            except ex.OptNotFound as exc:
                data["timeout"] = exc.default
            arbitrators.append(data)
        return arbitrators

    @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.Error("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.Error("too many columns in %s" % ":".join(subelements))

        return transport, host, port, app

    @lazy
    def collector_env(self):
        """
        Return the collector connection elements parsed from the node config
        node.uuid, node.dbopensvc and node.dbcompliance as a Storage().
        """
        data = Storage()
        url = self.oget("node", "dbopensvc")
        if url:
            try:
                (
                    data.dbopensvc_transport,
                    data.dbopensvc_host,
                    data.dbopensvc_port,
                    data.dbopensvc_app
                ) = self.split_url(url, default_app="feed")
                data.dbopensvc = "%s://%s:%s/%s/default/call/xmlrpc" % (
                    data.dbopensvc_transport,
                    data.dbopensvc_host,
                    data.dbopensvc_port,
                    data.dbopensvc_app
                )
            except ex.Error as exc:
                self.log.error("malformed dbopensvc url: %s (%s)",
                               url, str(exc))
        else:
            data.dbopensvc_transport = None
            data.dbopensvc_host = None
            data.dbopensvc_port = None
            data.dbopensvc_app = None
            data.dbopensvc = None

        url = self.oget("node", "dbcompliance")
        if url:
            try:
                (
                    data.dbcompliance_transport,
                    data.dbcompliance_host,
                    data.dbcompliance_port,
                    data.dbcompliance_app
                ) = self.split_url(url, default_app="init")
                data.dbcompliance = "%s://%s:%s/%s/compliance/call/xmlrpc" % (
                    data.dbcompliance_transport,
                    data.dbcompliance_host,
                    data.dbcompliance_port,
                    data.dbcompliance_app
                )
            except ex.Error as exc:
                self.log.error("malformed dbcompliance url: %s (%s)",
                               url, str(exc))
        else:
            data.dbcompliance_transport = data.dbopensvc_transport
            data.dbcompliance_host = data.dbopensvc_host
            data.dbcompliance_port = data.dbopensvc_port
            data.dbcompliance_app = "init"
            data.dbcompliance = "%s://%s:%s/%s/compliance/call/xmlrpc" % (
                data.dbcompliance_transport,
                data.dbcompliance_host,
                data.dbcompliance_port,
                data.dbcompliance_app
            )

        node_uuid = self.oget("node", "uuid")
        if node_uuid:
            data.uuid = node_uuid
        else:
            data.uuid = ""

        collector = self.oget("node", "collector")
        if collector:
            data.collector = collector
        else:
            data.collector = ""

        collector_server = self.oget("node", "collector_server")
        if collector_server:
            data.server = collector_server
        elif collector:
            data.server = "%s/server" % collector
        else:
            data.server = ""

        collector_feeder = self.oget("node", "collector_feeder")
        if collector_feeder:
            data.feeder = collector_feeder
        elif collector:
            data.feeder = "%s/feeder" % collector
        else:
            data.feeder = ""

        collector_timeout = self.oget("node", "collector_timeout")
        if collector_timeout > 20:
            collector_timeout = 20
        data.timeout = collector_timeout

        return data

    def call(self, *args, **kwargs):
        """
        Wrap utilities call function, setting the node logger.
        """
        kwargs["log"] = self.log
        return call(*args, **kwargs)

    def vcall(self, *args, **kwargs):
        """
        Wrap utilities vcall function, setting the node logger.
        """
        kwargs["log"] = self.log
        return vcall(*args, **kwargs)

    @staticmethod
    def filter_ns(paths, namespace):
        if not namespace:
            return paths
        if namespace == "root":
            return [path for path in paths if split_path(path)[1] is None]
        return [path for path in paths if split_path(path)[1] == namespace]

    def svcs_selector(self, selector, namespace=None, local=False):
        """
        Given a selector string, return a list of service names.
        This exposed method only aggregates ORed elements.
        """
        # fully qualified name
        path = is_service(selector, namespace, local=local)
        if path:
            self.options.single_service = True
            paths = self.filter_ns([path], namespace)
            return paths

        if not local and os.environ.get("OSVC_ACTION_ORIGIN") != "daemon":
            # the daemon always submits actions with simple, local selector.
            # avoid round trips.
            try:
                data = self._daemon_object_selector(selector, namespace, kind=os.environ.get("OSVC_KIND"))
                if isinstance(data, list):
                    return data
            except Exception as exc:
                print(exc, file=sys.stderr)
                # fallback to local lookup
                pass

        # full listing and namespace full listing
        if selector is None:
            # only svc kind by default
            selector = "*"

        # fnmatch on names and service config/status filtering
        try:
            paths = self._svcs_selector(selector, namespace)
        finally:
            del self.services
            self.services = None
        paths = self.filter_ns(paths, namespace)
        return paths

    def _svcs_selector(self, selector, namespace=None):
        if want_context():
            raise ex.Error("daemon is unreachable")
        self.build_services()
        paths = [svc.path for svc in self.svcs]
        paths = self.filter_ns(paths, namespace)
        if "," in selector:
            ored_selectors = selector.split(",")
        else:
            ored_selectors = [selector]
        result = []
        for _selector in ored_selectors:
            for path in self.__svcs_selector(_selector, paths, namespace=namespace):
                if path not in result:
                    result.append(path)
        if len(result) == 0 and not re.findall(r"[,+*=^:~><]", selector):
            raise ex.Error("object not found")
        return result

    def __svcs_selector(self, selector, paths, namespace=None):
        """
        Given a selector string, return a list of service names.
        This method only intersect the ANDed elements.
        """
        if selector in (None, ""):
            return []
        path = is_service(selector, namespace, local=True)
        if path:
            return [path]
        if "+" in selector:
            anded_selectors = selector.split("+")
        else:
            anded_selectors = [selector]
        if selector in (None, ""):
            result = paths
        else:
            result = None
            for _selector in anded_selectors:
                _paths = self.___svcs_selector(_selector, paths, namespace)
                if result is None:
                    result = _paths
                else:
                    common = set(result) & set(_paths)
                    result = [name for name in result if name in common]
        return result

    def ___svcs_selector(self, selector, paths, namespace):
        """
        Given a basic selector string (no AND nor OR), return a list of service
        names.
        """
        pds = paths_data(paths)
        kind = os.environ.get("OSVC_KIND")
        if kind:
            pds = [pd for pd in pds if pd["kind"] == kind]
            paths = [pd["display"] for pd in pds]

        negate, selector, elts = selector_parse_fragment(selector)

        if len(elts) == 1:
            return object_path_glob(selector, pds=pds, namespace=namespace, kind=kind, negate=negate)

        try:
            param, op, value = selector_parse_op_fragment(elts)
        except ValueError:
            return []

        # config keyword match
        result = []
        for svc in self.svcs:
            ret = selector_config_match(svc, param, op, value)
            if ret ^ negate:
                result.append(svc.path)

        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 \
                ('paths' not in kwargs or
                 (isinstance(kwargs['paths'], list) and len(kwargs['paths']) == 0)):
            return

        if 'paths' in kwargs and \
           isinstance(kwargs['paths'], list) and \
           len(kwargs['paths']) > 0 and \
           self.svcs is not None:
            paths_request = set(kwargs['paths'])
            paths_actual = set([s.path for s in self.svcs])
            if len(paths_request-paths_actual) == 0:
                return

        self.services = {}

        if want_context():
            # build volatile objects
            for path in kwargs['paths']:
                name, namespace, kind = split_path(path)
                self += factory(kind)(name, namespace=namespace, volatile=True,
                                      cf=os.devnull, node=self)
            return

        kwargs["node"] = self
        svcs, errors = core.objects.builder.build_services(*args, **kwargs)
        if 'paths' in kwargs:
            self.check_build_errors(kwargs['paths'], 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

    @staticmethod
    def check_build_errors(paths, svcs, errors):
        """
        Raise error if the service builder did not return a Svc object for
        each service we requested.
        """
        if isinstance(paths, list):
            n_args = len(paths)
        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])
        if n_args == 0 and not msg:
            return 0
        raise ex.Error(msg)

    def rebuild_services(self, paths):
        """
        Delete the list of Svc objects in the Node object and create a new one.

        Args:
          paths: add only Svc objects for services specified
        """
        del self.services
        self.services = None
        self.build_services(paths=paths, 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(Env.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.Error
        else:
            shutil.copy(Env.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 + [Env.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", Env.paths.nodeconf, self.paths.tmp_cf]
        elif diff_capable(["-u"]):
            cmd = ["diff", "-u", Env.paths.nodeconf, self.paths.tmp_cf]
        else:
            cmd = ["diff", Env.paths.nodeconf, self.paths.tmp_cf]
        call(cmd)

    def nodeconf_csum(self):
        from utilities.files import fsum
        return fsum(Env.paths.nodeconf)

    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.
        """
        try:
            editor = find_editor()
        except ex.Error as err:
            print(err, file=sys.stderr)
            return 1
        from utilities.files import fsum
        path = self.make_temp_config()
        os.system(' '.join((editor, path)))
        if fsum(path) == self.nodeconf_csum():
            os.unlink(path)
            return 0
        results = self._validate_config(path=path)
        if results["errors"] == 0:
            try:
                move_config_file(path, Env.paths.nodeconf)
            finally:
                try:
                    os.unlink(path)
                except Exception:
                    pass
        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 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 __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, "path"):
            return self
        if self.services is None:
            self.services = {}
        self.services[svc.path] = 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.AbortAction:
            return 0
        try:
            return self._action(action, options)
        except LOCK_EXCEPTIONS as exc:
            self.log.warning(exc)
            return 1

    @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.oget('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 core.collector.actions import CollectorActions
            coll = CollectorActions(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 ACTION_ASYNC 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, default_fmt=None):
        """
        A dummy method decorated by the formatter function.
        The formatter needs self to access the formatting options, so this
        can't be a staticmethod.
        """
        return data

    def print_schedule(self):
        """
        The 'print schedule' node and service action entrypoint.
        """
        return self.sched.print_schedule()

    def collect_stats(self):
        """
        Choose the os specific stats collection module and call its collect
        method.
        """
        try:
            import utilities.stats.collector
        except ImportError:
            return
        utilities.stats.collector.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.
        """
        try:
            start = self.sched.get_last(self.sched.actions["pushstats"][0].fname)
            now = time.time()
            delta = now - start
            interval = delta // 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.
            """
            disable = self.oget("stats", "disable")
            if not disable:
                return []
            return disable

        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):
        from utilities.asset import Asset
        return Asset(self)

    def oc3_version(self):
        "return cached oc3 version (value is maintained by collector thread)"
        try:
            with open(Env.paths.oc3_version, 'r') as f:
                return Semver.parse(json.load(f).get("version", ""))
        except:
            return Semver()

    def pushpkg(self):
        """
        The pushpkg action entrypoint.
        Inventories the installed packages.
        """
        import utilities.packages.list as p
        pkgs = p.listpkg()
        n = len(pkgs)
        if n == 0:
            print("No package found. Skip push.")
            return
        else:
            print("Pushing %d packages information." % n)

        if self.oc3_version() >= Semver(1, 0, 8):
            from utilities.rfc3339 import RFC3339

            rfc3339 = RFC3339()

            api_verb = "POST"
            api_path = oc3path.FEED_NODE_SYSTEM

            def to_pkg_dict(l):
                # l = (nodename, pkgname, version, arch, [type, [installed_at, [sig]]])
                #
                pkg = {"name": l[1], "version": l[2], "arch": l[3],
                       "type": "", "sig": "", "installed_at": None}
                n_fields = len(l)
                if n_fields >= 5:
                    pkg["type"] = l[4]
                if n_fields >= 6:
                    pkg["installed_at"] = rfc3339.from_epoch(l[5]) if l[5] is not None else None
                if n_fields >= 7:
                    pkg["sig"] = l[6]
                return pkg
            body = {"package": [to_pkg_dict(l) for l in pkgs]}
            status_code, resp = self.oc3_request_feed(api_verb, api_path, data=body)
            self.oc3_assert_status_code(api_verb, api_path, status_code, resp, expected=[202])
        else:
            self.collector.call('push_pkg', pkgs)

    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.
        """
        system_dict = self.asset.get_system_dict()
        try:
            if self.options.format is None:
                self.print_asset(system_dict)
                return
            self.print_data(system_dict)
        finally:
            try:
                if self.oc3_version() >= Semver(1, 0, 8):
                    from utilities.rfc3339 import RFC3339
                    api_verb = "POST"
                    api_path = oc3path.FEED_NODE_SYSTEM

                    if "last_boot" in system_dict.get("properties", {}):
                        last_boot = system_dict["properties"]["last_boot"]["value"]
                        system_dict["properties"]["last_boot"]["value"] = RFC3339().from_epoch(last_boot)
                    status_code, resp = self.oc3_request_feed(api_verb, api_path, data=system_dict)
                    self.oc3_assert_status_code(api_verb, api_path, status_code, resp, expected=[202])
                else:
                    asset_dict = self.asset.system_dict_to_asset_dict(system_dict)
                    self.collector.call('push_asset', self, asset_dict)
            except Exception as exc:
                raise ex.Error(str(exc))

    def print_asset(self, data):
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(Env.nodename, color.BOLD)
        head_node.add_column("Value", color.BOLD)
        head_node.add_column("Source", color.BOLD)

        if 'properties' in data:
            for key in sorted(data["properties"]):
                _data = data["properties"][key]
                node = head_node.add_node()
                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.out()

    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 pushpure(self):
        """
        The pushpure action entrypoint.
        Inventories PureStorage storage arrays.
        """
        self.collector.call('push_pure', self.options.objects)

    def pushxtremio(self):
        """
        The pushxtremio action entrypoint.
        Inventories XtremIO storage arrays.
        """
        self.collector.call('push_xtremio', 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 pushdorado(self):
        """
        The pushdorado action entrypoint.
        Inventories Huawei Dorado storage arrays.
        """
        self.collector.call('push_dorado', self.options.objects)

    def pushhcs(self):
        """
        The pushhcs action entrypoint.
        Inventories Hitachi Command Suite storage arrays.
        """
        self.collector.call('push_hcs', self.options.objects)

    def pushsym(self):
        """
        The pushsym action entrypoint.
        Inventories EMC Symmetrix server storage arrays.
        """
        self.collector.call('push_sym', self.options.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.actions["auto_reboot"][0]
        try:
            schedule = self.sched.get_schedule_raw(sch.section, sch.schedule_option)
        except Exception:
            schedule = ""

        print("reboot is scheduled")
        print("reboot schedule: %s" % schedule)

        if not schedule:
            return

        result, _ = self.sched.get_schedule("reboot", "schedule").get_next()
        if result:
            print("next reboot slot:", result.strftime("%a %Y-%m-%d %H:%M"))
        else:
            print("next reboot slot: none")

    def auto_reboot(self):
        """
        The scheduler task executing the node reboot if the scheduler
        constraints are satisfied and the reboot flag is set.
        """
        try:
            assert_file_exists(self.paths.reboot_flag)
            assert_file_is_root_only_writeable(self.paths.reboot_flag)
        except Exception as error:
            print('%s. abort scheduled reboot' % error)
            return
        once = self.oget("reboot", "once")
        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)

        try:
            if self.oc3_version() >= Semver(1, 0, 5):
                api_verb = "POST"
                api_path = oc3path.FEED_NODE_DISK
                l = []
                for disk_id, disk in data["disks"].items():
                    for svcname, service in disk["services"].items():
                        l.append({
                            "id": disk_id,
                            "object_path": svcname,
                            "size": disk["size"],
                            "used": service["used"],
                            "vendor": disk["vendor"],
                            "model": disk["model"],
                            "dg": disk["dg"],
                            "region": str(service["region"])
                        })
                    if disk["used"] < disk["size"]:
                        l.append({
                            "id": disk_id,
                            "object_path": "",
                            "size": disk["size"],
                            "used": disk["size"] - disk["used"],
                            "vendor": disk["vendor"],
                            "model": disk["model"],
                            "dg": disk["dg"],
                            "region": "0"
                        })
                status_code, resp = self.oc3_request_feed(api_verb, api_path, data={"data": l})
                self.oc3_assert_status_code(api_verb, api_path, status_code, resp, expected=[202])
                # TODO: ensure "served_disks" from data is not anymore used
            else:
                self.collector.call('push_disks', data)
        except Exception as exc:
            raise ex.Error(str(exc))


    def print_push_disks(self, data):
        from utilities.render.forest import Forest
        from utilities.render.color import color

        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(Env.nodename, color.BOLD)
        head_node.add_column("DiskGroup", 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(disk["dg"])
            disk_node.add_column(print_size(disk["size"]))
            disk_node.add_column(disk["vendor"])
            disk_node.add_column(disk["model"])

            for path, service in disk["services"].items():
                svc_node = disk_node.add_node()
                svc_node.add_column(path, color.LIGHTBLUE)
                svc_node.add_column(disk["dg"])
                svc_node.add_column(print_size(service["used"]))

            if disk["used"] < disk["size"]:
                svc_node = disk_node.add_node()
                svc_node.add_column(Env.nodename, color.LIGHTBLUE)
                svc_node.add_column(disk["dg"])
                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(disk["dg"])
            disk_node.add_column(print_size(disk["size"]))
            disk_node.add_column(disk["vdisk_id"])

        tree.out()

    def push_disks_data(self):
        if self.svcs is None:
            self.build_services()

        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, '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(Env.nodename+".loop"):
                            continue
                        dev = self.devtree.get_dev_by_devpath(d)
                        disk_size = dev.size

                        if disk_id not in data["disks"]:
                            data["disks"][disk_id] = {
                                "size": disk_size,
                                "dg": dev.dg,
                                "vendor": self.diskinfo.disk_vendor(d),
                                "model": self.diskinfo.disk_model(d),
                                "used": 0,
                                "services": {},
                            }

                        if svc.path not in data["disks"][disk_id]["services"]:
                            data["disks"][disk_id]["services"][svc.path] = {
                                "svcname": svc.path,
                                "used": used,
                                "region": region
                            }
                        else:
                            # consume space at service level
                            data["disks"][disk_id]["services"][svc.path]["used"] += used
                            if data["disks"][disk_id]["services"][svc.path]["used"] > disk_size:
                                data["disks"][disk_id]["services"][svc.path]["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(Env.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

            dev = self.devtree.get_dev_by_devpath(devpath)
            disk_size = dev.size

            data["disks"][disk_id] = {
                "size": disk_size,
                "dg": dev.dg,
                "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 = self.oget(action, when)
        blocking_trigger = self.oget(action, "blocking_"+when)
        if trigger:
            self.log.info("execute trigger %s", trigger)
            try:
                self.do_trigger(trigger)
            except ex.Error:
                pass
        if blocking_trigger:
            self.log.info("execute blocking trigger %s", blocking_trigger)
            try:
                self.do_trigger(blocking_trigger)
            except ex.Error:
                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.
        """
        shell = False
        if does_call_cmd_need_shell(cmd):
            shell = True
        cmd = get_call_cmd_from_str(cmd, shell=shell)
        ret, out, err = self.vcall(cmd, err_to_warn=err_to_warn, shell=shell)
        if ret != 0:
            raise ex.Error((ret, out, err))

    def suicide(self, method, delay=0):
        self.log.info('node commit suicide in %s seconds using method %s', delay, method)
        _suicide = {
            "crash": self.sys_crash,
            "reboot": self.sys_reboot,
        }.get(method)
        if _suicide:
            try:
                # Do our best to have most recent log sync on file system, node is going to crash of fast reboot
                if hasattr(os, "fsync"):
                    with open(os.path.join(Env.paths.pathlog, "node.log"), "a") as f:
                        f.flush()
                        os.fsync(f.fileno())
            except:
                pass
            _suicide(delay)
        else:
            self.log.warning("invalid commit suicide method %s", method)

    def sys_reboot(self, delay=0):
        pass

    def sys_crash(self, delay=0):
        pass

    def _reboot(self):
        """
        A system reboot method to be implemented by child classes.
        """
        self.log.warning("to be implemented")

    @lazy
    def sysreport_mod(self):
        try:
            import core.sysreport
            return core.sysreport
        except ImportError:
            print("sysreport is not supported on this os")
            return

    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.
        """
        if self.sysreport_mod is None:
            return
        self.sysreport_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.
        """
        hostid = self.oget("node", "prkey")
        if hostid:
            if len(hostid) > 18 or not hostid.startswith("0x") or \
               len(set(hostid[2:]) - set("0123456789abcdefABCDEF")) > 0:
                raise ex.Error("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 = self.new_prkey()
        self.set_multi(["node.prkey="+hostid])
        return hostid

    def prkey(self):
        """
        Print the persistent reservation key.
        """
        print(self.get_prkey())

    @staticmethod
    def new_prkey():
        """
        Create new pr key with a non zero heading hex to avoid comparaison
        errors caused by padding stripping.
        """
        from uuid import uuid4
        while True:
            v = uuid4().bytes[:8].hex()
            if not v.startswith("0"):
                return "0x"+v


    def checks(self):
        """
        The checks node action entrypoint.
        Runs health checks.
        """
        drvs = self.checks_drivers()
        data = drvs.do_checks()
        if self.options.format is None:
            drvs.print_checks(data)
        else:
            self.print_data(data)
        self.collector.call('push_checks', data)

    def checks_drivers(self, checkers=None):
        import drivers.check
        if self.svcs is None:
            self.build_services()
        objs = [svc for svc in self.svcs if svc.kind in ["vol", "svc"]]
        return drivers.check.Checks(objs, node=self, checkers=checkers)

    def wol(self):
        """
        Send a Wake-On-LAN packet to a mac address on the broadcast address.
        """
        import utilities.wakeonlan
        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 = utilities.wakeonlan.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 _service_action_worker(self, svc, action, options):
        """
        The method the per-service subprocesses execute
        """
        from utilities.process_title import set_process_title
        name, namespace, kind = split_path(svc.path)
        if options.parm_svcs:
            del options.parm_svcs
        for attr in ["svcs", "parallel", "namespace"]:
            if hasattr(options, attr):
                delattr(options, attr)
        cmd_log  = format_command(kind, action, options)
        set_process_title("om %s %s" % (svc.path, " ".join(cmd_log)))
        try:
            ret = svc.action(action, options)
        except Exception:
            self.close()
            sys.exit(1)
        finally:
            self.close()
        sys.exit(ret)

    def register_as_node(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.Error("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.Error(msg)
        elif isinstance(data, list):
            raise ex.Error(data[0])
        return data

    def snooze(self):
        """
        Snooze notifications on the node.
        """
        if self.options.duration is None:
            print("set --duration", file=sys.stderr)
            raise ex.Error
        try:
            data = self.collector_rest_post("/nodes/self/snooze", {
                "duration": self.options.duration,
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])
        print(data.get("info", ""))

    def unsnooze(self):
        """
        Unsnooze notifications on the node.
        """
        try:
            data = self.collector_rest_post("/nodes/self/snooze")
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])
        print(data.get("info", ""))

    def register_as_user(self):
        """
        Returns a node registration unique id, authenticating to the
        collector as a node.
        """
        try:
            data = self.collector_rest_post("/register", {
                "nodename": Env.nodename,
                "app": self.options.app
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])
        return data["data"]["uuid"]

    def register_node_oc3(self):
        api_verb = "POST"
        api_path = oc3path.SERVER_NODE_REGISTER

        headers = {"Accept": "application/json", "Content-Type": "application/json"}
        if self.options.user:
            username, password = self.collector_auth_user()
            basic_value = base64encode('%s:%s' % (username, password)).replace("\n", "")
            if basic_value:
                headers["Authorization"] = "Basic %s" % basic_value

        data = {"nodename": Env.nodename}
        if self.options.app:
            data["app"] = self.options.app

        status_code, resp = self.oc3_request_server(api_verb, api_path, collector_basic_node=False, headers=headers, data=data)
        if status_code != 200:
            raise ex.Error("%s %s unexpected status code: %d: %s" % (api_verb, api_path, status_code, resp))
        uuid = resp.get("uuid", "")
        info = resp.get("info")
        if info is None:
            print("node is registered")
        else:
            print(info)
        return uuid

    def register_node_oc2(self):
        if self.options.user is not None:
            return self.register_as_user()
        else:
            return self.register_as_node()

    def register(self):
        """
        Do anonymous or authenticated 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.
        """
        try:
            if self.collector_env.server:
                uuid = self.register_node_oc3()
            else:
                uuid = self.register_node_oc2()
                print("registered")
        except ex.Error as exc:
            print(exc, file=sys.stderr)
            return 1

        try:
            self.set_multi(["node.uuid="+uuid])
            self.unset_lazy("private_cd")
            self.unset_lazy("cd")
            self.unset_lazy("collector_env")
        except ex.Error:
            print("failed to write registration number: %s" % uuid,
                  file=sys.stderr)
            return 1

        self.options.syncrpc = True
        self.pushasset()
        self.pushdisks()
        self.pushpkg()
        self.pushpatch()
        self.sysreport()
        self.checks()
        return 0

    @lazy
    def diskinfo(self):
        from utilities.diskinfo import DiskInfo
        return DiskInfo()

    @lazy
    def devtree(self):
        from utilities.devtree import DevTree
        tree = 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.
        """

        def do(fpath):
            """
            Installs the compliance module archive from the downloaded archive.
            """
            tmpp = os.path.join(Env.paths.pathtmp, 'compliance')
            backp = os.path.join(Env.paths.pathtmp, 'compliance.bck')
            compp = os.path.join(Env.paths.pathvar, 'compliance')
            makedirs(compp)
            import shutil
            try:
                shutil.rmtree(backp)
            except (OSError, IOError):
                pass
            print("extract compliance in", Env.paths.pathtmp)
            import tarfile
            tar = tarfile.open(fpath)
            os.chdir(Env.paths.pathtmp)
            try:
                tar.extractall()
                tar.close()
            except (OSError, IOError):
                print("failed to unpack", file=sys.stderr)
                return 1
            upstream_mods_d = os.path.join(tmpp, "com.opensvc")
            prev_upstream_mods_d = os.path.join(compp, "com.opensvc")
            if not os.path.exists(upstream_mods_d) and \
               os.path.exists(prev_upstream_mods_d):
                print("merge upstream com.opensvc compliance objects")
                shutil.copytree(prev_upstream_mods_d, upstream_mods_d,
                                symlinks=True)
            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)

        repocomp = self.oget("node", "repocomp")
        repo = self.oget("node", "repo")
        if repocomp:
            pkg_name = repocomp.strip('/') + "/current"
        elif repo:
            pkg_name = 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

        from utilities.uri import Uri
        print("get %s" % pkg_name)
        secure = self.oget("node", "secure_fetch")
        try:
            with Uri(pkg_name, secure=secure).fetch() as fpath:
                do(fpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            if self.options.cron:
                return 0
            return 1
        return 0

    def updatepkg(self):
        """
        Downloads and upgrades the OpenSVC agent, using the system-specific
        packaging tools.
        """
        branch = self.oget("node", "branch")
        if branch:
            branch = "/" + branch
        else:
            branch = ""
        try:
            pkg_format = self.conf_get("node", "pkg_format")
        except ex.OptNotFound as exc:
            pkg_format = exc.default

        if pkg_format == "tar":
            modname = 'utilities.packages.update.osf1'
        else:
            modname = 'utilities.packages.update.'+Env.module_sysname

        import importlib
        try:
            mod = importlib.import_module(modname)
        except ImportError:
            print("updatepkg not implemented on", Env.sysname, file=sys.stderr)
            return 1
        repopkg = self.oget("node", "repopkg")
        repo = self.oget("node", "repo")
        if repopkg:
            pkg_name = repopkg.strip('/') + "/" + mod.repo_subdir + branch + '/current'
        elif repo:
            pkg_name = 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

        from utilities.uri import Uri
        print("get %s" % pkg_name)
        secure = self.oget("node", "secure_fetch")
        try:
            with Uri(pkg_name, secure=secure).fetch() as fpath:
                print("updating opensvc")
                mod.update(fpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            return 1
        os.system("%s node pushasset" % Env.paths.om)
        return 0

    def updateclumgr(self):
        """
        Downloads and installs the cluster manager bundle archive from the url
        specified as node.repopkg or node.repo in node.conf.
        """
        import daemon.shared as shared
        api_version = str(shared.API_VERSION)
        repopkg = self.oget("node", "repopkg")
        repo = self.oget("node", "repo")
        if repopkg:
            bundle_basename = repopkg.strip('/')
        elif repo:
            bundle_basename = repo.strip('/')
        else:
            if self.options.cron:
                return 0
            print("node.repo or node.repopkg must be set in node.conf",
                  file=sys.stderr)
            return 1
        bundle_name = bundle_basename + "/cluster-manager/" + api_version + '/current'
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        fpath = tmpf.name
        tmpf.close()
        try:
            ret = self._updateclumgr(bundle_name, fpath)
        finally:
            if os.path.exists(fpath):
                os.unlink(fpath)
        return ret

    def _updateclumgr(self, bundle_name, fpath):
        """
        Downloads and installs the cluster manager bundle archive from the url
        specified by the bundle_name argument. The download destination file
        is specified by fpath. The caller is responsible for its deletion.
        """
        def do(fpath):
            tmpp = os.path.join(Env.paths.pathtmp, 'html')
            backp = Env.paths.pathhtml + '.bck'
            htmlp = Env.paths.pathhtml
            makedirs(htmlp)
            makedirs(tmpp)

            print("extract cluster manager in", tmpp)
            import tarfile
            tar = tarfile.open(fpath)
            os.chdir(tmpp)
            try:
                tar.extractall()
                tar.close()
            except (OSError, IOError):
                print("failed to unpack", file=sys.stderr)
                return 1
            os.chdir("/")

            print("install new cluster manager in %s" % htmlp)
            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)

            import shutil
            try:
                shutil.rmtree(backp)
            except (OSError, IOError):
                pass

            shutil.move(htmlp, backp)
            shutil.move(tmpp, htmlp)

        from utilities.uri import Uri
        print("get %s" % bundle_name)
        secure = self.oget("node", "secure_fetch")
        try:
            with Uri(bundle_name, secure=secure).fetch() as fpath:
                do(fpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)
            if self.options.cron:
                return 0
            return 1
        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.Error("can not determine array driver (no --array)")

        ref_section = "array#" + array_name
        section = None
        for s in self.conf_sections(cat="array"):
            if s == ref_section:
                section = s
                break
            try:
                name = self.oget(s, "name")
            except Exception as exc:
                continue
            if name == array_name:
                section = s
                break
        if section is None:
            raise ex.Error("array '%s' not found in configuration" % array_name)

        try:
            driver = self.oget(section, "type")
        except Exception:
            raise ex.Error("'%s.type' keyword must be set" % section)

        driver = driver.lower()
        try:
            mod = driver_import("array", driver)
        except ImportError as exc:
            raise ex.Error("array driver %s load error: %s" % (driver, 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"
        ruser = self.oget("node", "ruser")
        if ruser == default:
            return ruser
        node_ruser = {}
        elements = 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.
        """
        method = "collector_get_action_queue_v2"
        ack_method = "collector_update_action_queue_received"
        use_retryable_version = True
        max_retry = 3
        retry = 0
        retry_delay = 30
        try:
            methods = self.collector.load_list_methods("proxy")
        except Exception:
            methods = []
        if method not in methods:
            method = "collector_get_action_queue"
            use_retryable_version = False
            max_retry = 0

        self.log.info("dequeue actions")
        while True:
            actions = self.collector.call(method)
            if actions is None:
                retry = retry + 1
                if retry > max_retry:
                    msg = "unable to fetch actions scheduled by the collector"
                    self.log.warning(msg)
                    raise ex.Error(msg)
                else:
                    msg = ("unable to fetch actions scheduled by the collector"
                           ", retry %s/%s in %ss" % (retry, max_retry, retry_delay))
                    self.log.info(msg)
                    time.sleep(retry_delay)
                    continue

            if not isinstance(actions, list):
                msg = "dequeue action failed %s" % actions
                self.log.warning(msg)
                raise ex.Error(msg)


            n_actions = len(actions)
            if n_actions == 0:
                break

            if use_retryable_version:
                ack_ids = [(action.get("id")) for action in actions]
                self.collector.call(ack_method, ack_ids)

            data = []
            reftime = time.time()
            for action in actions:
                ret, out, err = self.dequeue_action(action)
                out = ANSI_ESCAPE.sub('', out)
                err = ANSI_ESCAPE.sub('', err)
                data.append((action.get('id'), ret, out, err))
                now = time.time()
                if now > reftime + 2:
                    # this action was long, update the collector now
                    self.collector.call('collector_update_action_queue', data)
                    reftime = time.time()
                    data = []
            if len(data) > 0:
                self.collector.call('collector_update_action_queue', data)

    def dequeue_action(self, action):
        """
        Execute the node or object action described in payload element
        received from the collector's action queue.
        """
        if action.get("svc_id") in (None, "") or action.get("svcname") in (None, ""):
            cmd = [Env.paths.om, "node"]
        else:
            cmd = [Env.paths.om, "svc", "-s", action.get("svcname")]
        cmd += shlex.split(action.get("command", ""))
        self.log.info("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 core.collector.actions import CollectorActions
        coll = CollectorActions(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:
            from utilities.password import change_root_pw
        except ImportError:
            print("not implemented")
            return 1
        ret = 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.ascii_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.
        """
        self._scanscsi(self.options.hba, self.options.target, self.options.lun)

    def _scanscsi(self, hba=None, target=None, lun=None, log=None):
        log = log if log else self.log
        if not hasattr(self.diskinfo, 'scanscsi'):
            raise ex.Error("scanscsi is not implemented on %s" % Env.sysname)
        return self.diskinfo.scanscsi(
            hba=hba,
            target=target,
            lun=lun,
            log=log,
        )

    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.InitError("cloud sections must have a unique name in "
                               "the form '[cloud#n] in %s" % Env.paths.nodeconf)

        if self.clouds and section in self.clouds:
            return self.clouds[section]

        try:
            cloud_type = self.oget(section, 'type')
        except Exception:
            raise ex.InitError("type option is mandatory in cloud section "
                               "in %s" % Env.paths.nodeconf)

        auth_dict = self.section_kwargs(section, cloud_type)

        try:
            mod = driver_import("cloud", cloud_type.lower())
        except ImportError:
            raise ex.InitError("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

    @staticmethod
    def can_parallel_action(action, svcs, options):
        """
        Returns True if the action can be run in a subprocess per service
        """
        if Env.sysname == "Windows":
            return False
        if len(svcs) < 2:
            return False
        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 in ("pg_pids"):
            return True
        if action.startswith("print_") and options.format in ("json", "flat_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
        """
        svcs = [] + self.svcs
        if action == "monitor":
            from commands.svcmon import svcmon
            if not options.sections:
                options.sections = "services"
            elif "services" not in options.sections:
                options.sections += ",services"
            options.parm_svcs = ",".join([o.path for o in self.svcs])
            svcmon(self, options)
            return
        if action == "ls":
            data = strip_path(sorted([svc.path for svc in svcs if svc.path in options.svcs]), options.namespace)
            if options.format == "json":
                print(json.dumps(data, indent=4, sort_keys=True))
            elif data:
                print("\n".join(data))
            return

        begin = time.time()

        if not options.cron:
            # File cache janitoring.
            # Skip for tasks: the scheduler will purge the session cache itself, without dirlisting.
            purge_cache_expired()
        self.log.debug("session uuid: %s", Env.session_uuid)

        if action in ACTIONS_NO_MULTIPLE_SERVICES and len(svcs) > 1:
            print("action '%s' is not allowed on multiple services" % action, file=sys.stderr)
            return 1

        timeout = 0
        if not options.local and options.wait:
            # submit all async actions and wait only after to avoid
            # max_parallel breaking inter service dependencies
            timeout = convert_duration(options.time)
            options.wait = False

        need_aggregate = self.action_need_aggregate(action, options)
        parallel = self.can_parallel_action(action, svcs, options)
        if parallel:
            data, err, errs = self.do_svcs_action_parallel(action, options, svcs)
        else:
            data, err, errs = self.do_svcs_action_sequential(action, options, svcs, need_aggregate)

        if timeout:
            # async actions wait
            for svc in svcs:
                if errs.get(svc.path, -1) != 0:
                    continue
                try:
                    global_expect = svc.last_global_expect
                except AttributeError:
                    continue
                try:
                    _timeout = timeout - (time.time() - begin)
                    svc.wait_daemon_mon_action(global_expect, wait=True, timeout=_timeout, begin=begin)
                except Exception as exc:
                    err += 1

        if need_aggregate:
            if self.options.single_service:
                path = svcs[0].path
                if path not in data.outs:
                    return 1
                self.print_data(data.outs[path])
            else:
                self.print_data(data.outs)

        if options.watch or options.stats:
            from commands.svcmon import svcmon
            options.sections = ["services"]
            svcmon(self, options)
        return err

    def do_svcs_action_sequential(self, action, options, svcs, need_aggregate):
        err = 0
        errs = {}
        data = Storage()
        data.outs = {}
        for svc in svcs:
            try:
                ret = svc.action(action, options)
                if need_aggregate:
                    if ret is not None:
                        data.outs[svc.path] = ret
                elif action.startswith("print_") or action == "pg_stats":
                    self.print_data(ret)
                    if isinstance(ret, dict):
                        if "error" in ret:
                            err += 1
                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
                    if ret > 0:
                        err += ret
                    errs[svc.path] = ret
            except ex.Error as exc:
                ret = 1
                err += ret
                if not need_aggregate:
                    print("%s: %s" % (svc.path, exc), file=sys.stderr)
                continue
            except ex.Signal:
                break
        return data, err, errs

    def do_svcs_action_parallel(self, action, options, svcs):
        err = 0
        errs = {}
        data = Storage()
        data.outs = {}
        max_parallel = self.max_parallel
        data.procs = {}
        data.svcs = {}
        # noinspection PyUnresolvedReferences
        from utilities.process_title import set_process_title  # warm up for side effect

        def can_run_new_proc():
            count = 0
            for proc in data.procs.values():
                if proc.is_alive():
                    count += 1
            return count <= max_parallel

        for svc in svcs:
            while not can_run_new_proc():
                time.sleep(1)
            data.svcs[svc.path] = svc
            data.procs[svc.path] = Process(
                target=self._service_action_worker,
                name="worker_" + svc.path,
                args=(svc, action, options),
            )
            data.procs[svc.path].start()
        for path in data.procs:
            data.procs[path].join()
            ret = data.procs[path].exitcode
            errs[path] = ret
            if ret > 0:
                # r is negative when data.procs[path] is killed by signal.
                # in this case, we don't want to decrement the err counter.
                err += ret
            elif ret < 0:
                err += 1
        return data, err, errs

    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 self.collector_env.dbopensvc is None:
                    raise ex.Error("node.dbopensvc is not set in node.conf")
                data["api"] = self.collector_env.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 core.collector.cli import Cli
        cli = Cli(**data)
        return cli.run(argv=argv)

    def download_from_safe(self, safe_id, path=None):
        import base64
        if safe_id.startswith("safe://"):
            safe_id = safe_id[7:].lstrip("/")
        fpath = os.path.join(Env.paths.safe, safe_id)
        if os.path.exists(fpath):
            with open(fpath, "r") as ofile:
                buff = ofile.read()
            try:
                clustername, nodename, data = self.decrypt(buff)
                data = data["data"]
                try:
                    return base64.urlsafe_b64decode(data)
                except TypeError:
                    return base64.urlsafe_b64decode(data.encode())
            except Exception:
                pass
        rpath = "/safe/%s/download" % safe_id
        api = self.collector_api(path=path)
        request = self.collector_request(rpath)
        if not api["url"].startswith("https"):
            raise ex.Error("refuse to submit auth tokens through a non-encrypted transport")

        kwargs = {}
        kwargs = self.set_ssl_context(kwargs)
        try:
            f = urlopen(request, **kwargs)
        except HTTPError as e:
            try:
                err = json.loads(e.read())["error"]
                e = ex.Error(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))}
        makedirs(Env.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, path=None):
        """
        Prepare the authentication info, either as node or as user.
        Fetch and cache the collector's exposed rest api metadata.
        """
        if self.collector_env.dbopensvc is None:
            raise ex.Error("node.dbopensvc is not set in node.conf")
        elif self.collector_env.dbopensvc_host == "none":
            raise ex.Error("node.dbopensvc is set to 'none' in node.conf")
        data = {}
        if self.options.user is None:
            username, password = self.collector_auth_node()
            if path:
                username = path+"@"+username
        else:
            username, password = self.collector_auth_user()
        data["username"] = username
        data["password"] = password
        data["url"] = self.collector_env.dbopensvc.replace("/feed/default/call/xmlrpc", "/init/rest/api")
        if not data["url"].startswith("http"):
            data["url"] = "https://%s" % data["url"]
        from utilities.uri import Uri
        data["hosthdr"] = Uri(data["url"]).host_header()
        return data

    def collector_auth_node(self):
        """
        Returns the authentcation info for login as node
        """
        username = Env.nodename
        node_uuid = self.oget("node", "uuid")
        if not node_uuid:
            raise ex.Error("the node is not registered yet. use 'om node register [--user <user>]'")
        return username, node_uuid

    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, rpath, path=None):
        """
        Make a request to the collector's rest api
        """
        api = self.collector_api(path=path)
        url = api["url"]
        if not url.startswith("https"):
            raise ex.Error("refuse to submit auth tokens through a "
                           "non-encrypted transport")
        request = Request(url+rpath)
        auth_string = '%s:%s' % (api["username"], api["password"])
        base64string = base64encode(auth_string)
        base64string = base64string.replace('\n', '')
        request.add_header("Authorization", "Basic %s" % base64string)
        if api["hosthdr"]:
            request.add_header("Host", api["hosthdr"])
        return request

    def collector_rest_get(self, rpath, data=None, path=None):
        """
        Make a GET request to the collector's rest api
        """
        return self.collector_rest_request(rpath, data=data, path=path)

    def collector_rest_post(self, rpath, data=None, path=None):
        """
        Make a POST request to the collector's rest api
        """
        return self.collector_rest_request(rpath, data, path=path, get_method="POST")

    def collector_rest_put(self, rpath, data=None, path=None):
        """
        Make a PUT request to the collector's rest api
        """
        return self.collector_rest_request(rpath, data, path=path, get_method="PUT")

    def collector_rest_delete(self, rpath, data=None, path=None):
        """
        Make a DELETE request to the collector's rest api
        """
        return self.collector_rest_request(rpath, data, path=path, get_method="DELETE")

    @staticmethod
    def set_ssl_context(kwargs):
        from utilities.uri import ssl_context_kwargs
        kwargs.update(ssl_context_kwargs())
        return kwargs

    def collector_rest_request(self, rpath, data=None, path=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:
                rpath += "?" + urlencode(data)
                data = None

        request = self.collector_request(rpath, path=path)
        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.Error(err)
            except (ValueError, TypeError):
                pass
            raise exc
        except IOError as exc:
            if hasattr(exc, "reason"):
                raise ex.Error(getattr(exc, "reason"))
            raise ex.Error(str(exc))
        data = json.loads(ufile.read().decode("utf-8"))
        ufile.close()
        return data

    def collector_rest_get_to_file(self, rpath, fpath):
        """
        Download bulk chunked data from the collector's rest api
        """
        request = self.collector_request(rpath)
        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.Error(err)
            except ValueError:
                pass
            raise exc
        with open(fpath, 'wb') as ofile:
            os.chmod(fpath, 0o0600)
            for chunk in iter(lambda: ufile.read(4096), b""):
                ofile.write(chunk)
        ufile.close()

    def collector_url(self, rpath):
        return self.collector_env.dbopensvc.replace("/feed/default/call/xmlrpc", rpath)

    def collector_basic_node(self):
        username, password = self.collector_auth_node()
        return base64encode('%s:%s' % (username, password)).replace("\n", "")

    def oc3_request_server(self, method, rpath, base_url=None, headers=None, data=None, timeout=None, **kwargs):
        if base_url is None:
            base_url = self.collector_env.server
        return self.oc3_request(method, rpath, base_url=base_url, headers=headers, data=data, timeout=timeout, **kwargs)

    def oc3_request_feed(self, method, rpath, base_url=None, headers=None, data=None, timeout=None, **kwargs):
        if base_url is None:
            base_url = self.collector_env.feeder
        return self.oc3_request(method, rpath, base_url=base_url, headers=headers, data=data, timeout=timeout, **kwargs)

    def oc3_request(self, method, rpath, base_url=None, headers=None, data=None, timeout=None, collector_basic_node=True, **kwargs):
        """
        Make a request to the collector's oc3 api and returns status code and json decoded response

        it will raise if it can't decode the http response, or if it can't get http status code

        When timeout is None, the request will use timeout value from the node configuration kw: node.collector_timeout.
        When timeout is 0, the request will wait forever.

        Returns:
            tuple[status_code, data]: A tuple containing the http status code of
            the response and the json decoded data.
        """
        if base_url is None:
            url = self.collector_url(rpath)
        else:
            url = "%s%s" % (base_url, rpath)
        if not url.startswith("https://"):
            raise ex.Error("need https protocol, got %s" % url)

        if timeout is None:
            timeout = self.collector_env.timeout

        if headers is None:
            headers = {"Accept": "application/json"}
            if data is not None:
                headers["Content-Type"] = "application/json"

        from utilities.uri import Uri
        headers["Host"] = Uri(url).host_header()

        request = Request(url, headers=headers)
        request.get_method = lambda: method
        if collector_basic_node and "Authorization" not in headers.keys():
            request.add_header("Authorization", "Basic %s" % self.collector_basic_node())

        if data is not None:
            if isinstance(data, bytes):
                raw = data
            else:
                raw = json.dumps(data).encode('utf-8')
            try:
                request.add_data(raw)
            except AttributeError:
                request.data = raw

        kwargs = {}
        kwargs = self.set_ssl_context(kwargs)
        if timeout is not None:
            kwargs["timeout"] = timeout

        def returns(ret_code, read_closer):
            if ret_code == 204:
                return ret_code, None
            b = None
            try:
                b = read_closer.read()
                ret_data = json.loads(b.decode("utf-8"))
                return ret_code, ret_data
            except Exception as decode_err:
                self.log.debug("oc3 %s %s status code %d can't decode response: %s",
                               method, url, ret_code, str(decode_err))
                return ret_code, b
            finally:
                read_closer.close()

        try:
            resp = urlopen(request, **kwargs)
            return returns(resp.code, resp)
        except HTTPError as err:
            return returns(err.code, err)
        except Exception as err:
            raise ex.Error("oc3 %s %s error: %s" % (method, url, str(err)))

    @staticmethod
    def oc3_assert_status_code(api_verb, api_path, status_code, response, expected=None):
        if expected is None and status_code < 500:
            return
        elif status_code in expected:
            return
        elif response is None:
            raise ex.Error("oc3 %s %s status [%d]" % (api_verb, api_path, status_code))

        try:
            text = json.loads(response).get("text", "")
            raise ex.Error("oc3 %s %s status [%d] %s" % (api_verb, api_path, status_code, text))
        except Exception:
            raise ex.Error("oc3 %s %s status [%d]" % (api_verb, api_path, status_code))

    def svc_conf_from_templ(self, name, namespace, kind, template):
        """
        Download a provisioning template from the collector's rest api,
        and installs it as the service configuration file.
        """
        tmpfpath = self.svc_conf_tempfile()
        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.Error(data["error"])
        if len(data["data"]) == 0:
            raise ex.Error("service not found on the collector")
        if len(data["data"][0]["tpl_definition"]) == 0:
            raise ex.Error("service has an empty configuration")
        try:
            return json.loads(data["data"][0]["tpl_definition"])
        except Exception:
            pass
        with open(tmpfpath, "w") as ofile:
            os.chmod(tmpfpath, 0o0600)
            ofile.write(data["data"][0]["tpl_definition"].replace("\\n", "\n").replace("\\t", "\t"))
        try:
            return self.svc_conf_from_file(name, namespace, kind, tmpfpath)
        finally:
            try:
                os.unlink(tmpfpath)
            except OSError:
                pass

    def svc_conf_tempfile(self, content=None):
        import tempfile
        tmpf = tempfile.NamedTemporaryFile()
        tmpfpath = tmpf.name
        tmpf.close()
        with open(tmpfpath, "w"):
            pass
        os.chmod(tmpfpath, 0o0600)
        if content is None:
            return tmpfpath
        with open(tmpfpath, "w") as f:
            f.write(content)
        return tmpfpath

    def svc_conf_from_uri(self, name, namespace, kind, fpath):
        """
        Download a provisioning template from an arbitrary uri,
        and installs it as the service configuration file.
        """
        from utilities.uri import Uri
        print("get %s" % fpath)
        secure = self.oget("node", "secure_fetch")
        try:
            with Uri(fpath, secure=secure).fetch() as tmpfpath:
                return self.svc_conf_from_file(name, namespace, kind, tmpfpath)
        except IOError as exc:
            print("download failed", ":", exc, file=sys.stderr)

    def svc_conf_from_file(self, name, namespace, kind, fpath):
        """
        Installs a local template as the service configuration file.
        """
        if not os.path.exists(fpath):
            raise ex.Error("%s does not exists" % fpath)
        try:
            with open(fpath, "r") as f:
                return json.load(f)
        except Exception as exc:
            pass
        svc = factory(kind)(name, namespace=namespace, cf=fpath, node=self, volatile=True)
        svc.options.format = "json"
        data = {
            svc.path: svc._print_config()
        }
        return data

    def svc_conf_from_stdin(self, name, namespace, kind):
        feed = ""
        for line in sys.stdin.readlines():
            feed += line
        data = None
        if not feed:
            raise ex.Error("empty feed")
        try:
            data = json.loads("".join(feed))
        except ValueError:
            tmpfpath = self.svc_conf_tempfile(content=feed)
            try:
                data = self.svc_conf_from_file(name, namespace, kind, tmpfpath)
            finally:
                os.unlink(tmpfpath)
        return data

    def svc_conf_from_selector(self, selector):
        paths = self.svcs_selector(selector)
        data = {}
        for _path in paths:
            name, _namespace, kind = split_path(_path)
            svc = factory(kind)(name, _namespace, node=self, volatile=True)
            svc.options.format = "json"
            data[_path] = svc.print_config()
        return data

    def install_svc_conf_from_data(self, name, namespace, kind, data, restore=False, info=None):
        """
        Installs a service configuration file from section, keys and values
        fed from a data structure.
        """
        if info is None:
            info = self.install_service_info(name, namespace, kind)

        if not os.path.exists(info.cf):
            # 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
            path = fmt_path(name, namespace, kind)
            Freezer(path).freeze()

        if not restore:
            try:
                del data["DEFAULT"]["id"]
            except KeyError:
                pass

        svc = factory(kind)(name, namespace=namespace, cf=info.cf, cd=data, node=self)
        svc.commit()
        svc.postinstall()

    def install_service_info(self, name, namespace, kind):
        validate_kind(kind)
        validate_ns_name(namespace)
        validate_name(name)
        data = Storage()
        data.path = fmt_path(name, namespace, kind)
        data.pathetc = svc_pathetc(data.path, namespace)
        data.cf = os.path.join(data.pathetc, name+'.conf')
        data.id = new_id()
        makedirs(data.pathetc)
        return data

    def svc_conf_set(self, data=None, kw=None, env=None, interactive=False):
        if data is None:
            data = {}
        if kw is None:
            kw = {}
        for _kw in kw:
            if "=" not in _kw:
                continue
            _kw, val = _kw.split("=", 1)
            if "." in _kw:
                section, option = _kw.split(".", 1)
            else:
                section = "DEFAULT"
                option = _kw
            if section not in data:
                data[section] = {}
            data[section][option] = val
        current_env = data.get("env", {})
        new_env = self.svc_conf_setenv(env, interactive, current_env)
        if new_env:
            data["env"] = new_env
        return data

    def svc_conf_setenv(self, args=None, interactive=False, env=None):
        """
        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
        """
        if args is None:
            args = []
        if env is None:
            env = {}

        explicit_options = []

        for arg in args:
            idx = arg.index("=")
            option = arg[:idx]
            value = arg[idx+1:]
            env[option] = value
            explicit_options.append(option)

        if not interactive:
            return env

        if not os.isatty(0):
            raise ex.Error("--interactive is set but input fd is not a tty")

        def get_href(ref):
            ref = ref.strip("[]")
            try:
                response = urlopen(ref)
                return response.read()
            except:
                return ""

        def print_comment(comment):
            """
            Print a env keyword comment. For use in the interactive service
            create codepath.
            """
            comment = re.sub(r"(\[.+://.+])", lambda m: get_href(m.group(1)), comment)
            print(comment)

        from foreign.six.moves import input
        for key, default_val in env.items():
            if key.endswith(".comment"):
                continue
            if key in explicit_options:
                continue
            comment_key = key + ".comment"
            comment = env.get(comment_key)
            if comment:
                print_comment(comment)
            newval = input("%s [%s] > " % (key, str(default_val)))
            if newval != "":
                env[key] = newval
        return env

    def install_service(self, path, fpath=None, template=None,
                        restore=False, resources=None, kw=None, namespace=None,
                        env=None, interactive=False, provision=False, node=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 kw is None:
            kw = []
        if resources is None:
            resources = []
        if fpath is not None and template is not None:
            raise ex.Error("--config and --template can't both be specified")

        data = None
        installed = []
        env_to_merge = {}

        if path:
            name, _namespace, kind = split_path(path)
            if not namespace:
                namespace = _namespace
        else:
            name = "dummy"
            kind = "svc"

        if sys.stdin and env in (["-"], ["stdin"], ["/dev/stdin"]):
            env_to_merge = self.svc_conf_from_stdin(name, namespace, kind)
            env = []

        if template and want_context():
            req = {
                "action": "create",
                "options": {
                    "path": path,
                    "namespace": namespace,
                    "provision": provision,
                    "template": template,
                    "restore": restore,
                    "data": env_to_merge,
                }
            }
            result = self.daemon_post(req, timeout=DEFAULT_DAEMON_TIMEOUT, node=node)
            status, error, info = self.parse_result(result)
            if status:
                raise ex.Error(error)
            return

        # convert to a pivotal dataset: dict of configs, indexed by path
        if sys.stdin and fpath in ("-", "stdin", "/dev/stdin"):
            data = self.svc_conf_from_stdin(name, namespace, kind)
        elif fpath and "://" not in fpath and not os.path.exists(fpath):
            data = self.svc_conf_from_selector(fpath)
        elif template is not None:
            if "://" in template:
                data = self.svc_conf_from_uri(name, namespace, kind, template)
            elif os.path.exists(template):
                data = self.svc_conf_from_file(name, namespace, kind, template)
            else:
                data = self.svc_conf_from_templ(name, namespace, kind, template)
        elif fpath is not None:
            if "://" in fpath:
                data = self.svc_conf_from_uri(name, namespace, kind, fpath)
            else:
                data = self.svc_conf_from_file(name, namespace, kind, fpath)
        else:
            data = self.svc_conf_from_args(kind, resources)

        _data = {}
        if isinstance(data, dict):
            if "metadata" in data:
                tmppath = fmt_path(data["metadata"]["name"], data["metadata"]["namespace"], data["metadata"]["kind"])
                del data["metadata"]
                _data = {tmppath: data}
            else:
                for tmppath, __data in data.items():
                    try:
                        split_path(tmppath)
                    except ValueError:
                        raise ex.Error("invalid injected data format: %s is not a path" % tmppath)
                    if "metadata" in __data:
                        del __data["metadata"]
                    _data[tmppath] = __data
        elif isinstance(data, list):
            for __data in data:
                try:
                    tmppath = fmt_path(__data["metadata"]["name"], __data["metadata"]["namespace"], __data["metadata"]["kind"])
                except (ValueError, KeyError):
                    raise ex.Error("invalid injected data format: list need a metadata section in each entry")
                del __data["metadata"]
                _data[tmppath] = __data

        if _data:
            if path:
                 if len(_data) > 1:
                     raise ex.Error("multiple configs available to create a single service")
                 # force the new path
                 for tmppath, __data in _data.items():
                     break
                 if tmppath.endswith("svc/dummy"):
                     raise ex.Error("no path in deployment data")
                 _data = {
                     path: __data,
                 }
            data = _data
        else:
            if path:
                data = {path: {}}

        if data:
            for tmppath in data:
                data[tmppath] = self.svc_conf_set(data[tmppath], kw, env, interactive)
                if tmppath in env_to_merge:
                    if "env" in env_to_merge[tmppath]:
                        _env_to_merge = env_to_merge[tmppath]["env"]
                    else:
                        _env_to_merge = env_to_merge[tmppath]
                elif "env" in env_to_merge:
                    _env_to_merge = env_to_merge["env"]
                else:
                    _env_to_merge = env_to_merge
                if isinstance(_env_to_merge, dict) and _env_to_merge:
                    if "env" not in data[tmppath]:
                        data[tmppath]["env"] = _env_to_merge
                    else:
                        data[tmppath]["env"].update(_env_to_merge)

        if want_context() or node:
            req = {
                "action": "create",
                "options": {
                    "namespace": namespace,
                    "provision": provision,
                    "restore": restore,
                    "data": data,
                }
            }
            result = self.daemon_post(req, timeout=DEFAULT_DAEMON_TIMEOUT, node=node)
            status, error, info = self.parse_result(result)
            if status:
                raise ex.Error(error)
            return

        if path and not data:
            info = self.install_service_info(name, namespace, kind)
        elif not data:
            raise ex.Error("feed service configurations to stdin and set --config=-")
        else:
            for _path, _data in data.items():
                if namespace:
                    # discard namespace in path, use --namespace value instead
                    name, _, kind = split_path(_path)
                    _namespace = namespace
                else:
                    name, _namespace, kind = split_path(_path)
                info = self.install_service_info(name, _namespace, kind)
                print("create %s" % info.path)
                self.install_svc_conf_from_data(name, _namespace, kind, _data, restore, info)
                installed.append(info.path)
            self.wake_monitor()
            return installed

        if data is not None:
            self.install_svc_conf_from_data(name, namespace, kind, data, restore, info)
        installed.append(info.path)
        self.wake_monitor()
        return installed

    def set_rlimit(self, nofile=4096):
        """
        Set the operating system nofile rlimit to a sensible value for the
        number of services configured.
        """
        #self.log.debug("len self.svcs <%s>", len(self.svcs))
        n_conf = sum(1 for _ in glob_services_config())
        proportional_nofile = 64 * n_conf
        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 svc_conf_from_args(self, kind, resources=None):
        """
        Create a new service from resource definitions passed as individual
        dictionaries in json format.
        """
        if resources is None:
            resources = []
        defaults = {}
        sections = {}
        rtypes = {}

        for r in resources:
            try:
                d = json.loads(r)
            except:
                raise ex.Error("can not parse resource: %s" % r)
            if "rid" in d:
                section = d["rid"]
                if "#" not in section:
                    raise ex.Error("%s must be formatted as 'rtype#n'" % section)
                l = section.split('#')
                if len(l) != 2:
                    raise ex.Error("%s must be formatted as 'rtype#n'" % section)
                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)

        obj = factory(kind)("dummy", namespace="dummy", volatile=True, node=self)
        from core.keywords import MissKeyNoDefault, KeyInvalidValue
        try:
            defaults.update(obj.kwstore.update("DEFAULT", defaults))
            for section, d in sections.items():
                sections[section].update(obj.kwstore.update(section, d))
        except (MissKeyNoDefault, KeyInvalidValue):
            raise ex.Error
        del obj

        sections["DEFAULT"] = defaults
        return defaults

    def create_path(self, paths, namespace):
        if isinstance(paths, list):
            if len(paths) != 1:
                raise ex.Error("only one service must be specified")
            path = paths[0]
        else:
            raise ex.Error("create_path() accepts a list, got %s" % paths)

        try:
           path.encode("ascii")
        except Exception:
           raise ex.Error("the service name must be ascii-encodable")

        path = resolve_path(path, namespace)
        return path

    def create_service(self, paths, options):
        """
        The "om <kind> create" entrypoint.
        """
        ret = 0
        if paths:
            path = self.create_path(paths, options.namespace)
        else:
            path = None

        try:
            paths = self.install_service(path, fpath=options.config,
                                         template=options.template,
                                         restore=options.restore,
                                         resources=options.resource,
                                         kw=options.kw,
                                         namespace=options.namespace,
                                         env=options.env,
                                         interactive=options.interactive,
                                         provision=options.provision)
        except Exception as exc:
            print(str(exc), file=sys.stderr)
            return 1

        if want_context():
            return ret

        # force a refresh of self.svcs
        try:
            self.rebuild_services(paths)
        except ex.Error as exc:
            print(exc, file=sys.stderr)
            ret = 1

        for svc in self.svcs:
            svc.action("status", options)
            if options.provision:
                svc.action("provision", options)
            if hasattr(svc, "on_create"):
                getattr(svc, "on_create")()

        return ret

    def wait(self):
        """
        Wait for a condition on the monitor thread data.
        Catch broken pipe.
        """
        path = self.options.jsonpath_filter
        server = self.options.server
        duration = self.options.duration
        verbose = self.options.verbose
        begin = time.time()
        try:
            self._wait(server, path, duration)
            if self.options.verbose:
                print("elapsed %.2f seconds"% (time.time() - begin))
        except KeyboardInterrupt:
            return 1
        except (OSError, IOError) as exc:
            if exc.errno == EPIPE:
                return 1

    def _wait(self, server=None, path=None, duration=None):
        """
        Wait for a condition on the monitor thread data or
        a local event data.
        """
        from math import ceil
        duration = convert_duration(duration)
        if duration is None:
            timeout = None
            left = None
        elif duration == 0:
            timeout = 0
            left = 0
        else:
            timeout = _wait_get_time() + duration
            left = duration
        while True:
            if left is None:
                req_duration = 10
            elif left > 10:
                req_duration = 10
            else:
                req_duration = ceil(left)
            result = self.daemon_get(
                {
                    "action": "wait",
                    "options": {
                        "condition": path,
                        "duration": req_duration,
                    },
                },
                server=server,
                timeout=4 * DEFAULT_DAEMON_TIMEOUT,
            )
            status, error, info = self.parse_result(result)
            if status == 501:
                raise ex.Error(error)
            if result.get("data", {}).get("satisfied"):
                break
            if left is not None:
                left = timeout - _wait_get_time()
            if left is not None and left < 1:
                print("timeout", file=sys.stderr)
                raise KeyboardInterrupt()
            _wait_delay(0.2)  # short-loop prevention
            if left is not None:
                left = timeout - _wait_get_time()

    def events(self, server=None):
        try:
            self._events(server=server)
        except ex.Signal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == EPIPE:
                return

    def _events(self, server=None):
        if self.options.server:
            server = self.options.server
        elif server is None:
            server = Env.nodename
        for msg in self.daemon_events(server):
            if self.options.format == "json":
                try:
                    print(json.dumps(msg))
                    sys.stdout.flush()
                except BrokenPipeError:
                    return
            else:
                kind = msg.get("kind")
                print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f"), msg.get("nodename", ""), kind)
                if kind == "patch":
                    for event in msg["data"]:
                        try:
                            key, val = event
                            line = "  %s => %s" % (".".join([str(k) for k in key]), str(val))
                        except ValueError:
                            line = "  %s deleted" % ".".join([str(k) for k in event[0]])
                        try:
                            print(line)
                            sys.stdout.flush()
                        except BrokenPipeError:
                            return
                elif kind == "event":
                    for key, val in msg.get("data", {}).items():
                        print("  %s=%s" % (str(key).upper(), str(val)))

    def logs(self):
        node = "*"
        if self.options.local:
            node = None
        elif self.options.node:
            node = self.options.node
        nodes = self.nodes_selector(node)
        auto = sorted(nodes, reverse=True)
        self._backlogs(server=self.options.server, node=node,
                       backlog=self.options.backlog, since=self.options.since,
                       until=self.options.until, debug=self.options.debug,
                       auto=auto)
        if not self.options.follow:
            return
        try:
            self._followlogs(server=self.options.server, node=node,
                             debug=self.options.debug,
                             auto=auto)
        except ex.Signal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == EPIPE:
                return

    def _backlogs(self, server=None, node=None, backlog=None, since=None, until=None, debug=False, auto=None):
        from utilities.render.color import colorize_log_line
        lines = []
        for line in self.daemon_backlogs(server, node, backlog, since, until, debug):
            line = colorize_log_line(line, auto=auto)
            if line:
                try:
                    print(line)
                    sys.stdout.flush()
                except BrokenPipeError:
                    return

    def _followlogs(self, server=None, node=None, debug=False, auto=None):
        from utilities.render.color import colorize_log_line
        for line in self.daemon_logs(server, node, debug):
            line = colorize_log_line(line, auto=auto)
            if line:
                try:
                    print(line)
                    sys.stdout.flush()
                except BrokenPipeError:
                    return

    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)

    @formatter
    def print_config(self):
        """
        print_config node action entrypoint
        """
        if self.options.format is not None:
            return self.print_config_data()
        if not os.path.exists(Env.paths.nodeconf):
            return
        from utilities.render.color import print_color_config
        print_color_config(Env.paths.nodeconf)

    @formatter
    def ls(self):
        if self.options.node:
            node = self.options.node
        else:
            node = "*"
        data = self.nodes_selector(node)
        if self.options.format is not None:
            return data
        for node in data:
            print(node)

    def nodes_selector(self, selector, data=None):
        if selector in ("*", None):
            if data:
                # if data is provided (by svcmon usually), it is surely
                # more up-to-date then the cluster_node lazy relying on
                # the config lazy
                return sorted([node for node in data])
            elif want_context():
                return sorted([node for node in self.nodes_info])
            else:
                return self.cluster_nodes
        if selector == "":
            return []
        if isinstance(selector, (list, tuple, set)):
            return selector
        selector = selector.strip()
        if not re.search(r"[*?=,+]", selector):
            if re.search(r"\s", selector):
                # simple node list
                return selector.split()
            elif selector in self.cluster_nodes:
                return [selector]
        if data is None:
            data = self.nodes_info
        if data is None:
            # daemon down, at least decide if the local node matches
            data = {Env.nodename: {"labels": self.labels}}

        nodes = []
        for selector in selector.split():
            _nodes = self._nodes_selector(selector, data)
            for node in _nodes:
                if node not in nodes:
                    nodes.append(node)
        return nodes

    def _nodes_selector(self, selector, data=None):
        nodes = set([node for node in data])
        anded_selectors = selector.split("+")
        for _selector in anded_selectors:
            nodes = nodes & self.__nodes_selector(_selector, data)
        return sorted(list(nodes))

    def __nodes_selector(self, selector, data):
        nodes = set()
        ored_selectors = selector.split(",")
        for _selector in ored_selectors:
            nodes = nodes | self.___nodes_selector(_selector, data)
        return nodes

    def ___nodes_selector(self, selector, data):
        try:
            negate = selector[0] == "!"
            selector = selector.lstrip("!")
        except IndexError:
            negate = False
        if selector == "*":
            matching = set([node for node in data])
        elif selector.endswith(":"):
            label = selector.rstrip(":")
            matching = set([node for node, _data in data.items() \
                            if label in _data.get("labels", {})])
        elif "=" in selector:
            label, value = selector.split("=", 1)
            matching = set([node for node, _data in data.items() \
                            if _data.get("labels", {}).get(label) == value])
        else:
            matching = set([node for node in data if fnmatch.fnmatch(node, selector)])
        if negate:
            return set([node for node in data]) - matching
        else:
            return matching

    @lazy
    def agent_version(self):
        import utilities.version
        return utilities.version.agent_version()

    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.log.info('freeze node')
        self.freezer.node_freeze()

    def thaw(self):
        """
        Unset the node frozen flag.
        """
        self.log.info('thaw node')
        self.freezer.node_thaw()

    def stonith(self):
        self._stonith(self.options.node)

    def _stonith(self, node):
        if node in (None, ""):
            raise ex.Error("--node is mandatory")
        if node == Env.nodename:
            raise ex.Error("refuse to stonith ourself")
        if node not in self.cluster_nodes:
            raise ex.Error("refuse to stonith node %s not member of our cluster" % node)
        try:
            cmd = self._get("stonith#%s.cmd" % node)
        except (ex.OptNotFound, ex.Error) as exc:
            raise ex.Error("the stonith#%s.cmd keyword must be set in "
                              "node.conf" % node)
        cmd = shlex.split(cmd)
        self.log.info("stonith node %s", node)
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    def prepare_async_options(self):
        options = {}
        options.update(self.options)
        for opt in ("node", "cluster"):
            if opt in options:
                del options[opt]
        return options

    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 (want_context() and action not in ACTIONS_CUSTOM_REMOTE and (self.options.node or action not in ACTION_ASYNC)) or \
           self.options.node and action not in ACTIONS_CUSTOM_REMOTE:
            options = self.prepare_async_options()
            sync = action not in ACTIONS_NOWAIT_RESULT
            ret = self.daemon_node_action(action=action, options=options, node=self.options.node, sync=sync, action_mode=False)
            if ret == 0:
                raise ex.AbortAction()
            else:
                raise ex.Error()
        if self.options.local:
            return
        if action not in ACTION_ASYNC:
            return
        begin = time.time()
        data = self.set_node_monitor(global_expect=ACTION_ASYNC[action]["target"])
        for line in data.get("info", []):
            self.log.info(line)
            if " already " in line:
                raise ex.AbortAction()
        if not wait:
            raise ex.AbortAction()
        self.poll_async_action(ACTION_ASYNC[action]["target"], timeout=timeout, begin=begin)

    def poll_async_action(self, global_expect, timeout=None, begin=None):
        """
        Display an asynchronous action progress until its end or timeout
        """
        try:
            if global_expect == "frozen":
                self._wait(path="monitor.frozen=frozen", duration=timeout)
            elif global_expect == "thawed":
                self._wait(path="monitor.frozen=thawed", duration=timeout)
        except KeyboardInterrupt:
            raise ex.Error

    #
    # daemon actions
    #
    def daemon_collector_xmlrpc(self, *args, **kwargs):
        data = self.daemon_post(
            {
                "action": "collector_xmlrpc",
                "options": {
                    "args": args,
                    "kwargs": kwargs,
                },
            },
            server=self.options.server,
            silent=True,
            timeout=DEFAULT_DAEMON_TIMEOUT,
        )
        if data is None or data["status"] != 0:
            # the daemon is not running or refused the connection,
            # tell the collector ourselves
            self.collector.call(*args, **kwargs)

    @lazy
    def nodes_info(self):
        if not want_context() or os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            try:
                with open(Env.paths.nodes_info, "r") as ofile:
                    return json.load(ofile)
            except Exception as exc:
                pass
        import socket
        try:
            return self._daemon_nodes_info(silent=True)["data"]
        except (KeyError, TypeError, socket.error):
            return

    def get_ssh_pubkey(self, user="root", key_type="rsa"):
        path = os.path.expanduser("~%s/.ssh/id_%s.pub" % (user, key_type))
        try:
            with open(path, "r") as f:
                buff = f.read()
        except OSError:
            return
        # strip comment
        return self.normalize_ssh_key(buff)

    def _daemon_get_ssh(self):
        data = self.daemon_get(
            {
                "action": "ssh_key",
            },
            node="*",
            silent=True,
            timeout=5,
        )
        return data

    @staticmethod
    def normalize_ssh_key(buff):
        return " ".join(re.split(r"\s+", buff.strip())[0:2])

    def update_ssh_authorized_keys(self):
        data = self._daemon_get_ssh()
        errs = 0
        path = os.path.expanduser("~root/.ssh/authorized_keys")
        keys = []
        short_keys = []
        for node, _data in data.get("nodes", {}).items():
            try:
                _data = _data["data"]
                if _data["key"] is None:
                    raise KeyError
                _data["key"] = self.normalize_ssh_key(_data["key"])
            except KeyError:
                errs +=1
                print("node %s key not found" % node, file=sys.stderr)
                continue
            _data["node"] = node
            keys.append(_data)
            short_keys.append(_data["key"])
        if not keys:
            print("no keys to install")
            return
        try:
            with open(path, "r") as f:
                current_keys = f.read().split("\n")
                need_newline = current_keys and current_keys[-1] != ""
                # strip comments
                current_keys = [self.normalize_ssh_key(buff) for buff in current_keys]
        except OSError:
            current_keys = []
            need_newline = False
        keys = [k for k in keys if k["key"] not in current_keys]
        if not keys:
            print("all keys already installed")
        else:
            with open(path, "a") as f:
                if need_newline:
                    f.write("\n")
                for key in keys:
                    f.write("%s %s@%s\n" % (key["key"], key["user"], key["node"]))
                    print("install %s@%s key" % (key["user"], key["node"]))
        fstat = os.stat(path)
        current_mask = int(fstat.st_mode & 0o777)
        mask = 0o600
        if current_mask != mask:
            print("chmod", oct(mask), path)
            os.chmod(path, mask)
        if fstat.st_uid != 0 or fstat.st_gid != 0:
            print("chown 0:0", path)
            os.chown(path, 0, 0)

    def _daemon_nodes_info(self, silent=False, refresh=False, server=None):
        data = self.daemon_get(
            {
                "action": "nodes_info",
            },
            server=server,
            silent=silent,
            timeout=5,
        )
        return data

    def _daemon_lock(self, name, timeout=None, silent=False, on_error=None):
        if timeout is not None:
            request_timeout = timeout + DEFAULT_DAEMON_TIMEOUT
        else:
            request_timeout = timeout
        data = self.daemon_post(
            {
                "action": "lock",
                "options": {"name": name, "timeout": timeout},
            },
            silent=silent,
            timeout=request_timeout,
        )
        lock_id = data.get("data", {}).get("id")
        if not lock_id and on_error == "raise":
            raise ex.Error("cluster lock error")
        return lock_id

    def _daemon_unlock(self, name, lock_id, timeout=None, silent=False):
        if timeout is not None:
            request_timeout = timeout + DEFAULT_DAEMON_TIMEOUT
        else:
            request_timeout = timeout
        data = self.daemon_post(
            {
                "action": "unlock",
                "options": {"name": name, "lock_id": lock_id, "timeout": timeout},
            },
            silent=silent,
            timeout=request_timeout,
        )
        status, error, info = self.parse_result(data)
        if error:
            print(error, file=sys.stderr)
        return status

    def _daemon_object_selector(self, selector="*", namespace=None, kind=None, server=None):
        data = self.daemon_get(
            {
                "action": "object_selector",
                "options": {
                    "selector": selector,
                    "namespace": namespace,
                    "kind": kind,
                },
            },
            server=server,
            timeout=5,
        )
        return data

    def _daemon_status(self, silent=False, refresh=False, server=None, selector=None, namespace=None):
        data = self.daemon_get(
            {
                "action": "daemon_status",
                "options": {
                    "refresh": refresh,
                    "selector": selector,
                    "namespace": namespace,
                },
            },
            server=server,
            silent=silent,
            timeout=5,
        )
        return data

    @formatter
    def daemon_lock_show(self):
        data = self.daemon_get(
            {
                "action": "cluster/locks",
            },
            timeout=DEFAULT_DAEMON_TIMEOUT,
            with_result=True,
            server=self.options.server
        )
        status, error, info = self.parse_result(data)
        if error:
            raise ex.Error(error)
        if status != 0:
            raise ex.Error("cluster/locks api return status %s" % status)
        locks = data['data']
        if self.options.format in ("json", "flat_json"):
            return locks
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node = tree.add_node()
        node.add_column("name", color.BOLD)
        node.add_column("id", color.BOLD)
        node.add_column("requester", color.BOLD)
        node.add_column("requested", color.BOLD)
        for name in sorted(locks.keys()):
            lock_info = locks[name]
            leaf = node.add_node()
            leaf.add_column(name, color.BROWN)
            leaf.add_column(lock_info['id'])
            leaf.add_column(lock_info['requester'])
            leaf.add_column(lock_info['requested'])
        print(tree)

    def daemon_lock_release(self):
        timeout = convert_duration(self.options.timeout)
        self._daemon_unlock(self.options.name, self.options.id, timeout=timeout)

    def _daemon_mutex(self, server=None):
        action = "daemon_mutex"
        return self.daemon_get(
            {
                "action": action,
            },
            timeout=DEFAULT_DAEMON_TIMEOUT,
            with_result=True,
            server=server
        )

    @formatter
    def daemon_mutex_status(self):
        data = self._daemon_mutex(server=self.options.server)
        status, error, info = self.parse_result(data)
        if error:
            raise ex.Error(error)
        if status != 0:
            raise ex.Error("api return status %s" % status)
        mutexes = data['data'].get("mutexes", {})
        if self.options.format in ("json", "flat_json"):
            return mutexes
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node = tree.add_node()
        node.add_column("name", color.BOLD)
        node.add_column("detail", color.BOLD)
        for name in sorted(mutexes.keys()):
            leaf = node.add_node()
            lock_info = mutexes[name]
            leaf.add_column(name, color.BROWN)
            leaf.add_column(lock_info)
        print(tree)

    def daemon_stats(self, paths=None, node=None):
        if node:
            pass
        elif self.options.node:
            node = self.options.node
        else:
            node = '*'
        data = self._daemon_stats(paths=paths, node=node, server=self.options.server)
        return self.print_data(data, default_fmt="flat_json")

    def _daemon_stats(self, paths=None, silent=False, node=None, server=None):
        # this private function is used by svcmon, need fix this
        try:
            data = self.daemon_get(
                {
                    "action": "daemon_stats",
                    "options": {
                        "selector": ",".join(paths) if paths else "**",
                    }
                },
                node=node,
                server=server,
                silent=silent,
                timeout=5,
            )
        except:
            self.log.warning("daemon stats error")
            return
        if data is None:
            return
        if data.get("status", 0) != 0:
            if "nodes" in data:
                for node in data["nodes"]:
                    if data["nodes"][node].get("status", 0) != 0:
                        self.log.warning("daemon stats error for node %s", node)
            else:
                self.log.warning("daemon stats error")

        if "nodes" in data:
            return dict((n, d.get("data", {})) for n, d in data["nodes"].items())
        else:
            return data.get("data")

    def daemon_status(self, paths=None, server=None):
        if server:
            pass
        elif self.options.server:
            server = self.options.server
        data = self._daemon_status(server=server)
        if data is None or data.get("status", 0) != 0:
            return

        if self.options.format in ("json", "flat_json") or self.options.jsonpath_filter:
            self.print_data(data)
            return

        from utilities.render.cluster import format_cluster
        print(format_cluster(paths=paths, node=self.cluster_nodes, data=data))
        return 0

    def daemon_blacklist_clear(self):
        """
        Tell the daemon to clear the senders blacklist
        """
        data = self.daemon_post(
            {"action": "blacklist_clear"},
            server=self.options.server,
            timeout=DEFAULT_DAEMON_TIMEOUT,
        )
        status, error, info = self.parse_result(data)
        if error:
            print(error, file=sys.stderr)
        return status

    def daemon_dns_dump(self):
        """
        Dump the content of the cluster zone.
        """
        try:
            dns = self.dns
        except Exception:
            return
        from utilities.dns import zone_list
        data = zone_list("", dns)
        if data is None:
            return
        if self.options.format in ("json", "flat_json"):
            self.print_data(data)
            return
        widths = {
            "qname": 0,
            "qtype": 0,
            "ttl": 0,
            "content": 0,
        }
        for record in data:
            for key in ("qname", "qtype", "ttl", "content"):
                length = len(str(record[key]))
                if length > widths[key]:
                    widths[key] = length
        fmt = "{:%d}  IN   {:%d}  {:%d}  {:%d}" % (widths["qname"], widths["qtype"], widths["ttl"], widths["content"])
        for record in sorted(data, key=lambda x: x["qname"]):
            print(fmt.format(record["qname"], record["qtype"], record["ttl"], record["content"]))
        return

    def daemon_relay_status(self):
        """
        Show the daemon senders blacklist
        """
        secret = None
        cluster_name = None
        if self.options.server and self.options.server not in (None, Env.nodename):
            cluster_name = "join"
            for section in self.conf_sections("hb"):
                try:
                    relay = self.conf_get(section, "relay")
                except (ValueError, ex.OptNotFound):
                    continue
                if relay not in self.options.server:
                    continue
                try:
                    secret = self.conf_get(section, "secret")
                    break
                except ex.OptNotFound:
                    continue

        data = self.daemon_get(
            {"action": "daemon_relay_status"},
            server=self.options.server,
            secret=secret,
            cluster_name=cluster_name,
            timeout=5,
        )
        if self.options.format in ("json", "flat_json") or self.options.jsonpath_filter:
            self.print_data(data)
            return

        if data is None:
            return

        from utilities.render.forest import Forest
        from utilities.render.color import color

        tree = Forest()
        head = tree.add_node()
        head.add_column("cluster_id/node")
        head.add_column("cluster_name")
        head.add_column("last")
        head.add_column("elapsed")
        head.add_column("ipaddr")
        head.add_column("size")
        now = time.time()

        for nodename, _data in sorted(data.items(), key=lambda x: x[0]):
             try:
                 updated = _data.get("updated", 0)
                 size = _data.get("size", 0)
                 ipaddr = _data.get("ipaddr", "")
                 cluster_name = _data.get("cluster_name", "")
             except AttributeError:
                 continue
             node = head.add_node()
             node.add_column(nodename, color.BOLD)
             node.add_column(cluster_name)
             node.add_column("%s" % datetime.datetime.fromtimestamp(updated).strftime("%Y-%m-%d %H:%M:%S"))
             node.add_column("%s" % print_duration(now-updated))
             node.add_column("%s" % ipaddr)
             node.add_column("%s" % print_size(size, unit="B"))
        tree.out()

    def daemon_blacklist_status(self):
        """
        Show the daemon senders blacklist
        """
        data = self.daemon_get(
            {"action": "daemon_blacklist_status"},
            server=self.options.server,
            timeout=5,
        )
        status, error, info = self.parse_result(data)
        if status:
            raise ex.Error(error)
        print(json.dumps(data, indent=4, sort_keys=True))

    def _ping(self, node, timeout=5):
        """
        Fetch the daemon senders blacklist as a ping test, from either
        a peer or an arbitrator node, swiching between secrets as appropriate.
        """
        if not node or node in self.cluster_nodes or node in (Env.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.Error("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.Error("unable to find the node %(node)s cluster name. set cluster.name@drpnodes or cluster.name@%(node)s" % dict(node=node))
        elif want_context():
            # relay must be tested from a cluster node
            data = self.daemon_node_action(action="ping", options={"node": node}, node="ANY", action_mode=False)
            status, error, info = self.parse_result(data)
            return status, error, info
        else:
            secret = None
            for section in self.conf_sections("arbitrator"):
                try:
                    arbitrator = self.conf_get(section, "name")
                except Exception:
                    continue
                if arbitrator != node:
                    continue
                try:
                    secret = self.conf_get(section, "secret")
                    cluster_name = "join"
                    node = "raw://" + node
                    break
                except Exception:
                    self.log.warning("missing 'secret' in configuration section %s" % section)
                    continue
            if secret is None:
                raise ex.Error("unable to find a secret for node '%s': neither in cluster.nodes, cluster.drpnodes nor arbitrator#*.name" % node)
        data = self.daemon_get(
            {"action": "daemon_blacklist_status"},
            server=node,
            cluster_name=cluster_name,
            secret=secret,
            timeout=timeout,
        )
        if data is None:
            return 1, "", ""
        elif "status" not in data or data["status"] != 0:
            err = data.get("err", "")
            return 1, err, ""
        return 0, "", ""

    def ping(self):
        ret = 0
        if not self.options.node or want_context():
            return self.ping_node(self.options.node)
        nodes = self.nodes_selector(self.options.node)
        if not nodes:
            # maybe an ip addr
            return self.ping_node(self.options.node)
        for node in nodes:
            if self.ping_node(node):
                ret = 1
        return ret

    def ping_node(self, node):
        try:
            ret, error, _ = self._ping(node)
        except ex.Error as exc:
            print(exc)
            ret = 2
        if not node:
            node = "default endpoint"
        if ret == 0:
            print("%s is alive" % node)
        elif ret == 1:
            if error != "":
                print("%s is not alive: %s" % (node, error))
            else:
                print("%s is not alive" % node)
        return ret

    def drain(self):
        """
        Tell the daemon to freeze and drain all local object instances.
        """
        wait = self.options.wait
        time = self.options.time
        if wait and time:
            request_timeout = convert_duration(time) + DEFAULT_DAEMON_TIMEOUT
        elif wait:
            request_timeout = None
        else:
            request_timeout = DEFAULT_DAEMON_TIMEOUT
        data = self.daemon_post(
            {
                "action": "node_drain",
                "options": {
                    "wait": wait,
                    "time": time,
                }
            },
            server=self.options.server or self.options.node,
            timeout=request_timeout
        )
        if data is None:
            return 1
        status, error, info = self.parse_result(data)
        if error:
            print(error, file=sys.stderr)
        return status

    def daemon_shutdown(self):
        """
        Tell the daemon to shutdown all local object instances then die.
        """
        if not self._daemon_running():
            return
        data = self.daemon_post(
            {"action": "daemon_shutdown"},
            server=self.options.server,
        )
        if data is None:
            return 1
        status, error, info = self.parse_result(data)
        if error:
            print(error, file=sys.stderr)
        return status

    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 is not None:
            options["thr_id"] = self.options.thr_id
        if self.options.session_id is not None:
            options["session_id"] = self.options.session_id
        if os.environ.get("OPENSVC_AGENT_UPGRADE"):
            options["upgrade"] = True
        data = self.daemon_post(
            {"action": "daemon_stop", "options": options},
            server=self.options.node,
        )
        status, error, info = self.parse_result(data)
        if error:
            print(error, file=sys.stderr)
        if status:
            return data
        if self.options.session_id is not None:
            # Don't wait for stop when stopping a client session
            return data
        while self._daemon_running():
            time.sleep(0.1)
        return data

    def daemon_stop(self):
        data = None
        if self.options.thr_id is None and self.options.session_id is None and self.daemon_handled_by_systemd():
            # 'systemctl restart <osvcunit>' when the daemon has been started
            # manually causes a direct 'om daemon start', which fails,
            # and systemd fallbacks to 'om daemon stop' and leaves the
            # daemon stopped.
            # Detect this situation and stop the daemon ourselves.
            if self.daemon_active_systemd():
                self.daemon_stop_systemd()
            else:
                if self.options.session_id is not None or self._daemon_running():
                    data = self._daemon_stop()
        elif self.options.thr_id is not None and self.options.session_id is not None:
            raise ex.Error("can't use both --thread-id and --session-id")
        elif self._daemon_running():
            data = self._daemon_stop()
        if data is None:
            return
        if data.get("status") == 0:
            return
        raise ex.Error(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_running() == 0:
            self.log.info('daemon is already started')
            return
        if self.options.foreground:
            return self.daemon_start_foreground()
        return self.daemon_start_native()

    def daemon_start_foreground(self):
        import daemon.main
        daemon.main.main(args=["-f"])

    def daemon_start_native(self):
        """
        Can be overloaded by node<os>
        """
        if self.daemon_handled_by_systemd():
            return self.daemon_start_systemd()
        os.chdir(Env.paths.pathsvc)
        return os.system(sys.executable+" -m opensvc.daemon")

    def daemon_start_thread(self):
        options = {}
        options["thr_id"] = self.options.thr_id
        data = self.daemon_post(
            {"action": "daemon_start", "options": options},
            server=self.options.server,
            timeout=DEFAULT_DAEMON_TIMEOUT,
        )
        if data.get("status") == 0:
            return
        raise ex.Error(json.dumps(data, indent=4, sort_keys=True))

    def daemon_running(self):
        if self._daemon_running():
            return 0
        else:
            return 1

    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"
        return daemon_process_running()

    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 restart 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 'om daemon start', which fails,
            # and systemd fallbacks to 'om 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 Env.nodename in cluster_nodes:
            cluster_nodes.remove(Env.nodename)

        if not self.frozen():
            self.freeze()
            self.log.info("freeze local node")
        else:
            self.log.info("local node is already frozen")
        self.log.warning("DO NOT UNFREEZE before verifying split services won't double-start")

        # leave other nodes
        options = {"thr_id": "tx", "wait": True}
        data = self.daemon_post(
            {"action": "daemon_stop", "options": options},
            server=Env.nodename,
            timeout=DEFAULT_DAEMON_TIMEOUT,
        )

        errors = 0
        for nodename in cluster_nodes:
            data = self.daemon_post(
                {"action": "leave"},
                server=nodename,
                timeout=DEFAULT_DAEMON_TIMEOUT,
            )
            if data is None:
                self.log.error("leave node %s failed", nodename)
                errors += 1
            else:
                self.log.info("leave node %s", nodename)

        # remove obsolete node configurations
        todo = []
        for section in self.private_cd:
            if section == "cluster" or \
               section.startswith("hb#") or \
               section.startswith("arbitrator#"):
                self.log.info("remove configuration %s", section)
                todo.append(section)
        self.delete_sections(todo)

        # remove obsolete cluster configurations
        svc = factory("ccfg")(node=self)
        todo = []
        for section in svc.conf_sections():
            if section == "cluster" or \
               section.startswith("hb#") or \
               section.startswith("arbitrator#"):
                svc.log.info("remove configuration %s", section)
                todo.append(section)
        svc.delete_sections(todo)
        svc.unset_multi(["DEFAULT.id"])

        self.unset_lazy("cluster_nodes")
        self.unset_lazy("sorted_cluster_nodes")

    def daemon_join(self):
        if self.options.secret is None:
            raise ex.Error("--secret must be set")
        if not self.options.node:
            raise ex.Error("--node must be set")
        self._daemon_join(self.options.node, self.options.secret)

    def daemon_rejoin(self):
        if not self.options.node:
            raise ex.Error("--node must be set")
        self._daemon_join(self.options.node, self.cluster_key)

    def _daemon_join(self, *args):
        from daemon.shared import CONFIG_LOCK
        locked = CONFIG_LOCK.acquire()
        if not locked:
            return
        try:
            self.__daemon_join(*args)
        finally:
            CONFIG_LOCK.release()

    def __daemon_join(self, joined, secret):
        # 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")

        data = self.daemon_post(
            {"action": "join"},
            server=joined,
            cluster_name="join",
            secret=secret,
            timeout=120,
        )
        if data is None:
            raise ex.Error("join node %s failed" % joined)
        if "err" in data:
            raise ex.Error("join node %s failed: %s" % (joined, data.get("err")))

        data = data.get("data")
        if data is None:
            raise ex.Error("join failed: no data in response")
        ndata = data.get("node", {}).get("data", {})
        toadd = []
        toremove = []
        sectoremove = []

        cluster_name = ndata.get("cluster", {}).get("name")
        if cluster_name:
            toadd.append("cluster.name=" + cluster_name)
            self.set_lazy("cluster_name", cluster_name)
        else:
            toremove.append("cluster.name")
        cluster_nodes = ndata.get("cluster", {}).get("nodes")
        if cluster_nodes and isinstance(cluster_nodes, list):
            toadd.append("cluster.nodes=" + cluster_nodes)
        else:
            toremove.append("cluster.nodes")
        cluster_id = ndata.get("cluster", {}).get("id")
        if cluster_id:
            toadd.append("cluster.id=" + cluster_id)
        else:
            toremove.append("cluster.id")
        cluster_drpnodes = ndata.get("cluster", {}).get("drpnodes")
        if isinstance(cluster_drpnodes, list) and len(cluster_drpnodes) > 0:
            toadd.append("cluster.drpnodes=" + cluster_drpnodes)
        else:
            toremove.append("cluster.drpnodes")
        dns = ndata.get("cluster", {}).get("dns")
        if isinstance(dns, list) and len(dns) > 0:
            toadd.append("cluster.dns=" + dns)
        else:
            toremove.append("cluster.dns")
        quorum = ndata.get("cluster", {}).get("quorum", False)
        if quorum:
            toadd.append("cluster.quorum=true")
        else:
            toremove.append("cluster.quorum")
        peer_env = ndata.get("node", {}).get("env")
        if peer_env and peer_env != self.env:
            toadd.append("node.env="+peer_env)
        elif peer_env is None:
            toremove.append("node.env")
        cluster_key = ndata.get("cluster", {}).get("secret")
        if cluster_key:
            # secret might be bytes, when passed from rejoin
            toadd.append("cluster.secret=" + bdecode(secret))
        else:
            toremove.append("cluster.secret")

        config = self.private_cd
        for section, _data in ndata.items():
            if section.startswith("hb#"):
                if section in config:
                    self.log.info("update heartbeat %s", section)
                    sectoremove.append(section)
                else:
                    self.log.info("add heartbeat %s", section)
                for option, value in _data.items():
                    toadd.append("%s.%s=%s" % (section, option, value))
            elif section.startswith("stonith#"):
                if section in config:
                    self.log.info("update stonith %s", section)
                    sectoremove.append(section)
                else:
                    self.log.info("add stonith %s", section)
                for option, value in _data.items():
                    toadd.append("%s.%s=%s" % (section, option, value))
            elif section.startswith("arbitrator#"):
                if section in config:
                    self.log.info("update arbitrator %s", section)
                    sectoremove.append(section)
                else:
                    self.log.info("add arbitrator %s", section)
                for option, value in _data.items():
                    toadd.append("%s.%s=%s" % (section, option, value))
            elif section.startswith("pool#"):
                if section in config:
                    self.log.info("update pool %s", section)
                    sectoremove.append(section)
                else:
                    self.log.info("add pool %s", section)
                for option, value in _data.items():
                    toadd.append("%s.%s=%s" % (section, option, value))
            elif section.startswith("network#"):
                if section in config:
                    self.log.info("update network %s", section)
                    sectoremove.append(section)
                else:
                    self.log.info("add network %s", section)
                for option, value in _data.items():
                    toadd.append("%s.%s=%s" % (section, option, value))

        # remove obsolete hb configurations
        for section in config:
            if section.startswith("hb#") and section not in ndata:
                self.log.info("remove heartbeat %s", section)
                sectoremove.append(section)

        # remove obsolete stonith configurations
        for section in config:
            if section.startswith("stonith#") and section not in ndata:
                self.log.info("remove stonith %s", section)
                sectoremove.append(section)

        # remove obsolete arbitrator configurations
        for section in config:
            if section.startswith("arbitrator#") and section not in ndata:
                self.log.info("remove arbitrator %s", section)
                sectoremove.append(section)

        # remove obsolete pool configurations
        for section in config:
            if section.startswith("pool#") and section not in ndata:
                self.log.info("remove pool %s", section)
                sectoremove.append(section)

        # remove obsolete network configurations
        for section in config:
            if section.startswith("network#") and section not in ndata:
                self.log.info("remove network %s", section)
                sectoremove.append(section)

        self.log.info("join node %s", joined)
        self.set_lazy("cluster_key", bdecode(secret))
        self.delete_sections(sectoremove)
        self.unset_multi(toremove)
        self.set_multi(toadd)

        # install cluster config
        cluster_config_data = data.get("cluster", {}).get("data")
        cluster_config_mtime = data.get("cluster", {}).get("mtime")
        if cluster_config_data:
            if not cluster_name:
                cluster_name = cluster_config_data.get("cluster", {}).get("name", "default")
            if not cluster_nodes:
                cluster_nodes = cluster_config_data.get("cluster", {}).get("nodes", [])
            self.set_lazy("cluster_name", cluster_name)
            self.set_lazy("cluster_key", bdecode(secret))

            self.install_svc_conf_from_data("cluster", None, "ccfg", cluster_config_data, restore=True)
            os.utime(Env.paths.clusterconf, (cluster_config_mtime, cluster_config_mtime))


        errors = 0
        if not cluster_config_data or not cluster_config_data.get("cluster", {}).get("nodes"):
            # join other nodes
            for nodename in cluster_nodes.split():
                if nodename in (Env.nodename, joined):
                    continue
                data = self.daemon_post(
                    {"action": "join"},
                    server=nodename,
                    cluster_name="join",
                    secret=secret,
                    timeout=DEFAULT_DAEMON_TIMEOUT,
                )
                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_post(
                {"action": "node_monitor", "options": options},
                server=self.options.server,
                silent=True,
                timeout=DEFAULT_DAEMON_TIMEOUT,
            )
            if data is None:
                raise ex.Error("the daemon is not running")
            if data and data["status"] != 0:
                if data.get("error"):
                    raise ex.Error("set monitor status failed: %s" % data.get("error"))
                else:
                    raise ex.Error("set monitor status failed")
        except ex.Error:
            raise
        except Exception as exc:
            raise ex.Error("set monitor status failed: %s" % str(exc))
        return data

    def daemon_node_action(self, action=None, options=None, server=None, node=None, sync=True, collect=False, action_mode=True):
        """
        Execute a node action on a peer node.
        If sync is set, wait for the action result.
        """
        if options is None:
            options = {}
        if want_context():
            if not node:
                if action in ACTION_ANY_NODE:
                    node = "ANY"
                else:
                    raise ex.Error("the --node <selector> option is required")
        if action_mode:
            self.log.info("request node action '%s' on node %s", action, node)
        req = {
            "action": "node_action",
            "options": {
                "action": action,
                "options": options,
                "sync": sync,
            }
        }
        try:
            data = self.daemon_post(
                req,
                server=server,
                node=node,
                silent=True,
                timeout=DEFAULT_DAEMON_TIMEOUT,
            )
        except Exception as exc:
            self.log.error("node action on node %s failed: %s",
                           node, exc)
            return 1

        status, error, info = self.parse_result(data)
        if error:
            self.log.error(error)

        def print_node_data(nodename, data):
            outs = False
            if data.get("out") and len(data["out"]) > 0:
                for line in data["out"].splitlines():
                   print(line)
                   outs = True
            if data.get("err") and len(data["err"]) > 0:
                for line in data["err"].splitlines():
                   print(line, file=sys.stderr)
                   outs = True
            if not outs:
                if nodename:
                    prefix = nodename + ": "
                else:
                    prefix = ""
                if data.get("status"):
                    print("%sfailed" % prefix)
                else:
                    print("%spassed" % prefix)

        if collect:
            if "data" not in data:
                return 0
            data = data["data"]
            return data["ret"], data.get("out", ""), data.get("err", "")
        else:
            if "nodes" in data:
                if self.options.format in ("json", "flat_json"):
                    if len(data["nodes"]) == 1:
                        for _data in data["nodes"].values():
                            return _data
                    return data
                else:
                    ret = 0
                    for n, _data in data["nodes"].items():
                        status = _data.get("status", 0)
                        _data = _data.get("data", {})
                        print_node_data(n, _data)
                        ret += _data.get("ret", 0) + status
                    return ret
            else:
                if "data" not in data:
                    return 0
                data = data["data"]
                print_node_data(server, data)
                return data.get("ret", 0)

    def daemon_events(self, server=None, selector=None, full=False, namespace=None):
        req = {
            "action": "events",
            "options": {
                "selector": selector,
                "namespace": namespace,
                "full": full,
            },
        }
        while True:
            for msg in self.daemon_stream(req, server=server):
                if msg is None:
                    continue
                yield msg

            # retry until daemon restart
            time.sleep(1)

            # the node.conf might have changed (join, set, ...)
            self.unset_lazy("cluster_name")
            self.unset_lazy("cluster_names")
            self.unset_lazy("cluster_key")

    def daemon_backlogs(self, server=None, node=None, backlog=None, since=None, until=None, debug=False):
        req = {
            "action": "node_backlogs",
            "options": {
                "backlog": backlog,
                "since": since,
                "until": until,
                "debug": debug,
            }
        }
        # use extra timeout for huge logs
        result = self.daemon_get(req, server=server, node=node, timeout=20)
        if "nodes" in result:
            lines = []
            for logs in result["nodes"].values():
                if not isinstance(logs, list):
                    continue
                lines += logs
        else:
            lines = result
        try:
            return sorted(lines, key=lambda x: x.get("t", 0))
        except AttributeError:
            return []

    def daemon_logs(self, server=None, node=None, debug=False):
        req = {
            "action": "node_logs",
            "options": {
                "debug": debug,
            }
        }
        for lines in self.daemon_stream(req, server=server, node=node):
            if lines is None:
                break
            for line in lines:
                yield line

    def wake_monitor(self):
        if not daemon_process_running():
            # no need to wake to monitor when the daemon is not running
            return
        options = {
            "immediate": True,
        }
        try:
            data = self.daemon_post(
                {
                    "action": "wake_monitor",
                    "options": options
                },
                server=self.options.server,
                silent=True,
                timeout=DEFAULT_DAEMON_TIMEOUT,
            )
            status, error, info = self.parse_result(data)
            if status and data.get("errno") != ECONNREFUSED:
                if error:
                    self.log.warning("wake monitor failed: %s", error)
                else:
                    self.log.warning("wake monitor failed")
        except Exception as exc:
            self.log.warning("wake monitor failed: %s", str(exc))


    def array_show(self):
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node = tree.add_node()
        node.add_column("name", color.BOLD)
        node.add_column("type", color.BOLD)
        for name in self.array_names():
            leaf = node.add_node()
            leaf.add_column(name)
            leaf.add_column(self.oget("array#"+name, "type"))
        print(tree)

    def array_ls(self):
        for name in self.array_names():
            print(name)

    def array_names(self):
        data = set()
        for section in self.conf_sections("array"):
            data.add(section.split("#")[-1])
        return sorted(list(data))

    ##########################################################################
    #
    # Pool
    #
    ##########################################################################
    @formatter
    def pool_ls(self):
        data = self.pool_ls_data()
        if self.options.format in ("json", "flat_json"):
            return data
        print("\n".join([name for name in data]))

    def pool_ls_data(self):
        data = set(["default", "shm"])
        for section in self.conf_sections("pool"):
            data.add(section.split("#")[-1])
        return sorted(list(data))

    @formatter
    def pool_status(self):
        data = self.pool_status_data()
        if self.options.name:
            try:
                data = {self.options.name: data[self.options.name]}
            except KeyError:
                data = {}
        if self.options.format in ("json", "flat_json"):
            return data
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node = tree.add_node()
        node.add_column("name", color.BOLD)
        node.add_column("type", color.BOLD)
        node.add_column("caps", color.BOLD)
        node.add_column("head", color.BOLD)
        node.add_column("vols", color.BOLD)
        node.add_column("size", color.BOLD)
        node.add_column("used", color.BOLD)
        node.add_column("free", color.BOLD)
        for name, _data in data.items():
            leaf = node.add_node()
            leaf.add_column(name, color.BROWN)
            if _data is None:
                continue
            leaf.add_column(_data["type"])
            leaf.add_column(",".join(_data["capabilities"]))
            leaf.add_column(_data.get("head",""))
            leaf.add_column(len(_data["volumes"]))
            for key in ("size", "used", "free"):
                if _data.get(key, -1) < 0:
                    val = "-"
                else:
                    val = print_size(_data[key], unit="k", compact=True)
                leaf.add_column(val)
            if not self.options.verbose:
                continue
            vols_node = leaf.add_node()
            vols_node.add_column("volume", color.BOLD)
            vols_node.add_column("size", color.BOLD)
            vols_node.add_column("children", color.BOLD)
            vols_node.add_column("orphan", color.BOLD)
            for vol in sorted(_data.get("volumes", []), key=lambda x: x["path"]):
                vol_node = vols_node.add_node()
                vol_node.add_column(vol["path"])
                vol_node.add_column(print_size(vol["size"], unit="b", compact=True))
                vol_node.add_column(",".join(vol["children"]))
                vol_node.add_column(str(vol["orphan"]).lower())

        print(tree)

    def pools_volumes(self, mon_status=None):
        if mon_status is None:
            try:
                mon_status = self._daemon_status(silent=True)["monitor"]
            except Exception as exc:
                return {}
        pools = {}
        done = []
        for nodename, ndata in mon_status["nodes"].items():
            if not isinstance(ndata, dict):
                continue
            for path, sdata in ndata.get("services", {}).get("status", {}).items():
                if path in done:
                    continue
                done.append(path)
                poolname = sdata.get("pool")
                children = sdata.get("children", [])
                vdata = {
                    "path": path,
                    "size": sdata.get("size", 0),
                    "children": children,
                    "orphan": not children or not any(child in mon_status["services"] for child in children),
                }
                try:
                    pools[poolname].append(vdata)
                except Exception:
                    pools[poolname] = [vdata]
        return pools

    def pool_status_data(self, usage=True, pools=None, mon_status=None):
        all_pools = self.pool_ls_data()
        if isinstance(pools, list):
            try:
                pools = [p["name"] for p in pools if p["name"] in all_pools]
            except TypeError:
                # already a list of names
                pass
        else:
            pools = all_pools
        volumes = self.pools_volumes(mon_status=mon_status)

        from utilities.concurrent_futures import get_concurrent_futures
        futures = {}
        data = {}
        concurrent_futures = get_concurrent_futures()
        # defines max_workers to avoid ThreadPoolExecutor(max_worker=None) with python 3.4 to avoir following
        # exception:
        #   File "/usr/lib64/python3.4/concurrent/futures/thread.py", line 116, in _adjust_thread_count
        #     if len(self._threads) < self._max_workers:
        # TypeError: unorderable types: int() < NoneType()
        try:
            from os import cpu_count
        except ImportError:
            from multiprocessing import cpu_count
        except ImportError:
            cpu_count = lambda: 1

        max_workers = min(32, (cpu_count() or 1) + 4)
        with concurrent_futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            for name in pools:
                pool = self.get_pool(name)
                futures[name] = executor.submit(self._pool_status_job, name, pool, volumes, usage)
            for name, future in futures.items():
                data[name] = future.result(timeout=None)
        return data

    @staticmethod
    def _pool_status_job(name, pool, volumes, usage):
        try:
            pool_status = pool.pool_status(usage=usage)
        except Exception as exc:
            pool_status = {
                "name": name,
                "type": "unknown",
                "capabilities": [],
                "head": "err: " + str(exc),
            }
        if name in volumes:
            pool_status["volumes"] = sorted(volumes[name], key=lambda x: x["path"])
        else:
            pool_status["volumes"] = []
        return pool_status

    def find_pool(self, poolname=None, pooltype=None, access=None, size=None, fmt=None, shared=False, usage=True):
        candidates1 = []
        candidates = []
        cause = []
        for pool in self.pool_status_data(usage=False).values():
            if shared is True and "shared" not in pool["capabilities"]:
                cause.append((pool["name"], "not shared capable"))
                continue
            if fmt is False and "blk" not in pool["capabilities"]:
                cause.append((pool["name"], "not blk capable"))
                continue
            if access and access not in pool["capabilities"]:
                caps = ','.join(pool["capabilities"])
                cause.append((pool["name"], "not %s capable (%s)" % (access, caps)))
                continue
            if pooltype and pool["type"] != pooltype:
                cause.append((pool["name"], "wrong type: %s, requested %s" % (pool["type"], pooltype)))
                continue
            if not pooltype and pool["type"] == "shm":
                cause.append((pool["name"], "volatile, type not requested, assume persistence is expected."))
                continue
            if poolname and pool["name"] != poolname:
                cause.append((pool["name"], "not named %s" % poolname))
                continue
            candidates1.append(pool)

        if usage:
            for pool in self.pool_status_data(usage=True, pools=candidates1).values():
                if size and "free" in pool and pool["free"] < size//1024+1:
                    cause.append((pool["name"], "not enough free space: %s free, %s requested" % (print_size(pool["free"], unit="KB", compact=True), print_size(size, unit="B", compact=True))))
                    continue
                candidates.append(pool)
        else:
            candidates = candidates1

        if not candidates:
            cause = "\n".join(["    discard pool %s: %s" % (name, reason) for name, reason in cause])
            raise ex.Error(cause)

        def shared_weight(pool):
            if not shared and "shared" in pool["capabilities"]:
                # try not to select a shared capable pool when the resource
                # doesn't require the shared cap
                return 0
            return 1

        def free_weight(pool):
            return pool.get("free", 0)

        def weight(pool):
            return (shared_weight(pool), free_weight(pool))

        candidates = sorted(candidates, key=lambda x: weight(x))
        return self.get_pool(candidates[-1]["name"])

    def get_pool(self, poolname):
        try:
            section = "pool#"+poolname
        except TypeError:
            raise ex.Error("invalid pool name: %s" % poolname)
        if poolname not in ("shm", "default") and not section in self.cd:
            raise ex.Error("pool not found: %s" % poolname)
        if poolname == "shm":
            ptype = "shm"
        else:
            try:
                ptype = self.conf_get(section, "type")
            except ex.OptNotFound as exc:
                ptype = exc.default
        mod = driver_import("pool", ptype)
        return mod.Pool(node=self, name=poolname, log=self.log)

    def pool_create_volume(self):
        self._pool_create_volume(poolname=self.options.pool,
                                 name=self.options.name,
                                 namespace=self.options.namespace,
                                 size=self.options.size,
                                 access=self.options.access,
                                 nodes=self.options.nodes,
                                 shared=self.options.shared,
                                 fmt=not self.options.blk)

    def _pool_create_volume(self, poolname=None, **kwargs):
        try:
            pool = self.get_pool(poolname)
        except ImportError as exc:
            raise ex.Error(str(exc))
        pool.create_volume(**kwargs)

    ##########################################################################
    #
    # 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
        """
        return {}

    def stats(self, refresh=False):
        now = time.time()
        if not refresh and self.stats_data and \
           self.stats_updated > now - STATS_INTERVAL:
            return self.stats_data
        data = {}
        self.stats_updated = now
        try:
            data["load_15m"] = round(os.getloadavg()[2], 1)
        except:
            # None < 0 == True
            pass
        try:
            meminfo = self.stats_meminfo()
        except OSError as exc:
            self.log.error("failed to get mem/swap info: %s", exc)
            meminfo = None
        if isinstance(meminfo, dict):
            data.update(meminfo)
        data["score"] = self.score(data)
        self.stats_data = data
        return data

    @staticmethod
    def get_tid():
        return

    def cpu_time(self, stat_path='/proc/stat'):
        return 0.0

    def pid_cpu_time(self, pid):
        return 0.0

    def tid_cpu_time(self, tid):
        return 0.0

    def pid_mem_total(self, pid):
        return 0.0

    def tid_mem_total(self, tid):
        return 0.0

    ##########################################################################

    def set_lazy(self, prop, val):
        """
        Expose the set_lazy(self, ...) utility function as a method,
        so Node() users don't have to import it from utilities.
        """
        set_lazy(self, prop, val)

    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 utilities.
        """
        unset_lazy(self, prop)

    @lazy
    def hooks(self):
        """
        A hash of hook command sets, indexed by event.
        """
        data = {}
        for section in self.conf_sections("hook"):
            try:
                command = tuple(self.conf_get(section, "command"))
            except Exception:
                continue
            events = self.conf_get(section, "events")
            for event in events:
                if event not in data:
                    data[event] = set()
                data[event].add(command)
        return data

    def write_boot_id(self):
        with open(self.paths.last_boot_id, "w") as ofile:
            ofile.write(self.asset.get_boot_id())

    def last_boot_id(self):
        try:
            with open(self.paths.last_boot_id, "r") as ofile:
                return ofile.read()
        except Exception:
            return

    @lazy
    def targets(self):
        return self.asset.get_targets()

    def unset_all_lazy(self):
        unset_all_lazy(self)

    def delete(self):
        self.delete_sections(self.options.kw)

    @lazy
    def oci(self):
        oci = self.oget("node", "oci")
        if oci:
            return oci
        if "node.x.podman" in capabilities:
            return "podman"
        else:
            return "docker"

    @formatter
    def scan_capabilities(self):
        return capabilities.as_list(capabilities.scan(node=self))

    @formatter
    def print_capabilities(self):
        return capabilities.as_list(capabilities.data)

    def post_commit(self):
        self.unset_all_lazy()


# helper for tests mock
_wait_get_time = time.time
_wait_delay = delay
0707010001f189000081a40000000000000000000000016a100daf00000e81000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/core/node/windows.py   from __future__ import print_function

import os
import time

import core.exceptions as ex

from .node import Node as BaseNode

try:
    import pythoncom
    import foreign.wmi as wmi
    import win32serviceutil
    from foreign.six.moves import winreg
except ImportError:
    raise


WINSVCNAME = "OsvcAgent"

class Node(BaseNode):
    def shutdown(self):
        cmd = ["shutdown", "/s", "/f"]
        ret, out, err = self.vcall(cmd)

    def _reboot(self):
        cmd = ["shutdown", "/r", "/f"]
        ret, out, err = self.vcall(cmd)

    def wmi(self):
        pythoncom.CoInitialize()
        return wmi.WMI()

    def stats_meminfo(self):
        """
        Memory sizes are store in MB.
        Avails are percentages.
        """
        raw_data = {}
        data = {}
        wmi = self.wmi()
        queueinfo = wmi.Win32_PerfFormattedData_PerfOS_System()
        swapinfo = wmi.Win32_PageFileUsage()
        meminfo = wmi.Win32_ComputerSystem()
        perfinfo = wmi.Win32_PerfRawData_PerfOS_Memory()
        raw_data["queuelength"] = int(queueinfo[-1].ProcessorQueueLength)
        raw_data["SwapAvailable"] = int(swapinfo[-1].AllocatedBaseSize) - int(swapinfo[-1].CurrentUsage)
        raw_data["SwapTotal"] = int(swapinfo[-1].AllocatedBaseSize)
        raw_data["MemAvailable"] = int(perfinfo[-1].AvailableBytes)
        raw_data["MemTotal"] = int(meminfo[-1].TotalPhysicalMemory)
        data["mem_total"] = raw_data["MemTotal"] // 1048576
        data["mem_avail"] = int(100 * raw_data["MemAvailable"] // raw_data["MemTotal"])
        data["swap_total"] = raw_data["SwapTotal"]
        data["swap_avail"] = int(100 * raw_data["SwapAvailable"] // raw_data["SwapTotal"])
        data["load_15m"] = raw_data["queuelength"]
        return data

    def daemon_start_native(self):
        self.set_upgrade_envvar()
        try:
            win32serviceutil.StartService(WINSVCNAME)
        except Exception as exc:
            raise ex.Error(str(exc))
        finally:
            self.unset_upgrade_envvar()

        def fn():
            _, state, _, _, _, _, _ = win32serviceutil.QueryServiceStatus(WINSVCNAME)
            if state == 4:
                return True
            return False

        for step in range(5):
            if fn():
                return
            time.sleep(1)
        raise ex.Error("waited too long for startup")

    def daemon_stop_native(self):
        try:
            win32serviceutil.StopService(WINSVCNAME)
        except Exception as exc:
            raise ex.Error(str(exc))

    def unset_upgrade_envvar(self):
        path = r"SYSTEM\CurrentControlSet\Services\%s\Environment" % WINSVCNAME
        reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
        try:
            key = winreg.OpenKey(reg, path, 0, winreg.KEY_WRITE)
            winreg.DeleteValue(key, "OPENSVC_AGENT_UPGRADE")
            winreg.CloseKey(key)
        except Exception as exc:
            if hasattr(exc, "errno") and getattr(exc, "errno") == 2:
                # key does not exist
                return
            raise
        finally:
            winreg.CloseKey(reg)

    def set_upgrade_envvar(self):
        if not os.environ.get("OPENSVC_AGENT_UPGRADE"):
            return
        path = r"SYSTEM\CurrentControlSet\Services\%s\Environment" % WINSVCNAME
        reg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
        key = winreg.CreateKeyEx(reg, path, 0, winreg.KEY_WRITE)
        try:
            winreg.SetValue(key, "OPENSVC_AGENT_UPGRADE", winreg.REG_SZ, "1")
        except EnvironmentError:
            raise ex.Error("failed to set OPENSVC_AGENT_UPGRADE=1 in %s" % path)
        winreg.CloseKey(key)
        winreg.CloseKey(reg)
   0707010001f183000081a40000000000000000000000016a100daf000002a1000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/core/node/hpux.py  import time

from utilities.proc import justcall

from .node import Node as BaseNode


class Node(BaseNode):
    def sys_reboot(self, delay=0):
        if delay:
            self.log.info("reboot -q in %s seconds", delay)
            time.sleep(delay)
        justcall(['reboot', '-q'])

    def sys_crash(self, delay=0):
        if delay:
            self.log.info("toc crash in %s seconds", delay)
            time.sleep(delay)
        justcall(['toc'])

    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)
   0707010001f180000081a40000000000000000000000016a100daf000000ce000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/node/__init__.py  import importlib
from env import Env

_package = __package__ or __spec__.name # pylint: disable=undefined-variable
_os = importlib.import_module("." + Env.module_sysname, package=_package)
Node = _os.Node

  0707010001f18a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/core/objects   0707010001f192000081a40000000000000000000000016a100daf00000cb1000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/core/objects/nscfg.py  from core.objects.svc import BaseSvc
from core.objects.pg import PgMixin
from utilities.lazy import lazy
from utilities.drivers import driver_import
from utilities.naming import list_services, factory, split_path

DEFAULT_STATUS_GROUPS = [
]

class Nscfg(PgMixin, BaseSvc):
    kind = "nscfg"

    def __init__(self, *args, **kwargs):
        if len(args) >= 1:
            args = list(args)
            args[0] = "namespace"
        else:
            kwargs["name"] = "namespace"
        BaseSvc.__init__(self, *args, **kwargs)

    @lazy
    def kwstore(self):
        from .nscfgdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .nscfgdict import KEYS
        return KEYS

    @lazy
    def nscfg(self):
        return lambda x: x

    @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 = driver_import("pg", fallback=False)
        except ImportError:
            return
        except Exception as exc:
            print(exc)
            raise
        try:
            mod.DRIVER_BASENAME
        except AttributeError:
            return
        return mod

    def iterate_objects(self, volatile=True):
        for path in list_services(namespace=self.namespace, kinds=["svc", "vol"]):
            name, namespace, kind = split_path(path)
            obj = factory(kind)(name, namespace, volatile=volatile, node=self.node)
            yield obj

    def pg_data(self):
        data = []
        for obj in self.iterate_objects():
             for pid in obj.pg_pids():
                 data.append({
                     "pid": pid,
                     "path": obj.path,
                 })
        return data

    def pg_status(self):
        if not self.pg:
            return []
        data = self.pg_data()
        return data

    def pg_update(self, children=True):
        if not self.pg:
            return
        self.pg._create_pg(self)
        if not children:
            return
        for obj in self.iterate_objects(volatile=False):
            obj.pg_update()

    def pg_remove(self):
        if not self.pg:
            return
        if self.options.force:
            self.pg_kill()
        for obj in self.iterate_objects(volatile=False):
            obj.pg_remove()
        PgMixin.pg_remove(self)

    def pg_freeze(self):
        """
        Freeze all process of the process groups of the service.
        """
        if not self.pg:
            return
        self._pg_freeze()
        for obj in self.iterate_objects(volatile=False):
            obj.print_status_data_eval()

    def pg_thaw(self):
        """
        Thaw all process of the process groups of the service.
        """
        if not self.pg:
            return
        self._pg_thaw()
        for obj in self.iterate_objects(volatile=False):
            obj.print_status_data_eval()

    def pg_kill(self):
        """
        Kill all process of the process groups of the service.
        """
        if not self.pg:
            return
        self._pg_kill()
        for obj in self.iterate_objects(volatile=False):
            obj.print_status_data_eval()

   0707010001f191000081a40000000000000000000000016a100daf00003803000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/core/objects/data.py   import os
import sys
import fnmatch
import shutil
import glob
import tempfile
import re

import core.exceptions as ex
import core.status
import foreign.six
from core.comm import DEFAULT_DAEMON_TIMEOUT
from core.contexts import want_context
from utilities.edit_file import edit_file
from utilities.files import create_protected_file, makedirs, read_unicode_file
from utilities.string import try_decode
from utilities.naming import factory, split_path, list_services
from utilities.proc import find_editor
from utilities.string import bencode


class DataMixin(object):
    def add(self):
        if self.options.key and self.has_key(self.options.key):
            if self.options.value is None and self.options.value_from is None:
                return
            raise ex.Error("key '%s' already exists. use the 'change' action to change the current value." % self.options.key)
        self._add(self.options.key, self.options.value_from)

    def change(self):
        if not self.has_key(self.options.key):
            raise ex.Error("key '%s' does not exist. use the 'add' action to add it." % self.options.key)
        if self.options.value is None and self.options.value_from is None:
            raise ex.Error("a value or value source mut be specified for a change action.")
        self._add(self.options.key, self.options.value_from)

    def remove(self):
        return self.remove_key(self.options.key)

    def append(self):
        self._add(self.options.key, self.options.value_from, append=True)

    def remove_key(self, key):
        if key not in self.data_keys():
            return
        return self.unset_multi(["data." + key])

    def _add_key(self, key, data):
        pass

    def add_key(self, key, data, append=False):
        if append:
            data = self.decode_key(key) + data
        if want_context():
            self.remote_add_key(key, data)
        else:
            self._add_key(key, data)

    def remote_add_key(self, key, data):
        req = {
            "action": "set_key",
            "node": "ANY",
            "options": {
                "path": self.path,
                "key": key,
                "data": data,
            }
        }
        result = self.daemon_post(req, timeout=DEFAULT_DAEMON_TIMEOUT)
        status, error, info = self.parse_result(result)
        if info:
            print(info)
        if status:
            raise ex.Error(error)

    def _add(self, key=None, value_from=None, append=False):
        if key and sys.stdin and value_from in ("-", "/dev/stdin"):
            self.add_stdin(key, append=append)
        elif key and self.options.value is not None:
            self.add_key(key, self.options.value, append=append)
        elif value_from and re.match("^(http://|https://|ftp://|ftps://)", value_from):
            self.add_uri(key, value_from, append=append)
        elif value_from and os.path.isdir(value_from):
            self.add_directory(key, value_from, append=append)
        elif value_from and os.path.isfile(value_from):
            self.add_file(key, value_from, append=append)
        elif value_from:
            self.add_glob(key, value_from, append=append)
        else:
            self.add_key(key, "", append=append)

    def add_stdin(self, key, append=False):
        if append:
            data = self.decode_key(key)
        else:
            data = b""
        if foreign.six.PY2:
            data += sys.stdin.read()
        else:
            data += sys.stdin.buffer.read()  # pylint: disable=no-member
        self.add_key(key, data)

    def add_uri(self, key, path, append=None):
        from utilities.uri import Uri
        secure = self.node.oget("node", "secure_fetch")
        try:
            with Uri(path, secure=secure).fetch() as fpath:
                return self.add_file(key, fpath, append=append)
        except IOError as exc:
            raise ex.Error("download %s failed: %s" % (path, exc))

    def add_file(self, key, path, append=None):
        if key is None:
            key = os.path.basename(path)
        if append:
            data = bencode(self.decode_key(key))
        else:
            data = b""
        if os.path.islink(path) and not os.path.exists(path):
            raise ex.Error("broken symlink %s => %s" % (path, os.readlink(path)))
        with open(path, "rb") as ofile:
            data += ofile.read()
        self.add_key(key, data)

    def add_glob(self, key, path, append=False):
        if key is None:
            key = ""
        fpaths = glob.glob(path)
        if not fpaths:
            raise ex.Error("not data could be found at %s" % path)
        for path in fpaths:
            if os.path.isfile(path):
                _key = os.path.join(key, os.path.basename(path))
                try:
                    self.add_file(_key, path, append=append)
                except ex.Error as exc:
                    self.log.warning("skip: %s", exc)
            elif os.path.isdir(path):
                dir_key = os.path.join(key, os.path.basename(path))
                self.add_directory(dir_key, path, append=append)

    def add_directory(self, key, path, append=False):
        if key:
            sub_key_position = len(path)
            key_prefix = key
        else:
            key_prefix = ''
            sub_key_position = len(os.path.dirname(path))
        for root, dirs, files in os.walk(path):
            for fname in files:
                fpath = os.path.join(root, fname)
                file_key = os.path.join(key_prefix, fpath[sub_key_position:].lstrip(os.sep))
                try:
                    self.add_file(file_key, fpath, append=append)
                except ex.Error as exc:
                    self.log.warning("skip: %s", exc)

    @staticmethod
    def tempfilename():
        tmpf = tempfile.NamedTemporaryFile()
        try:
            return tmpf.name
        finally:
            tmpf.close()

    def edit(self):
        if self.options.key is None:
            self.edit_config()
            return
        buff = self.decode_key(self.options.key)
        if buff is None:
            raise ex.Error("could not decode the secret key '%s'" % self.options.key)
        if isinstance(buff, bytes):
            try:
                self.log.debug("try decode binary key")
                buff = buff.decode()
            except:
                raise ex.Error("binary keys are not editable")
        no_newline = os.linesep not in try_decode(buff)
        editor = find_editor()
        fpath = self.tempfilename()
        create_protected_file(fpath, buff)
        try:
            edit_file(editor, fpath)
            edited = read_unicode_file(fpath)
            if no_newline and edited.count(os.linesep) == 1 and edited.endswith(os.linesep):
                self.log.debug("striping trailing newline from edited key value")
                edited = edited.rstrip(os.linesep)
            if buff == edited:
                return
            self.add_key(self.options.key, edited)
        finally:
            os.unlink(fpath)

    def decode(self):
        buff = self.decode_key(self.options.key)
        if buff is None:
            raise ex.Error("could not decode the secret key '%s'" % self.options.key)
        try:
            sys.stdout.buffer.write(buff)
        except (TypeError, AttributeError):
            # buff is not binary, .buffer is not supported
            sys.stdout.write(buff)

    def keys(self):
        data = sorted(self.data_keys(pattern=self.options.match))
        if self.options.format in ("json", "flat_json"):
            return data
        for key in data:
            print(key)

    def has_key(self, key):
        return key in self.data_keys()

    def data_keys(self, pattern="**"):
        """
        Return the list of keys in the data section.
        """
        config = self.print_config_data()
        return [key for key in config.get("data", {}).keys()
                if pattern is None or fnmatch.fnmatch(key, pattern)]

    def data_dirs(self):
        dirs = set()
        keys = self.data_keys()
        for key in keys:
            path = key
            while True:
                path = os.path.dirname(path)
                if not path or path == '/':
                    break
                if path in keys:
                    continue
                dirs.add(path)
        return sorted(list(dirs))

    def resolve_key(self, key_to_resolve):
        if key_to_resolve is None:
            return []
        keys = self.data_keys()
        dirs = self.data_dirs()

        def recurse(key, done):
            data = []
            for path in dirs:
                if path != key and not fnmatch.fnmatch(path, key):
                    continue
                rkeys, rdone = recurse(path + "/*", done)
                done |= rdone
                if len(rkeys) > 0:
                    data.append({
                        "type": "dir",
                        "path": path,
                        "keys": rkeys,
                    })
            for path in keys:
                if path != key and not fnmatch.fnmatch(path, key):
                    continue
                if path in done:
                    continue
                done.add(path)
                data.append({
                    "type": "file",
                    "path": path,
                })
            return data, done

        return recurse(key_to_resolve, set())[0]

    def install_key(self, key, path, uid=None, gid=None, mode=None, dirmode=None):
        if key["type"] == "file":
            vpath = self.key_path(key, path)
            return self.install_file_key(key["path"], vpath, uid=uid, gid=gid, mode=mode, dirmode=dirmode)
        elif key["type"] == "dir":
            return self.install_dir_key(key, path, uid=uid, gid=gid, mode=mode, dirmode=dirmode)

    def install_dir_key(self, data, path, uid=None, gid=None, mode=None, dirmode=None):
        """
        Install a key decoded data in the host's volatile storage.
        """
        if path == "/":
            self.log.warning("install dir key skip unexpected path: '/' for data %s", data)
            return
        elif path.endswith("/"):
            dirname = os.path.basename(data["path"])
            dirpath = os.path.join(path.rstrip("/"), dirname, "")
        else:
            dirpath = path + "/"
        makedirs(dirpath, uid=uid, gid=gid, mode=dirmode)
        changed = False
        for key in data["keys"]:
            changed |= self.install_key(key, dirpath, uid=uid, gid=uid, mode=mode, dirmode=dirmode)
        return changed

    def install_file_key(self, key, vpath, uid=None, gid=None, mode=None, dirmode=None):
        """
        Install a key decoded data in the host's volatile storage.
        """
        # paranoid checks before rmtree()/unlink()
        if ".." in vpath:
            return
        data = self.decode_key(key)
        if data is None:
            raise ex.Error("no data in key %s" % key)
        if os.path.isdir(vpath):
            self.log.info("remove %s key %s directory at location %s", self.desc, key, vpath)
            shutil.rmtree(vpath)
        vdir = os.path.dirname(vpath)
        if os.path.isfile(vdir) or os.path.islink(vdir):
            self.log.info("remove %s key %s file at parent location %s", self.desc, key, vdir)
            os.unlink(vdir)
        makedirs(vdir, uid=uid, gid=gid, mode=dirmode)
        return self.write_key(vpath, data, key=key, uid=uid, gid=gid, mode=mode)

    @staticmethod
    def key_path(key, path):
        """
        The full path to host's volatile storage file containing the key decoded data.
        """
        if path.endswith("/"):
            name = os.path.basename(key["path"].rstrip("/"))
            npath = os.path.join(path.rstrip("/"), name)
        else:
            npath = path
        return npath

    def write_key(self, vpath, data, key=None, uid=None, gid=None, mode=None):
        """
        Return False if the file did not change.
        Return True if the file changed.
        """
        mtime = os.path.getmtime(self.paths.cf)
        try:
            data = data.encode()
        except (AttributeError, UnicodeDecodeError, UnicodeEncodeError):
            # already bytes
            pass
        if os.path.exists(vpath):
            self.set_perm(vpath, uid, gid, mode)
            if mtime == os.path.getmtime(vpath):
                return False
            with open(vpath, "rb") as ofile:
                current = ofile.read()
            if current == data:
                os.utime(vpath, (mtime, mtime))
                return False
        self.log.info("install %s/%s in %s", self.name, key, vpath)
        with open(vpath, "wb") as ofile:
            self.set_perm(vpath, uid, gid, mode)
            ofile.write(data)
            os.utime(vpath, (mtime, mtime))
        return True

    def set_perm(self, path, uid=None, gid=None, mode=None):
        os.chmod(path, mode or self.default_mode)
        uid = uid if uid is not None else -1
        gid = gid if gid is not None else -1
        os.chown(path, uid, gid)

    def _install(self, key, path):
        """
        Install the <key> decoded data in the host's volatile storage.
        """
        keys = self.resolve_key(key)
        if not keys:
            raise ex.Error("%s key %s not found" % (self.desc, key))
        for _key in keys:
            self.install_key(_key, path)

    def post_commit(self):
        self.postinstall()

    def install(self):
        self.postinstall(self.options.key)

    def postinstall(self, key=None):
        """
        Refresh installed keys
        """
        changed_volumes = set()
        for path in list_services(namespace=self.namespace, kinds=["svc"]):
            name, _, _ = split_path(path)
            svc = factory("svc")(name, namespace=self.namespace, volatile=True, node=self.node, log=self.log)
            for res in svc.get_resources("volume"):
                if res.has_data(self.kind, self.path, key) and res._status() == core.status.UP:
                    installed = res._install_data(self.kind)
                    if installed:
                        changed_volumes.add(res.volsvc.path)
                    if res.volsvc.path in changed_volumes:
                        res.send_signals()
 0707010001f19a000081a40000000000000000000000016a100daf00001a3e000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/objects/usrdict.py    import sys
from env import Env
from core.keywords import KeywordStore

# deprecated => supported
DEPRECATED_KEYWORDS = {
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
    "data",
]

KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "disable",
        "protoname": "disabled",
        "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:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": 60,
        "convert": "duration",
        "text": "A duration expression, like '1m30s'. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."
    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": "{clusternodes}",
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "cn",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Common Name.",
        "example": "foo"
    },
    {
        "section": "DEFAULT",
        "keyword": "c",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Country.",
        "example": "france"
    },
    {
        "section": "DEFAULT",
        "keyword": "st",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request State.",
        "example": "oise"
    },
    {
        "section": "DEFAULT",
        "keyword": "l",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Location.",
        "example": "gouvieux"
    },
    {
        "section": "DEFAULT",
        "keyword": "o",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Organization.",
        "example": "opensvc"
    },
    {
        "section": "DEFAULT",
        "keyword": "ou",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Organizational Unit.",
        "example": "lab"
    },
    {
        "section": "DEFAULT",
        "keyword": "email",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Email.",
        "example": "test@opensvc.com"
    },
    {
        "section": "DEFAULT",
        "keyword": "alt_names",
        "inheritance": "head",
        "convert": "list",
        "at": True,
        "text": "Certificate Signing Request Alternative Domain Names.",
        "example": "www.opensvc.com opensvc.com"
    },
    {
        "section": "DEFAULT",
        "keyword": "bits",
        "inheritance": "head",
        "convert": "size",
        "at": True,
        "text": "Certificate Private Key Length.",
        "example": "4k",
        "default": 4096,
    },
    {
        "section": "DEFAULT",
        "keyword": "validity",
        "inheritance": "head",
        "convert": "duration_to_day",
        "at": True,
        "text": "Certificate Validity duration.",
        "example": "365d",
        "default": "365d",
    },
    {
        "section": "DEFAULT",
        "keyword": "ca",
        "inheritance": "head",
        "at": True,
        "text": "The name of secret containing a certificate to use as a Certificate Authority. This secret must be in the same namespace.",
        "example": "ca",
    },
    {
        "section": "DEFAULT",
        "keyword": "comment",
        "default": "",
        "text": "Helps users understand the role of the service and resources, which is nice to on-call support people having to operate on a service they are not usually responsible for."
    },
    {
        "section": "DEFAULT",
        "keyword": "grant",
        "example": "admin:test* guest:*",
        "text": "Grant roles on namespaces to the user. A whitespace-separated list of root|squatter|prioritizer|blacklistadmin|<role selector>:<namespace selector>, where role selector is a comma-separated list of role in admin,operator,guest and the namespace selector is a glob pattern applied to existing namespaces. The root role is required to add resource triggers and non-containerized resources other than (container.docker, container.podman task.docker, task.podman and volume). The squatter role is required to create a new namespace. The admin role is required to create, deploy and delete objects. The guest role is required to list and read objects configurations and status."
    },
]


KEYS = KeywordStore(
    name="usr",
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.usr.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
)
  0707010001f190000081a40000000000000000000000016a100daf00000d64000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/objects/cfgdict.py    import sys
from env import Env
from core.keywords import KeywordStore

# deprecated => supported
DEPRECATED_KEYWORDS = {
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
    "data",
]

KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "disable",
        "protoname": "disabled",
        "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 :c-action:`start` and :c-action:`stop` actions. These actions immediately return success.\n\n:cmd:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": 60,
        "convert": "duration",
        "text": "A duration expression, like ``1m30s``. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."

    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": "{clusternodes}",
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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 :c-res:`sync` resources.",
        "example": "node1 node2"
    },
    {
        "section": "DEFAULT",
        "keyword": "comment",
        "default": "",
        "text": "Helps users understand the role of the service and resources, which is nice to on-call support people having to operate on a service they are not usually responsible for."
    },
]


KEYS = KeywordStore(
    name="cfg",
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.cfg.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
)
0707010001f198000081a40000000000000000000000016a100daf0000d5cd000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/objects/svcdict.py    import sys

from core.keywords import KeywordStore
from env import Env

SECTIONS = [
    "DEFAULT",
    "sync",
    "ip",
    "fs",
    "disk",
    "share",
    "container",
    "app",
    "task",
    "volume",
]

STARTABLE_SECTIONS = [
    "DEFAULT",
    "sync",
    "ip",
    "fs",
    "disk",
    "share",
    "container",
    "app",
    "volume",
]

DATA_SECTIONS = [
    "certificate",
    "expose",
    "hashpolicy",
    "route",
    "vhost",
]

# 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",
    "DEFAULT.docker_data_dir": "container_data_dir",
    "DEFAULT.flex_min_nodes": "flex_min",
    "DEFAULT.flex_max_nodes": "flex_max",
    "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",
    "DEFAULT.container_data_dir": "docker_data_dir",
    "DEFAULT.flex_min": "flex_min_nodes",
    "DEFAULT.flex_max": "flex_max_nodes",
}

DEPRECATED_SECTIONS = {}

PG_KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "create_pg",
        "default": True,
        "at": 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."
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_cpus",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_mems",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_cpu_shares",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "integer",
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_cpu_quota",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_mem_oom_control",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "integer",
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_mem_limit",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "size",
        "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"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_vmem_limit",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "size",
        "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 :kw:`pg_mem_limit`.",
        "example": "1024000000"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_mem_swappiness",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "integer",
        "depends": [('create_pg', [True])],
        "text": "Set a swappiness value for the process group.",
        "example": "40"
    },
    {
        "sections": SECTIONS + ["subset"],
        "keyword": "pg_blkio_weight",
        "generic": True,
        "inheritance": "leaf",
        "at": True,
        "convert": "integer",
        "depends": [('create_pg', [True])],
        "text": "Block IO relative weight. Value: between 10 and 1000. Kernel default: 1000.",
        "example": "50"
    },
]

KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "priority",
        "default": Env.default_priority,
        "convert": "integer",
        "text": "A scheduling priority (0 for top priority) used by the monitor thread to trigger actions for the top priority services, so that the :kw:`node.max_parallel` constraint doesn't prevent high priority services to start first. The priority setting is dropped from a service configuration injected via the api by a user not granted the prioritizer role."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": "60s",
        "convert": "duration",
        "text": "A duration expression, like ``1m30s``. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."
    },
    {
        "section": "DEFAULT",
        "keyword": "mode",
        "default": "hosted",
        "candidates": ["hosted"],
        "text": "Deprecated. The value is always ``hosted``. The keyword is kept around for now the ease transition from older agents."
    },
    {
        "section": "DEFAULT",
        "keyword": "rollback",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "If set to ``false``, the default 'rollback on action error' "
                "behaviour is inhibited, leaving the service in its "
                "half-started state. The daemon also refuses to takeover "
                "a service if rollback is disabled and a peer instance is "
                "'start failed'."
    },
    {
        "section": "DEFAULT",
        "keyword": "comp_schedule",
        "at": True,
        "default": "~00:00-06:00",
        "text": "The service compliance run schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "status_schedule",
        "at": True,
        "default": "@10",
        "text": "The service status evaluation schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "sync_schedule",
        "at": True,
        "default": "04:00-06:00@121",
        "text": "The default sync resources schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "run_schedule",
        "at": True,
        "text": "The default task resources schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "aws",
        "at": True,
        "text": "The aws cli executable fullpath. If not provided, aws is expected to be found in the PATH."
    },
    {
        "section": "DEFAULT",
        "keyword": "aws_profile",
        "at": True,
        "default": "default",
        "text": "The profile to use with the AWS api."
    },
    {
        "section": "DEFAULT",
        "keyword": "resinfo_schedule",
        "at": True,
        "default": "@60",
        "text": "The service resource info push schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "monitor_schedule",
        "at": True,
        "text": "The service resource monitor schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "push_schedule",
        "at": True,
        "default": "~00:00-06:00",
        "text": "The service configuration emission to the collector schedule. See ``usr/share/doc/schedule`` for the schedule syntax."
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_primary",
        "inheritance": "head",
        "convert": "lower",
        "at": True,
        "depends": [('topology', ["flex"])],
        "default_text": "<first node of the nodes parameter>",
        "text": "The node in charge of syncing the other nodes. :opt:`--cluster` actions on the flex_primary are executed on all peer nodes (ie, not drpnodes)."
    },
    {
        "section": "DEFAULT",
        "keyword": "drp_flex_primary",
        "inheritance": "head",
        "convert": "lower",
        "at": True,
        "depends": [('topology', ["flex"])],
        "default_text": "<first node of the drpnodes parameter>",
        "text": "The drpnode in charge of syncing the other drpnodes. :opt:`--cluster` actions on the drp_flex_primary are executed on all drpnodes (ie, not pri nodes)."
    },
    {
        "section": "DEFAULT",
        "keyword": "docker_exe",
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "dockerd_exe",
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "container_data_dir",
        "at": True,
        "text": "If the service has lxc, docker or podman-type container resources and this keyword is set, the service configures a service-private containers data store. This setup is allows stateful service relocalization.",
        "example": "/srv/svc1/data/containers"
    },
    {
        "section": "DEFAULT",
        "keyword": "docker_daemon_private",
        "at": True,
        "default_text": "<true if container_data_dir is set, else false>",
        "convert": "boolean",
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "docker_daemon_args",
        "at": True,
        "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 :opt:`--ip` parameter for a docker registry service.",
        "example": "--ip 1.2.3.4"
    },
    {
        "section": "DEFAULT",
        "keyword": "registry_creds",
        "at": True,
        "text": "The name of a secret in the same namespace having a config.json key which value is used to login to the container image registry. If not specified, the node-level registry credential store is used.",
        "example": "creds-registry-opensvc-com"
    },
    {
        "section": "DEFAULT",
        "keyword": "devices_from",
        "inheritance": "head",
        "depends": [("kind", "vol")],
        "at": False,
        "required": False,
        "convert": "list",
        "text": "The list of resources that contribute their exposed devices to the volume exposed devices. If not specified, only the last disk contributes.",
    },
    {
        "section": "DEFAULT",
        "keyword": "access",
        "inheritance": "head",
        "depends": [("kind", "vol")],
        "default": "rwo",
        "candidates": ["rwo", "roo", "rwx", "rox"],
        "at": True,
        "required": False,
        "text": "The access mode of the volume. ``rwo`` is Read Write Once, ``roo`` is Read Only Once, ``rwx`` is Read Write Many, ``rox`` is Read Only Many. ``rox`` and ``rwx`` modes are served by flex volume services.",
    },
    {
        "section": "DEFAULT",
        "keyword": "pool",
        "inheritance": "head",
        "depends": [("kind", "vol")],
        "at": True,
        "required": False,
        "text": "The name of the pool this volume was allocated from.",
    },
    {
        "section": "DEFAULT",
        "keyword": "size",
        "inheritance": "head",
        "depends": [("kind", "vol")],
        "convert": "integer",
        "at": True,
        "required": False,
        "text": "The size of the volume allocated from its pool.",
    },
    {
        "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.",
    },
    {
        "section": "container",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "candidates": [],
        "text": "The type of container.",
        "required": False,
        "default": "oci",
    },
    {
        "section": "DEFAULT",
        "keyword": "hard_affinity",
        "inheritance": "head",
        "convert": "set",
        "default": set(),
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "hard_anti_affinity",
        "inheritance": "head",
        "convert": "set",
        "default": set(),
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "soft_affinity",
        "inheritance": "head",
        "convert": "set",
        "default": set(),
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "soft_anti_affinity",
        "inheritance": "head",
        "convert": "set",
        "default": set(),
        "at": True,
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "prkey",
        "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``."
    },
    {
        "section": "DEFAULT",
        "keyword": "no_preempt_abort",
        "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, :kw:`no_preempt_abort` can be activated on a per-resource basis."
    },
    {
        "section": "DEFAULT",
        "keyword": "show_disabled",
        "inheritance": "head",
        "at": True,
        "default": True,
        "convert": "boolean",
        "candidates": [True, False],
        "text": "Specifies if the disabled resources must be included in the print status and json status output."
    },
    {
        "section": "DEFAULT",
        "keyword": "topology",
        "inheritance": "head",
        "at": True,
        "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 :kw:`flex_target` nodes, where :kw:`flex_target` must be in the [flex_min, flex_max] range."
    },
    {
        "section": "DEFAULT",
        "keyword": "scale",
        "inheritance": "head",
        "at": True,
        "convert": "integer",
        "text": "If set, create and provision the necessary slave services, named ``<n>.<name>``, to meet the target ``<scale>`` number of started instances.",
        "example": "4"
    },
    {
        "section": "DEFAULT",
        "keyword": "scaler_slave",
        "inheritance": "head",
        "convert": "boolean",
        "default": False,
        "at": True,
        "text": "Automatically set to ``true`` by the daemon monitor when creating new scaler slaves."
    },
    {
        "section": "DEFAULT",
        "keyword": "orchestrate",
        "inheritance": "head",
        "at": True,
        "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 restart the :kw:`flex_target` number of up instances. Resource restart is still active whatever the :kw:`orchestrate` value.",
    },
    {
        "section": "DEFAULT",
        "keyword": "stonith",
        "inheritance": "head",
        "convert": "boolean",
        "default": False,
        "candidates": (True, False),
        "depends": [("topology", ["failover"])],
        "text": "Stonith the node previously running the service if stale upon start by the daemon monitor.",
    },
    {
        "section": "DEFAULT",
        "keyword": "placement",
        "inheritance": "head",
        "default": "nodes order",
        "candidates": ["none", "nodes order", "load avg", "shift", "spread", "score"],
        "text": "Set a service instances placement policy:\n\n"
                       "* ``none`` no placement policy. a policy for dummy, observe-only, services.\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",
    },
    {
        "section": "DEFAULT",
        "keyword": "constraints",
        "inheritance": "head",
        "at": True,
        "depends": [("orchestrate", "ha")],
        "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.",
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_min",
        "inheritance": "head",
        "default": 1,
        "convert": "integer",
        "depends": [("topology", ["flex"])],
        "text": "Minimum number of up instances in the cluster. Below this number the aggregated service status is degraded to warn.."
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_max",
        "inheritance": "head",
        "default_text": "<number of svc nodes>",
        "convert": "integer",
        "depends": [("topology", ["flex"])],
        "text": "Maximum number of up instances in the cluster. Above this number the aggregated service status is degraded to warn. ``0`` means unlimited."
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_target",
        "inheritance": "head",
        "default_text": "<the value of flex_min>",
        "convert": "integer",
        "depends": [("topology", ["flex"])],
        "text": "Optimal number of up instances in the cluster. The value must be between :kw:`flex_min` and :kw:`flex_max`. If ``orchestrate=ha``, the monitor ensures the :kw:`flex_target` is met."
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_cpu_low_threshold",
        "inheritance": "head",
        "default": 0,
        "convert": "integer",
        "depends": [("topology", ["flex"])],
        "text": "Cluster-wide load average below which flex service instances will be stopped.",
    },
    {
        "section": "DEFAULT",
        "keyword": "flex_cpu_high_threshold",
        "inheritance": "head",
        "default": 100,
        "convert": "integer",
        "depends": [("topology", ["flex"])],
        "text": "Cluster-wide load average above which flex new service instances will be started.",
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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 :kw:`env`."
    },
    {
        "section": "DEFAULT",
        "keyword": "parents",
        "inheritance": "head",
        "at": True,
        "default": [],
        "default_text": "",
        "convert": "list_lower",
        "text": "List of services or instances expressed as ``<path>[@<nodename>]`` that must be ``avail up`` before allowing this service to be started by the daemon monitor. Whitespace separated."
    },
    {
        "section": "DEFAULT",
        "keyword": "children",
        "inheritance": "head",
        "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."
    },
    {
        "section": "DEFAULT",
        "keyword": "slaves",
        "inheritance": "head",
        "at": True,
        "default": [],
        "convert": "list",
        "text": "List of services to propagate the :c-action:`start` and :c-action:`stop` actions to."
    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": Env.nodename,
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnode",
        "inheritance": "head",
        "convert": "lower",
        "default": "",
        "at": True,
        "text": "The backup node where the service is activated in a DRP situation. This node is also a data synchronization target for :c-res:`sync` resources.",
        "example": "node1"
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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 :c-res:`sync` resources.",
        "example": "node1 node2"
    },
    {
        "section": "DEFAULT",
        "keyword": "encapnodes",
        "inheritance": "head",
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "app",
        "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."
    },
    {
        "section": "DEFAULT",
        "keyword": "scsireserv",
        "at": True,
        "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, :kw:`scsireserv` can be activated on a per-resource basis."
    },
    {
        "section": "DEFAULT",
        "keyword": "bwlimit",
        "convert": "speed_kps",
        "text": "Bandwidth limit in KB applied to all rsync transfers. Leave empty to enforce no limit.",
        "example": "3 mb/s"
    },
    {
        "section": "DEFAULT",
        "keyword": "sync_interval",
        "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 :kw:`sync_min_delay` ago. The mecanism is enforced by a timestamp created upon each sync completion in ``<pathvar>/services/<namespace>/<kind>/<name>/<rid>/last_sync_<node>``"
    },
    {
        "section": "DEFAULT",
        "keyword": "sync_max_delay",
        "default": "1d3h",
        "convert": "duration_minute",
        "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 :kw:`sync_min_delay` and :kw:`sync_max_delay`."
    },
    {
        "section": "DEFAULT",
        "keyword": "presnap_trigger",
        "convert": "shlex",
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "postsnap_trigger",
        "convert": "shlex",
        "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 :kw:`presnap_trigger`.",
        "example": "/srv/svc1/etc/init.d/post_snap.sh"
    },
    {
        "section": "DEFAULT",
        "keyword": "monitor_action",
        "at": True,
        "candidates": ("reboot", "crash", "freezestop", "switch"),
        "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"
    },
    {
        "section": "DEFAULT",
        "keyword": "pre_monitor_action",
        "at": True,
        "text": "A script to execute before the :kw:`monitor_action`. For example, if the :kw:`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"
    },
    {
        "section": "app",
        "keyword": "type",
        "inheritance": "leaf",
        "candidates": [],
        "default": "forking",
        "text": "The app driver to use. ``simple`` for foreground-running apps. ``forking`` for daemonizing apps."
    },
    {
        "section": "sync",
        "keyword": "type",
        "inheritance": "leaf",
        "candidates": [],
        "default": "rsync",
        "text": "Point a sync driver to use."
    },
    {
        "section": "sync",
        "keyword": "schedule",
        "default_keyword": "sync_schedule",
        "at": True,
        "text": "Set the this resource synchronization schedule. See ``/usr/share/doc/opensvc/schedule`` for the schedule syntax reference.",
        "example": '["00:00-01:00@61 mon", "02:00-03:00@61 tue-sun"]'
    },
    {
        "section": "sync",
        "keyword": "sync_max_delay",
        "default": "1d3h",
        "convert": "duration_minute",
        "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 :kw:`sync_min_delay` and :kw:`sync_max_delay`."
    },
    {
        "section": "ip",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "default": "host",
        "candidates": [],
        "text": "The opensvc ip driver name.",
        "example": "crossbow",
    },
    {
        "section": "disk",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "default": "vg",
        "candidates": [],
        "text": "The volume group driver to use. Leave empty to activate the native volume group manager."
    },
    {
        "section": "fs",
        "keyword": "type",
        "protoname": "fs_type",
        "inheritance": "leaf",
        "at": True,
        "required": True,
        "strict_candidates": False,
        "candidates": [],
        "text": "The filesystem type for the generic driver or the fs driver."
    },
    {
        "section": "share",
        "keyword": "type",
        "inheritance": "leaf",
        "candidates": [],
        "text": "The type of share.",
        "required": True,
    },
    {
        "section": "task",
        "keyword": "type",
        "candidates": [],
        "default": "host",
        "text": "The type of task. Default tasks run on the host, their use is limited to the cluster admin population. Containerized tasks are safe for unprivileged population."
    },
    {
        "section": "expose",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "candidates": [],
        "text": "The type of expose.",
        "default": "envoy",
    },
    {
        "section": "vhost",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "candidates": [],
        "text": "The type of vhost.",
        "default": "envoy",
    },
    {
        "section": "route",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "candidates": [],
        "text": "The type of route.",
        "default": "envoy",
    },
    {
        "section": "hashpolicy",
        "keyword": "type",
        "inheritance": "leaf",
        "candidates": [],
        "text": "The type of hash policy.",
        "default": "envoy",
    },
    {
        "section": "certificate",
        "keyword": "type",
        "inheritance": "leaf",
        "at": True,
        "candidates": [],
        "text": "The type of certificate.",
        "default": "tls",
    },
    {
        "sections": SECTIONS,
        "keyword": "tags",
        "convert": "set",
        "generic": True,
        "at": True,
        "candidates": None,
        "default": set(),
        "default_text": "",
        "example": "encap noaction",
        "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 :c-tag:`noaction` avoids any state changing action from the driver and implies ``optional=true``, :c-tag:`nostatus` forces the status to n/a."
    },
    {
        "sections": SECTIONS,
        "keyword": "subset",
        "inheritance": "leaf",
        "generic": True,
        "at": True,
        "text": "Assign the resource to a specific subset."
    },
    {
        "sections": STARTABLE_SECTIONS,
        "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. "
                "A resource restart is triggered if :"
                "the resource is not disabled and its status is not up, "
                "and the node is not frozen, "
                "and the service instance is not frozen "
                "and its local expect is set to ``started``. "
                "and resource has not been stopped by user. "
                "If a resource has a restart set to a value >0, its status is evaluated "
                "at the frequency defined by :kw:`DEFAULT.status_schedule`, "
                "additional frequency can be added using :kw:`DEFAULT.monitor_schedule`. "
                ":kw:`restart_delay` defines the interval between two restarts. "
                "Standby resources have a particular value to ensure best effort to restart standby resources, "
                "default value is 2, and value lower than 2 are changed to 2."
    },
    {
        "sections": STARTABLE_SECTIONS,
        "keyword": "restart_delay",
        "generic": True,
        "at": True,
        "default": 0,
        "convert": "duration",
        "text": "Define minimum delay between two triggered restarts of a same resource (used when :kw:`restart`is defined). "
                "Default value is 0 (no delay)."
    },
    {
        "sections": SECTIONS,
        "keyword": "provision",
        "protoname": "enable_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: Provision implies destructive operations like formating. Unprovision destroys service data."
    },
    {
        "sections": SECTIONS,
        "keyword": "unprovision",
        "protoname": "enable_unprovision",
        "generic": True,
        "at": True,
        "candidates": (True, False),
        "default": True,
        "convert": "boolean",
        "text": "Set to false to skip the resource on unprovision actions. Warning: Unprovision destroys service data."
    },
    {
        "sections": SECTIONS,
        "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."
    },
    {
        "sections": SECTIONS,
        "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."
    },
    {
        "sections": SECTIONS,
        "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."
    },
    {
        "sections": SECTIONS,
        "keyword": "disable",
        "protoname": "disabled",
        "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 :c-action:`start` and :c-action:`stop` actions. These actions immediately return success.\n\n:cmd:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "sections": SECTIONS,
        "keyword": "optional",
        "generic": True,
        "at": True,
        "convert": "tristate",
        "default_text": "true for tasks, syncs and resources tagged 'noaction', else false",
        "text": "Action failures on optional resources are logged but do not stop the action sequence. Also the optional resource status is not aggregated to the instance 'availstatus', but aggregated to the 'overallstatus'. Resource tagged :c-tag:`noaction` and sync resources are automatically considered optional. Useful for resources like dump filesystems for example."
    },
    {
        "sections": SECTIONS,
        "keyword": "standby",
        "generic": True,
        "at": True,
        "convert": "tristate",
        "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."
    },
    {
        "sections": SECTIONS,
        "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 !!"
    },
    {
        "sections": SECTIONS,
        "keyword": "pre_unprovision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`unprovision` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "post_unprovision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`unprovision` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "pre_provision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`provision` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "post_provision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`provision` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "pre_start",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`start` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "post_start",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`start` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "pre_startstandby",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`startstandby` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "post_startstandby",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`startstandby` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_pre_startstandby",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`startstandby` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_post_startstandby",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`startstandby` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "pre_stop",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`stop` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "post_stop",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`stop` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "pre_sync_nodes",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_nodes` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "post_sync_nodes",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_nodes` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "pre_sync_drp",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_drp` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "post_sync_drp",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_drp` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "pre_sync_restore",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_restore` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "post_sync_restore",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_restore` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "pre_sync_resync",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_resync` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "post_sync_resync",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_resync` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "pre_sync_update",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_update` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "post_sync_update",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_update` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT"],
        "keyword": "pre_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`run` action. Errors do not interrupt the action."
    },
    {
        "sections": ["DEFAULT"],
        "keyword": "post_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`run` action. Errors do not interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_pre_unprovision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`unprovision` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_post_unprovision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`unprovision` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_pre_provision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`provision` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_post_provision",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`provision` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_pre_start",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`start` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_post_start",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`start` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_pre_stop",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`stop` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "keyword": "blocking_post_stop",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`stop` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_pre_sync_nodes",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_nodes` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_post_sync_nodes",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_nodes` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_pre_sync_drp",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_drp` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_post_sync_drp",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_drp` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_pre_sync_restore",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_restore` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_post_sync_restore",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_restore` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_pre_sync_resync",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_resync` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_post_sync_resync",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_resync` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_pre_sync_update",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`sync_update` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "keyword": "blocking_post_sync_update",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`sync_update` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT"],
        "keyword": "blocking_pre_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`run` action. Errors interrupt the action."
    },
    {
        "sections": ["DEFAULT"],
        "keyword": "blocking_post_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`run` action. Errors interrupt the action."
    },
    {
        "sections": SECTIONS,
        "prefixes": ["unprovision", "provision", "start", "stop"],
        "keyword": "_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 '{prefix}' action. A condition is expressed as ``<rid>(<state>,...)``. If states are omitted, ``up,stdby up`` is used as the default expected states."
    },
    {
        "section": "DEFAULT",
        "keyword": "stat_timeout",
        "convert": "duration",
        "default": 5,
        "at": True,
        "text": "The maximum wait time for a stat call to respond. When expired, the resource status is degraded is to warn, which might cause a TOC if the resource is monitored."
    },
    {
        "sections": ["DEFAULT"],
        "prefixes": ["run"],
        "keyword": "_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 '{prefix}' action. A condition is expressed as ``<rid>(<state>,...)``. If states are omitted, ``up,stdby up`` is used as the default expected states."
    },
    {
        "sections": ["DEFAULT", "sync"],
        "prefixes": ["sync_nodes", "sync_drp", "sync_update", "sync_break", "sync_resync", "sync_restore"],
        "keyword": "_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 '{prefix}' action. A condition is expressed as ``<rid>(<state>,...)``. If states are omitted, ``up,stdby up`` is used as the default expected states."
    },
    {
        "sections": DATA_SECTIONS + SECTIONS + ["subset"],
        "keyword": "comment",
        "default": "",
        "text": "Helps users understand the role of the service and resources, which is nice to on-call support people having to operate on a service they are not usually responsible for."
    },
] + PG_KEYWORDS


KEYS = KeywordStore(
    name="svc",
    provision=True,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    base_sections=["env", "DEFAULT"],
    template_prefix="template.service.",
)
   0707010001f18d000081a40000000000000000000000016a100daf00000246000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/core/objects/ccfg.py   from utilities.lazy import lazy
from core.objects.svc import BaseSvc

DEFAULT_STATUS_GROUPS = [
]

class Ccfg(BaseSvc):
    kind = "ccfg"

    def __init__(self, *args, **kwargs):
        for kwarg in ("name", "namespace"):
            try:
                del kwargs[kwarg]
            except KeyError:
                pass
        BaseSvc.__init__(self, name="cluster", namespace=None, **kwargs)

    @lazy
    def kwstore(self):
        from .ccfgdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .ccfgdict import KEYS
        return KEYS

  0707010001f18f000081a40000000000000000000000016a100daf00000817000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/objects/cfg.py    import base64
import foreign.six as six

from utilities.lazy import lazy
from core.objects.svc import BaseSvc
from utilities.converters import print_size
from core.objects.data import DataMixin
from utilities.string import bencode, bdecode, is_string
import core.exceptions as ex

DEFAULT_STATUS_GROUPS = [
]

class Cfg(DataMixin, BaseSvc):
    kind = "cfg"
    desc = "configuration"
    default_mode = 0o0644

    @lazy
    def kwstore(self):
        from .cfgdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .cfgdict import KEYS
        return KEYS

    def _add_key(self, key, data):
        if not key:
            raise ex.Error("configuration key name can not be empty")
        if data is None:
            raise ex.Error("configuration value can not be empty")
        if not is_string(data):
            data = "base64:"+bdecode(base64.urlsafe_b64encode(data))
        elif "\n" in data:
            data = "base64:"+bdecode(base64.urlsafe_b64encode(bencode(data)))
        else:
            data = "literal:"+data
        applied = self.set_multi(["data.%s=%s" % (key, data)])
        if len(applied) == 0:
            return
        did = "added" if self.running_action == "add" else "changed"
        self.log.info("configuration key '%s' %s (%s)", key, did, print_size(len(data), compact=True, unit="b"))
        # refresh if in use
        self.postinstall(key)

    def decode_key(self, key):
        if not key:
            raise ex.Error("configuration key name can not be empty")
        data = self.oget("data", key)
        if not data:
            raise ex.Error("configuration key %s does not exist or has no value" % key)
        if data.startswith("base64:"):
            if six.PY2:
                data = str(data)
            data = data[7:]
            data = base64.urlsafe_b64decode(data)
            try:
                return data.decode()
            except:
                return data
        elif data.startswith("literal:"):
            return data[8:]
        else:
            return data

 0707010001f193000081a40000000000000000000000016a100daf00000cea000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/core/objects/nscfgdict.py  import sys
from env import Env
from core.keywords import KeywordStore
from core.objects.svcdict import PG_KEYWORDS

KEYWORDS = []

for d in PG_KEYWORDS:
    d["sections"] = ["DEFAULT"]
    KEYWORDS.append(d)

# deprecated => supported
DEPRECATED_KEYWORDS = {
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
]

PRIVATE_KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "disable",
        "protoname": "disabled",
        "protoname": "disabled",
        "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:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": 60,
        "convert": "duration",
        "text": "A duration expression, like ``1m30s``. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."
    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": "{clusternodes}",
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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 :c-res:`sync` resources.",
        "example": "node1 node2"
    },
]


KEYS = KeywordStore(
    name="nscfg",
    keywords=PRIVATE_KEYWORDS+KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.nscfg.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
)
  0707010001f18e000081a40000000000000000000000016a100daf00000d8c000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/core/objects/ccfgdict.py   import sys
from env import Env
from core.keywords import KeywordStore
from core.node.nodedict import KEYWORDS

# deprecated => supported
DEPRECATED_KEYWORDS = {
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
    "node",
    "cluster",
    "compliance",
    "dequeue_actions",
    "stats",
    "checks",
    "packages",
    "patches",
    "asset",
    "disks",
    "rotate_root_pw",
    "listener",
    "syslog",
    "sysreport",
    "stats_collection",
    "reboot",
]

PRIVATE_KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "disable",
        "protoname": "disabled",
        "protoname": "disabled",
        "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:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": 60,
        "convert": "duration",
        "text": "A duration expression, like ``1m30s``. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."
    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": "{clusternodes}",
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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 :c-res:`sync` resources.",
        "example": "node1 node2"
    },
]


KEYS = KeywordStore(
    name="ccfg",
    keywords=PRIVATE_KEYWORDS+KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.cluster.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
)
0707010001f19b000081a40000000000000000000000016a100daf000005dc000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/objects/vol.py    import core.status

from core.objects.svc import Svc
from utilities.lazy import lazy
from utilities.naming import split_path, factory

class Vol(Svc):
    kind = "vol"

    def users(self, exclude=None):
        exclude = exclude or []
        users = []

        # purge lazies that may have changed due to claims
        # that occured in the lifespan of this object
        self.unset_lazy("cd")
        self.unset_lazy("children")

        for child in self.children:
            if child in exclude:
                continue
            name, namespace, kind = split_path(child)
            obj = factory(kind)(name=name, namespace=self.namespace, volatile=True, node=self.node)
            for res in obj.get_resources("volume"):
                if res.name != self.name:
                    continue
                if res.status() in (core.status.UP, core.status.STDBY_UP, core.status.WARN):
                    users.append(child)
        return users

    @lazy
    def devices_from(self):
        return self.oget("DEFAULT", "devices_from")

    def devices(self):
        devs = set()
        if not self.devices_from:
            dev = self.device()
            if dev is None:
                return set()
            return set([dev])
        for rid in self.devices_from:
            r = self.get_resource(rid)
            if r:
                try:
                    devs |= r.exposed_devs()
                except AttributeError:
                    continue
        return devs

0707010001f18c000081a40000000000000000000000016a100daf000020b2000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/objects/builder.py    from __future__ import print_function

import os
import re

import core.exceptions as ex
import utilities.configparser
from env import Env
from utilities.drivers import driver_class
from utilities.naming import factory, list_services, split_path
from utilities.storage import Storage


def get_tags(svc, section):
    try:
        return svc.oget(section, "tags")
    except ValueError:
        # Raised by the kwstore when the keyword is not valid in
        # the section.
        # Data resources for example don't have a tags keyword.
        return set()

def get_encap(svc, section):
    try:
        return svc.oget(section, "encap")
    except ValueError:
        # Raised by the kwstore when the keyword is not valid in
        # the section.
        # Data resources for example don't have a tags keyword.
        return False

def get_optional(svc, section):
    return svc.oget(section, "optional")

def get_monitor(svc, section, impersonate=None):
    return svc.oget(section, "monitor", impersonate=impersonate)

def get_subset(svc, section, impersonate=None):
    return svc.oget(section, "subset", impersonate=impersonate)

def get_restart(svc, section, impersonate=None):
    try:
        return svc.oget(section, "restart", impersonate=impersonate)
    except ValueError:
        # tasks don't support restart
        return 0

def get_restart_delay(svc, section, impersonate=None):
    try:
        return svc.oget(section, "restart_delay", impersonate=impersonate)
    except ValueError:
        # tasks don't support restart_delay
        return 0

def get_disabled(svc, section, impersonate=None):
    return svc.oget(section, "disable", impersonate=impersonate)

def base_kwargs(svc, s):
    """
    Common kwargs for all drivers.
    """
    return {
        "rid": s,
        "subset": get_subset(svc, s),
        "tags": get_tags(svc, s),
        "disabled": get_disabled(svc, s),
        "optional": get_optional(svc, s),
    }

def sync_kwargs(svc, s):
    """
    Common kwargs for all sync drivers.
    """
    kwargs = {}
    kwargs.update(base_kwargs(svc, s))
    kwargs["sync_max_delay"] = svc.oget(s, "sync_max_delay")
    kwargs["schedule"] = svc.oget(s, "schedule")
    return kwargs

def add_resource(svc, driver_group, s):
    if s == "sync#i0":
        return
    if driver_group == "pool":
        driver_group = "zpool"
        match = "[z]{0,1}pool#"
    else:
        match = driver_group+"#"

    if driver_group 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

    try:
        driver_basename = svc.oget(s, "type")
    except Exception:
        driver_basename = ""

    try:
        mod = svc.load_driver(driver_group, driver_basename)
    except Exception:
        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:
            enode = list(svc.encapnodes)[0]
        except IndexError:
            return
        svc.encap_resources[s] = Storage({
            "rid": s,
            "tags": tags,
            "encap": encap,
            "subset": svc.oget(s, "subset", impersonate=enode),
            "nb_restart": get_restart(svc, s, impersonate=enode),
            "restart_delay": get_restart_delay(svc, s, impersonate=enode),
            "monitor": get_monitor(svc, s, impersonate=enode),
            "disabled": get_disabled(svc, s, impersonate=enode),
        })
        svc.encap_resources[s].is_disabled = lambda: svc.encap_resources[s].disabled
        return

    if s in svc.resources_by_id:
        return

    kwargs = svc.section_kwargs(s)
    kwargs.update(svc.section_kwargs(s, rtype=mod.DRIVER_BASENAME))
    kwargs["rid"] = s
    try:
        del kwargs["type"]
    except KeyError:
        pass
    svc += driver_class(mod)(**kwargs)

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 Env.nodename in svc.nodes and len(svc.drpnodes) == 0:
        target.remove("drpnodes")
    if Env.nodename in svc.drpnodes and len(svc.drpnodes) < 2:
        target.remove("drpnodes")
    if len(target) == 0:
        return

    mod = svc.load_driver("sync", "rsync")
    kwargs = {}
    src = []
    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
    try:
        kwargs["options"] += svc.conf_get(kwargs["rid"], "options")
    except ex.OptNotFound:
        pass
    try:
        kwargs["schedule"] = svc.conf_get(kwargs["rid"], "schedule")
    except ex.OptNotFound:
        kwargs["schedule"] = "@60m"
    try:
        kwargs["sync_max_delay"] = svc.conf_get(kwargs["rid"], "sync_max_delay")
    except ex.OptNotFound:
        kwargs["sync_max_delay"] = 3660
    kwargs["reset_options"] = svc.oget(kwargs["rid"], "reset_options")
    kwargs["target"] = list(target)
    kwargs["internal"] = True
    kwargs["disabled"] = get_disabled(svc, kwargs["rid"])
    kwargs["optional"] = get_optional(svc, kwargs["rid"])
    r = mod.SyncRsync(**kwargs)
    svc += r

def add_resources(svc):
    """
    Instanciate resource objects and add them to the service.
    Return the number of resource add errors.
    """
    ret = 0
    sections = {}
    for section in svc.cd:
        restype = section.split("#")[0]
        if restype in ("subset", "env"):
            continue
        try:
            sections[restype].add(section)
        except KeyError:
            sections[restype] = set([section])

    ordered_restypes = [
        "volume",
        "container",
        "ip",
        "disk",
        "fs",
        "share",
        "app",
        "sync",
        "task",
        "expose",
        "vhost",
        "route",
        "certificate",
        "hashpolicy",
    ]

    for restype in ordered_restypes:
        for section in sections.get(restype, []):
            try:
                add_resource(svc, restype, section)
            except (ex.Error, ex.RequiredOptNotFound):
                ret += 1
    add_mandatory_syncs(svc)
    return ret

def build_services(status=None, paths=None, create_instance=False,
                   node=None):
    """
    Returns a list of all services of status matching the specified status.
    If no status is specified, returns all services.
    """
    if paths is None:
        paths = []

    errors = []
    services = {}

    if isinstance(paths, str):
        paths = [paths]

    if len(paths) == 0:
        paths = list_services()
        missing_paths = []
    else:
        local_paths = list_services()
        missing_paths = sorted(list(set(paths) - set(local_paths)))
        for m in missing_paths:
            name, namespace, kind = split_path(m)
            if create_instance:
                services[m] = factory(kind)(name, namespace, node=node)
            else:
                # foreign service
                services[m] = factory(kind)(name, namespace, node=node, volatile=True)
        paths = list(set(paths) & set(local_paths))

    for path in paths:
        name, namespace, kind = split_path(path)
        try:
            svc = factory(kind)(name, namespace, node=node)
        except (ex.Error, ex.InitError, ValueError, utilities.configparser.Error) as e:
            errors.append("%s: %s" % (path, str(e)))
            node.log.error(str(e))
            continue
        except ex.AbortAction:
            continue
        except:
            import traceback
            traceback.print_exc()
            continue
        services[svc.path] = svc
    return [s for _, s in sorted(services.items())], errors
  0707010001f195000081a40000000000000000000000016a100daf00001e06000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/objects/sec.py    from __future__ import print_function

import base64
import os
import sys

import foreign.six as six

import core.exceptions as ex
from core.objects.data import DataMixin
from core.objects.svc import BaseSvc
from utilities.converters import print_size
from utilities.lazy import lazy
from utilities.naming import factory, split_path
from utilities.ssl import gen_cert, get_expire
from utilities.string import bdecode, bencode

DEFAULT_STATUS_GROUPS = [
]

class Sec(DataMixin, BaseSvc):
    kind = "sec"
    desc = "secret"
    default_mode = 0o0600

    @lazy
    def kwstore(self):
        from .secdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .secdict import KEYS
        return KEYS

    def on_create(self):
        if self.oget("DEFAULT", "cn") and "certificate" not in self.data_keys():
            self.gen_cert()

    def _add_key(self, key, data):
        if not key:
            raise ex.Error("secret key name can not be empty")
        if data is None:
            raise ex.Error("secret value can not be empty")
        data = "crypt:"+base64.urlsafe_b64encode(self.encrypt(data, cluster_name="join", encode=True)).decode()
        applied = self.set_multi(["data.%s=%s" % (key, data)])
        if len(applied) == 0:
            return
        did = "added" if self.running_action == "add" else "changed"
        self.log.info("secret key '%s' %s (%s)", key, did, print_size(len(data), compact=True, unit="b"))
        # refresh if in use
        self.postinstall(key)

    def _add_keys(self, data):
        if not data:
            return
        sdata = []
        for key, val in data:
            if not key:
                raise ex.Error("secret key name can not be empty")
            if val is None:
                raise ex.Error("secret value can not be empty")
            val = "crypt:"+base64.urlsafe_b64encode(self.encrypt(val, cluster_name="join", encode=True)).decode()
            sdata.append("data.%s=%s" % (key, val))
        self.set_multi(sdata)
        self.log.info("secret keys '%s' added", ",".join([k for k, v in data]))
        # refresh if in use
        self.postinstall(key)

    def decode_key(self, key):
        if not key:
            raise ex.Error("secret key name can not be empty")
        data = self.oget("data", key)
        if not data:
            raise ex.Error("secret %s key %s does not exist or has no value" % (self.path, key))
        if data.startswith("crypt:"):
            data = data[6:]
            return self.decrypt(base64.urlsafe_b64decode(data.encode("ascii")), structured=False)[2]

    def gen_cert(self):
        data = {}
        for key in ("cn", "c", "st", "l", "o", "ou", "email", "alt_names", "bits", "validity", "ca"):
            val = self.oget("DEFAULT", key)
            if val is not None:
                data[key] = val

        ca = data.get("ca")
        casec = None
        if ca is not None:
            casecname, canamespace, _ = split_path(ca)
            casec = factory("sec")(casecname, namespace=canamespace, log=self.log, volatile=True)
            if not casec.exists():
                raise ex.Error("ca secret %s does not exist" % ca)

        for key in ("crt", "key", "csr"):
            data[key] = self.tempfilename()

        if "alt_names" in data:
            data["cnf"] = self.tempfilename()

        try:
            add_data = []
            if casec:
                for key, kw in (("cacrt", "certificate"), ("cakey", "private_key")):
                    if kw not in casec.data_keys():
                        continue
                    data[key] = self.tempfilename()
                    buff = bdecode(casec.decode_key(kw))
                    with open(data[key], "w") as ofile:
                        ofile.write(buff)
            gen_cert(log=self.log, **data)
            with open(data["key"], "r") as ofile:
                buff = ofile.read()
            fullpem = ""
            fullpem += buff
            add_data.append(("private_key", buff))
            if data.get("crt") is not None:
                with open(data["crt"], "r") as ofile:
                    buff = ofile.read()
                add_data.append(("certificate", buff))
            if data.get("csr") is not None:
                with open(data["csr"], "r") as ofile:
                    buff = ofile.read()
                add_data.append(("certificate_signing_request", buff))
            if data.get("cakey") is None:
                with open(data["crt"], "r") as ofile:
                    buff = ofile.read()
                fullpem += buff
                add_data.append(("certificate_chain", buff))
            else:
                # merge cacrt and crt
                with open(data["crt"], "r") as ofile:
                    buff = ofile.read()
                with open(data["cacrt"], "r") as ofile:
                    buff += ofile.read()
                fullpem += buff
                add_data.append(("certificate_chain", buff))
            add_data.append(("fullpem", fullpem))
            self._add_keys(add_data)
        finally:
            for key in ("crt", "key", "cacrt", "cakey", "csr", "cnf"):
                if key not in data:
                    continue
                try:
                    os.unlink(data[key])
                except Exception:
                    pass

    def get_cert_expire(self):
        buff = bdecode(self.decode_key("certificate"))
        return get_expire(buff)

    def pkcs12(self):
        if six.PY3:
            sys.stdout.buffer.write(self._pkcs12(self.options.password))  # pylint: disable=no-member
        else:
            print(self._pkcs12(self.options.password))

    def _pkcs12(self, password):
        required = set(["private_key", "certificate_chain"])
        if required & set(self.data_keys()) != required:
            self.gen_cert()
        from subprocess import Popen, PIPE
        import tempfile
        _tmpcert = tempfile.NamedTemporaryFile()
        _tmpkey = tempfile.NamedTemporaryFile()
        tmpcert = _tmpcert.name
        tmpkey = _tmpkey.name
        _tmpcert.close()
        _tmpkey.close()
        if password is None:
            from getpass import getpass
            pwd = getpass("Password: ", stream=sys.stderr)
            if not pwd:
                pwd = "\n"
        elif password in ["/dev/stdin", "-"]:
            pwd = sys.stdin.readline()
        else:
            pwd = password+"\n"
        if six.PY3:
            pwd = bencode(pwd)
        try:
            with open(tmpkey, "w") as _tmpkey:
                os.chmod(tmpkey, 0o600)
                _tmpkey.write(bdecode(self.decode_key("private_key")))
            with open(tmpcert, "w") as _tmpcert:
                os.chmod(tmpcert, 0o600)
                _tmpcert.write(bdecode(self.decode_key("certificate_chain")))
            cmd = ["openssl", "pkcs12", "-export", "-in", tmpcert, "-inkey", tmpkey, "-passout", "stdin"]
            proc = Popen(cmd, stdout=PIPE, stderr=PIPE, stdin=PIPE)
            out, err = proc.communicate(input=pwd)
            if err:
                print(err, file=sys.stderr)
            return out
        finally:
            if os.path.exists(tmpcert):
                os.unlink(tmpcert)
            if os.path.exists(tmpkey):
                os.unlink(tmpkey)

    def fullpem(self):
        print(self._fullpem())

    def _fullpem(self):
        required = set(["private_key", "certificate_chain"])
        if required & set(self.data_keys()) != required:
            self.gen_cert()
        buff = bdecode(self.decode_key("private_key"))
        buff += bdecode(self.decode_key("certificate_chain"))
        return buff
  0707010001f18b000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/core/objects/__init__.py   0707010001f199000081a40000000000000000000000016a100daf00001307000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/objects/usr.py    import os

from env import Env
from utilities.naming import split_path, factory
from utilities.lazy import lazy
from core.objects.svc import BaseSvc
from .sec import Sec
import core.exceptions as ex

DEFAULT_STATUS_GROUPS = [
]

OPENSSL_CRL_CONF = """
[ ca ]
default_ca = %(clustername)s

[ %(clustername)s ]
database = %(p_crlindex)s
crlnumber = %(p_crlnumber)s
default_days = 365
default_crl_days = 30
default_md = default
preserve = no

[ crl_ext ]
authorityKeyIdentifier = keyid:always,issuer:always
"""

DEFAULT_SACC_CERT_VALIDITY = "1d"

class Usr(Sec, BaseSvc):
    kind = "usr"
    desc = "user"

    @lazy
    def kwstore(self):
        from .usrdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .usrdict import KEYS
        return KEYS

    def on_create(self):
        changes = []
        if not self.oget("DEFAULT", "cn"):
            if self.namespace == "system":
                changes.append("cn=%s" % self.name)
            else:
                changes.append("cn=%s" % self.fullname)
        if self.namespace != "system":
            try:
                self.conf_get("DEFAULT", "validity")
            except ex.OptNotFound:
                changes.append("validity=%s" % DEFAULT_SACC_CERT_VALIDITY)
            grant = "guest:" + self.namespace
            changes.append("grant=%s" % grant)
        if self.ca:
            changes.append("ca=%s" % self.ca.path)
        else:
            print("no signing-capable CA in %s. skip certificate generation." % ",".join(self.capaths))
        if changes:
            self.set_multi(changes)
        if self.ca and "certificate" not in self.data_keys() and "private_key" in self.ca.data_keys():
            self.gen_cert()

    @property
    def capaths(self):
        capath = self.oget("DEFAULT", "ca")
        if capath:
            capaths = [capath]
        else:
            capaths = self.node.oget("cluster", "ca")
            if not capaths:
                capaths = ["system/sec/ca-" + self.node.cluster_name]
        return capaths
        
    @lazy
    def ca(self):
        for capath in self.capaths:
            name, namespace, kind = split_path(capath)
            casec = factory("sec")(name, namespace="system", volatile=True, log=self.log)
            if casec.exists() and "private_key" in casec.data_keys():
                return casec

    def revoke(self):
        if "certificate" not in self.data_keys():
            raise ex.Error("can not revoke: this certificate is signed by an external CA, and should be revoked there.")
        ca = factory("sec")(self.ca.name, namespace=self.ca.namespace, node=self.node)
        p_ca_key = os.path.join(Env.paths.certs, "ca_private_key." + ca.fullname)
        p_ca_crt = os.path.join(Env.paths.certs, "ca_certiticate." + ca.fullname)
        p_crl = os.path.join(Env.paths.certs, "ca_crl." + ca.fullname)
        p_crlconf = os.path.join(Env.paths.certs, "openssl-crl.conf." + ca.fullname)
        p_crlnumber = os.path.join(Env.paths.certs, "crlnumber." + ca.fullname)
        p_crlindex = os.path.join(Env.paths.certs, "crlindex." + ca.fullname)
        p_usr_crt = os.path.join(Env.paths.certs, "certificate." + self.fullname)
        if "crlnumber" not in ca.data_keys():
            ca.add_key("crlnumber", "00")
        if "crlconf" not in ca.data_keys():
            ca.add_key("crlconf", OPENSSL_CRL_CONF % dict(p_crlindex=p_crlindex, p_crlnumber=p_crlnumber, clustername=self.node.cluster_name))
        if "crlindex" in ca.data_keys():
            ca.install_file_key("crlindex", p_crlindex)
        else:
            with open(p_crlindex, "w") as f:
                pass
        ca.install_file_key("crlnumber", p_crlnumber)
        ca.install_file_key("crlconf", p_crlconf)
        ca.install_file_key("private_key", p_ca_key)
        ca.install_file_key("certificate", p_ca_crt)
        self.install_file_key("certificate", p_usr_crt)
        cmd = ["openssl", "ca",
               "-keyfile", p_ca_key,
               "-cert", p_ca_crt,
               "-revoke", p_usr_crt,
               "-config", p_crlconf]
        ret, out, err = self.vcall(cmd, err_to_info=True)
        if "Already revoked" in err:
            return
        cmd = ["openssl", "ca",
               "-keyfile", p_ca_key,
               "-cert", p_ca_crt,
               "-gencrl",
               "-out", p_crl,
               "-config", p_crlconf]
        self.vcall(cmd, err_to_info=True)
        if not os.path.exists(p_crl):
            raise ex.Error("%s does not exist. rollback transaction." % p_crl)
        with open(p_crl) as f:
            buff = f.read()
        ca.add_key("crl", buff)
        with open(p_crlnumber) as f:
            buff = f.read()
        ca.add_key("crlnumber", buff)
        with open(p_crlindex) as f:
            buff = f.read()
        ca.add_key("crlindex", buff)

 0707010001f197000081a40000000000000000000000016a100daf000334b4000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/core/objects/svc.py    """
The module defining the Svc class.
"""
from __future__ import print_function, unicode_literals

import base64
import hashlib
import itertools
import logging
import os
import signal
import shutil
import sys
import tempfile
import time
from errno import ECONNREFUSED

import core.exceptions as ex
import core.logger
import core.status
import core.oc3path as oc3path
import utilities.lock
from core.comm import Crypt, DEFAULT_DAEMON_TIMEOUT
from core.configfile import move_config_file
from core.contexts import want_context
from core.extconfig import ExtConfigMixin
from core.freezer import Freezer
from core.node import Node
from core.objects.pg import PgMixin
from core.resource import Resource
from core.resourceset import ResourceSet
from core.scheduler import SchedOpts, Scheduler, sched_action
from env import Env, Paths
from utilities.converters import *
from utilities.drivers import driver_import, rtypes_with_callable
from utilities.fcache import fcache
from utilities.files import makedirs
from utilities.lazy import lazy, set_lazy, unset_all_lazy, unset_lazy
from utilities.naming import (fmt_path, resolve_path, svc_pathcf, svc_pathetc,
                              svc_pathlog, svc_pathtmp, svc_pathvar, new_id, factory, split_path)
from utilities.proc import (action_triggers, drop_option, has_option, find_editor,
                            init_locale, justcall, lcall, vcall)
from utilities.rfc3339 import RFC3339, RFC3339Formatter
from utilities.semver import Semver
from utilities.storage import Storage
from utilities.string import is_string

if six.PY2:
    BrokenPipeError = IOError


def signal_handler(*args):
    """
    A signal handler raising the Signal exception.
    Args can be signum and frame, but we don't use them.
    """
    raise ex.Signal


# Actions with a special handling of remote/peer relaying
ACTION_NO_ASYNC = [
    "add",
    "clear",
    "edit_config",
    "logs",
    "print_config",
    "print_status",
]

KIND_ACTION_NO_ASYNC = ["ccfg"]

ACTION_ANY_NODE = (
    "decode",
    "delete",
    "eval",
    "gen_cert",
    "get",
    "keys",
    "validate_config",
    "set",
    "unset",
)

ACTION_ASYNC = {
    "abort": {
        "target": "aborted",
        "progress": "aborting",
    },
    "delete": {
        "target": "deleted",
        "progress": "deleting",
        "local": True,
        "kinds": ["svc", "vol", "usr", "sec", "cfg"],
    },
    "freeze": {
        "target": "frozen",
        "progress": "freezing",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "giveback": {
        "target": "placed",
        "progress": "placing",
        "kinds": ["svc"],
    },
    "move": {
        "target": "placed@",
        "progress": "placing@",
        "kinds": ["svc"],
    },
    "provision": {
        "target": "provisioned",
        "progress": "provisioning",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "purge": {
        "target": "purged",
        "progress": "purging",
        "local": True,
        "kinds": ["svc", "vol", "usr", "sec", "cfg"],
    },
    "restart": {
        "target": "restarted",
        "progress": "restarting",
        "local": True,
    },
    "shutdown": {
        "target": "shutdown",
        "progress": "shutting",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "start": {
        "target": "started",
        "progress": "starting",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "stop": {
        "target": "stopped",
        "progress": "stopping",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "switch": {
        "target": "placed@",
        "progress": "placing@",
        "kinds": ["svc", "vol"],
    },
    "takeover": {
        "target": "placed@",
        "progress": "placing@",
        "kinds": ["svc", "vol"],
    },
    "toc": {
        "progress": "tocing",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "thaw": {
        "target": "thawed",
        "progress": "thawing",
        "local": True,
        "kinds": ["svc", "vol"],
    },
    "unprovision": {
        "target": "unprovisioned",
        "progress": "unprovisioning",
        "local": True,
        "kinds": ["svc", "vol"],
    },
}

TOP_STATUS_GROUPS = [
    "overall",
    "avail",
    "optional",
]

DEFAULT_STATUS_GROUPS = [
    "ip",
    "volume",
    "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",
    "decode",
    "docker",
    "eval",
    "frozen",
    "get",
    "giveback",
    "json_config",
    "json_status",
    "json_devs",
    "json_exposed_devs",
    "json_sub_devs",
    "json_base_devs",
    "logs",
    "oci",
    "podman",
    "pg_pids",
    "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_status",
    "push_config",
    "push_encap_config",
    "prstatus",
    "resource_monitor",
    "status",
    "validate_config",
]

#
# don't refresh the status at the end of these actions because
# we need a Svc rebuild to produce an accurate status dump.
# osvcd will refresh the status due to cf_mtime>status_mtime.
#
ACTIONS_CF_CHANGE = [
    "edit_config",
    "scale",
    "set",
    "unset",
]

ACTIONS_ALLOW_ON_INVALID_NODE = [
    "abort",
    "clear",
    "delete",
    "disable",
    "edit_config",
    "eval",
    "frozen",
    "freeze",
    "get",
    "logs",
    "print_config",
    "print_status",
    "set",
    "status",
    "thaw",
    "unset",
    "update",
    "validate_config",
]

ACTIONS_NO_LOG = [
    "delete",
    "edit_config",
    "eval",
    "get",
    "group_status",
    "install_secrets",
    "logs",
    "push_resinfo",
    "push_status",
    "push_config",
    "push_encap_config",
    "service_status",
    "resource_monitor",
    "set",
    "status",
    "unset",
    "validate_config",
]

ACTIONS_NO_TRIGGER = [
    "abort",
    "clear",
    "delete",
    "disable",
    "dns_update",
    "edit_config",
    "enable",
    "group_status",
    "install_secrets",
    "logs",
    "pg_freeze",
    "pg_thaw",
    "pg_kill",
    "postsync",
    "push_encap_config",
    "push_config",
    "push_resinfo",
    "push_status",
    "presync",
    "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",
    "clear",
    "docker",
    "edit_config",
    "enter",
    "freeze",
    "frozen",
    "eval",
    "get",
    "logs",
    "oci",
    "podman",
    "push_resinfo",
    "push_status",
    "push_config",
    "push_encap_config",
    "run",
    "status",
    "set_provisioned",
    "set_unprovisioned",
    "thaw",
    "validate_config",
]

START_GROUPS = [
    "ip",
    "sync.netapp",
    "sync.nexenta",
    "sync.symclone",
    "sync.symsnap",
    "sync.symsrdfs",
    "sync.hp3par",
    "sync.ibmdssnap",
    "volume",
    "disk",
    "fs",
    "share",
    "container",
    "app",
    "task",
]

STOP_GROUPS = [
    "task",
    "app",
    "container",
    "share",
    "fs",
    "sync.btrfssnap",
    "disk",
    "volume",
    "ip",
]

ACTIONS_DO_MASTER = [
    "clear",
    "freeze",
    "install_secrets",
    "set_provisioned",
    "set_unprovisioned",
    "run",
    "thaw",
    "toc",
]

ACTIONS_DO_MASTER_AND_SLAVE = [
    "boot",
    "migrate",
    "pg_update",
    "provision",
    "prstart",
    "prstop",
    "restart",
    "shutdown",
    "start",
    "startstandby",
    "stop",
    "toc",
    "unprovision"
]

ACTIONS_NEED_SNAP_TRIGGER = [
    "sync_drp",
    "sync_nodes",
    "sync_resync",
    "sync_update",
]

TOPOLOGIES = [
    "failover",
    "flex",
    "span",
]

DRV_GRP_XLATE = {
    "drbd": ["disk", "drbd"],
    "vdisk": ["disk", "vdisk"],
    "vmdg": ["disk", "ldom"],
    "pool": ["disk", "zpool"],
    "zpool": ["disk", "zpool"],
    "loop": ["disk", "loop"],
    "md": ["disk", "md"],
    "zvol": ["disk", "zvol"],
    "lv": ["disk", "lv"],
    "raw": ["disk", "raw"],
    "vxdg": ["disk", "vxdg"],
    "vxvol": ["disk", "vxvol"],
}


init_locale()


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.Error("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.Error(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.Error("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 ObjPaths(object):
    def __init__(self, path, name, cf):
        self.name = name
        self.path = path
        nsetc = svc_pathetc(path)
        if cf:
            self.cf = cf
        else:
            self.cf = svc_pathcf(path)
        self.initd = os.path.join(nsetc, name+'.d')
        self.alt_initd = os.path.join(nsetc, name+'.dir')

    @property
    def tmp_cf(self):
        nstmp = svc_pathtmp(self.path)
        return os.path.join(nstmp, self.name+".conf.tmp")


class BaseSvc(Crypt, ExtConfigMixin):
    kind = "base"

    def __init__(self, name=None, namespace=None, node=None, cf=None, cd=None, volatile=False, log=None, log_handlers=None):
        self.log_handlers = log_handlers
        self.raw_cd = cd
        ExtConfigMixin.__init__(self, default_status_groups=DEFAULT_STATUS_GROUPS)
        self.name = name
        self.namespace = namespace.strip("/") if namespace else None
        self.node = node
        self.hostid = Env.nodename
        self.volatile = volatile
        self.path = fmt_path(self.name, self.namespace, self.kind)
        self.prev_env = None

        if log:
            self.set_lazy("log", log)

        self.paths = ObjPaths(self.path, name, cf)
        self.reset_resources()

        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.action_rid = []
        self.action_rid_before_depends = []
        self.action_rid_depends = []
        self.dependencies = {}
        self.running_action = None
        self.presync_done = False
        self.stats_data = {}
        self.stats_updated = 0

        # needed for kw scoping
        self.nodes = set([Env.nodename])
        self.drpnodes = set()
        self.encapnodes = set()
        self.flex_primary = ""
        self.drp_flex_primary = ""

        # real values for kw needed by scoping
        self.init_nodes()

        # 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=None,
            wait=False,
        )

    def reset_resources(self):
        self.init_resources_errors = 0
        self.resources_initialized = False
        self.resources_by_id = {}
        self.encap_resources = {}
        self.resourcesets_by_id = {}
        self.has_encap_resources = False

    def get_node(self):
        """
        helper for the comm module to find the Node(), for accessing
        its configuration.
        """
        if self.node is None:
            self.node = Node()
        return self.node

    def init_nodes(self):
        """
        Called from __init__, and on node labels change by entities
        holding long-lived BaseSvc objects.
        """
        if want_context():
            return
        try:
            ordered_encapnodes = self.oget("DEFAULT", "encapnodes") or []
            self.encapnodes = set(ordered_encapnodes)
        except (AttributeError, ValueError):
            ordered_encapnodes = []
            self.encapnodes = set()
        try:
            self.ordered_nodes = self.oget("DEFAULT", "nodes") or []
        except (AttributeError, ValueError):
            self.ordered_nodes = [Env.nodename]
        if self.encap and Env.nodename not in self.ordered_nodes:
            self.ordered_nodes = [Env.nodename]
        try:
            self.ordered_drpnodes = self.oget("DEFAULT", "drpnodes") or []
        except (AttributeError, ValueError):
            self.ordered_drpnodes = []
        try:
            self.drpnode = self.oget("DEFAULT", "drpnode") or ""
        except (AttributeError, ValueError):
            self.drpnode = ""
        if self.drpnode and self.drpnode not in self.ordered_drpnodes:
            self.ordered_drpnodes.insert(0, self.drpnode)
        self.nodes = set(self.ordered_nodes)
        self.drpnodes = set(self.ordered_drpnodes)
        self.flex_primary = self.get_flex_primary()
        self.drp_flex_primary = self.get_drp_flex_primary()

    @lazy
    def monitor_action(self):
        return "none"

    @lazy
    def fullname(self):
        return "%s.%s.%s.%s" % (
            self.name,
            self.namespace if self.namespace else "root",
            self.kind,
            self.node.cluster_name
        )

    @lazy
    def var_d(self):
        var_d = svc_pathvar(self.path)
        if not self.volatile:
            makedirs(var_d)
        return var_d

    @lazy
    def log_d(self):
        log_d = svc_pathlog(self.path)
        if not self.volatile:
            makedirs(log_d)
        return log_d

    @lazy
    def loggerpath(self):
        return Env.nodename+"."+self.path.replace("/", ".")

    @lazy
    def log(self):  # pylint: disable=method-hidden
        extra = {
            "path": self.path,
            "node": Env.nodename,
            "sid": Env.session_uuid,
            "cron": self.options.cron,
        }
        return logging.LoggerAdapter(self.logger, extra)

    @lazy
    def logger(self):  # pylint: disable=method-hidden
        if self.volatile:
            handlers = ["stream"]
        else:
            handlers = self.log_handlers
        log_file = os.path.join(self.log_d, self.name+".log")
        return core.logger.initLogger(self.loggerpath, log_file, handlers=handlers)

    @lazy
    def compliance(self):
        from core.compliance import Compliance
        comp = Compliance(self)
        return comp

    @lazy
    def sched(self):
        """
        Lazy init of the service scheduler.
        """
        return Scheduler(
            config_defaults=CONFIG_DEFAULTS,
            options=self.options,
            svc=self,
            scheduler_actions={},
            configure_method="configure_scheduler",
        )

    @lazy
    def orchestrate(self):
        return "no"

    @lazy
    def disable_rollback(self):
        return True

    @lazy
    def show_disabled(self):
        return True

    @lazy
    def encap(self):
        return False

    @lazy
    def freezer(self):
        """
        Lazy allocator for the freezer object.
        """
        return Freezer(self.path)

    @lazy
    def id(self):
        """
        return object id:
        When object has no id a new id is created and if object is not volatile
        the object config file is updated.
        id should not be called on deleted object else new empty object will be created
        """
        try:
            return self.conf_get("DEFAULT", "id")
        except ex.OptNotFound as exc:
            new_id = self.new_id()
            if not self.volatile:
                self._set("DEFAULT", "id", new_id, validation=False)
            return new_id

    @staticmethod
    def new_id():
        return new_id()

    @lazy
    def peers(self):
        if Env.nodename in self.nodes:
            return self.nodes
        elif Env.nodename in self.drpnodes:
            return self.drpnodes
        else:
            return []

    @lazy
    def ordered_peers(self):
        if Env.nodename in self.nodes:
            return self.ordered_nodes
        elif Env.nodename in self.drpnodes:
            return self.ordered_drpnodes
        else:
            return []

    @lazy
    def placement(self):
        return "nodes order"

    @lazy
    def topology(self):
        return "span"

    @lazy
    def svc_env(self):
        val = self.oget("DEFAULT", "env")
        if val is None:
            return self.node.env
        return val

    @lazy
    def lock_timeout(self):
        return self.oget("DEFAULT", "lock_timeout")

    @lazy
    def priority(self):
        prio = self.oget("DEFAULT", "priority")
        if prio is None:
            return Env.default_priority
        return prio

    @lazy
    def cd(self):
        if self.raw_cd is not None:
            return self.raw_cd
        return self.parse_config_file(self.paths.cf)

    def cd_clear_caches(self):
        self.raw_cd = None
        self.unset_lazy("cd")

    @lazy
    def disabled(self):
        return self.oget("DEFAULT", "disable")

    def svclock(self, action=None, timeout=30, delay=1):
        """
        Acquire the service action lock.
        """
        if want_context():
            return
        suffix = None

        if action == "toc" and self.monitor_action in ("reboot", "crash"):
            return

        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 \
           self.options.nolock 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(self.var_d, "lock.generic")
        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 = utilities.lock.lock(
                    timeout=0,
                    delay=delay,
                    lockfile=lockfile,
                    intent=action
                )
                if lockfd is not None:
                    self.lockfd = lockfd
                return
            except utilities.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 = utilities.lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=lockfile,
                intent=action
            )
        except utilities.lock.LockTimeout as exc:
            raise ex.Error("timed out waiting for lock %s: %s" % (details, str(exc)))
        except utilities.lock.LockNoLockFile:
            raise ex.Error("lock_nowait: set the 'lockfile' param %s" % details)
        except utilities.lock.LockCreateError:
            raise ex.Error("can not create lock file %s" % details)
        except utilities.lock.LockAcquire as exc:
            raise ex.Error("another action is currently running %s: %s" % (details, str(exc)))
        except ex.Signal:
            raise ex.Error("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.Error("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def svcunlock(self):
        """
        Release the service action lock.
        """
        utilities.lock.unlock(self.lockfd)
        self.lockfd = None

    @staticmethod
    def setup_signal_handlers():
        """
        Install signal handlers.
        """
        try:
            signal.signal(signal.SIGINT, signal_handler)
            signal.signal(signal.SIGTERM, signal_handler)
        except ValueError:
            # signal only works in main thread
            pass

    def systemd_join_agent_service(self):
        from utilities.systemd import systemd_system, systemd_join
        if os.environ.get("OSVC_ACTION_ORIGIN") == "daemon" or not systemd_system():
            return
        systemd_join("opensvc-agent.service")

    def action(self, action, options=None):
        self.systemd_join_agent_service()
        try:
            options = self.prepare_options(action, options)
            ret = self.async_action(action)
            if ret is not None:
                return ret
        except ex.Error as exc:
            msg = str(exc)
            if msg:
                self.log.error(msg)
            return 1
        except ex.AbortAction as exc:
            msg = str(exc)
            if msg:
                self.log.info(msg)
            return 0
        except ex.AlreadyDone as exc:
            # so do_svcs_action() can decide not to wait for the
            # service to reach the global_expect
            return -1
        self.allow_on_this_node(action)
        try:
            return self._action(action, options=options)
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.Error(str(exc))

    def barrier_sanity_check(self, barrier):
        """
        Raise if the barrier (--upto <barrier> or --downto <barrier>) does not
        match any resource, to avoid a full start when the user makes a typo
        in the barrier selector.
        """
        if barrier is None:
            return
        if self.get_resource(barrier):
            return
        if self.get_resources(barrier):
            return
        raise ex.Error("barrier '%s' does not match any resource" % barrier)

    @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.
        """
        self.barrier_sanity_check(self.options.upto)
        self.barrier_sanity_check(self.options.downto)

        try:
            self.action_rid_before_depends = self.options_to_rids(options, action)
        except ex.AbortAction 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)

        self.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 |= depends

        self.action_rid = list(self.action_rid)
        self.action_rid_depends = list(depends)
        self.action_start_date = datetime.datetime.now()

        if self.node is None:
            self.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("oci") and \
                not action.startswith("docker") and \
                not action.startswith("podman"):
            #
            # 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_last()

        self.setup_signal_handlers()
        self.set_skip_resources(keeprid=self.action_rid, xtags=options.xtags)
        if action in ("status", "decode", "pg_pids", "pg_stats") or \
           action.startswith("print_") or \
           action.startswith("collector") or \
           action.startswith("json_"):
            return self.do_print_action(action, options)

        def is_logged_action():
            if self.options.cron and not self.node.oget("node", "dblogcron"):
                return False
            if self.node.oget("node", "dblog") and self.node.collector_env.dbopensvc and self.node.collector_env.uuid:
                return True
            return False

        if self.published_action(action, options):
            if is_logged_action():
                err = self.do_logged_action(action, options)
            else:
                self.log_action_header(action, options)
                err = self.do_action(action, options)
        else:
            err = self.do_action(action, options)

        return err

    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
        """
        try:
            os.unlink(self.status_data_dump)
        except Exception:
            pass

    def purge_status_last(self):
        """
        Purge all service resources on-disk status caches.
        """
        import glob
        for fpath in glob.glob(os.path.join(self.var_d, "*#*", "status.last")):
            try:
                os.unlink(fpath)
            except:
                pass

    def published_action(self, action, options):
        if self.volatile:
            return False
        if not os.path.exists(self.paths.cf):
            return False
        if action in ACTIONS_NO_LOG or \
           action.startswith("compliance") or \
           action.startswith("oci") or \
           action.startswith("docker") or \
           action.startswith("podman") or \
           options.dry_run:
            return False
        return True

    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"

        try:
            if action.startswith("collector_"):
                from core.collector.actions import CollectorActions
                collector = CollectorActions(options, self.node, self.path)
                func = getattr(collector, action)
            else:
                func = getattr(self, action)
        except AttributeError:
            raise ex.Error("%s is not implemented" % action)

        if not hasattr(func, "__call__"):
            raise ex.Error("%s is not callable" % action)

        try:
            data = func()
        except Exception as exc:
            data = {"error": str(exc)}

        return data

    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.Error("invalid cluster type '%s'. allowed: %s" % (
                self.topology,
                ', '.join(TOPOLOGIES),
            ))

        err = 0
        waitlock = convert_duration(options.waitlock)
        if waitlock is None or waitlock < 0:
            waitlock = self.lock_timeout

        if action == "sync_all" and self.command_is_scoped():
            for rid in self.action_rid:
                resource = self.get_resource(rid)  # pylint: disable=assignment-from-none
                if not resource or not resource.type.startswith("sync"):
                    continue
                try:
                    resource.reslock(action=action, suffix="sync")
                except ex.Error as exc:
                    self.log.error(str(exc))
                    return 1
        else:
            try:
                self.svclock(action, timeout=waitlock)
            except ex.Error as exc:
                self.log.error(str(exc))
                return 1

        def call_action(action):
            restore_environ = self.setup_environ(action=action, options=options)
            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)
            restore_environ()
            return err

        try:
            if action.startswith("compliance_"):
                err = getattr(self.compliance, action)()
            elif hasattr(self, action):
                self.running_action = action
                self.notify_action(action, force=options.notify)
                err = call_action(action)
                if err is None:
                    err = 0
            else:
                self.log.info("action '%s' is not applicable to '%s' kind objects", action, self.kind)
                err = 0
        except ex.EndAction as exc:
            self.log.info(exc)
            err = 0
        except ex.AbortAction 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.Error 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.Signal:
            self.log.error("interrupted by signal")
            err = 1
        except:
            err = 1
            self.save_exc()
        finally:
            if action in ACTIONS_CF_CHANGE:
                self.unset_conf_lazy()
                self.reset_resources()
                self.init_resources()
            if not want_context() and \
               action not in ACTIONS_NO_STATUS_CHANGE and \
               not (action == "delete" and not self.command_is_scoped()):
                data = self.print_status_data(refresh=True)
                if action == "start" and not self.command_is_scoped() and \
                   err == 0 and data.get("avail") not in ("up", "stdby up", "n/a", None) and \
                   not self.options.dry_run:
                    # catch drivers reporting no error, but instance not
                    # evaluating as "up", to avoid the daemon entering a
                    # start loop. This also catches resources going down
                    # a short time a startup (app.simple for example)
                    self.log.error("start action returned 0 but instance "
                                   "avail status is %s", data.get("avail"))
                    err = 1
            if action != "toc" or self.monitor_action in ("freezestop", "switch"):
                self.clear_action(action, err, force=options.notify)
            if action not in ("sync_all", "run"):
                # sync_all and run handle notfications at the resource level
                self.notify_done(action)
            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()
            self.running_action = None

        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 action_need_freeze_instance(self, action):
        if self.orchestrate not in ("ha", "start"):
            return False
        if self.command_is_scoped():
            return False
        if action not in ("stop", "shutdown", "unprovision", "delete", "rollback"):
            return False
        return True

    def action_need_unset_local_expect(self, action):
        if self.command_is_scoped():
            return False
        if action not in ("stop", "shutdown", "unprovision", "delete", "rollback", "toc"):
            return False
        return True

    def notify_action(self, action, force=False):
        if not force and os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            return
        if self.options.dry_run:
            return
        progress = self.action_progress(action)
        if progress is None:
            return
        local_expect = None
        if self.action_need_unset_local_expect(action):
            local_expect = "unset"
        if self.action_need_freeze_instance(action):
            self.freezer.freeze()
        try:
            self.set_service_monitor(local_expect=local_expect, status=progress, best_effort=True)
            self.log.debug("daemon notified of action '%s' begin" % action)
        except Exception as exc:
            pass

    def clear_action(self, action, err, force=False):
        if not force and os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            return
        progress = self.action_progress(action)
        local_expect = None
        if progress is None:
            return
        if progress == "tocing" and self.monitor_action == "switch":
            return
        if err:
            status = action + " failed"
        else:
            status = "idle"
            if action == "start" and not self.command_is_scoped():
                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:
            pass

    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 (r.rollback_even_if_standby or not r.is_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.Error:
            self.log.error("rollback %s failed", action)

    def push_begin_action(self, action, argv, begin):
        if self.node.oc3_version() >= Semver(1, 0, 11):
            rfc_time = RFC3339()
            api_verb = "POST"
            api_path = oc3path.FEED_INSTANCE_ACTION
            headers = {"Accept": "application/json", "Content-Type": "application/json"}
            self.log.debug("%s %s", api_verb, api_path)
            try:
                data = {
                    "path": self.path,
                    "action": action,
                    "argv": argv,
                    "begin": rfc_time.from_epoch(begin),
                    "cron": self.options.cron,
                    "session_uuid": Env.session_uuid,
                    "version": self.node.agent_version,
                }
                status_code, response_data = self.node.oc3_request_feed(api_verb, api_path, data=data, headers=headers,
                                                                        timeout=1)
                if status_code == 202:
                    self.log.debug("%s %s accepted", api_verb, api_path)
                else:
                    self.node.oc3_assert_status_code(api_verb, api_path, status_code, response_data, expected=[202])
            except Exception:
                # Ignore the error and continue, push_end_action may succeed
                pass
        else:
            self.node.daemon_collector_xmlrpc("begin_action", self.path,
                                              action, self.node.agent_version,
                                              begin, self.options.cron, Env.session_uuid,
                                              argv)

    def push_end_action(self, action, argv, begin, end, err, logfile):
        """
        Send to the collector the service status after an action, and
        the action log.
        """
        if self.node.oc3_version() >= Semver(1, 0, 11):
            api_verb = "PUT"
            api_path = oc3path.FEED_INSTANCE_ACTION
            headers = {"Accept": "application/json", "Content-Type": "application/json"}
            self.log.debug("%s %s", api_verb, api_path)
            data = {}
            rfc_time = RFC3339()
            try:
                status = "ok"
                if err != 0:
                    status = "err"

                log_contents = ""
                try:
                    with open(logfile, "r") as file:
                        log_contents = file.read()
                except:
                    pass
                finally:
                    try:
                        os.unlink(logfile)
                    except:
                        pass

                data = {
                    "path": self.path,
                    "action": action,
                    "argv": argv,
                    "begin": rfc_time.from_epoch(begin),
                    "end": rfc_time.from_epoch(end),
                    "status": status,
                    "status_log": log_contents,
                    "cron": self.options.cron,
                    "session_uuid": Env.session_uuid,
                    "version": self.node.agent_version,
                }
                status_code, response_data = self.node.oc3_request_feed(api_verb, api_path, data=data, headers=headers,
                                                                        timeout=1)
                if status_code == 202:
                    self.log.debug("%s %s accepted", api_verb, api_path)
                elif status_code == 400:
                    self.log.debug("%s %s bad request ignored, no replay", api_verb, api_path)
                else:
                    self.node.oc3_assert_status_code(api_verb, api_path, status_code, response_data, expected=[202, 400])
            except Exception as exc:
                self.log.debug(
                    "%s %s unexpected error: %s" % (api_verb, api_path, str(exc)))

                # perhaps oc3 is not available yet, prepare a oc3 replay_file
                replay_file = None
                replay_file_need_close = False
                try:
                    import glob
                    max_replay = 100
                    replay_dir = os.path.join(Env.paths.pathtmpv, "oc3_replay")
                    os.makedirs(replay_dir, exist_ok=True)
                    replay_file_need_close = True
                    replay_file = tempfile.NamedTemporaryFile(mode='w+', delete=False, dir=replay_dir, suffix=".tmp",
                                                              prefix="oc3_feed_instance_action_%s.%s."% (self.name, action))
                    replay_files = sorted(glob.glob(os.path.join(replay_dir, "*")), key=lambda x: os.stat(x).st_mtime)
                    if len(replay_files) > max_replay:
                        try:
                            os.unlink(replay_files[0])
                        except:
                            # perhaps already removed by another process
                            pass
                    json.dump(data, replay_file)
                    self.log.debug("created retry oc3 file: %s", replay_file.name)
                    replay_file.close()
                    replay_file_need_close = False
                    replay_file_json = replay_file.name[:-4] + ".json"
                    os.rename(replay_file.name, replay_file_json)
                    replay_file = None
                except Exception as exc:
                    self.log.debug("created retry oc3 failed: %s", exc)
                    if replay_file:
                        if replay_file_need_close:
                            try:
                                replay_file.close()
                            except:
                                pass
                        try: os.unlink(replay_file.name)
                        except: pass

        else:
            try:
                self.node.daemon_collector_xmlrpc("end_action", self.path, action,
                                                  begin, end, self.options.cron, Env.session_uuid,
                                                  logfile, err)
            except Exception as exc:
                self.log.warning("failed to send logs to the collector: %s", exc)

        try:
            logging.shutdown()
        except:
            pass

    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.
        """
        begin = time.time()

        # Provision a database entry to store action log later
        try:
            argv = sys.argv[1:]
            if has_option("--value", argv):
                drop_option("--value", argv, drop_value=True)
                argv.append("--value=xxx")
            self.push_begin_action(action, argv, begin)
        except Exception as exc:
            self.log.warning("failed to init logs on the collector: %s", exc)
            self.log_action_header(action, options)
            return self.do_action(action, options)

        # Per action logfile to push to database at the end of the action
        tmpfile = tempfile.NamedTemporaryFile(delete=False, dir=Env.paths.pathtmp,
                                              prefix=self.name+'.'+action)
        actionlogfile = tmpfile.name
        tmpfile.close()
        if self.node.oc3_version() >= Semver(1, 0, 11):
            fmt = "%(asctime)s %(levelname)s [%(process)d] %(message)s"
            actionlogformatter = RFC3339Formatter(fmt)
        else:
            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.logger.addHandler(actionlogfilehandler)

        self.log_action_header(action, options)
        err = self.do_action(action, options)

        # Push result and logs to database
        actionlogfilehandler.close()
        self.logger.removeHandler(actionlogfilehandler)
        end = time.time()
        self.push_end_action(action, argv, begin, end, err, actionlogfile)
        return err

    def log_action_obfuscate_secret(self, options):
        data = {}
        data.update(options)
        for k in ("svcs", "parm_svcs", "namespace"):
            try:
                del data[k]
            except KeyError:
                pass
        if self.kind not in ("usr", "sec"):
            return data
        for k, v in data.items():
            if k == "value" and data[k]:
                data["value"] = "xxx"
        return data

    def log_action_header(self, action, options):
        from utilities.render.command import format_command
        origin = os.environ.get("OSVC_ACTION_ORIGIN", "user")
        data = self.log_action_obfuscate_secret(options)
        cmd = format_command(self.kind, action, data)
        buff = "do %s (%s origin)" % (" ".join(cmd), origin)
        buff = buff.replace("%", "%%")
        self.log.info(buff, {"f_stream": False})

    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.Error("invalid json in resource definition: "
                                   "%s" % options.resource[idx])

        self.options.update(options)
        options = self.options

        return options

    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):
        """
        For encap commands
        """
        if "__main__" in sys.argv[0] or sys.argv[0] == "om":
            # skip selector or subsystem name too
            cmd = sys.argv[2:]
        else:
            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 prepare_async_options(self):
        """
        For jsonrpc commands
        """
        options = {}
        options.update(self.options)
        for opt in ("svcs", "node", "local"):
            if opt in options:
                del options[opt]
        return options

    def is_remote_action(self, action):
        if want_context() and (self.options.node or self.command_is_scoped() or action not in ACTION_ASYNC):
            return True
        if self.options.node is not None and self.options.node != "":
            return True
        if action in ACTION_ANY_NODE and not self.exists():
            return True
        return False

    def async_action(self, action, wait=None, timeout=None):
        if action in ACTION_NO_ASYNC or self.kind in KIND_ACTION_NO_ASYNC:
            return
        if self.is_remote_action(action):
            options = self.prepare_async_options()
            ret = self.daemon_service_action(action=action, options=options, node=self.options.node, action_mode=False)
            if isinstance(ret, (dict, list)):
                return ret
            if ret == 0:
                raise ex.AbortAction()
            else:
                raise ex.Error()
        if self.options.local or self.options.slave or self.options.slaves or \
           self.options.master:
            return
        if action not in ACTION_ASYNC:
            return
        allowed_kinds = ACTION_ASYNC[action].get("kinds")
        if allowed_kinds is not None and self.kind not in allowed_kinds:
            self.log.info("ignore %s request: not supported on %s objects", action, self.kind)
            return
        if "target" not in ACTION_ASYNC[action]:
            return
        if self.command_is_scoped():
            return
        if self.options.dry_run:
            raise ex.AbortAction()
        self.daemon_mon_action(action, wait=wait, timeout=timeout)
        raise ex.AbortAction()

    def daemon_log_result(self, ret, raise_on_errors=True):
        if ret is None:
            return
        info = ret.get("info", [])
        if info is None:
            info = []
        elif not isinstance(info, list):
            info = [info]
        for line in info:
            if not line:
                continue
            self.log.info(line)

        errors = ret.get("error", [])
        if errors is None:
            errors = []
        if not isinstance(errors, list):
            errors = [errors]
        for line in errors:
            if not line:
                continue
            self.log.error(line)
        if errors:
            raise ex.Error
        status = ret.get("status")
        if status not in (None, 0):
            raise ex.Error

    def daemon_mon_action(self, action, wait=None, timeout=None):
        global_expect = self.prepare_global_expect(action)
        if global_expect is None:
            # not applicable action on this service
            return
        begin = time.time()
        data = self.set_service_monitor(global_expect=global_expect)
        if data:
            for line in data.get("error", []):
                self.log.error(line)
            for line in data.get("info", []):
                self.log.info(line)
                if " already " in line:
                    raise ex.AlreadyDone
            if data.get("error", []):
                raise ex.Error
        try:
            # the daemon may have changed and return global expect
            # (placed@<peer>)
            global_expect = data["data"]["global_expect"]
            # save for Node::do_svcs_action()
            self.last_global_expect = global_expect
        except KeyError:
            pass
        self.wait_daemon_mon_action(global_expect, wait=wait, timeout=timeout, begin=begin)

    def prepare_global_expect(self, action):
        global_expect = ACTION_ASYNC[action]["target"]
        if action == "delete" and self.options.unprovision:
            global_expect = "purged"
            action = "purge"
        elif action == "move":
            if self.options.to is None:
                raise ex.Error("the --to <node>[,<node>,...] option is required")
            global_expect += self.options.to
        elif action == "switch":
            dst = self.destination_node_sanity_checks()  # pylint: disable=assignment-from-none
            if dst is None:
                return
            global_expect += dst
        elif action == "takeover":
            dst = self.destination_node_sanity_checks(Env.nodename)  # pylint: disable=assignment-from-none
            if dst is None:
                return
            global_expect += dst
        return global_expect

    def wait_daemon_mon_action(self, global_expect, wait=None, timeout=None, log_progress=True, begin=None):
        if wait is None:
            wait = self.options.wait
        if not wait:
            return
        if timeout is None:
            timeout = self.options.time
        try:
            if global_expect == "frozen":
                self.node._wait(path="monitor.services.'%s'.frozen=frozen" % self.path, duration=timeout)
            elif global_expect == "thawed":
                self.node._wait(path="monitor.services.'%s'.frozen=thawed" % self.path, duration=timeout)
            elif global_expect == "purged":
                self.node._wait(path="!monitor.services.'%s'" % self.path, duration=timeout)
            elif global_expect == "deleted":
                self.node._wait(path="!monitor.services.'%s'" % self.path, duration=timeout)
            elif global_expect == "aborted":
                self.node._wait(path="!monitor.services.'%s'.global_expect" % self.path, duration=timeout)
            elif global_expect == "provisioned":
                self.node._wait(path="monitor.services.'%s'.provisioned=true" % self.path, duration=timeout)
            elif global_expect == "unprovisioned":
                self.node._wait(path="monitor.services.'%s'.provisioned=false" % self.path, duration=timeout)
            elif global_expect == "shutdown":
                self.node._wait(path="monitor.services.'%s'.avail~(down|n/a)" % self.path, duration=timeout)
            elif global_expect == "stopped":
                self.node._wait(path="monitor.services.'%s'.avail~(down|stdby up|n/a)" % self.path, duration=timeout)
            elif global_expect == "started":
                self.node._wait(path="monitor.services.'%s'.avail~(up|n/a)" % self.path, duration=timeout)
            elif global_expect == "placed":
                self.node._wait(path="monitor.services.'%s'.avail~(up|n/a)" % self.path, duration=timeout)
                self.node._wait(path="monitor.services.'%s'.placement=optimal" % self.path, duration=timeout)
            elif global_expect.startswith("placed@"):
                node = global_expect[7:]
                self.node._wait(path="monitor.nodes.'%s'.services.status.'%s'.avail~(up|n/a)" %
                                     (node, self.path), duration=timeout)
        except KeyboardInterrupt:
            raise ex.Error

    def current_node(self):
        data = self.node._daemon_status()
        if not data:
            raise ex.Error("can not migrate when daemon is down")
        for nodename, _data in data["monitor"]["nodes"].items():
            try:
                __data = _data["services"]["status"][self.path]
            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 or options.tags or options.subsets or options.upto or options.downto:
            return True
        return False

    def save_exc(self):
        """
        A helper method to save stacks in the service log.
        """
        self.log.error("a stack has been saved in the logs", exc_info=True)

    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):
        result = self._update(self.options.resource or [],
                              interactive=self.options.interactive,
                              provision=self.options.provision)
        buff = " "
        if result["created"]:
            buff += "created: %s" % ",".join(result["created"])
        if result["updated"]:
            if buff:
                buff += " "
            buff += "updated: %s" % ",".join(result["updated"])
        if buff:
            self.log.info("%s", buff)

    def _update(self, resources, interactive=False, provision=False):
        """
        The 'update' action entry point.
        Add resources to the service configuration, and provision them if
        instructed to do so.
        """

        result = {
            "created": [],
            "updated": [],
        }
        rtypes = {}
        for section in self.cd:
            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)

        import core.objects.builder

        rid = []

        for data in resources:
            is_resource = False
            if 'rid' in data:
                section = data['rid']
                if '#' not in section:
                    raise ex.Error("%s must be formatted as 'rtype#n'" % section)
                elements = section.split('#')
                if len(elements) != 2:
                    raise ex.Error("%s must be formatted as 'rtype#n'" % section)
                del data['rid']
                if section in self.cd:
                    self.cd[section].update(data)
                    result["updated"].append(section)
                else:
                    self.cd[section] = data
                    result["created"].append(section)
                is_resource = True
            elif 'rtype' in data and data["rtype"] == "env":
                del data["rtype"]
                if "env" in self.cd:
                    self.cd["env"].update(data)
                    result["updated"].append("env")
                else:
                    self.cd["env"] = data
                    result["created"].append("env")
            elif 'rtype' in data and data["rtype"] != "DEFAULT":
                section = self.allocate_rid(data['rtype'], self.cd)
                del data['rtype']
                self.cd[section] = data
                result["created"].append(section)
                is_resource = True
            else:
                if "rtype" in data:
                    del data["rtype"]
                if "DEFAULT" in self.cd:
                    self.cd["DEFAULT"].update(data)
                    result["updated"].append("DEFAULT")
                else:
                    self.cd["DEFAULT"] = data
                    result["created"].append("DEFAULT")

            if is_resource:
                rid.append(section)

        self.commit()

        for section in rid:
            group = section.split("#")[0]
            core.objects.builder.add_resource(self, group, section)

        if provision and len(rid) > 0:
            options = Storage(self.options)
            options.rid = rid
            self.action("provision", options)

        return result

    def allow_on_this_node(self, action):
        """
        Raise Error 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 want_context():
            return
        if action in ACTIONS_ALLOW_ON_INVALID_NODE:
            return
        if self.svc_env != 'PRD' and self.node.env == 'PRD':
            raise ex.Error('not allowed to run on this node (svc env=%s node env=%s)' % (self.svc_env, self.node.env))
        if Env.nodename in self.nodes:
            return
        if Env.nodename in self.drpnodes:
            return
        raise ex.Error("action '%s' aborted because this node's hostname "
                       "'%s' is not a member of DEFAULT.nodes, "
                       "DEFAULT.drpnode nor DEFAULT.drpnodes" %
                       (action, Env.nodename))

    def setup_environ(self, action=None, options=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.
        """
        if action in ACTIONS_NO_TRIGGER:
            return lambda:None
        if not action and os.environ.get("OPENSVC_SVCPATH") == self.path:
            return lambda:None
        prev_env = {}
        prev_env.update(os.environ)
        def restore():
            os.environ.clear()
            os.environ.update(prev_env)
        os.environ['OPENSVC_SVCPATH'] = self.path
        os.environ['OPENSVC_SVCNAME'] = self.name
        os.environ['OPENSVC_SVC_ID'] = self.id
        os.environ['OPENSVC_KIND'] = self.kind
        if self.namespace:
            os.environ['OPENSVC_NAMESPACE'] = self.namespace
        if action:
            os.environ['OPENSVC_ACTION'] = action
        if options and options.leader:
            os.environ['OPENSVC_LEADER'] = "1"
        else:
            os.environ['OPENSVC_LEADER'] = "0"
        for resource in self.get_resources():
            resource.setup_environ()
        return restore

    def print_config(self):
        """
        The 'print config' action entry point.
        Print the service configuration in the format specified by --format.
        """
        if want_context() or (not self.cd and not os.path.exists(self.paths.cf)):
            node, buff = self.remote_service_config(self.options.node)
            if buff is None:
                raise ex.Error("could not fetch remote config")
            try:
                tmpfile = tempfile.NamedTemporaryFile()
                fname = tmpfile.name
                tmpfile.close()
                with open(fname, "w") as tmpfile:
                    tmpfile.write(buff)
                svc = Svc(self.name, self.namespace, node=self.node, cf=fname, volatile=True)
                svc.options = self.options
                return svc._print_config()
            finally:
                try:
                    os.unlink(fname)
                except Exception:
                    pass
        return self._print_config()

    def _print_config(self):
        if self.options.format is not None or self.options.jsonpath_filter:
            return self.print_config_data(evaluate=self.options.eval,
                                          impersonate=self.options.impersonate)
        from utilities.render.color 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.
        """
        makedirs(os.path.dirname(self.paths.tmp_cf))
        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.Error
        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.
        """
        try:
            editor = find_editor()
        except ex.Error as error:
            print(error, file=sys.stderr)
            return 1
        from utilities.files import fsum
        if want_context() or not os.path.exists(self.paths.cf):
            node, refcf = self.remote_service_config_fetch()
            need_send = True
            tmpcf = refcf + ".tmp"
            shutil.copy2(refcf, tmpcf)
        else:
            refcf = self.paths.cf
            need_send = False
            tmpcf = self.make_temp_config()
            node = None
        os.system(' '.join((editor, tmpcf)))
        if fsum(tmpcf) == fsum(refcf):
            os.unlink(tmpcf)
            if refcf != self.paths.cf:
                os.unlink(refcf)
            return 0
        if need_send:
            try:
                return self.node.install_service(self.path, fpath=tmpcf,
                                                 restore=True, node=node)
            finally:
                os.unlink(refcf)
                os.unlink(tmpcf)
        else:
            results = self._validate_config(path=tmpcf)
            if results["errors"] == 0:
                try:
                    move_config_file(tmpcf, self.paths.cf)
                finally:
                    try:
                        os.unlink(tmpcf)
                    except Exception:
                        pass
            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"]

    #########################################################################
    #
    # daemon communications
    #
    #########################################################################
    def daemon_backlogs(self, server=None, node=None, backlog=None, sid=None, since=None, until=None, debug=False):
        req = {
            "action": "object_backlogs",
            "options": {
                "path": self.path,
                "backlog": backlog,
                "sid": sid,
                "since": since,
                "until": until,
                "debug": debug,
            }
        }
        result = self.daemon_get(req, server=server, node=node)
        if "nodes" in result:
            lines = []
            for logs in result["nodes"].values():
                if not isinstance(logs, list):
                    # happens when no log is present on a peer or when peer
                    # is down
                    continue
                lines += logs
        else:
            lines = result
        try:
            return sorted(lines, key=lambda x: x.get("t", 0))
        except AttributeError:
            return []

    def daemon_logs(self, server=None, node=None, backlog=None, debug=None):
        req = {
            "action": "object_logs",
            "options": {
                "path": self.path,
                "debug": debug,
            }
        }
        for lines in self.daemon_stream(req, server=server, node=node):
            if lines is None:
                break
            for line in lines:
                yield line

    def abort(self):
        pass

    def clear(self):
        self.master_clear()
        self.slave_clear()

    @_slave_action
    def slave_clear(self):
        self.encap_cmd(["clear"], verbose=True)

    @_master_action
    def master_clear(self):
        self._clear(node=self.options.node)

    def _clear(self, server=None, node=None):
        if not node and not self.options.local:
            node = self.ordered_nodes
        req = {
            "action": "object_clear",
            "options": {
                "path": self.path,
            }
        }
        data = self.daemon_post(req, timeout=DEFAULT_DAEMON_TIMEOUT, server=server, node=node)
        status, error, info = self.parse_result(data)
        if info:
            print(info)
        if status:
            raise ex.Error(error)

    def notify_done(self, action, rids=None):
        if not self.options.cron:
            return
        if rids is None:
            rids = self.action_rid
        req = {
            "action": "run_done",
            "options": {
                "action": action,
                "path": self.path,
                "rids": rids,
            }
        }
        try:
            data = self.daemon_post(req, server=self.options.node, silent=True)
            if data and data["status"] != 0:
                if "error" in data:
                    self.log.warning("notify scheduler action is done failed: %s", data["error"])
                else:
                    self.log.warning("notify scheduler action is done failed")
        except Exception as exc:
            self.log.warning("notify scheduler action is done failed: %s", str(exc))

    def post_object_status(self, data):
        req = {
            "action": "object_status",
            "options": {
                "path": self.path,
                "data": data,
            }
        }
        try:
            data = self.daemon_post(
                req,
                server=self.options.server,
                node=self.options.node,
                silent=True,
                timeout=DEFAULT_DAEMON_TIMEOUT,
            )
            status, error, info = self.parse_result(data)
            if status and data.get("errno") != ECONNREFUSED:
                # ECONNREFUSED (ie daemon down)
                if error:
                    self.log.warning("post object status failed: %s", error)
                else:
                    self.log.warning("post object status failed")
        except Exception as exc:
            self.log.warning("post object status failed: %s", str(exc))

    def set_service_monitor(self, status=None, local_expect=None, global_expect=None, stonith=None, path=None,
                            best_effort=False):
        if path is None:
            path = self.path
        if best_effort:
            log = self.log.warning
        else:
            log = self.log.error
        options = {
            "path": path,
            "status": status,
            "local_expect": local_expect,
            "global_expect": global_expect,
            "stonith": stonith,
        }
        try:
            data = self.daemon_post(
                {"action": "object_monitor", "options": options},
                server=self.options.server,
                node=self.options.node,
                silent=True,
                with_result=True,
            )
            status, error, info = self.parse_result(data)
            if info:
                for line in error.splitlines():
                    self.log.info(line)
            if status:
                # ECONNREFUSED (ie daemon down)
                if error and data.get("errno") != ECONNREFUSED:
                    for line in error.splitlines():
                        log(line)
                if not best_effort:
                    raise ex.Error
            return data
        except ex.Error:
            raise
        except Exception as exc:
            log("set monitor status failed: %s", str(exc))
            if not best_effort:
                raise

    def remote_service_config_fetch(self, nodename=None):
        node, buff = self.remote_service_config(nodename=nodename)
        if not buff:
            raise ex.Error
        tmpfile = tempfile.NamedTemporaryFile()
        fname = tmpfile.name
        tmpfile.close()
        with open(fname, "w") as tmpfile:
            tmpfile.write(buff)
        return node, fname

    def remote_service_config(self, nodename=None):
        req = {
            "action": "object_config",
            "options": {
                "path": self.path,
            }
        }
        node = nodename if nodename else "ANY"
        data = self.daemon_get(req, server=self.options.server, node=node, silent=True)
        if not data or data.get("status", 1) != 0:
            try:
                err = data.get("error", "")
            except Exception:
                err = ""
            raise ex.Error(err)
        if "nodes" in data:
            for node in data["nodes"]:
                break
            try:
                return node, data["nodes"][node]["data"]
            except Exception:
                return None, None
        try:
            return None, data["data"]
        except Exception:
            return None, None

    def daemon_service_action(self, action=None, options=None, server=None, node=None, sync=True, timeout=None,
                              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 options is None:
            options = {}
        req = {
            "action": "object_action",
            "options": {
                "path": self.path,
                "sync": sync,
                "action": action,
                "options": options,
            }
        }
        if not node and action in ACTION_ANY_NODE:
            node = "ANY"
        display_node = node if node else server
        if action_mode:
            self.log.info("request action '%s' on node %s", action, display_node)
        try:
            data = self.daemon_post(
                req,
                server=server,
                silent=True,
                timeout=timeout,
                node=node,
            )
        except Exception as exc:
            self.log.error("request action '%s' on node %s failed: %s",
                           action, display_node, exc)
            return 1
        status, error, info = self.parse_result(data)
        if error:
            self.log.error(error)

        def print_node_data(nodename, 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)

        if collect:
            if "data" not in data:
                return 0
            data = data["data"]
            return data["ret"], data.get("out", ""), data.get("err", "")
        else:
            if data is None:
                return 1
            if "nodes" in data:
                if self.options.format in ("json", "flat_json"):
                    if len(data["nodes"]) == 1:
                        for _data in data["nodes"].values():
                            return _data
                    return data
                else:
                    ret = 0
                    for n, _data in data["nodes"].items():
                        status = _data.get("status", 0)
                        _data = _data.get("data", {})
                        print_node_data(n, _data)
                        ret += _data.get("ret", 0) + status
                    return ret
            else:
                if "data" not in data:
                    return 0
                data = data["data"]
                print_node_data(server, data)
                return data.get("ret", 0)

    def logs(self):
        node = "*"
        if self.options.local:
            node = None
        elif self.options.node:
            node = self.options.node
        nodes = self.node.nodes_selector(node)
        auto = sorted(nodes, reverse=True)
        self._backlogs(server=self.options.server, node=node,
                       backlog=self.options.backlog, sid=self.options.sid,
                       since=self.options.since, until=self.options.until,
                       debug=self.options.debug, auto=auto)
        if not self.options.follow:
            return
        try:
            self._followlogs(server=self.options.server, node=node,
                             debug=self.options.debug, auto=auto)
        except ex.Signal:
            return
        except (OSError, IOError) as exc:
            if exc.errno == 32:
                # broken pipe
                return

    def _backlogs(self, server=None, node=None, backlog=None, sid=None, since=None, until=None, debug=False, auto=None):
        from utilities.render.color import colorize_log_line
        lines = []
        for line in self.daemon_backlogs(server, node, backlog, sid, since, until, debug):
            try:
                line = colorize_log_line(line, auto=auto)
            except Exception as exc:
                print(exc, file=sys.stderr)
            if line:
                try:
                    print(line)
                    sys.stdout.flush()
                except BrokenPipeError:
                    return

    def _followlogs(self, server=None, node=None, debug=False, auto=None):
        from utilities.render.color import colorize_log_line
        lines = []
        for line in self.daemon_logs(server, node, debug):
            line = colorize_log_line(line, auto=auto)
            if line:
                try:
                    print(line)
                    sys.stdout.flush()
                except BrokenPipeError:
                    return

    def support(self):
        """
        Send a tarball to the OpenSVC support upload site.
        """
        if self.node.sysreport_mod is None:
            return

        todo = [
          ('INC', os.path.join(Env.paths.pathlog, "node.log")),
          ('INC', os.path.join(Env.paths.pathlog, "node.scheduler.log")),
          ('INC', os.path.join(Env.paths.pathlog, "xmlrpc.log")),
          ('INC', os.path.join(self.log_d, self.name+".log")),
          ('INC', self.var_d),
        ]

        collect_d = os.path.join(Env.paths.pathvar, "support")
        try:
            shutil.rmtree(collect_d)
        except:
            pass
        srep = self.node.sysreport_mod.SysReport(node=self.node, collect_d=collect_d, compress=True)
        srep.todo += todo
        srep.collect()
        tmpf = srep.archive(srep.full)

        try:
            import requests
        except:
            print("uploading the support tarball requires the requests module", file=sys.stderr)
            return 1
        print("uploading the support tarball")
        content_size = os.stat(tmpf).st_size
        import base64
        with open(tmpf, 'rb') as filep:
            files = {"upload": filep}
            headers = {
                'Content-Type': 'application/octet-stream',
                'Content-Filename': "%s_%s_at_%s.tar.gz" %
                                    (self.namespace if self.namespace else "", self.name, Env.nodename),
                'Content-length': '%d' % content_size,
                'Content-Range': 'bytes 0-%d/%d' % (content_size-1, content_size),
                'Maxlife-Unit': "DAYS",
                'Maxlife-Value': "7",
            }
            resp = requests.post("https://sfx.opensvc.com/apis/rest/items",
                                 headers=headers,
                                 data=base64.b64encode(filep.read(content_size)),
                                 auth=("user", "support"))
        loc = resp.headers.get("Content-Location")
        if not loc:
            print(json.dumps(resp.content, indent=4), file=sys.stderr)
            return 1
        print("uploaded as https://sfx.opensvc.com%s" % loc)

    def skip_config_section(self, rid):
        if rid == "DEFAULT":
            return False
        self.init_resources()
        if self.encap and rid not in self.resources_by_id:
            return True
        if not self.encap and rid in self.encap_resources:
            return True
        return False

    def exists(self):
        """
        Return True if the service exists, ie has a configuration file on the
        local node.
        """
        return os.path.exists(self.paths.cf)

    def set_lazy(self, prop, val):
        """
        Expose the set_lazy(self, ...) utility function as a method,
        so Svc() users don't have to import it from utilities.
        """
        set_lazy(self, prop, val)

    def unset_lazy(self, prop):
        """
        Expose the unset_lazy(self, ...) utility function as a method,
        so Svc() users don't have to import it from utilities.
        """
        unset_lazy(self, prop)

    def unset_conf_lazy(self):
        self.clear_ref_cache()
        self.init_nodes()
        self.unset_lazy("cd")
        self.unset_lazy("nodes")
        self.unset_lazy("ordered_nodes")
        self.unset_lazy("peers")
        self.unset_lazy("ordered_peers")
        self.unset_lazy("flex")
        self.unset_lazy("flex_max")
        self.unset_lazy("flex_target")

    def unset_all_lazy(self):
        self.clear_ref_cache()
        self.init_nodes()
        if self.volatile:
            exclude = ["cd"]
        else:
            exclude = []
        unset_all_lazy(self, exclude=exclude)
        for res in self.resources_by_id.values():
            unset_all_lazy(res)

    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 availstatus(self):
        data = self.print_status_data(mon_data=False, refresh=False)
        return core.status.Status(data.get("avail", "n/a")).value()

    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 core.status.Status(data.get("overall", "n/a")).value()

    @fcache
    def get_mon_data(self):
        paths = [self.path]
        paths += [resolve_path(p.split("@")[0], namespace=self.namespace)
                  for p in self.parents+self.children_and_slaves]
        selector = ",".join(paths)
        data = self.node._daemon_status(silent=True, selector=selector)
        if data is not None and "monitor" in data:
            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.path]
            data["instances"] = {}
            for nodename in mon_data["nodes"]:
                try:
                    data["instances"][nodename] = mon_data["nodes"][nodename]["services"]["status"][self.path]["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 load_status_json(self):
        """
        Return a structure containing hierarchical status of
        the service and monitor information. Fetch CRM status from cache if
        possible and allowed by kwargs.
        """
        if self.status_data_dump_outdated():
            return
        try:
            with open(self.status_data_dump, 'r') as filep:
                data = json.load(filep)
        except (OSError, ValueError):
            return
        running = self.get_running(data.get("resources", {}).keys())
        if running:
            data["running"] = running
        return data

    def locking_status_data_eval(self, refresh=False):
        waitlock = convert_duration(self.options.waitlock)
        if waitlock is None or waitlock < 0:
            waitlock = self.lock_timeout
        # use a different lock to not block the faster "from cache" codepath
        lockfile = os.path.join(self.var_d, "lock.status")
        try:
            with utilities.lock.cmlock(timeout=waitlock, delay=1, lockfile=lockfile, intent="status"):
                return self.print_status_data_eval(refresh=refresh)
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.AbortAction(str(exc))

    def status_smon_data(self, mon_data=False):
        if not mon_data:
            return {}
        mon_data = self.get_smon_data()
        data = {"cluster": {}}
        try:
            data["cluster"]["compat"] = mon_data["compat"]
        except:
            pass
        try:
            data["cluster"]["avail"] = mon_data["service"]["avail"]
        except:
            pass
        try:
            data["cluster"]["overall"] = mon_data["service"]["overall"]
        except:
            pass
        try:
            data["cluster"]["placement"] = mon_data["service"]["placement"]
        except:
            pass
        try:
            data["monitor"] = mon_data["instances"][Env.nodename]
        except:
            pass
        return data

    def print_status_data(self, from_resource_status_cache=False, mon_data=False, refresh=False):
        if from_resource_status_cache or refresh:
            data = None
        else:
            data = self.load_status_json()
        if data is None:
            data = self.locking_status_data_eval(refresh=refresh)
        data.update(self.status_smon_data(mon_data=mon_data))
        return data

    def print_status_data_eval(self, refresh=False, write_data=True, clear_rstatus=False):
        """
        Return a structure containing hierarchical status of
        the service.
        """
        now = time.time()
        data = {
            "updated": now,
            "kind": self.kind,
        }
        if write_data:
            self.write_status_data(data)
        return data

    def csum_status_data(self, data):
        """
        This checksum is used by the collector thread to detect changes
        requiring a collector update.
        """
        h = hashlib.md5()

        def fn(h, val):
            if type(val) == dict:
                for key in sorted(val.keys()):
                    _val = val[key]
                    if key in ("status_updated", "updated", "mtime", "csum"):
                        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):
        if self.volatile:
            return
        data["csum"] = self.csum_status_data(data)
        fpath = None
        try:
            with tempfile.NamedTemporaryFile(mode="w", delete=False, dir=self.var_d, prefix='status.json.') as filep:
                fpath = filep.name
                json.dump(data, filep)
            os.utime(fpath, (-1, data["updated"]))
            shutil.move(fpath, self.status_data_dump)
            self.post_object_status(data)
        except Exception as exc:
            self.log.warning("failed to update %s: %s",
                             self.status_data_dump, str(exc))
        finally:
            if fpath:
                try:
                    os.unlink(fpath)
                except Exception:
                    pass
        return data

    def update_status_data(self):
        self.log.debug("update status dump")
        # print_status_data() with from_resource_status_cache=True does a status.json write
        return self.print_status_data(from_resource_status_cache=True)

    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_status(self):
        """
        Display in human-readable format the hierarchical service status.
        """
        if want_context():
            mon_data = self.get_mon_data()
            if self.options.node:
                nodename = self.options.node
            else:
                nodename = [n for n in mon_data.get("nodes", {})][0]
            data = mon_data.get("nodes", {}).get(nodename, {}).get("services", {}).get("status", {}).get(self.path, {})
            data["cluster"] = mon_data.get("services", {}).get(self.path, {})
            data["cluster"]["compat"] = mon_data.get("compat")
        elif self.options.node:
            nodename = self.options.node
            mon_data = self.get_mon_data()
            data = mon_data.get("nodes", {}).get(self.options.node, {}).get("services", {}).get("status", {}).get(self.path, {})
            data["cluster"] = mon_data.get("services", {}).get(self.path, {})
            data["cluster"]["compat"] = mon_data.get("compat")
        else:
            nodename = Env.nodename
            data = self.print_status_data(mon_data=True, refresh=self.options.refresh)

        if self.options.format is not None or self.options.jsonpath_filter:
            return data

        # 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

        from utilities.render.instance import format_instance
        mon_data = self.get_mon_data()
        format_instance(self.path, data, mon_data=mon_data, discard_disabled=discard_disabled, nodename=nodename)

    def purge(self):
        # Set volatile value to True to prevent config file re-creation after deletion (call to self.id will
        # update/create config file when DEFAULT.id option is not found)
        self.volatile = True
        self.options.unprovision = True
        self.delete()

    def delete(self):
        """
        The 'delete' action entrypoint.
        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.
        """
        rids = self.action_rid
        if rids:
            self.delete_sections(rids)
        else:
            self.set_purge_collector_tag()
            self.delete_service_conf()
            self.delete_service_logs()

    def delete_service_logs(self):
        """
        Delete the service configuration logs
        """
        import glob
        patterns = [
            os.path.join(self.log_d, self.name+".log*"),
            os.path.join(self.log_d, self.name+".debug.log*"),
            os.path.join(self.log_d, '.'+self.name+".log*"),
            os.path.join(self.log_d, '.'+self.name+".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_sched(self):
        dpath = os.path.join(self.var_d, "scheduler")
        self.log.info("remove %s", dpath)
        try:
            shutil.rmtree(dpath)
        except OSError:
            # errno 39: not empty (racing with a writer)
            pass

    def purge_var_d(self):
        scoped = self.command_is_scoped()
        for res in self.get_resources():
            if scoped and res.skip:
                continue
            res.purge_var_d()

    def delete_service_conf(self):
        """
        Delete the service configuration files
        """
        dpaths = [
            self.paths.alt_initd,
            self.paths.initd,
            self.var_d,
        ]
        fpaths = [
            self.paths.cf,
            self.paths.initd,
        ]
        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)
                try:
                    shutil.rmtree(dpath)
                except OSError:
                    # errno 39: not empty (racing with a writer)
                    pass

    def set_purge_collector_tag(self):
        if not self.node.collector_env.dbopensvc:
            return
        if not self.options.purge_collector:
            return
        try:
            self._set_purge_collector_tag()
        except Exception as exc:
            self.log.warning(exc)

    def _set_purge_collector_tag(self):
        self.log.info("tag the service for purge on the collector")
        try:
            data = self.collector_rest_get("/services/self", {"props": "svc_id"})
            svc_id = data["data"][0]["svc_id"]
            data = self.collector_rest_get("/tags/@purge")
            if len(data["data"]) == 0:
                data = self.collector_rest_post("/tags", {"tag_name": "@purge"})
            data = self.collector_rest_post("/tags/services", {
                "svc_id": svc_id,
                "tag_id": data["data"][0]["tag_id"],
            })
            if "info" in data:
                self.log.info(data["info"])
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])

    def collector_rest_get(self, *args, **kwargs):
        kwargs["path"] = self.path
        return self.node.collector_rest_get(*args, **kwargs)

    def collector_rest_post(self, *args, **kwargs):
        kwargs["path"] = self.path
        return self.node.collector_rest_post(*args, **kwargs)

    def collector_rest_put(self, *args, **kwargs):
        kwargs["path"] = self.path
        return self.node.collector_rest_put(*args, **kwargs)

    def collector_rest_delete(self, *args, **kwargs):
        kwargs["path"] = self.path
        return self.node.collector_rest_delete(*args, **kwargs)

    def options_to_rids(self, options, action):
        rid = options.get("rid", [])
        if rid is None:
            rid = []
        elif is_string(rid):
            if rid:
                rid = rid.split(',')
            else:
                rid = []
        return set(rid)

    def set_skip_resources(self, *args, **kwargs):
        pass

    def init_resources(self):
        return 0

    def freeze(self):
        pass

    def thaw(self):
        pass

    def frozen(self):
        return 0

    def action_rid_dependencies(self, action, rid):
        return set()

    def get_running(self, *args, **kwargs):
        return []

    def get_resources(self, *args, **kwargs):
        return []

    def destination_node_sanity_checks(self, *args, **kwargs):
        return

    def encap_cmd(self, *args, **kwargs):
        pass

    def get_resource(self, *args, **kwargs):
        return

    def rollback(self):
        pass

    @lazy
    def parents(self):
        return []

    @lazy
    def children(self):
        return []

    @lazy
    def slaves(self):
        return []

    @lazy
    def children_and_slaves(self):
        return []

    @lazy
    def scale_target(self):
        pass

    def pg_stats(self):
        return {}

    @lazy
    def flex_min(self):
        return 0

    @lazy
    def flex_max(self):
        return 0

    @lazy
    def flex_target(self):
        return 0

    @lazy
    def flex_cpu_low_threshold(self):
        return 0

    @lazy
    def flex_cpu_high_threshold(self):
        return 0

    @lazy
    def comment(self):
        return ""

    @lazy
    def app(self):
        return

    @lazy
    def ha(self):
        return False

    def get_flex_primary(self):
        return ""

    def get_drp_flex_primary(self):
        return ""

    def postinstall(self, *args, **kwargs):
        pass

    def post_commit(self):
        self.unset_all_lazy()
        self.sched.reconfigure()

    def configure_scheduler(self, *args, **kwargs):
        pass

    def send_service_config_args(self):
        return Storage({
            "path": self.path,
            "path_cf": self.paths.cf,
            "cluster_id": self.node.cluster_id,
            "topology": self.topology,
            "flex_min": self.flex_min,
            "flex_max": self.flex_max,
            "flex_target": self.flex_target,
            "flex_cpu_low_threshold": self.flex_cpu_low_threshold,
            "flex_cpu_high_threshold": self.flex_cpu_high_threshold,
            "svc_env": self.svc_env,
            "nodes": self.ordered_nodes,
            "drpnode": self.drpnode,
            "drpnodes": self.ordered_drpnodes,
            "comment": self.comment,
            "app": self.app,
            "encap": self.encap,
            "ha": self.ha,
        })

    def has_monitored_resources(self):
        return False

    def oc3_object_config_body(self):
        try:
            with open(self.paths.cf, "rb") as file:
                file_content = file.read()
                raw_config = base64.b64encode(file_content).decode("utf-8")
                if raw_config == "":
                    raise ex.Error("empty config file")

                return {
                    "path": self.path,
                    "orchestrate": self.orchestrate,
                    "topology": self.topology,
                    "flex_min": self.flex_min,
                    "flex_max": self.flex_max,
                    "flex_target": self.flex_target,
                    "env": self.svc_env,
                    "scope": self.ordered_nodes,
                    "drpnode": self.drpnode,
                    "drpnodes": self.ordered_drpnodes,
                    "comment": self.comment,
                    "app": self.app,
                    # TODO: use real value for monitored_resource_count instead of 1
                    "monitored_resource_count": 1 if self.has_monitored_resources() else 0,
                    "encap": self.encap,
                    "raw_config": raw_config,
                }
        except Exception as err:
            raise ex.Error("prepare oc3 feed object body: %s" % str(err))


class Svc(PgMixin, BaseSvc):
    """
    The svc kind class.
    A service is a collection of resources.
    It exposes operations methods like provision, unprovision, stop, start,
    and sync.
    """
    kind = "svc"

    @lazy
    def kwstore(self):
        from .svcdict import KEYS
        return KEYS

    @lazy
    def full_kwstore(self):
        from .svcdict import KEYS, SECTIONS, DATA_SECTIONS
        from utilities.drivers import load_drivers
        load_drivers(SECTIONS + DATA_SECTIONS)
        return KEYS

    def load_driver(self, driver_group, driver_basename):
        try:
            driver_group, driver_basename = DRV_GRP_XLATE[driver_group]
        except KeyError:
            pass
        if driver_group in ("container", "task") and driver_basename == "oci":
            driver_basename = self.node.oci
        elif driver_group == "ip" and driver_basename == "docker":
            driver_basename = "netns"
        elif driver_group == "disk" and driver_basename == "lvm":
            driver_basename = "vg"
        elif driver_group == "disk" and driver_basename == "veritas":
            driver_basename = "vxdg"
        return driver_import("resource", driver_group, driver_basename)

    @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 placement(self):
        return self.oget("DEFAULT", "placement")

    @lazy
    def stonith(self):
        return self.oget("DEFAULT", "stonith")

    @lazy
    def comment(self):
        return self.oget("DEFAULT", "comment")

    @lazy
    def aws(self):
        return self.oget("DEFAULT", "aws")

    @lazy
    def disable_rollback(self):
        return not self.oget("DEFAULT", "rollback")

    @lazy
    def aws_profile(self):
        return self.oget("DEFAULT", "aws_profile")

    @lazy
    def show_disabled(self):
        return self.oget("DEFAULT", "show_disabled")

    @lazy
    def pre_monitor_action(self):
        return self.oget("DEFAULT", "pre_monitor_action")

    @lazy
    def monitor_action(self):
        return self.oget("DEFAULT", "monitor_action")

    @lazy
    def bwlimit(self):
        return self.oget("DEFAULT", "bwlimit")

    @lazy
    def encap(self):
        return Env.nodename in self.encapnodes

    @lazy
    def presnap_trigger(self):
        return self.oget("DEFAULT", "presnap_trigger")

    @lazy
    def postsnap_trigger(self):
        return self.oget("DEFAULT", "postsnap_trigger")

    @lazy
    def pool(self):
        return self.oget("DEFAULT", "pool")

    @lazy
    def size(self):
        return self.oget("DEFAULT", "size")

    @lazy
    def orchestrate(self):
        if self.encap:
            return "no"
        return self.oget("DEFAULT", "orchestrate")

    @lazy
    def topology(self):
        return self.oget("DEFAULT", "topology")

    @lazy
    def access(self):
        """
        Volume service property
        """
        return self.oget("DEFAULT", "access")

    @lazy
    def parents(self):
        parents = self.oget("DEFAULT", "parents")
        for i, parent in enumerate(parents):
            parents[i] = resolve_path(parent, self.namespace)
        return parents

    @lazy
    def children(self):
        children = self.oget('DEFAULT', "children")
        for i, child in enumerate(children):
            children[i] = resolve_path(child, self.namespace)
        return children

    @lazy
    def slaves(self):
        slaves = self.oget('DEFAULT', "slaves")
        for i, slave in enumerate(slaves):
            slaves[i] = resolve_path(slave, self.namespace)
        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):
        return self.oget('DEFAULT', "scaler_slave")

    @lazy
    def scale_target(self):
        val = self.oget("DEFAULT", "scale")
        if isinstance(val, int) and val < 0:
            val = 0
        return val

    def get_flex_primary(self):
        try:
            flex_primary = self.conf_get("DEFAULT", "flex_primary")
        except ex.OptNotFound as exc:
            if len(self.ordered_nodes) > 0:
                flex_primary = self.ordered_nodes[0]
            else:
                flex_primary = ""
        return flex_primary

    def get_drp_flex_primary(self):
        try:
            drp_flex_primary = self.conf_get("DEFAULT", "drp_flex_primary")
        except ex.OptNotFound as exc:
            if len(self.ordered_drpnodes) > 0:
                drp_flex_primary = self.ordered_drpnodes[0]
            else:
                drp_flex_primary = ""
        return drp_flex_primary

    @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.name
            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.Error:
            return True

    @lazy
    def hard_affinity(self):
        return self.oget("DEFAULT", "hard_affinity")

    @lazy
    def hard_anti_affinity(self):
        return self.oget("DEFAULT", "hard_anti_affinity")

    @lazy
    def soft_affinity(self):
        return self.oget("DEFAULT", "soft_affinity")

    @lazy
    def soft_anti_affinity(self):
        return self.oget("DEFAULT", "soft_anti_affinity")

    @lazy
    def flex_min(self):
        try:
            val = self.oget("DEFAULT", "flex_min")
            int(val)
        except (ValueError, TypeError, ex.OptNotFound):
            val = 0
        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(self):
        nb_nodes = len(self.peers)
        try:
            val = self.conf_get("DEFAULT", "flex_max")
            int(val)
        except (ValueError, TypeError, ex.OptNotFound):
            return nb_nodes
        if val > nb_nodes:
            val = nb_nodes
        if val < self.flex_min:
            val = self.flex_min
        return val

    @lazy
    def flex_target(self):
        try:
            val = self.conf_get("DEFAULT", "flex_target")
            return int(val)
        except (ValueError, TypeError, ex.OptNotFound):
            return self.flex_min

    @lazy
    def flex_cpu_low_threshold(self):
        val = self.oget("DEFAULT", "flex_cpu_low_threshold")
        if val < 0:
            return 0
        if val > 100:
            return 100
        return val

    @lazy
    def flex_cpu_high_threshold(self):
        val = self.oget("DEFAULT", "flex_cpu_high_threshold")
        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.
        """
        return self.oget("DEFAULT", "app")

    def __lt__(self, other):
        """
        Order by service name
        """
        return self.path < other.path

    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", "toc"):
            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", "toc"):
            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_file(self, path):
        path = os.path.dirname(path)
        return self.resource_handling_dir(path)

    def resource_handling_dir(self, path):
        mntpts = {}
        for res in self.get_resources(["fs", "volume"]):
            if not hasattr(res, "mount_point"):
                # fs.flag for ex. has no mount_point
                continue
            mntpts[res.mount_point] = res
        while True:
            if path in mntpts.keys():
                return mntpts[path]
            path = os.path.dirname(path)
            if path in [os.sep, ""]:
                # end loop: reach "/" or was not an absolute path and reached ""
                return None

    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(with_encap=True):
            if res.monitor and not res.is_disabled():
                return True
        return False

    def configure_scheduler(self, action=None):
        """
        Add resource-dependent tasks to the scheduler.
        Called by the @scheduler decorator if not already run once.
        Rearm with .reconfigure_scheduler()
        """
        def need_configure(action):
            if action in (None, "print_schedule"):
                return True
            if self.options.cron and action in ("push_resinfo",
                                                "compliance_auto",
                                                "run",
                                                "resource_monitor",
                                                "sync_all",
                                                "status"):
                return True
            return False

        if not need_configure(action):
            return

        monitor_schedule = self.oget('DEFAULT', 'monitor_schedule')

        self.sched.update({
            "compliance_auto": [SchedOpts(
                "DEFAULT",
                fname="last_comp_check",
                schedule_option="comp_schedule",
                req_collector=True,
            )],
            "push_resinfo": [SchedOpts(
                "DEFAULT",
                fname="last_push_resinfo",
                schedule_option="resinfo_schedule",
                req_collector=True,
            )],
        })
        if not self.encap:
            self.sched.update({
                "status": [SchedOpts(
                    "DEFAULT",
                    fname="last_status",
                    schedule_option="status_schedule"
                )]
            })
            if self.has_monitored_resources() or monitor_schedule is not None:
                self.sched.update({
                    "resource_monitor": [SchedOpts(
                        "DEFAULT",
                        fname="last_resource_monitor",
                        schedule_option="monitor_schedule"
                    )]
                })

        resource_schedules = {}
        for resource in self.get_resources():
            if resource.is_disabled():
                continue
            try:
                if resource.confirmation is True:
                    continue
            except AttributeError:
                pass
            sopts = resource.schedule_options()
            if not sopts:
                continue
            for saction, sopt in sopts.items():
                if saction not in resource_schedules:
                    resource_schedules[saction] = [sopt]
                else:
                    resource_schedules[saction] += [sopt]
        self.sched.update(resource_schedules)

    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
        return self.oget(subset_section, "parallel")

    def get_scsireserv(self, rid):
        """
        Get the 'scsireserv' config keyword value for rid.
        """
        return self.oget(rid, 'scsireserv')

    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 = self.load_driver("disk", "scsireserv")
        except ImportError:
            return

        kwargs = {}
        pr_rid = resource.rid+"pr"
        kwargs["prkey"] = self.oget(resource.rid, 'prkey')
        kwargs['no_preempt_abort'] = self.oget(resource.rid, 'no_preempt_abort')

        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['restart_delay'] = self.conf_get(pr_rid, "restart_delay")
        except ex.OptNotFound:
            kwargs['restart_delay'] = resource.restart_delay if hasattr(resource, "restart_delay") else 0

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

        kwargs['rid'] = resource.rid
        kwargs['peer_resource'] = resource

        r = sr.DiskScsireserv(**kwargs)
        self += r

    def add_requires(self, resource):
        actions = [
          'unprovision', 'provision',
          'stop', 'start',
        ]
        if resource.driver_group == "sync":
            actions += [
                'sync_nodes', 'sync_drp',
                'sync_resync', 'sync_break',
                'sync_update',
            ]
        if resource.driver_group == "task":
            actions += [
                'run',
            ]
        for action in actions:
            try:
                s = self.conf_get(resource.rid, action+'_requires')
            except ex.OptNotFound:
                continue
            except ValueError:
                # keyword not supported. data resources for example.
                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'):
            return self.__iadd_resourceset__(other)
        elif isinstance(other, Resource):
            return self.__iadd_resource__(other)
        else:
            return self

    def __iadd_resourceset__(self, other):
        """
        Svc += ResourceSet
        """
        other.svc = self
        if other.rid in self.resourcesets_by_id:
            self.resourcesets_by_id[other.rid] += other
        else:
            self.resourcesets_by_id[other.rid] = other
        return self

    def __iadd_resource__(self, other):
        """
        Svc += Resource
        """
        if not other.rid or "#" not in other.rid:
            self.log.error("__iadd_resource__ unexpected rid: %s", other)
            return self
        if other.rset_id in self.resourcesets_by_id:
            # the resource set already exists. add resource or resourceset.
            self.resourcesets_by_id[other.rset_id] += other
        else:
            parallel = self.get_subset_parallel(other.rset_id)
            rset = ResourceSet(other.rset_id, resources=[other], parallel=parallel)
            rset.svc = self
            rset.pg_settings = self.get_pg_settings("subset#"+other.rset_id)
            self += rset

        other.svc = self
        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():
            other.on_add()

        return self

    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 init_resources(self):
        if self.resources_initialized:
            return self.init_resources_errors
        self.resources_initialized = True
        if self.scale_target is not None:
            # scalers can't have resources
            return 0
        from core.objects.builder import add_resources
        self.init_resources_errors = add_resources(self)
        self.log.debug("resources initialized")
        return self.init_resources_errors

    def get_resource(self, rid, with_encap=False):
        """
        Return a resource object by id.
        Return None if the rid is not found.
        """
        self.init_resources()
        if rid in self.resources_by_id:
            return self.resources_by_id[rid]
        if with_encap and rid in self.encap_resources:
            return self.encap_resources[rid]
        return

    def get_resources(self, _type=None, discard_disabled=True, with_encap=False):
        """
        Return the list of resources matching criteria.

        <_type> can be:
          None: all resources are returned
        """
        self.init_resources()
        if with_encap:
            allresources = itertools.chain(self.resources_by_id.values(), self.encap_resources.values())
        else:
            allresources = self.resources_by_id.values()
        if _type is None:
            return allresources
        if not isinstance(_type, (list, tuple)):
            _types = [_type]
        else:
            _types = _type

        resources = []
        for resource in allresources:
            if not with_encap and 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.driver_group:
                    resources.append(resource)
        return resources

    def get_resourcesets(self, _type=None, strict=False):
        """
        Return the list of resourceset matching the specified types.
        """
        self.init_resources()
        if _type is None:
            _types = [res.type for res in self.resources_by_id.values()]
        elif 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 \
                    __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 \
                   Env.nodename in self.nodes:
                    return True
        return False

    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,
            * Error, 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 Error
            """
            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.Error:
                    raise
                except:
                    self.save_exc()
                    raise ex.Error
            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.Error(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.driver_group
            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.path
        for rset in self.resourcesets_by_id.values():
            output += "  [%s]" % str(rset)
        return output

    def prstatus(self):
        status = core.status.Status()
        for resource in self.get_resources("disk.scsireserv"):
            status += resource.status()
        return int(status)

    def get_running(self, rids=None):
        lockfile = os.path.join(self.var_d, "lock.generic")
        running = []
        running += [self._get_running(lockfile).get("rid")]

        # sync
        lockfile = os.path.join(self.var_d, "lock.sync")
        running += [self._get_running(lockfile).get("rid")]

        # tasks
        if rids is None:
            rids = [r.rid for r in self.get_resources("task")]
        else:
            rids = [rid for rid in rids if rid.startswith("task")]
        import utilities.runfiles
        for rid in rids:
            d = os.path.join(self.var_d, rid, "run")
            task_infos = utilities.runfiles.has_running(d)
            for info in task_infos:
                info["rid"] = rid
                running.append(info)
        return [info for info in running if info]

    def _get_running(self, lockfile):
        try:
            with open(lockfile, "r") as ofile:
                lock_data = json.load(ofile)
                return lock_data
        except Exception:
            pass
        return {}

    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:
            resource = self.get_resource(rid)
            if resource is None:
                print("resource %s not found" % rid)
                continue
            print(core.status.colorize_status(str(resource.status(refresh=self.options.refresh))))
        return 0

    def print_status_data_eval(self, refresh=False, write_data=True, clear_rstatus=False):
        """
        Return a structure containing hierarchical status of
        the service.
        """
        now = time.time()

        if clear_rstatus:
            # Clear resource status in-memory cache, so the value is loaded
            # from on-disk cache if refresh=False.
            # Used by the daemon which holds long lived Svc objects that can
            # have outdated in-mem caches.
            for res in self.get_resources():
                res.clear_status_cache()

        group_status = self.group_status(refresh=refresh)

        data = {
            "updated": now,
            "kind": self.kind,
            "app": self.app,
            "env": self.svc_env,
            "placement": self.placement,
            "topology": self.topology,
            "frozen": self.frozen(),
            "subsets": {},
            "resources": {},
        }
        if self.kind == "svc" and self.priority != Env.default_priority:
            data["priority"] = self.priority
        running = self.get_running()
        if running:
            data["running"] = running
        if self.topology == "flex":
            data.update({
                "flex_target": self.flex_target,
                "flex_min": self.flex_min,
                "flex_max": self.flex_max,
            })
        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
        if Env.nodename in self.drpnodes:
            data["drp"] = True
        if self.pool:
            data["pool"] = self.pool
        if self.size:
            data["size"] = self.size

        for sid, subset in self.resourcesets_by_id.items():
            if not subset.parallel:
                continue
            data["subsets"][sid] = {
                "parallel": subset.parallel,
            }

        containers = self.get_resources('container')
        if self.encap:
            data["encap"] = True
        elif len(containers) > 0 and self.has_encap_resources:
            data["encap"] = {}
            for container in containers:
                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"] += core.status.Status(data["encap"][container.rid]["overall"] if "overall" in data["encap"][container.rid] else "n/a")
                    group_status["avail"] += core.status.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

        prov_states = set()
        for rset in self.get_resourcesets(strict=True):
            for resource in rset.resources:
                status = core.status.Status(resource.status(verbose=True))
                log = resource.status_logs_strlist()
                info = resource.last_status_info # refreshed by resource.status() if necessary
                tags = sorted(list(resource.tags))
                disable = resource.is_disabled()
                _data = {
                    "status": str(status),
                    "type": resource.type,
                    "label": resource.label,
                }
                prov_data = resource.provisioned_data()
                if prov_data:
                    _data["provisioned"] = prov_data
                if disable:
                    _data["disable"] = disable
                if resource.is_standby:
                    _data["standby"] = resource.is_standby
                if resource.encap:
                    _data["encap"] = resource.encap
                if resource.optional:
                    _data["optional"] = resource.optional
                if resource.monitor:
                    _data["monitor"] = resource.monitor
                if resource.nb_restart:
                    _data["restart"] = resource.nb_restart
                if resource.stopped():
                    _data["stopped"] = True
                if len(log) > 0:
                    _data["log"] = log
                if len(info) > 0:
                    _data["info"] = info
                if len(tags) > 0:
                    _data["tags"] = tags
                if not disable and not resource.skip_unprovision and not resource.skip_provision:
                    prov_states.add(_data.get("provisioned", {}).get("state"))
                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 prov_states == set():
            pass
        elif prov_states <= set([True, None]):
            data["provisioned"] = True
        elif prov_states <= set([False, None]):
            data["provisioned"] = False
        else:
            data["provisioned"] = "mixed"
        if self.stonith and self.topology == "failover" and data["avail"] == "up":
            data["stonith"] = True
        if write_data:
            self.write_status_data(data)
        return data

    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.
        """
        restore_environ = self.setup_environ()
        rsets_status = {}
        for rset in self.get_resourcesets(groups):
            rsets_status[rset.rid] = rset.status(refresh=refresh)
        restore_environ()
        return rsets_status

    def need_encap_resource_monitor(self):
        for res in self.encap_resources.values():
            if res.monitor or res.restart:
                return True
        return False

    def resource_monitor(self):
        """
        The resource monitor action. Refresh important resources at a different
        schedule.
        """
        from utilities.journaled_data import JournaledData
        dataset = JournaledData(
            initial_data=self.print_status_data(),
            journal_head=[],
        )
        for resource in self.get_resources():
            if resource.monitor or resource.nb_restart:
                resource.status(refresh=True)
        if self.need_encap_resource_monitor():
            self.encap_cmd(["resource_monitor"])
        data = self.print_status_data_eval(write_data=False)
        dataset.set([], data)
        diff = dataset.pop_diff()
        significant_changes = [change for change in diff if change[0][-1] not in ("updated", "csum")]
        if significant_changes:
            self.log.debug("changes detected in monitored resources: %s", significant_changes)
            self.write_status_data(data)

    def reboot(self):
        """
        A method wrapper the node reboot method.
        """
        self.node.sys_reboot()

    def crash(self):
        """
        A method wrapper the node crash method.
        """
        self.node.sys_crash()

    @lazy
    def nscfgpath(self):
        return fmt_path("", self.namespace, "nscfg")

    def nscfg(self):
        return factory("nscfg")("", self.namespace, volatile=True, node=self.node)

    def ns_pg_update(self):
        nscfg = self.nscfg()
        if not nscfg:
            return
        nscfg.pg_update(children=False)

    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 instance")
            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)
        if self.monitor_action not in ("freezestop", "switch"):
            time.sleep(2)

        try:
            # Do our best to have most recent log sync on file system, node is going to crash of fast reboot
            if hasattr(os, "fsync"):
                with open(os.path.join(self.log_d, self.name+".log"), "a") as f:
                    f.flush()
                    os.fsync(f.fileno())
        except:
            pass
        getattr(self, self.monitor_action)()

    def encap_cmd(self, cmd, verbose=False, unjoinable="raise", 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=container, verbose=verbose)
            except ex.EncapUnjoinable:
                if unjoinable != "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))
            except ex.Error as exc:
                if error != "continue":
                    raise

    def _encap_cmd(self, cmd, container, verbose=False, push_config=True, fwd_options=True):
        """
        Execute a command in a service container.
        """
        if container.pg_frozen():
            raise ex.Error("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.Error:
                pass

        # wait for the container multi-user state
        if cmd[0] in ["start"] and hasattr(container, "wait_multi_user"):
            container.wait_multi_user()

        if fwd_options:
            options = []
            if self.options.dry_run:
                options.append('--dry-run')
                cmd = drop_option("--dry-run", cmd, drop_value=False)
            if self.options.restore:
                options.append("--restore")
                cmd = drop_option("--restore", cmd, drop_value=False)
            if self.options.force:
                options.append('--force')
                cmd = drop_option("--force", cmd, drop_value=False)
            if self.options.local and "status" not in cmd:
                options.append('--local')
                cmd = drop_option("--local", cmd, drop_value=False)
            if self.options.leader:
                options.append('--leader')
                cmd = drop_option("--leader", cmd, drop_value=False)
            if self.options.disable_rollback:
                options.append('--disable-rollback')
                cmd = drop_option("--disable-rollback", cmd, drop_value=False)
            if self.options.rid:
                options.append('--rid')
                options.append(self.options.rid if is_string(self.options.rid) else ",".join(self.options.rid))
                cmd = drop_option("--rid", cmd, drop_value=True)
            if self.options.tags:
                options.append('--tags')
                options.append(self.options.tags if is_string(self.options.tags) else ",".join(self.options.tags))
                cmd = drop_option("--tags", cmd, drop_value=True)
            if self.options.subsets:
                options.append('--subsets')
                options.append(self.options.subsets if is_string(self.options.subsets) else ",".join(self.options.subsets))
                cmd = drop_option("--subsets", cmd, drop_value=True)
        else:
            options = []

        cmd = drop_option("--slaves", cmd, drop_value=False)
        cmd = drop_option("--slave", cmd, drop_value=True)

        if self.options.namespace:
            options += ["--namespace", self.options.namespace]

        paths = Paths(osvc_root_path=container.osvc_root_path)
        cmd = [paths.om, "svc", "-s", self.path] + options + cmd
        if verbose:
            self.log.info(" ".join(cmd))

        cmd = ["env", "OSVC_DETACHED=1", "OSVC_ACTION_ORIGIN='master %s'" % os.environ.get("OSVC_ACTION_ORIGIN", "user")] + cmd
        if container is not None and hasattr(container, "rcmd") and callable(container.rcmd):
            try:
                out, err, ret = container.rcmd(cmd)
            except Exception as exc:
                self.log.error(exc)
                out, err, ret = "", "", 1
        elif hasattr(container, "runmethod"):
            cmd = container.runmethod + cmd
            out, err, ret = justcall(cmd, stdin=self.node.devnull)
        else:
            raise ex.EncapUnjoinable("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 == 127:
            # opensvc is not installed
            raise ex.EncapUnjoinable
        if ret == 255:
            raise ex.EncapUnjoinable
        if "resource_monitor" in cmd:
            try:
                self.encap_json_status(container, refresh=False, push_config=False, cache=False)
            except (ex.NotAvailable, ex.EncapUnjoinable, ex.Error):
                pass
        elif "print" not in cmd and "create" not in cmd:
            self.log.info("refresh encap json status after action")
            try:
                self.encap_json_status(container, refresh=True, push_config=push_config)
            except (ex.NotAvailable, ex.EncapUnjoinable, ex.Error):
                pass
        if ret != 0:
            raise ex.Error("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>.
        """
        if self.volatile:
            return
        self.encap_json_status_cache[rid] = data
        path = self.get_encap_json_status_path(rid)
        directory = os.path.dirname(path)
        makedirs(directory)
        try:
            with open(path, "w") as ofile:
                json.dump(data, ofile)
                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, push_config=True, cache=True):
        """
        Return the status data from the agent runnning the encapsulated part
        of the service.
        """
        if container.guestos == 'windows':
            raise ex.NotAvailable

        if container.status(ignore_nostatus=True, refresh=refresh) == core.status.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.driver_group
                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.driver_group
                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 and cache:
            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', '--color=no']
        if refresh:
            cmd.append('--refresh')
        try:
            results = self._encap_cmd(cmd, container, fwd_options=False, push_config=push_config)
        except ex.Error as exc:
            return group_status
        except Exception as exc:
            print(exc, file=sys.stderr)
            return group_status

        try:
            group_status = json.loads(results[0])
        except Exception:
            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(self.kwstore.sections)

        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] = core.status.Status(core.status.NA)
        for group in groups:
            status["status_group"][group] = core.status.Status(core.status.NA)

        for group in self.kwstore.sections:
            if group not in groups:
                continue
            for resource in self.get_resources(group):
                if resource.type in excluded_groups:
                    continue
                rstatus = resource.status()
                if resource.type.startswith("sync"):
                    if rstatus == core.status.UP:
                        rstatus = core.status.NA
                    elif rstatus == core.status.DOWN:
                        rstatus = core.status.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"] += core.status.WARN

        if status["avail"].status == core.status.STDBY_UP_WITH_UP:
            # now we know the avail status we can promote
            # stdbyup to up
            status["avail"].status = core.status.UP
            for group in status:
                if status[group] == core.status.STDBY_UP:
                    status[group].status = core.status.UP
        elif status["avail"].status == core.status.STDBY_UP_WITH_DOWN:
            status["avail"].status = core.status.STDBY_UP

        if status["optional"].status == core.status.STDBY_UP_WITH_UP:
            status["optional"].status = core.status.UP
        elif status["optional"].status == core.status.STDBY_UP_WITH_DOWN:
            status["optional"].status = core.status.STDBY_UP

        status["overall"] += core.status.Status(status["avail"])
        status["overall"] += core.status.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 or self.options.jsonpath_filter:
            return data
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node1 = tree.add_node()
        node1.add_column(self.path, 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)
        tree.out()

    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:
                try:
                    devs = sorted(list(getattr(resource, cat+"_devs")()))
                except Exception as exc:
                    import traceback
                    traceback.print_exc()
                    continue
                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 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", "podman"]))

    @_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.ns_pg_update()
        self.sub_set_action(START_GROUPS, "start", xtags=set(["zone", "docker", "podman"]))

    @_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", "podman"]))

    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", "podman"]))
        self.pg_remove()

    @_slave_action
    def slave_stop(self):
        self.encap_cmd(['stop'], verbose=True, unjoinable="continue")

    @_slave_action
    def slave_freezestop(self):
        self.encap_cmd(['stop'], verbose=True, unjoinable="continue", error="continue")

    def boot(self):
        self.options.force = True
        self.master_boot()

    @_master_action
    def master_boot(self):
        self.sub_set_action(START_GROUPS, "boot")

    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", "podman"]))
        self.pg_remove()

    @_slave_action
    def slave_shutdown(self):
        self.encap_cmd(['shutdown'], verbose=True, unjoinable="continue", error="continue")

    def unprovision(self):
        self.slave_unprovision()
        self.master_unprovision()

    @_master_action
    def master_unprovision(self):
        self.sub_set_action("disk.scsireserv", "stop", xtags=set(["zone", "docker", "podman"]))
        self.sub_set_action(STOP_GROUPS, "unprovision", xtags=set(["zone", "docker", "podman"]))
        if not self.command_is_scoped():
            self.pg_remove()
            self.delete_service_sched()
        self.purge_var_d()

    @_slave_action
    def slave_unprovision(self):
        cmd = self.prepare_async_cmd()
        self.encap_cmd(cmd, verbose=True)

    def provision(self):
        self.master_provision()
        self.slave_provision()

    @_master_action
    def master_provision(self):
        self.sub_set_action(START_GROUPS, "provision", xtags=set(["zone", "docker", "podman"]))

        if not self.options.disable_rollback and len(self.peers) > 1:
            # set by the daemon on the placement leaders.
            # return the service to standby if not a placement leader
            self.rollback()

    @_slave_action
    def slave_provision(self):
        cmd = self.prepare_async_cmd()
        self.encap_cmd(cmd, verbose=True)

    def set_provisioned(self):
        self.sub_set_action(START_GROUPS, "set_provisioned")

    def set_unprovisioned(self):
        self.sub_set_action(START_GROUPS, "set_unprovisioned")

    def abort_start(self):
        self.abort_start_drivers()
        self.abort_start_affinity()

    def abort_start_affinity(self):
        if self.running_action != "start":
            return
        if self.options.force or os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            # allow CRM actions originating from the daemon
            # allow CRM actions with --force
            return
        for path in self.hard_affinity:
            name, namespace, kind = split_path(path)
            svc = factory(kind)(name, namespace, volatile=True, node=self.node)
            s = svc.availstatus()
            if s not in (core.status.UP, core.status.NA):
                raise ex.Error("hard affinity with %s is not satisfied (currently %s). use --force if you really want to start" % (path, core.status.status_str(s)))
        for path in self.hard_anti_affinity:
            name, namespace, kind = split_path(path)
            svc = factory(kind)(name, namespace, volatile=True, node=self.node)
            s = svc.availstatus()
            if s not in (core.status.DOWN, core.status.STDBY_UP, core.status.STDBY_DOWN, core.status.NA):
                raise ex.Error("hard anti affinity with %s is not satisfied (currently %s). use --force if you really want to start" % (path, core.status.status_str(s)))

    def abort_start_drivers(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
        resources = [res for res in self.get_resources()
                     if not res.skip and not res.is_disabled() and hasattr(res, "abort_start")]
        if len(resources) < 2:
            parallel = False
        else:
            try:
                # noinspection PyUnresolvedReferences
                from multiprocessing import Process
                parallel = True
            except ImportError:
                parallel = False

        if not parallel:
            for resource in resources:
                if resource.abort_start():
                    raise ex.Error("start aborted due to resource %s "
                                   "conflict" % resource.rid)
        else:
            from multiprocessing import Process
            from utilities.process_title import set_process_title

            def wrapper(proc_title, func):
                set_process_title(proc_title)
                try:
                    if func():
                        sys.exit(1)
                except ex.Signal:
                    sys.exit(1)

            procs = {}
            for resource in resources:
                title = "om %s --rid %s check abort start" % (resource.svc.path, resource.rid)
                # noinspection PyUnboundLocalVariable
                proc = Process(target=wrapper, args=(title, resource.abort_start))
                proc.start()
                procs[resource.rid] = proc

            err = []
            for rid, proc in procs.items():
                proc.join()
                if proc.exitcode > 0:
                    err.append(rid)

            if len(err) > 0:
                raise ex.Error("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 presync(self):
        """ prepare files to send to slave nodes in var/.
            Each resource can prepare its own set of files.
        """
        if self.presync_done:
            return
        self.all_set_action("presync")
        self.presync_done = True

    def _sync_gen(self, method):
        self.sub_set_action(rtypes_with_callable(method), method)

    def sync_nodes(self):
        rtypes = rtypes_with_callable("sync_nodes")
        if not self.can_sync(rtypes, 'nodes'):
            return
        self.sub_set_action(rtypes, "sync_nodes")

    def sync_drp(self):
        rtypes = rtypes_with_callable("sync_drp")
        if not self.can_sync(rtypes, 'drpnodes'):
            return
        self.sub_set_action(rtypes, "sync_drp")

    def sync_swap(self):
        self._sync_gen("sync_swap")

    def sync_revert(self):
        self._sync_gen("sync_revert")

    def sync_resume(self):
        self._sync_gen("sync_resume")

    def sync_quiesce(self):
        self._sync_gen("sync_quiesce")

    def resync(self):
        self.stop()
        self.sync_resync()
        self.start()

    def sync_resync(self):
        self._sync_gen("sync_resync")

    def sync_break(self):
        self._sync_gen("sync_break")

    def sync_update(self):
        self._sync_gen("sync_update")

    def sync_full(self):
        self._sync_gen("sync_full")

    def sync_restore(self):
        self._sync_gen("sync_restore")

    def sync_split(self):
        self._sync_gen("sync_split")

    def sync_establish(self):
        self._sync_gen("sync_establish")

    def sync_verify(self):
        self._sync_gen("sync_verify")

    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.Error 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._sync_gen("sync_all")

    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 oc3_instance_resource_info_body(self):
        """
        Returns the body to POST oc3 instance resource info
        """
        data = {
            "path": self.path,
            "topology": self.topology,
        }
        info = []
        for res in self.get_resources():
            keys = []
            try:
                _data = res.info()
            except Exception as exc:
                _data = []
                import traceback
                traceback.print_exc()
            for __data in _data:
                keys.append({"key": __data[-2], "value": __data[-1]})
            if len(keys) > 0:
                info.append({"rid": res.rid, "keys": keys})

        if "env" in self.cd:
            keys = []
            for key in self.cd["env"]:
                try:
                    val = self.conf_get("env", key)
                except ex.OptNotFound as exc:
                    continue
                keys.append({"key": key if key is not None else "", "value": val if val is not None else ""})
            if len(keys) > 0:
                info.append({"rid": "env", "keys": keys})

        data["info"] = info
        return data

    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 = []
                import traceback
                traceback.print_exc()
            for __data in _data:
                rid = __data[-3]
                if rid not in data:
                    data[rid] = []
                data[rid].append(__data)
        if "env" not in self.cd:
            return data
        for key in self.cd["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.path,
                Env.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 and not self.options.jsonpath_filter:
            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 utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        node1 = tree.add_node()
        node1.add_column(self.path, 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])
        tree.out()

    def push_status(self):
        """
        Push the service instance status to the collector synchronously.
        Usually done asynchronously and automatically by the collector thread.
        """
        status_data = self.print_status_data(mon_data=False, refresh=True)
        if status_data.get("encap", False) is True:
            self.log.info("skip push status for encap object %s", self.path)
            return
        if self.node.oc3_version() >= Semver(1, 0, 7):
            api_verb = "POST"
            api_path = oc3path.FEED_INSTANCE_STATUS + "?sync=true"
            headers = {"Accept": "application/json", "Content-Type": "application/json"}
            self.log.info("%s %s", api_verb, api_path)
            try:
                data = {
                    "path": self.path,
                    "version": "2.1",
                    "data": status_data,
                }
                status_code, response_data = self.node.oc3_request_feed(api_verb, api_path, data=data, headers=headers)
                if status_code == 200:
                    return None
                elif status_code == 202:
                    self.log.info("%s %s accepted but not yet processed", api_verb, api_path)
                else:
                    self.node.oc3_assert_status_code(api_verb, api_path, status_code, response_data, expected=[200, 202])
            except Exception as exc:
                raise ex.Error(str(exc))
        else:
            self.node.collector.call('push_status', self.path, status_data)

    def push_config(self):
        """
        Push the service config to the collector. Usually done
        automatically by the collector thread.
        """
        if self.node.oc3_version() >= Semver(1, 0, 3):
            api_verb = "POST"
            api_path = oc3path.FEED_OBJECT_CONFIG
            headers = {"Accept": "application/json", "Content-Type": "application/json"}
            self.log.info("%s %s", api_verb, api_path)
            try:
                data = self.oc3_object_config_body()
                status_code, response_data = self.node.oc3_request_feed(api_verb, api_path, data=data, headers=headers)
                self.node.oc3_assert_status_code(api_verb, api_path, status_code, response_data, expected=[202])
            except Exception as exc:
                raise ex.Error(str(exc))
        else:
            self.node.collector.call('push_config', self.send_service_config_args())

    def push_resinfo(self):
        """
        The 'push_resinfo' scheduler task and action entrypoint.
        Push the per-resource key/value pairs to the collector.
        """
        if self.node.oc3_version() >= Semver(1, 0, 6):
            api_verb = "POST"
            api_path = oc3path.FEED_INSTANCE_RESINFO
            headers = {"Accept": "application/json", "Content-Type": "application/json"}
            self.log.info("%s %s", api_verb, api_path)
            try:
                data = self.oc3_instance_resource_info_body()
                status_code, response_data = self.node.oc3_request_feed(api_verb, api_path, data=data, headers=headers)
                self.node.oc3_assert_status_code(api_verb, api_path, status_code, response_data, expected=[202])
            except Exception as exc:
                raise ex.Error(str(exc))
        else:
            return self._push_resinfo(sync=self.options.syncrpc)

    def _push_resinfo(self, sync=False):
        """
        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, sync=sync)

    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.
        """
        self.init_resources()
        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 (core.status.STDBY_UP, core.status.UP):
                continue
            self._push_encap_config(resource)

    def _push_encap_config(self, container):
        if len(self.logger.handlers) > 1:
            self.logger.handlers[1].setLevel(logging.CRITICAL)
        try:
            self.__push_encap_config(container)
        finally:
            if len(self.logger.handlers) > 1:
                self.logger.handlers[1].setLevel(Env.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.
        """
        def pulled_config_sanity_check(cf):
            """
            If the encap cf is flushed at the time of the copy,
            avoid installing it, and hope the new version will
            arrive later.
            """
            if os.path.getsize(cf) <= 60:
                raise ex.Error("pulled an empty configuration from %s. "
                               "abort install." % container.name)

        def encap_config_mtime():
            cmd = ['print', 'config', 'mtime']
            try:
                cmd_results = self._encap_cmd(cmd, container, push_config=False, fwd_options=False)
                out = cmd_results[0]
            except ex.Error as exc:
                return

            if out == "":
                # this is what happens when the container is down
                raise ex.EncapUnjoinable

            try:
                return int(float(out.strip()))
            except Exception:
                return

        def pull_encap_config():
            paths = Paths(osvc_root_path=container.osvc_root_path)
            encap_cf = os.path.join(paths.pathetc, self.paths.cf[len(Env.paths.pathetc)+1:])
            tmpfile = tempfile.NamedTemporaryFile(delete=False,
                                                  dir=os.path.dirname(self.paths.cf),
                                                  prefix="." + self.name + ".conf.")
            tmpcf = tmpfile.name
            tmpfile.close()
            try:
                if hasattr(container, 'rcp_from'):
                    cmd_results = container.rcp_from(encap_cf, tmpcf)
                else:
                    cmd = Env.rcp.split() + [container.name+':'+encap_cf, tmpcf]
                    cmd_results = justcall(cmd)
                self.log.info("fetch %s from %s", encap_cf, container.name)
                if cmd_results[2] != 0:
                    raise ex.Error()
                pulled_config_sanity_check(tmpcf)
                os.utime(tmpcf, (encap_mtime, encap_mtime))
                shutil.move(tmpcf, self.paths.cf)
                return
            finally:
                try:
                    os.unlink(tmpcf)
                except Exception:
                    pass

        def push_encap_config():
            """
            Use a tempory conf staging to not have to care about ns dir create
            """
            paths = Paths(osvc_root_path=container.osvc_root_path)
            encap_cf = os.path.join(paths.pathtmp, self.id+".conf")
            if hasattr(container, 'rcp'):
                cmd_results = container.rcp(self.paths.cf, encap_cf)
                try:
                    container.set_encap_file_ownership(encap_cf)
                except Exception:
                    pass
            else:
                cmd = Env.rcp.split() + [self.paths.cf, container.name+':'+encap_cf]
                cmd_results = justcall(cmd)
            if cmd_results[2] != 0:
                raise ex.Error("failed to send %s to %s" % (self.paths.cf, container.name))
            self.log.info("sent %s to %s", self.paths.cf, container.name)

            cmd = ["create", "--restore", "--config", encap_cf]
            try:
                cmd_results = self._encap_cmd(cmd, container=container,
                                              push_config=False, fwd_options=False)
            except ex.Error:
                raise ex.Error("failed to create %s slave service" % container.name)
            self.log.info("create %s slave service", container.name)

        try:
            encap_mtime = encap_config_mtime()
        except ex.EncapUnjoinable:
            return

        local_mtime = os.path.getmtime(self.paths.cf)
        if encap_mtime == local_mtime:
            return

        if encap_mtime and encap_mtime > local_mtime:
            pull_encap_config()
            return

        push_encap_config()

    @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 all_rids(self):
        self.init_resources()
        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 itertools.chain(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 itertools.chain(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.is_standby]

    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 or tags or subsets or xtags:
            self.init_resources()

        if rid is None:
            rid = []
        elif is_string(rid):
            if rid:
                rid = rid.split(',')
            else:
                rid = []

        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.AbortAction("no resource match the given --rid, --subset "
                                     "and --tags specifiers")
        else:
            # let the action go on. 'delete', for one, takes a --rid but does
            # not need resource initialization
            rids = rid

        return rids

    def restart(self):
        """
        The 'restart' action entrypoint.
        This action translates into 'stop' followed by 'start'
        """
        self.options.local = True
        self.stop()
        self.log.info("instance stopped, ready for restart.")
        self.unset_all_lazy()
        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 Error 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 destination_node is None:
            destination_node = self.options.to
        if destination_node is None:
            destination_node = "<peer>"
        return destination_node

    @_master_action
    def migrate(self):
        """
        Service online migration.
        """
        dst = self.destination_node_sanity_checks()
        self.svcunlock()
        self._clear(server=Env.nodename)
        self._clear(server=dst)
        self.daemon_mon_action("freeze", wait=True)
        src_node = self.current_node()
        self.daemon_service_action(action="prstop", server=src_node)
        try:
            self.daemon_service_action(action="startfs", options={"master": True}, server=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(action="stop", server=src_node)
        self.daemon_service_action(action="prstart", options={"master": True}, server=dst)

    def takeover(self):
        """
        Orchestrated move of a failover running instance to the local node.
        """
        pass

    def giveback(self):
        """
        Optimize service placement.
        """
        pass

    def scale(self):
        """
        Set the scale keyword.
        """
        if self.scale_target is None:
            raise ex.Error("can't scale: not a scaler")
        try:
            value = int(self.options.to)
            assert value >= 0
        except Exception:
            raise ex.Error("invalid scale target: set '--to <n>' where n>=0")
        self._set("DEFAULT", "scale", str(value), validation=False)
        self.set_service_monitor()

    def move(self):
        """
        Orchestrated move of running instances to the nodes specified by --to.
        """
        pass

    def switch(self):
        """
        The "switch" Monitor Action implementation.
        """
        self.stop()
        self.set_service_monitor(status="start failed", global_expect="placed")

    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
        """
        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']

        changes = []
        unsets = []
        for rid in rids:
            if disable:
                self.log.info("set %s.disable = true", rid)
                changes.append("%s.disable=true" % rid)
            elif "disable" in self.cd[rid]:
                self.log.info("remove %s.disable", rid)
                unsets.append("%s.disable" % rid)

            #
            # if we set <section>.disable = <bool>,
            # remove <section>.disable@<scope> = <not bool>
            #
            if rid not in self.cd:
                items = {}
            else:
                items = self.cd[rid]
            for option, value in items.items():
                if not option.startswith("disable@"):
                    continue
                if value is True:
                    continue
                self.log.info("remove %s.%s = false", rid, option)
                unsets.append("DEFAULT.%s" % 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()

        if changes:
            self.set_multi(changes)
        if unsets:
            self.unset_multi(unsets)

    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_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
        """
        self.delete_sections(rids)

    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():
            self.set_purge_collector_tag()
            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 Env.nodename in self.nodes:
                    self.set_multi([
                       "nodes="+Env.nodename,
                       "drpnodes=",
                    ])
                elif Env.nodename in self.drpnodes:
                    self.set_multi([
                       "drpnodes="+Env.nodename,
                       "nodes=",
                    ])
                self.svcunlock()
                for peer in self.peers:
                    if peer == Env.nodename:
                        continue
                    self.daemon_service_action(
                        action="set",
                        options={
                            "kw": [
                                "nodes-=" + Env.nodename,
                                "drpnodes-=" + Env.nodename,
                            ],
                            "eval": True
                        },
                        server=peer,
                        sync=True
                    )
            self.delete_service_conf()
            self.delete_service_logs()
        else:
            self.delete_resources()

    def enter(self):
        if not self.options.rid:
            resources = self.get_resources("container")
            if len(resources) == 1:
                rid = resources[0].rid
            elif len(resources) == 0:
                raise ex.Error("this svc has no container")
            else:
                raise ex.Error("this svc has multiple containers. select one with --rid <id>")
        elif is_string(self.options.rid):
            rid = self.options.rid
        else:
            rid = self.options.rid[0]
        self._enter(rid)

    def _enter(self, rid):
        res = self.get_resource(rid)
        if res is None:
            raise ex.Error("rid %s not found" % rid)
        if not hasattr(res, "enter"):
            raise ex.Error("rid %s does not support enter" % rid)
        res.enter()

    def docker(self):
        self.container_manager_passthrough("docker")

    def podman(self):
        self.container_manager_passthrough("podman")

    def oci(self):
        self.container_manager_passthrough("oci")

    def container_manager_passthrough(self, ctype):
        """
        The 'docker|podman|oci' 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
        if ctype == "oci":
            containers = self.get_resources(["container.docker", "container.podman"])
        else:
            containers = self.get_resources("container." + ctype)
        if self.options.extra_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 re.match(r'[%{][#.-_\w]+[%\}]', arg):
                    container_rid = arg.strip("{}%")
                    if not container_rid.startswith("container#"):
                        container_rid = "container#" + container_rid
                    if container_rid not in self.resources_by_id:
                        continue
                    container = self.resources_by_id[container_rid]
                    name = container.container_name
                    del argv[idx]
                    argv.insert(idx, name)
            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.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}"):
                    container = containers[0]
                    del argv[idx]
                    argv[idx:idx] = container.lib.login_as_service_args()
            return argv

        if len(containers) == 0:
            print("this service has no %s resource" % ctype, file=sys.stderr)
            return 1

        if containers[0].type == "docker":
            containers[0].lib.docker_start(verbose=False)
        cmd = containers[0].lib.docker_cmd + subst(self.options.extra_argv)
        proc = subprocess.Popen(cmd)
        proc.communicate()
        return proc.returncode

    def freezestop(self):
        """
        The freezestop monitor action.
        """
        self.master_freeze()
        self.slave_freezestop()
        self.master_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 and install it.
        """
        data = self.node.collector_rest_get("/services/"+self.path+"?props=svc_config&meta=0")
        if "error" in data:
            raise ex.Error(data["error"])
        if len(data["data"]) == 0:
            raise ex.Error("service not found on the collector")
        if data["data"][0]["svc_config"] is None:
            raise ex.Error("service has an empty configuration on the collector")
        if len(data["data"][0]["svc_config"]) == 0:
            raise ex.Error("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)

        if self.options.provision:
            self.action("provision")

    @lazy
    def slave_num(self):
        try:
            return int(self.name.split(".")[0])
        except ValueError:
            return 0

    def snooze(self):
        """
        Snooze notifications on the service.
        """
        if self.options.duration is None:
            print("set --duration", file=sys.stderr)
            raise ex.Error
        data = self._snooze(self.options.duration)
        print(data.get("info", ""))

    def unsnooze(self):
        data = self._unsnooze()
        print(data.get("info", ""))

    def _snooze(self, duration):
        """
        Snooze notifications on the service.
        """
        try:
            data = self.collector_rest_post("/services/self/snooze", {
                "duration": self.options.duration,
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])
        return data

    def _unsnooze(self):
        """
        Unsnooze notifications on the service.
        """
        try:
            data = self.collector_rest_post("/services/self/snooze")
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(data["error"])
        return data

    def mount_point(self):
        """
        Return the shortest service mount point.
        The volume resource in the consumer service uses this function as the
        prefix of its own mount_point property.
        """
        candidates = [res for res in self.get_resources("fs")]
        if not candidates:
            return
        for candidate in sorted(candidates):
            if not hasattr(candidate, "mount_point"):
                continue
            return candidate.mount_point
        raise IndexError

    def device(self):
        """
        Return the unambiguous exposed device. Volume services naturally
        have such a device.
        """
        candidates = sorted([res for res in self.get_resources("disk")
                             if res.type != "disk.scsireserv"], key=lambda r: r.rid)
        if not candidates:
            return
        try:
            return list(candidates[-1].exposed_devs())[0]
        except Exception:
            return

    def get_volume(self, name):
        """
        Return the volume resource matching name.
        Raise Error if not found or found more than one matching resource.
        """
        candidates = [res for res in self.get_resources("volume") if res.name == name]
        if not candidates:
            raise ex.Error("volume %s not found" % name)
        if not candidates or len(candidates) > 1:
            raise ex.Error("found multiple volumes names" % name)
        return candidates[0]

    def get_volume_rid(self, volname):
        candidates = [rid for rid, res in self.resources_by_id.items()
                      if rid.startswith("volume#") and res.name == volname]
        try:
            return candidates[0]
        except IndexError:
            return

    def replace_volname(self, buff, mode="file", strict=False, errors=None):
        """
        In a string starting with a volume name, replace the volume name with,
        * the volume mount point path if mode=="file"
        * the volume device path if mode=="blk"

        If strict is True, raise if the string does not start with a volume
        name (starts with / actually).

        If errors is "ignore", ignore all errors:
        * string does not start with a volume name (even if strict is True)
        * volume does not have a mount point
        * volume instance is down
        """
        l = buff.split("/")
        volname = l[0]
        if not volname:
            if strict and errors != "ignore":
                raise ex.Error("a volume path can't start with /")
            else:
                return buff, None
        vol = self.get_volume(volname)
        if mode == "file" and vol.mount_point is None:
            if errors == "ignore":
                return buff, None
            raise ex.Error("referenced volume %s has no mount point" % l[0])
        volstatus = vol.status()
        if volstatus not in (core.status.UP, core.status.STDBY_UP, core.status.NA):
            if errors != "ignore":
                raise ex.Error("volume %s is %s" % (volname, core.status.Status(volstatus)))
        if mode == "blk":
            if vol.device is None:
                return None, vol
            l[0] = vol.device
        else:
            if vol.mount_point is None:
                return None, vol
            l[0] = vol.mount_point
        return "/".join(l), vol

    def install_data(self):
        rtypes = [
            "volume",
        ]
        self.sub_set_action(rtypes, "install_data")
0707010001f194000081a40000000000000000000000016a100daf000010be000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/core/objects/pg.py import os
import time

import core.exceptions as ex
from utilities.drivers import driver_import
from utilities.lazy import lazy

STATS_INTERVAL = 1


class PgMixin(object):
    @lazy
    def create_pg(self):
        return self.oget("DEFAULT", "create_pg")

    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
            except ValueError:
                # keyword not supported. data resource for example.
                pass

        return d

    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)

    def pg_remove(self):
        if self.pg is None:
            return
        if self.options.force:
            self.pg_kill()
        self.pg.remove_pg(self)

    def pg_pids(self):
        if self.pg is None:
            return []
        return sorted(self.pg.pids(self))

    def pg_update(self):
        nscfg = self.nscfg()
        if nscfg:
            nscfg.pg_update(children=False)
        for res in self.get_resources():
            res.create_pg()

    @lazy
    def pg(self):
        """
        A lazy property to import the system-specific process group module
        on-demand and expose it as self.pg
        """
        if os.path.exists('/.dockerenv') or os.path.exists('/run/.containerenv'):
            return
        try:
            mod = driver_import("pg", fallback=False)
        except ImportError:
            return
        except Exception as exc:
            print(exc)
            raise
        try:
            mod.DRIVER_BASENAME
        except AttributeError:
            return
        return mod

    def pg_stats(self):
        if self.pg is None:
            return {}
        now = time.time()
        if now - self.stats_updated < STATS_INTERVAL:
            return self.stats_data
        self.stats_data = self.pg.get_stats(self)
        self.stats_updated = now
        return self.stats_data

    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)

    @lazy
    def pg_settings(self):
        return self.get_pg_settings("DEFAULT")



  0707010001f196000081a40000000000000000000000016a100daf00001726000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/core/objects/secdict.py    import sys
from env import Env
from core.keywords import KeywordStore

# deprecated => supported
DEPRECATED_KEYWORDS = {
}

# supported => deprecated
REVERSE_DEPRECATED_KEYWORDS = {
}

DEPRECATED_SECTIONS = {
}

BASE_SECTIONS = [
    "data",
]

KEYWORDS = [
    {
        "section": "DEFAULT",
        "keyword": "id",
        "inheritance": "head",
        "default_text": "<random uuid>",
        "text": "A RFC 4122 random uuid generated by the agent. To use as reference in resources definitions instead of the service name, so the service can be renamed without affecting the resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "disable",
        "protoname": "disabled",
        "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:`om <path> disable` only sets :kw:`DEFAULT.disable`. As resources disabled state is not changed, :cmd:`om <path> enable` does not enable disabled resources."
    },
    {
        "section": "DEFAULT",
        "keyword": "env",
        "inheritance": "head",
        "default_text": "<same as node env>",
        "candidates": Env.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."
    },
    {
        "section": "DEFAULT",
        "keyword": "lock_timeout",
        "default": 60,
        "convert": "duration",
        "text": "A duration expression, like ``1m30s``. The maximum wait time for the action lock acquire. The :cmd:`--waitlock` option overrides this parameter."
    },
    {
        "section": "DEFAULT",
        "keyword": "nodes",
        "inheritance": "head",
        "at": True,
        "convert": "nodes_selector",
        "default": "{clusternodes}",
        "default_text": "<hostname of the current node>",
        "text": "A node selector expression specifying the list of cluster nodes hosting service instances."
    },
    {
        "section": "DEFAULT",
        "keyword": "drpnodes",
        "inheritance": "head",
        "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 :c-res:`sync` resources.",
        "example": "node1 node2"
    },
    {
        "section": "DEFAULT",
        "keyword": "cn",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Common Name.",
        "example": "foo"
    },
    {
        "section": "DEFAULT",
        "keyword": "c",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Country.",
        "example": "france"
    },
    {
        "section": "DEFAULT",
        "keyword": "st",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request State.",
        "example": "oise"
    },
    {
        "section": "DEFAULT",
        "keyword": "l",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Location.",
        "example": "gouvieux"
    },
    {
        "section": "DEFAULT",
        "keyword": "o",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Organization.",
        "example": "opensvc"
    },
    {
        "section": "DEFAULT",
        "keyword": "ou",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Organizational Unit.",
        "example": "lab"
    },
    {
        "section": "DEFAULT",
        "keyword": "email",
        "inheritance": "head",
        "at": True,
        "text": "Certificate Signing Request Email.",
        "example": "test@opensvc.com"
    },
    {
        "section": "DEFAULT",
        "keyword": "alt_names",
        "inheritance": "head",
        "convert": "list",
        "at": True,
        "text": "Certificate Signing Request Alternative Domain Names.",
        "example": "www.opensvc.com opensvc.com"
    },
    {
        "section": "DEFAULT",
        "keyword": "bits",
        "inheritance": "head",
        "convert": "size",
        "at": True,
        "text": "Certificate Private Key Length.",
        "example": "4k",
        "default": 4096,
    },
    {
        "section": "DEFAULT",
        "keyword": "validity",
        "inheritance": "head",
        "convert": "duration_to_day",
        "at": True,
        "text": "Certificate Validity duration.",
        "example": "365d",
        "default": "365d",
    },
    {
        "section": "DEFAULT",
        "keyword": "ca",
        "inheritance": "head",
        "at": True,
        "text": "The name of secret containing a certificate to use as a Certificate Authority. This secret must be in the same namespace.",
        "example": "ca",
    },
    {
        "section": "DEFAULT",
        "keyword": "comment",
        "default": "",
        "text": "Helps users understand the role of the service and resources, which is nice to on-call support people having to operate on a service they are not usually responsible for."
    },
]


KEYS = KeywordStore(
    name="sec",
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    template_prefix="template.secret.",
    base_sections=BASE_SECTIONS,
    has_default_section=False,
)
  0707010001f175000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/core/configfile    0707010001f176000081a40000000000000000000000016a100daf0000047c000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/core/configfile/__init__.py    import os
import shutil
from env import Env
from utilities.lock import cmlock


def move_config_file(src, dest):
    """
    move_config_file is a wrapper to shutil.move
    after file moved, os.fsync is called to ensure write dest to disk.

    This reduces the risk of dest updates lost when crash short time after
    move_config_file returns (detected on centos 8 with xfs /).
    """
    lockfile = os.path.join(Env.paths.pathlock, "move_file_" + os.path.basename(dest))
    with cmlock(timeout=3, delay=0.2, lockfile=lockfile, intent=dest):
        _move_config_file(src, dest)


def _move_config_file(src, dest):
    if not os.path.exists(src):
        raise Exception("move_file source file %s is absent" % src)
    if os.path.exists(dest) and not os.path.isfile(dest):
        raise Exception("move_file destination %s exists and is not a regular file" % dest)
    dest_dir = os.path.dirname(dest)
    if not os.path.isdir(dest_dir):
        raise Exception("move_file destination dir %s is not a directory" % dest_dir)

    shutil.move(src, dest)

    if hasattr(os, "fsync"):
        with open(dest, "a") as fd:
            os.fsync(fd)
0707010001f431000041ed00000000000000000000000f6a102a9300000000000000e600010003ffffffffffffffff0000002700000000root/usr/share/opensvc/opensvc/foreign    0707010001f491000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000002b00000000root/usr/share/opensvc/opensvc/foreign/jwt    0707010001f497000081a40000000000000000000000016a100daf00000658000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/jwt/compat.py  """
The `compat` module provides support for backwards compatibility with older
versions of python, and compatibility wrappers around optional packages.
"""
# flake8: noqa
import hmac
import struct
import sys


PY3 = sys.version_info[0] == 3


if PY3:
    text_type = str
    binary_type = bytes
else:
    text_type = unicode
    binary_type = str

string_types = (text_type, binary_type)

try:
    # Importing ABCs from collections will be removed in PY3.8
    from collections.abc import Iterable, Mapping
except ImportError:
    from collections import Iterable, Mapping

try:
    constant_time_compare = hmac.compare_digest
except AttributeError:
    # Fallback for Python < 2.7
    def constant_time_compare(val1, val2):
        """
        Returns True if the two strings are equal, False otherwise.

        The time taken is independent of the number of characters that match.
        """
        if len(val1) != len(val2):
            return False

        result = 0

        for x, y in zip(val1, val2):
            result |= ord(x) ^ ord(y)

        return result == 0

# Use int.to_bytes if it exists (Python 3)
if getattr(int, 'to_bytes', None):
    def bytes_from_int(val):
        remaining = val
        byte_length = 0

        while remaining != 0:
            remaining = remaining >> 8
            byte_length += 1

        return val.to_bytes(byte_length, 'big', signed=False)
else:
    def bytes_from_int(val):
        buf = []
        while val:
            val, remainder = divmod(val, 256)
            buf.append(remainder)

        buf.reverse()
        return struct.pack('%sB' % len(buf), *buf)
0707010001f492000081a40000000000000000000000016a100daf0000032b000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/jwt/__init__.py    # -*- coding: utf-8 -*-
# flake8: noqa

"""
JSON Web Token implementation

Minimum implementation based on this spec:
https://self-issued.info/docs/draft-jones-json-web-token-01.html
"""


__title__ = 'pyjwt'
__version__ = '1.7.1'
__author__ = 'JosÃ© Padilla'
__license__ = 'MIT'
__copyright__ = 'Copyright 2015-2018 JosÃ© Padilla'


from .api_jwt import (
    encode, decode, register_algorithm, unregister_algorithm,
    get_unverified_header, PyJWT
)
from .api_jws import PyJWS
from .exceptions import (
    InvalidTokenError, DecodeError, InvalidAlgorithmError,
    InvalidAudienceError, ExpiredSignatureError, ImmatureSignatureError,
    InvalidIssuedAtError, InvalidIssuerError, ExpiredSignature,
    InvalidAudience, InvalidIssuer, MissingRequiredClaimError,
    InvalidSignatureError,
    PyJWTError,
)
 0707010001f494000081a40000000000000000000000016a100daf00003418000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/jwt/algorithms.py  import hashlib
import hmac
import json


from .compat import constant_time_compare, string_types
from .exceptions import InvalidKeyError
from .utils import (
    base64url_decode, base64url_encode, der_to_raw_signature,
    force_bytes, force_unicode, from_base64url_uint, raw_to_der_signature,
    to_base64url_uint
)

try:
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.serialization import (
        load_pem_private_key, load_pem_public_key, load_ssh_public_key
    )
    from cryptography.hazmat.primitives.asymmetric.rsa import (
        RSAPrivateKey, RSAPublicKey, RSAPrivateNumbers, RSAPublicNumbers,
        rsa_recover_prime_factors, rsa_crt_dmp1, rsa_crt_dmq1, rsa_crt_iqmp
    )
    from cryptography.hazmat.primitives.asymmetric.ec import (
        EllipticCurvePrivateKey, EllipticCurvePublicKey
    )
    from cryptography.hazmat.primitives.asymmetric import ec, padding
    from cryptography.hazmat.backends import default_backend
    from cryptography.exceptions import InvalidSignature

    has_crypto = True
except ImportError:
    has_crypto = False

requires_cryptography = set(['RS256', 'RS384', 'RS512', 'ES256', 'ES384',
                             'ES521', 'ES512', 'PS256', 'PS384', 'PS512'])


def get_default_algorithms():
    """
    Returns the algorithms that are implemented by the library.
    """
    default_algorithms = {
        'none': NoneAlgorithm(),
        'HS256': HMACAlgorithm(HMACAlgorithm.SHA256),
        'HS384': HMACAlgorithm(HMACAlgorithm.SHA384),
        'HS512': HMACAlgorithm(HMACAlgorithm.SHA512)
    }

    if has_crypto:
        default_algorithms.update({
            'RS256': RSAAlgorithm(RSAAlgorithm.SHA256),
            'RS384': RSAAlgorithm(RSAAlgorithm.SHA384),
            'RS512': RSAAlgorithm(RSAAlgorithm.SHA512),
            'ES256': ECAlgorithm(ECAlgorithm.SHA256),
            'ES384': ECAlgorithm(ECAlgorithm.SHA384),
            'ES521': ECAlgorithm(ECAlgorithm.SHA512),
            'ES512': ECAlgorithm(ECAlgorithm.SHA512),  # Backward compat for #219 fix
            'PS256': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256),
            'PS384': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384),
            'PS512': RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512)
        })

    return default_algorithms


class Algorithm(object):
    """
    The interface for an algorithm used to sign and verify tokens.
    """
    def prepare_key(self, key):
        """
        Performs necessary validation and conversions on the key and returns
        the key value in the proper format for sign() and verify().
        """
        raise NotImplementedError

    def sign(self, msg, key):
        """
        Returns a digital signature for the specified message
        using the specified key value.
        """
        raise NotImplementedError

    def verify(self, msg, key, sig):
        """
        Verifies that the specified digital signature is valid
        for the specified message and key values.
        """
        raise NotImplementedError

    @staticmethod
    def to_jwk(key_obj):
        """
        Serializes a given RSA key into a JWK
        """
        raise NotImplementedError

    @staticmethod
    def from_jwk(jwk):
        """
        Deserializes a given RSA key from JWK back into a PublicKey or PrivateKey object
        """
        raise NotImplementedError


class NoneAlgorithm(Algorithm):
    """
    Placeholder for use when no signing or verification
    operations are required.
    """
    def prepare_key(self, key):
        if key == '':
            key = None

        if key is not None:
            raise InvalidKeyError('When alg = "none", key value must be None.')

        return key

    def sign(self, msg, key):
        return b''

    def verify(self, msg, key, sig):
        return False


class HMACAlgorithm(Algorithm):
    """
    Performs signing and verification operations using HMAC
    and the specified hash function.
    """
    SHA256 = hashlib.sha256
    SHA384 = hashlib.sha384
    SHA512 = hashlib.sha512

    def __init__(self, hash_alg):
        self.hash_alg = hash_alg

    def prepare_key(self, key):
        key = force_bytes(key)

        invalid_strings = [
            b'-----BEGIN PUBLIC KEY-----',
            b'-----BEGIN CERTIFICATE-----',
            b'-----BEGIN RSA PUBLIC KEY-----',
            b'ssh-rsa'
        ]

        if any([string_value in key for string_value in invalid_strings]):
            raise InvalidKeyError(
                'The specified key is an asymmetric key or x509 certificate and'
                ' should not be used as an HMAC secret.')

        return key

    @staticmethod
    def to_jwk(key_obj):
        return json.dumps({
            'k': force_unicode(base64url_encode(force_bytes(key_obj))),
            'kty': 'oct'
        })

    @staticmethod
    def from_jwk(jwk):
        obj = json.loads(jwk)

        if obj.get('kty') != 'oct':
            raise InvalidKeyError('Not an HMAC key')

        return base64url_decode(obj['k'])

    def sign(self, msg, key):
        return hmac.new(key, msg, self.hash_alg).digest()

    def verify(self, msg, key, sig):
        return constant_time_compare(sig, self.sign(msg, key))


if has_crypto:

    class RSAAlgorithm(Algorithm):
        """
        Performs signing and verification operations using
        RSASSA-PKCS-v1_5 and the specified hash function.
        """
        SHA256 = hashes.SHA256
        SHA384 = hashes.SHA384
        SHA512 = hashes.SHA512

        def __init__(self, hash_alg):
            self.hash_alg = hash_alg

        def prepare_key(self, key):
            if isinstance(key, RSAPrivateKey) or \
               isinstance(key, RSAPublicKey):
                return key

            if isinstance(key, string_types):
                key = force_bytes(key)

                try:
                    if key.startswith(b'ssh-rsa'):
                        key = load_ssh_public_key(key, backend=default_backend())
                    else:
                        key = load_pem_private_key(key, password=None, backend=default_backend())
                except ValueError:
                    key = load_pem_public_key(key, backend=default_backend())
            else:
                raise TypeError('Expecting a PEM-formatted key.')

            return key

        @staticmethod
        def to_jwk(key_obj):
            obj = None

            if getattr(key_obj, 'private_numbers', None):
                # Private key
                numbers = key_obj.private_numbers()

                obj = {
                    'kty': 'RSA',
                    'key_ops': ['sign'],
                    'n': force_unicode(to_base64url_uint(numbers.public_numbers.n)),
                    'e': force_unicode(to_base64url_uint(numbers.public_numbers.e)),
                    'd': force_unicode(to_base64url_uint(numbers.d)),
                    'p': force_unicode(to_base64url_uint(numbers.p)),
                    'q': force_unicode(to_base64url_uint(numbers.q)),
                    'dp': force_unicode(to_base64url_uint(numbers.dmp1)),
                    'dq': force_unicode(to_base64url_uint(numbers.dmq1)),
                    'qi': force_unicode(to_base64url_uint(numbers.iqmp))
                }

            elif getattr(key_obj, 'verify', None):
                # Public key
                numbers = key_obj.public_numbers()

                obj = {
                    'kty': 'RSA',
                    'key_ops': ['verify'],
                    'n': force_unicode(to_base64url_uint(numbers.n)),
                    'e': force_unicode(to_base64url_uint(numbers.e))
                }
            else:
                raise InvalidKeyError('Not a public or private key')

            return json.dumps(obj)

        @staticmethod
        def from_jwk(jwk):
            try:
                obj = json.loads(jwk)
            except ValueError:
                raise InvalidKeyError('Key is not valid JSON')

            if obj.get('kty') != 'RSA':
                raise InvalidKeyError('Not an RSA key')

            if 'd' in obj and 'e' in obj and 'n' in obj:
                # Private key
                if 'oth' in obj:
                    raise InvalidKeyError('Unsupported RSA private key: > 2 primes not supported')

                other_props = ['p', 'q', 'dp', 'dq', 'qi']
                props_found = [prop in obj for prop in other_props]
                any_props_found = any(props_found)

                if any_props_found and not all(props_found):
                    raise InvalidKeyError('RSA key must include all parameters if any are present besides d')

                public_numbers = RSAPublicNumbers(
                    from_base64url_uint(obj['e']), from_base64url_uint(obj['n'])
                )

                if any_props_found:
                    numbers = RSAPrivateNumbers(
                        d=from_base64url_uint(obj['d']),
                        p=from_base64url_uint(obj['p']),
                        q=from_base64url_uint(obj['q']),
                        dmp1=from_base64url_uint(obj['dp']),
                        dmq1=from_base64url_uint(obj['dq']),
                        iqmp=from_base64url_uint(obj['qi']),
                        public_numbers=public_numbers
                    )
                else:
                    d = from_base64url_uint(obj['d'])
                    p, q = rsa_recover_prime_factors(
                        public_numbers.n, d, public_numbers.e
                    )

                    numbers = RSAPrivateNumbers(
                        d=d,
                        p=p,
                        q=q,
                        dmp1=rsa_crt_dmp1(d, p),
                        dmq1=rsa_crt_dmq1(d, q),
                        iqmp=rsa_crt_iqmp(p, q),
                        public_numbers=public_numbers
                    )

                return numbers.private_key(default_backend())
            elif 'n' in obj and 'e' in obj:
                # Public key
                numbers = RSAPublicNumbers(
                    from_base64url_uint(obj['e']), from_base64url_uint(obj['n'])
                )

                return numbers.public_key(default_backend())
            else:
                raise InvalidKeyError('Not a public or private key')

        def sign(self, msg, key):
            return key.sign(msg, padding.PKCS1v15(), self.hash_alg())

        def verify(self, msg, key, sig):
            try:
                key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg())
                return True
            except InvalidSignature:
                return False

    class ECAlgorithm(Algorithm):
        """
        Performs signing and verification operations using
        ECDSA and the specified hash function
        """
        SHA256 = hashes.SHA256
        SHA384 = hashes.SHA384
        SHA512 = hashes.SHA512

        def __init__(self, hash_alg):
            self.hash_alg = hash_alg

        def prepare_key(self, key):
            if isinstance(key, EllipticCurvePrivateKey) or \
               isinstance(key, EllipticCurvePublicKey):
                return key

            if isinstance(key, string_types):
                key = force_bytes(key)

                # Attempt to load key. We don't know if it's
                # a Signing Key or a Verifying Key, so we try
                # the Verifying Key first.
                try:
                    if key.startswith(b'ecdsa-sha2-'):
                        key = load_ssh_public_key(key, backend=default_backend())
                    else:
                        key = load_pem_public_key(key, backend=default_backend())
                except ValueError:
                    key = load_pem_private_key(key, password=None, backend=default_backend())

            else:
                raise TypeError('Expecting a PEM-formatted key.')

            return key

        def sign(self, msg, key):
            der_sig = key.sign(msg, ec.ECDSA(self.hash_alg()))

            return der_to_raw_signature(der_sig, key.curve)

        def verify(self, msg, key, sig):
            try:
                der_sig = raw_to_der_signature(sig, key.curve)
            except ValueError:
                return False

            try:
                key.verify(der_sig, msg, ec.ECDSA(self.hash_alg()))
                return True
            except InvalidSignature:
                return False

    class RSAPSSAlgorithm(RSAAlgorithm):
        """
        Performs a signature using RSASSA-PSS with MGF1
        """

        def sign(self, msg, key):
            return key.sign(
                msg,
                padding.PSS(
                    mgf=padding.MGF1(self.hash_alg()),
                    salt_length=self.hash_alg.digest_size
                ),
                self.hash_alg()
            )

        def verify(self, msg, key, sig):
            try:
                key.verify(
                    sig,
                    msg,
                    padding.PSS(
                        mgf=padding.MGF1(self.hash_alg()),
                        salt_length=self.hash_alg.digest_size
                    ),
                    self.hash_alg()
                )
                return True
            except InvalidSignature:
                return False
0707010001f49e000081a40000000000000000000000016a100daf000003da000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/jwt/exceptions.py  class PyJWTError(Exception):
    """
    Base class for all exceptions
    """
    pass


class InvalidTokenError(PyJWTError):
    pass


class DecodeError(InvalidTokenError):
    pass


class InvalidSignatureError(DecodeError):
    pass


class ExpiredSignatureError(InvalidTokenError):
    pass


class InvalidAudienceError(InvalidTokenError):
    pass


class InvalidIssuerError(InvalidTokenError):
    pass


class InvalidIssuedAtError(InvalidTokenError):
    pass


class ImmatureSignatureError(InvalidTokenError):
    pass


class InvalidKeyError(PyJWTError):
    pass


class InvalidAlgorithmError(InvalidTokenError):
    pass


class MissingRequiredClaimError(InvalidTokenError):
    def __init__(self, claim):
        self.claim = claim

    def __str__(self):
        return 'Token is missing the "%s" claim' % self.claim


# Compatibility aliases (deprecated)
ExpiredSignature = ExpiredSignatureError
InvalidAudience = InvalidAudienceError
InvalidIssuer = InvalidIssuerError
  0707010001f4a0000081a40000000000000000000000016a100daf00000a45000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/jwt/utils.py   import base64
import binascii
import struct

from .compat import binary_type, bytes_from_int, text_type

try:
    from cryptography.hazmat.primitives.asymmetric.utils import (
        decode_dss_signature, encode_dss_signature
    )
except ImportError:
    pass


def force_unicode(value):
    if isinstance(value, binary_type):
        return value.decode('utf-8')
    elif isinstance(value, text_type):
        return value
    else:
        raise TypeError('Expected a string value')


def force_bytes(value):
    if isinstance(value, text_type):
        return value.encode('utf-8')
    elif isinstance(value, binary_type):
        return value
    else:
        raise TypeError('Expected a string value')


def base64url_decode(input):
    if isinstance(input, text_type):
        input = input.encode('ascii')

    rem = len(input) % 4

    if rem > 0:
        input += b'=' * (4 - rem)

    return base64.urlsafe_b64decode(input)


def base64url_encode(input):
    return base64.urlsafe_b64encode(input).replace(b'=', b'')


def to_base64url_uint(val):
    if val < 0:
        raise ValueError('Must be a positive integer')

    int_bytes = bytes_from_int(val)

    if len(int_bytes) == 0:
        int_bytes = b'\x00'

    return base64url_encode(int_bytes)


def from_base64url_uint(val):
    if isinstance(val, text_type):
        val = val.encode('ascii')

    data = base64url_decode(val)

    buf = struct.unpack('%sB' % len(data), data)
    return int(''.join(["%02x" % byte for byte in buf]), 16)


def merge_dict(original, updates):
    if not updates:
        return original

    try:
        merged_options = original.copy()
        merged_options.update(updates)
    except (AttributeError, ValueError) as e:
        raise TypeError('original and updates must be a dictionary: %s' % e)

    return merged_options


def number_to_bytes(num, num_bytes):
    padded_hex = '%0*x' % (2 * num_bytes, num)
    big_endian = binascii.a2b_hex(padded_hex.encode('ascii'))
    return big_endian


def bytes_to_number(string):
    return int(binascii.b2a_hex(string), 16)


def der_to_raw_signature(der_sig, curve):
    num_bits = curve.key_size
    num_bytes = (num_bits + 7) // 8

    r, s = decode_dss_signature(der_sig)

    return number_to_bytes(r, num_bytes) + number_to_bytes(s, num_bytes)


def raw_to_der_signature(raw_sig, curve):
    num_bits = curve.key_size
    num_bytes = (num_bits + 7) // 8

    if len(raw_sig) != 2 * num_bytes:
        raise ValueError('Invalid signature')

    r = bytes_to_number(raw_sig[:num_bytes])
    s = bytes_to_number(raw_sig[num_bytes:])

    return encode_dss_signature(r, s)
   0707010001f493000081a40000000000000000000000016a100daf00001042000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/jwt/__main__.py    #!/usr/bin/env python

from __future__ import absolute_import, print_function

import argparse
import json
import sys
import time

from . import DecodeError, __version__, decode, encode


def encode_payload(args):
    # Try to encode
    if args.key is None:
        raise ValueError('Key is required when encoding. See --help for usage.')

    # Build payload object to encode
    payload = {}

    for arg in args.payload:
        k, v = arg.split('=', 1)

        # exp +offset special case?
        if k == 'exp' and v[0] == '+' and len(v) > 1:
            v = str(int(time.time()+int(v[1:])))

        # Cast to integer?
        if v.isdigit():
            v = int(v)
        else:
            # Cast to float?
            try:
                v = float(v)
            except ValueError:
                pass

        # Cast to true, false, or null?
        constants = {'true': True, 'false': False, 'null': None}

        if v in constants:
            v = constants[v]

        payload[k] = v

    token = encode(
        payload,
        key=args.key,
        algorithm=args.algorithm
    )

    return token.decode('utf-8')


def decode_payload(args):
    try:
        if args.token:
            token = args.token
        else:
            if sys.stdin.isatty():
                token = sys.stdin.readline().strip()
            else:
                raise IOError('Cannot read from stdin: terminal not a TTY')

        token = token.encode('utf-8')
        data = decode(token, key=args.key, verify=args.verify)

        return json.dumps(data)

    except DecodeError as e:
        raise DecodeError('There was an error decoding the token: %s' % e)


def build_argparser():

    usage = '''
    Encodes or decodes JSON Web Tokens based on input.

    %(prog)s [options] <command> [options] input

    Decoding examples:

    %(prog)s --key=secret decode json.web.token
    %(prog)s decode --no-verify json.web.token

    Encoding requires the key option and takes space separated key/value pairs
    separated by equals (=) as input. Examples:

    %(prog)s --key=secret encode iss=me exp=1302049071
    %(prog)s --key=secret encode foo=bar exp=+10

    The exp key is special and can take an offset to current Unix time.
    '''

    arg_parser = argparse.ArgumentParser(
        prog='pyjwt',
        usage=usage
    )

    arg_parser.add_argument(
        '-v', '--version',
        action='version',
        version='%(prog)s ' + __version__
    )

    arg_parser.add_argument(
        '--key',
        dest='key',
        metavar='KEY',
        default=None,
        help='set the secret key to sign with'
    )

    arg_parser.add_argument(
        '--alg',
        dest='algorithm',
        metavar='ALG',
        default='HS256',
        help='set crypto algorithm to sign with. default=HS256'
    )

    subparsers = arg_parser.add_subparsers(
        title='PyJWT subcommands',
        description='valid subcommands',
        help='additional help'
    )

    # Encode subcommand
    encode_parser = subparsers.add_parser('encode', help='use to encode a supplied payload')

    payload_help = """Payload to encode. Must be a space separated list of key/value
    pairs separated by equals (=) sign."""

    encode_parser.add_argument('payload', nargs='+', help=payload_help)
    encode_parser.set_defaults(func=encode_payload)

    # Decode subcommand
    decode_parser = subparsers.add_parser('decode', help='use to decode a supplied JSON web token')
    decode_parser.add_argument(
        'token',
        help='JSON web token to decode.',
        nargs='?')

    decode_parser.add_argument(
        '-n', '--no-verify',
        action='store_false',
        dest='verify',
        default=True,
        help='ignore signature and claims verification on decode'
    )

    decode_parser.set_defaults(func=decode_payload)

    return arg_parser


def main():
    arg_parser = build_argparser()

    try:
        arguments = arg_parser.parse_args(sys.argv[1:])

        output = arguments.func(arguments)

        print(output)
    except Exception as e:
        print('There was an unforseen error: ', e)
        arg_parser.print_help()
  0707010001f498000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib    0707010001f49a000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms 0707010001f49c000081a40000000000000000000000016a100daf000006eb000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/py_ecdsa.py # Note: This file is named py_ecdsa.py because import behavior in Python 2
# would cause ecdsa.py to squash the ecdsa library that it depends upon.

import hashlib

import ecdsa

from jwt.algorithms import Algorithm
from jwt.compat import string_types, text_type


class ECAlgorithm(Algorithm):
    """
    Performs signing and verification operations using
    ECDSA and the specified hash function

    This class requires the ecdsa package to be installed.

    This is based off of the implementation in PyJWT 0.3.2
    """
    SHA256 = hashlib.sha256
    SHA384 = hashlib.sha384
    SHA512 = hashlib.sha512

    def __init__(self, hash_alg):
        self.hash_alg = hash_alg

    def prepare_key(self, key):

        if isinstance(key, ecdsa.SigningKey) or \
           isinstance(key, ecdsa.VerifyingKey):
            return key

        if isinstance(key, string_types):
            if isinstance(key, text_type):
                key = key.encode('utf-8')

            # Attempt to load key. We don't know if it's
            # a Signing Key or a Verifying Key, so we try
            # the Verifying Key first.
            try:
                key = ecdsa.VerifyingKey.from_pem(key)
            except ecdsa.der.UnexpectedDER:
                key = ecdsa.SigningKey.from_pem(key)

        else:
            raise TypeError('Expecting a PEM-formatted key.')

        return key

    def sign(self, msg, key):
        return key.sign(msg, hashfunc=self.hash_alg,
                        sigencode=ecdsa.util.sigencode_string)

    def verify(self, msg, key, sig):
        try:
            return key.verify(sig, msg, hashfunc=self.hash_alg,
                              sigdecode=ecdsa.util.sigdecode_string)
        except AssertionError:
            return False
 0707010001f49b000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/__init__.py 0707010001f49d000081a40000000000000000000000016a100daf000004e1000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib/algorithms/pycrypto.py import Crypto.Hash.SHA256
import Crypto.Hash.SHA384
import Crypto.Hash.SHA512
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5

from jwt.algorithms import Algorithm
from jwt.compat import string_types, text_type


class RSAAlgorithm(Algorithm):
    """
    Performs signing and verification operations using
    RSASSA-PKCS-v1_5 and the specified hash function.

    This class requires PyCrypto package to be installed.

    This is based off of the implementation in PyJWT 0.3.2
    """
    SHA256 = Crypto.Hash.SHA256
    SHA384 = Crypto.Hash.SHA384
    SHA512 = Crypto.Hash.SHA512

    def __init__(self, hash_alg):
        self.hash_alg = hash_alg

    def prepare_key(self, key):

        if isinstance(key, RSA._RSAobj):
            return key

        if isinstance(key, string_types):
            if isinstance(key, text_type):
                key = key.encode('utf-8')

            key = RSA.importKey(key)
        else:
            raise TypeError('Expecting a PEM- or RSA-formatted key.')

        return key

    def sign(self, msg, key):
        return PKCS1_v1_5.new(key).sign(self.hash_alg.new(msg))

    def verify(self, msg, key, sig):
        return PKCS1_v1_5.new(key).verify(self.hash_alg.new(msg), sig)
   0707010001f499000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/jwt/contrib/__init__.py    0707010001f495000081a40000000000000000000000016a100daf00001fb3000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/jwt/api_jws.py import binascii
import json
import warnings
try:
    # import required by mypy to perform type checking, not used for normal execution
    from typing import Callable, Dict, List, Optional, Type, Union # NOQA
except ImportError:
    pass

from .algorithms import (
    Algorithm, get_default_algorithms, has_crypto, requires_cryptography  # NOQA
)
from .compat import Mapping, binary_type, string_types, text_type
from .exceptions import (
    DecodeError, InvalidAlgorithmError, InvalidSignatureError,
    InvalidTokenError
)
from .utils import base64url_decode, base64url_encode, force_bytes, merge_dict


class PyJWS(object):
    header_typ = 'JWT'

    def __init__(self, algorithms=None, options=None):
        self._algorithms = get_default_algorithms()
        self._valid_algs = (set(algorithms) if algorithms is not None
                            else set(self._algorithms))

        # Remove algorithms that aren't on the whitelist
        for key in list(self._algorithms.keys()):
            if key not in self._valid_algs:
                del self._algorithms[key]

        if not options:
            options = {}

        self.options = merge_dict(self._get_default_options(), options)

    @staticmethod
    def _get_default_options():
        return {
            'verify_signature': True
        }

    def register_algorithm(self, alg_id, alg_obj):
        """
        Registers a new Algorithm for use when creating and verifying tokens.
        """
        if alg_id in self._algorithms:
            raise ValueError('Algorithm already has a handler.')

        if not isinstance(alg_obj, Algorithm):
            raise TypeError('Object is not of type `Algorithm`')

        self._algorithms[alg_id] = alg_obj
        self._valid_algs.add(alg_id)

    def unregister_algorithm(self, alg_id):
        """
        Unregisters an Algorithm for use when creating and verifying tokens
        Throws KeyError if algorithm is not registered.
        """
        if alg_id not in self._algorithms:
            raise KeyError('The specified algorithm could not be removed'
                           ' because it is not registered.')

        del self._algorithms[alg_id]
        self._valid_algs.remove(alg_id)

    def get_algorithms(self):
        """
        Returns a list of supported values for the 'alg' parameter.
        """
        return list(self._valid_algs)

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Type[json.JSONEncoder]]
               ):
        segments = []

        if algorithm is None:
            algorithm = 'none'

        if algorithm not in self._valid_algs:
            pass

        # Header
        header = {'typ': self.header_typ, 'alg': algorithm}

        if headers:
            self._validate_headers(headers)
            header.update(headers)

        json_header = force_bytes(
            json.dumps(
                header,
                separators=(',', ':'),
                cls=json_encoder
            )
        )

        segments.append(base64url_encode(json_header))
        segments.append(base64url_encode(payload))

        # Segments
        signing_input = b'.'.join(segments)
        try:
            alg_obj = self._algorithms[algorithm]
            key = alg_obj.prepare_key(key)
            signature = alg_obj.sign(signing_input, key)

        except KeyError:
            if not has_crypto and algorithm in requires_cryptography:
                raise NotImplementedError(
                    "Algorithm '%s' could not be found. Do you have cryptography "
                    "installed?" % algorithm
                )
            else:
                raise NotImplementedError('Algorithm not supported')

        segments.append(base64url_encode(signature))

        return b'.'.join(segments)

    def decode(self,
               jwt,  # type: str
               key='',   # type: str
               verify=True,  # type: bool
               algorithms=None,  # type: List[str]
               options=None,  # type: Dict
               **kwargs):

        merged_options = merge_dict(self.options, options)
        verify_signature = merged_options['verify_signature']

        if verify_signature and not algorithms:
            warnings.warn(
                'It is strongly recommended that you pass in a ' +
                'value for the "algorithms" argument when calling decode(). ' +
                'This argument will be mandatory in a future version.',
                DeprecationWarning
            )

        payload, signing_input, header, signature = self._load(jwt)

        if not verify:
            warnings.warn('The verify parameter is deprecated. '
                          'Please use verify_signature in options instead.',
                          DeprecationWarning, stacklevel=2)
        elif verify_signature:
            self._verify_signature(payload, signing_input, header, signature,
                                   key, algorithms)

        return payload

    def get_unverified_header(self, jwt):
        """Returns back the JWT header parameters as a dict()

        Note: The signature is not verified so the header parameters
        should not be fully trusted until signature verification is complete
        """
        headers = self._load(jwt)[2]
        self._validate_headers(headers)

        return headers

    def _load(self, jwt):
        if isinstance(jwt, text_type):
            jwt = jwt.encode('utf-8')

        if not issubclass(type(jwt), binary_type):
            raise DecodeError("Invalid token type. Token must be a {0}".format(
                binary_type))

        try:
            signing_input, crypto_segment = jwt.rsplit(b'.', 1)
            header_segment, payload_segment = signing_input.split(b'.', 1)
        except ValueError:
            raise DecodeError('Not enough segments')

        try:
            header_data = base64url_decode(header_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid header padding')

        try:
            header = json.loads(header_data.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid header string: %s' % e)

        if not isinstance(header, Mapping):
            raise DecodeError('Invalid header string: must be a json object')

        try:
            payload = base64url_decode(payload_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid payload padding')

        try:
            signature = base64url_decode(crypto_segment)
        except (TypeError, binascii.Error):
            raise DecodeError('Invalid crypto padding')

        return (payload, signing_input, header, signature)

    def _verify_signature(self, payload, signing_input, header, signature,
                          key='', algorithms=None):

        alg = header.get('alg')

        if algorithms is not None and alg not in algorithms:
            raise InvalidAlgorithmError('The specified alg value is not allowed')

        try:
            alg_obj = self._algorithms[alg]
            key = alg_obj.prepare_key(key)

            if not alg_obj.verify(signing_input, key, signature):
                raise InvalidSignatureError('Signature verification failed')

        except KeyError:
            raise InvalidAlgorithmError('Algorithm not supported')

    def _validate_headers(self, headers):
        if 'kid' in headers:
            self._validate_kid(headers['kid'])

    def _validate_kid(self, kid):
        if not isinstance(kid, string_types):
            raise InvalidTokenError('Key ID header parameter must be a string')


_jws_global_obj = PyJWS()
encode = _jws_global_obj.encode
decode = _jws_global_obj.decode
register_algorithm = _jws_global_obj.register_algorithm
unregister_algorithm = _jws_global_obj.unregister_algorithm
get_unverified_header = _jws_global_obj.get_unverified_header
 0707010001f49f000081a40000000000000000000000016a100daf00000649000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/jwt/help.py    from __future__ import print_function

import json
import platform
import sys

from . import __version__ as pyjwt_version

try:
    import cryptography
except ImportError:
    cryptography = None

try:
    import ecdsa
except ImportError:
    ecdsa = None


def info():
    """
    Generate information for a bug report.
    Based on the requests package help utility module.
    """
    try:
        platform_info = {"system": platform.system(), "release": platform.release()}
    except IOError:
        platform_info = {"system": "Unknown", "release": "Unknown"}

    implementation = platform.python_implementation()

    if implementation == "CPython":
        implementation_version = platform.python_version()
    elif implementation == "PyPy":
        implementation_version = "%s.%s.%s" % (
            sys.pypy_version_info.major,
            sys.pypy_version_info.minor,
            sys.pypy_version_info.micro,
        )
        if sys.pypy_version_info.releaselevel != "final":
            implementation_version = "".join(
                [implementation_version, sys.pypy_version_info.releaselevel]
            )
    else:
        implementation_version = "Unknown"

    return {
        "platform": platform_info,
        "implementation": {"name": implementation, "version": implementation_version},
        "cryptography": {"version": getattr(cryptography, "__version__", "")},
        "pyjwt": {"version": pyjwt_version},
    }


def main():
    """Pretty-print the bug information as JSON."""
    print(json.dumps(info(), sort_keys=True, indent=2))


if __name__ == "__main__":
    main()
   0707010001f496000081a40000000000000000000000016a100daf00001f1f000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/jwt/api_jwt.py import json
import warnings
from calendar import timegm
from datetime import datetime, timedelta
try:
    # import required by mypy to perform type checking, not used for normal execution
    from typing import Any, Callable, Dict, List, Optional, Type, Union # NOQA
except ImportError:
    pass

from .api_jws import PyJWS
from .algorithms import Algorithm, get_default_algorithms  # NOQA
from .compat import Iterable, Mapping, string_types
from .exceptions import (
    DecodeError, ExpiredSignatureError, ImmatureSignatureError,
    InvalidAudienceError, InvalidIssuedAtError,
    InvalidIssuerError, MissingRequiredClaimError
)
from .utils import merge_dict


class PyJWT(PyJWS):
    header_type = 'JWT'

    @staticmethod
    def _get_default_options():
        # type: () -> Dict[str, bool]
        return {
            'verify_signature': True,
            'verify_exp': True,
            'verify_nbf': True,
            'verify_iat': True,
            'verify_aud': True,
            'verify_iss': True,
            'require_exp': False,
            'require_iat': False,
            'require_nbf': False
        }

    def encode(self,
               payload,  # type: Union[Dict, bytes]
               key,  # type: str
               algorithm='HS256',  # type: str
               headers=None,  # type: Optional[Dict]
               json_encoder=None  # type: Optional[Type[json.JSONEncoder]]
               ):
        # Check that we get a mapping
        if not isinstance(payload, Mapping):
            raise TypeError('Expecting a mapping object, as JWT only supports '
                            'JSON objects as payloads.')

        # Payload
        for time_claim in ['exp', 'iat', 'nbf']:
            # Convert datetime to a intDate value in known time-format claims
            if isinstance(payload.get(time_claim), datetime):
                payload[time_claim] = timegm(payload[time_claim].utctimetuple())  # type: ignore

        json_payload = json.dumps(
            payload,
            separators=(',', ':'),
            cls=json_encoder
        ).encode('utf-8')

        return super(PyJWT, self).encode(
            json_payload, key, algorithm, headers, json_encoder
        )

    def decode(self,
               jwt,  # type: str
               key='',   # type: str
               verify=True,  # type: bool
               algorithms=None,  # type: List[str]
               options=None,  # type: Dict
               **kwargs):
        # type: (...) -> Dict[str, Any]

        if verify and not algorithms:
            warnings.warn(
                'It is strongly recommended that you pass in a ' +
                'value for the "algorithms" argument when calling decode(). ' +
                'This argument will be mandatory in a future version.',
                DeprecationWarning
            )

        payload, _, _, _ = self._load(jwt)

        if options is None:
            options = {'verify_signature': verify}
        else:
            options.setdefault('verify_signature', verify)

        decoded = super(PyJWT, self).decode(
            jwt, key=key, algorithms=algorithms, options=options, **kwargs
        )

        try:
            payload = json.loads(decoded.decode('utf-8'))
        except ValueError as e:
            raise DecodeError('Invalid payload string: %s' % e)
        if not isinstance(payload, dict):
            raise DecodeError('Invalid payload string: must be a json object')

        if verify:
            merged_options = merge_dict(self.options, options)
            self._validate_claims(payload, merged_options, **kwargs)

        return payload

    def _validate_claims(self, payload, options, audience=None, issuer=None,
                         leeway=0, **kwargs):

        if 'verify_expiration' in kwargs:
            options['verify_exp'] = kwargs.get('verify_expiration', True)
            warnings.warn('The verify_expiration parameter is deprecated. '
                          'Please use verify_exp in options instead.',
                          DeprecationWarning)

        if isinstance(leeway, timedelta):
            leeway = leeway.total_seconds()

        if not isinstance(audience, (string_types, type(None), Iterable)):
            raise TypeError('audience must be a string, iterable, or None')

        self._validate_required_claims(payload, options)

        now = timegm(datetime.utcnow().utctimetuple())

        if 'iat' in payload and options.get('verify_iat'):
            self._validate_iat(payload, now, leeway)

        if 'nbf' in payload and options.get('verify_nbf'):
            self._validate_nbf(payload, now, leeway)

        if 'exp' in payload and options.get('verify_exp'):
            self._validate_exp(payload, now, leeway)

        if options.get('verify_iss'):
            self._validate_iss(payload, issuer)

        if options.get('verify_aud'):
            self._validate_aud(payload, audience)

    def _validate_required_claims(self, payload, options):
        if options.get('require_exp') and payload.get('exp') is None:
            raise MissingRequiredClaimError('exp')

        if options.get('require_iat') and payload.get('iat') is None:
            raise MissingRequiredClaimError('iat')

        if options.get('require_nbf') and payload.get('nbf') is None:
            raise MissingRequiredClaimError('nbf')

    def _validate_iat(self, payload, now, leeway):
        try:
            int(payload['iat'])
        except ValueError:
            raise InvalidIssuedAtError('Issued At claim (iat) must be an integer.')

    def _validate_nbf(self, payload, now, leeway):
        try:
            nbf = int(payload['nbf'])
        except ValueError:
            raise DecodeError('Not Before claim (nbf) must be an integer.')

        if nbf > (now + leeway):
            raise ImmatureSignatureError('The token is not yet valid (nbf)')

    def _validate_exp(self, payload, now, leeway):
        try:
            exp = int(payload['exp'])
        except ValueError:
            raise DecodeError('Expiration Time claim (exp) must be an'
                              ' integer.')

        if exp < (now - leeway):
            raise ExpiredSignatureError('Signature has expired')

    def _validate_aud(self, payload, audience):
        if audience is None and 'aud' not in payload:
            return

        if audience is not None and 'aud' not in payload:
            # Application specified an audience, but it could not be
            # verified since the token does not contain a claim.
            raise MissingRequiredClaimError('aud')

        if audience is None and 'aud' in payload:
            # Application did not specify an audience, but
            # the token has the 'aud' claim
            raise InvalidAudienceError('Invalid audience')

        audience_claims = payload['aud']

        if isinstance(audience_claims, string_types):
            audience_claims = [audience_claims]
        if not isinstance(audience_claims, list):
            raise InvalidAudienceError('Invalid claim format in token')
        if any(not isinstance(c, string_types) for c in audience_claims):
            raise InvalidAudienceError('Invalid claim format in token')

        if isinstance(audience, string_types):
            audience = [audience]

        if not any(aud in audience_claims for aud in audience):
            raise InvalidAudienceError('Invalid audience')

    def _validate_iss(self, payload, issuer):
        if issuer is None:
            return

        if 'iss' not in payload:
            raise MissingRequiredClaimError('iss')

        if payload['iss'] != issuer:
            raise InvalidIssuerError('Invalid issuer')


_jwt_global_obj = PyJWT()
encode = _jwt_global_obj.encode
decode = _jwt_global_obj.decode
register_algorithm = _jwt_global_obj.register_algorithm
unregister_algorithm = _jwt_global_obj.unregister_algorithm
get_unverified_header = _jwt_global_obj.get_unverified_header
 0707010001f432000081a40000000000000000000000016a100daf00000054000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/__init__.py    import os
import sys

sys.path.append(os.path.realpath(os.path.join(__path__[0])))

0707010001f47f000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/foreign/hyperframe 0707010001f482000081a40000000000000000000000016a100daf00000506000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/hyperframe/flags.py    # -*- coding: utf-8 -*-
"""
hyperframe/flags
~~~~~~~~~~~~~~~~

Defines basic Flag and Flags data structures.
"""
import collections

try:
    from collections.abc import MutableSet
except ImportError:  # pragma: no cover
    # Python 2.7 compatibility
    from collections import MutableSet

Flag = collections.namedtuple("Flag", ["name", "bit"])


class Flags(MutableSet):
    """
    A simple MutableSet implementation that will only accept known flags as
    elements.

    Will behave like a regular set(), except that a ValueError will be thrown
    when .add()ing unexpected flags.
    """
    def __init__(self, defined_flags):
        self._valid_flags = set(flag.name for flag in defined_flags)
        self._flags = set()

    def __contains__(self, x):
        return self._flags.__contains__(x)

    def __iter__(self):
        return self._flags.__iter__()

    def __len__(self):
        return self._flags.__len__()

    def discard(self, value):
        return self._flags.discard(value)

    def add(self, value):
        if value not in self._valid_flags:
            raise ValueError(
                "Unexpected flag: {}. Valid flags are: {}".format(
                    value, self._valid_flags
                )
            )
        return self._flags.add(value)
  0707010001f483000081a40000000000000000000000016a100daf00006828000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/hyperframe/frame.py    # -*- coding: utf-8 -*-
"""
hyperframe/frame
~~~~~~~~~~~~~~~~

Defines framing logic for HTTP/2. Provides both classes to represent framed
data and logic for aiding the connection when it comes to reading from the
socket.
"""
import struct
import binascii

from .exceptions import (
    UnknownFrameError, InvalidPaddingError, InvalidFrameError
)
from .flags import Flag, Flags


# The maximum initial length of a frame. Some frames have shorter maximum
# lengths.
FRAME_MAX_LEN = (2 ** 14)

# The maximum allowed length of a frame.
FRAME_MAX_ALLOWED_LEN = (2 ** 24) - 1

# Stream association enumerations.
_STREAM_ASSOC_HAS_STREAM = "has-stream"
_STREAM_ASSOC_NO_STREAM = "no-stream"
_STREAM_ASSOC_EITHER = "either"

# Structs for packing and unpacking
_STRUCT_HBBBL = struct.Struct(">HBBBL")
_STRUCT_LL = struct.Struct(">LL")
_STRUCT_HL = struct.Struct(">HL")
_STRUCT_LB = struct.Struct(">LB")
_STRUCT_L = struct.Struct(">L")
_STRUCT_H = struct.Struct(">H")
_STRUCT_B = struct.Struct(">B")


class Frame(object):
    """
    The base class for all HTTP/2 frames.
    """
    #: The flags defined on this type of frame.
    defined_flags = []

    #: The byte used to define the type of the frame.
    type = None

    # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream',
    # it must be zero. If 'either', it's not checked.
    stream_association = None

    def __init__(self, stream_id, flags=()):
        #: The stream identifier for the stream this frame was received on.
        #: Set to 0 for frames sent on the connection (stream-id 0).
        self.stream_id = stream_id

        #: The flags set for this frame.
        self.flags = Flags(self.defined_flags)

        #: The frame length, excluding the nine-byte header.
        self.body_len = 0

        for flag in flags:
            self.flags.add(flag)

        if (not self.stream_id and
           self.stream_association == _STREAM_ASSOC_HAS_STREAM):
            raise ValueError('Stream ID must be non-zero')
        if (self.stream_id and
           self.stream_association == _STREAM_ASSOC_NO_STREAM):
            raise ValueError('Stream ID must be zero')

    def __repr__(self):
        flags = ", ".join(self.flags) or "None"
        body = binascii.hexlify(self.serialize_body()).decode('ascii')
        if len(body) > 20:
            body = body[:20] + "..."
        return (
            "{type}(Stream: {stream}; Flags: {flags}): {body}"
        ).format(
            type=type(self).__name__,
            stream=self.stream_id,
            flags=flags,
            body=body
        )

    @staticmethod
    def parse_frame_header(header, strict=False):
        """
        Takes a 9-byte frame header and returns a tuple of the appropriate
        Frame object and the length that needs to be read from the socket.

        This populates the flags field, and determines how long the body is.

        :param strict: Whether to raise an exception when encountering a frame
            not defined by spec and implemented by hyperframe.

        :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown
            type is received.

        .. versionchanged:: 5.0.0
            Added :param:`strict` to accommodate :class:`ExtensionFrame`
        """
        try:
            fields = _STRUCT_HBBBL.unpack(header)
        except struct.error:
            raise InvalidFrameError("Invalid frame header")

        # First 24 bits are frame length.
        length = (fields[0] << 8) + fields[1]
        type = fields[2]
        flags = fields[3]
        stream_id = fields[4] & 0x7FFFFFFF

        try:
            frame = FRAMES[type](stream_id)
        except KeyError:
            if strict:
                raise UnknownFrameError(type, length)
            frame = ExtensionFrame(type=type, stream_id=stream_id)

        frame.parse_flags(flags)
        return (frame, length)

    def parse_flags(self, flag_byte):
        for flag, flag_bit in self.defined_flags:
            if flag_byte & flag_bit:
                self.flags.add(flag)

        return self.flags

    def serialize(self):
        """
        Convert a frame into a bytestring, representing the serialized form of
        the frame.
        """
        body = self.serialize_body()
        self.body_len = len(body)

        # Build the common frame header.
        # First, get the flags.
        flags = 0

        for flag, flag_bit in self.defined_flags:
            if flag in self.flags:
                flags |= flag_bit

        header = _STRUCT_HBBBL.pack(
            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
            self.body_len & 0xFF,
            self.type,
            flags,
            self.stream_id & 0x7FFFFFFF  # Stream ID is 32 bits.
        )

        return header + body

    def serialize_body(self):
        raise NotImplementedError()

    def parse_body(self, data):
        """
        Given the body of a frame, parses it into frame data. This populates
        the non-header parts of the frame: that is, it does not populate the
        stream ID or flags.

        :param data: A memoryview object containing the body data of the frame.
                     Must not contain *more* data than the length returned by
                     :meth:`parse_frame_header
                     <hyperframe.frame.Frame.parse_frame_header>`.
        """
        raise NotImplementedError()


class Padding(object):
    """
    Mixin for frames that contain padding. Defines extra fields that can be
    used and set by frames that can be padded.
    """
    def __init__(self, stream_id, pad_length=0, **kwargs):
        super(Padding, self).__init__(stream_id, **kwargs)

        #: The length of the padding to use.
        self.pad_length = pad_length

    def serialize_padding_data(self):
        if 'PADDED' in self.flags:
            return _STRUCT_B.pack(self.pad_length)
        return b''

    def parse_padding_data(self, data):
        if 'PADDED' in self.flags:
            try:
                self.pad_length = struct.unpack('!B', data[:1])[0]
            except struct.error:
                raise InvalidFrameError("Invalid Padding data")
            return 1
        return 0

    @property
    def total_padding(self):
        return self.pad_length


class Priority(object):
    """
    Mixin for frames that contain priority data. Defines extra fields that can
    be used and set by frames that contain priority data.
    """
    def __init__(self,
                 stream_id,
                 depends_on=0x0,
                 stream_weight=0x0,
                 exclusive=False,
                 **kwargs):
        super(Priority, self).__init__(stream_id, **kwargs)

        #: The stream ID of the stream on which this stream depends.
        self.depends_on = depends_on

        #: The weight of the stream. This is an integer between 0 and 256.
        self.stream_weight = stream_weight

        #: Whether the exclusive bit was set.
        self.exclusive = exclusive

    def serialize_priority_data(self):
        return _STRUCT_LB.pack(
            self.depends_on + (0x80000000 if self.exclusive else 0),
            self.stream_weight
        )

    def parse_priority_data(self, data):
        try:
            self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5])
        except struct.error:
            raise InvalidFrameError("Invalid Priority data")

        self.exclusive = True if self.depends_on >> 31 else False
        self.depends_on &= 0x7FFFFFFF
        return 5


class DataFrame(Padding, Frame):
    """
    DATA frames convey arbitrary, variable-length sequences of octets
    associated with a stream. One or more DATA frames are used, for instance,
    to carry HTTP request or response payloads.
    """
    #: The flags defined for DATA frames.
    defined_flags = [
        Flag('END_STREAM', 0x01),
        Flag('PADDED', 0x08),
    ]

    #: The type byte for data frames.
    type = 0x0

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def __init__(self, stream_id, data=b'', **kwargs):
        super(DataFrame, self).__init__(stream_id, **kwargs)

        #: The data contained on this frame.
        self.data = data

    def serialize_body(self):
        padding_data = self.serialize_padding_data()
        padding = b'\0' * self.total_padding
        if isinstance(self.data, memoryview):
            self.data = self.data.tobytes()
        return b''.join([padding_data, self.data, padding])

    def parse_body(self, data):
        padding_data_length = self.parse_padding_data(data)
        self.data = (
            data[padding_data_length:len(data)-self.total_padding].tobytes()
        )
        self.body_len = len(data)

        if self.total_padding and self.total_padding >= self.body_len:
            raise InvalidPaddingError("Padding is too long.")

    @property
    def flow_controlled_length(self):
        """
        The length of the frame that needs to be accounted for when considering
        flow control.
        """
        padding_len = 0
        if 'PADDED' in self.flags:
            # Account for extra 1-byte padding length field, which is still
            # present if possibly zero-valued.
            padding_len = self.total_padding + 1
        return len(self.data) + padding_len


class PriorityFrame(Priority, Frame):
    """
    The PRIORITY frame specifies the sender-advised priority of a stream. It
    can be sent at any time for an existing stream. This enables
    reprioritisation of existing streams.
    """
    #: The flags defined for PRIORITY frames.
    defined_flags = []

    #: The type byte defined for PRIORITY frames.
    type = 0x02

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def serialize_body(self):
        return self.serialize_priority_data()

    def parse_body(self, data):
        self.parse_priority_data(data)
        self.body_len = len(data)


class RstStreamFrame(Frame):
    """
    The RST_STREAM frame allows for abnormal termination of a stream. When sent
    by the initiator of a stream, it indicates that they wish to cancel the
    stream or that an error condition has occurred. When sent by the receiver
    of a stream, it indicates that either the receiver is rejecting the stream,
    requesting that the stream be cancelled or that an error condition has
    occurred.
    """
    #: The flags defined for RST_STREAM frames.
    defined_flags = []

    #: The type byte defined for RST_STREAM frames.
    type = 0x03

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def __init__(self, stream_id, error_code=0, **kwargs):
        super(RstStreamFrame, self).__init__(stream_id, **kwargs)

        #: The error code used when resetting the stream.
        self.error_code = error_code

    def serialize_body(self):
        return _STRUCT_L.pack(self.error_code)

    def parse_body(self, data):
        if len(data) != 4:
            raise InvalidFrameError(
                "RST_STREAM must have 4 byte body: actual length %s." %
                len(data)
            )

        try:
            self.error_code = _STRUCT_L.unpack(data)[0]
        except struct.error:  # pragma: no cover
            raise InvalidFrameError("Invalid RST_STREAM body")

        self.body_len = 4


class SettingsFrame(Frame):
    """
    The SETTINGS frame conveys configuration parameters that affect how
    endpoints communicate. The parameters are either constraints on peer
    behavior or preferences.

    Settings are not negotiated. Settings describe characteristics of the
    sending peer, which are used by the receiving peer. Different values for
    the same setting can be advertised by each peer. For example, a client
    might set a high initial flow control window, whereas a server might set a
    lower value to conserve resources.
    """
    #: The flags defined for SETTINGS frames.
    defined_flags = [Flag('ACK', 0x01)]

    #: The type byte defined for SETTINGS frames.
    type = 0x04

    stream_association = _STREAM_ASSOC_NO_STREAM

    # We need to define the known settings, they may as well be class
    # attributes.
    #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting.
    HEADER_TABLE_SIZE = 0x01
    #: The byte that signals the SETTINGS_ENABLE_PUSH setting.
    ENABLE_PUSH = 0x02
    #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting.
    MAX_CONCURRENT_STREAMS = 0x03
    #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting.
    INITIAL_WINDOW_SIZE = 0x04
    #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting.
    MAX_FRAME_SIZE = 0x05
    #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting.
    MAX_HEADER_LIST_SIZE = 0x06
    #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting.
    ENABLE_CONNECT_PROTOCOL = 0x08

    def __init__(self, stream_id=0, settings=None, **kwargs):
        super(SettingsFrame, self).__init__(stream_id, **kwargs)

        if settings and "ACK" in kwargs.get("flags", ()):
            raise ValueError("Settings must be empty if ACK flag is set.")

        #: A dictionary of the setting type byte to the value of the setting.
        self.settings = settings or {}

    def serialize_body(self):
        return b''.join([_STRUCT_HL.pack(setting & 0xFF, value)
                         for setting, value in self.settings.items()])

    def parse_body(self, data):
        body_len = 0
        for i in range(0, len(data), 6):
            try:
                name, value = _STRUCT_HL.unpack(data[i:i+6])
            except struct.error:
                raise InvalidFrameError("Invalid SETTINGS body")

            self.settings[name] = value
            body_len += 6

        self.body_len = body_len


class PushPromiseFrame(Padding, Frame):
    """
    The PUSH_PROMISE frame is used to notify the peer endpoint in advance of
    streams the sender intends to initiate.
    """
    #: The flags defined for PUSH_PROMISE frames.
    defined_flags = [
        Flag('END_HEADERS', 0x04),
        Flag('PADDED', 0x08)
    ]

    #: The type byte defined for PUSH_PROMISE frames.
    type = 0x05

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def __init__(self, stream_id, promised_stream_id=0, data=b'', **kwargs):
        super(PushPromiseFrame, self).__init__(stream_id, **kwargs)

        #: The stream ID that is promised by this frame.
        self.promised_stream_id = promised_stream_id

        #: The HPACK-encoded header block for the simulated request on the new
        #: stream.
        self.data = data

    def serialize_body(self):
        padding_data = self.serialize_padding_data()
        padding = b'\0' * self.total_padding
        data = _STRUCT_L.pack(self.promised_stream_id)
        return b''.join([padding_data, data, self.data, padding])

    def parse_body(self, data):
        padding_data_length = self.parse_padding_data(data)

        try:
            self.promised_stream_id = _STRUCT_L.unpack(
                data[padding_data_length:padding_data_length + 4]
            )[0]
        except struct.error:
            raise InvalidFrameError("Invalid PUSH_PROMISE body")

        self.data = data[padding_data_length + 4:].tobytes()
        self.body_len = len(data)

        if self.total_padding and self.total_padding >= self.body_len:
            raise InvalidPaddingError("Padding is too long.")


class PingFrame(Frame):
    """
    The PING frame is a mechanism for measuring a minimal round-trip time from
    the sender, as well as determining whether an idle connection is still
    functional. PING frames can be sent from any endpoint.
    """
    #: The flags defined for PING frames.
    defined_flags = [Flag('ACK', 0x01)]

    #: The type byte defined for PING frames.
    type = 0x06

    stream_association = _STREAM_ASSOC_NO_STREAM

    def __init__(self, stream_id=0, opaque_data=b'', **kwargs):
        super(PingFrame, self).__init__(stream_id, **kwargs)

        #: The opaque data sent in this PING frame, as a bytestring.
        self.opaque_data = opaque_data

    def serialize_body(self):
        if len(self.opaque_data) > 8:
            raise InvalidFrameError(
                "PING frame may not have more than 8 bytes of data, got %s" %
                self.opaque_data
            )

        data = self.opaque_data
        data += b'\x00' * (8 - len(self.opaque_data))
        return data

    def parse_body(self, data):
        if len(data) != 8:
            raise InvalidFrameError(
                "PING frame must have 8 byte length: got %s" % len(data)
            )

        self.opaque_data = data.tobytes()
        self.body_len = 8


class GoAwayFrame(Frame):
    """
    The GOAWAY frame informs the remote peer to stop creating streams on this
    connection. It can be sent from the client or the server. Once sent, the
    sender will ignore frames sent on new streams for the remainder of the
    connection.
    """
    #: The flags defined for GOAWAY frames.
    defined_flags = []

    #: The type byte defined for GOAWAY frames.
    type = 0x07

    stream_association = _STREAM_ASSOC_NO_STREAM

    def __init__(self,
                 stream_id=0,
                 last_stream_id=0,
                 error_code=0,
                 additional_data=b'',
                 **kwargs):
        super(GoAwayFrame, self).__init__(stream_id, **kwargs)

        #: The last stream ID definitely seen by the remote peer.
        self.last_stream_id = last_stream_id

        #: The error code for connection teardown.
        self.error_code = error_code

        #: Any additional data sent in the GOAWAY.
        self.additional_data = additional_data

    def serialize_body(self):
        data = _STRUCT_LL.pack(
            self.last_stream_id & 0x7FFFFFFF,
            self.error_code
        )
        data += self.additional_data

        return data

    def parse_body(self, data):
        try:
            self.last_stream_id, self.error_code = _STRUCT_LL.unpack(
                data[:8]
            )
        except struct.error:
            raise InvalidFrameError("Invalid GOAWAY body.")

        self.body_len = len(data)

        if len(data) > 8:
            self.additional_data = data[8:].tobytes()


class WindowUpdateFrame(Frame):
    """
    The WINDOW_UPDATE frame is used to implement flow control.

    Flow control operates at two levels: on each individual stream and on the
    entire connection.

    Both types of flow control are hop by hop; that is, only between the two
    endpoints. Intermediaries do not forward WINDOW_UPDATE frames between
    dependent connections. However, throttling of data transfer by any receiver
    can indirectly cause the propagation of flow control information toward the
    original sender.
    """
    #: The flags defined for WINDOW_UPDATE frames.
    defined_flags = []

    #: The type byte defined for WINDOW_UPDATE frames.
    type = 0x08

    stream_association = _STREAM_ASSOC_EITHER

    def __init__(self, stream_id, window_increment=0, **kwargs):
        super(WindowUpdateFrame, self).__init__(stream_id, **kwargs)

        #: The amount the flow control window is to be incremented.
        self.window_increment = window_increment

    def serialize_body(self):
        return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF)

    def parse_body(self, data):
        try:
            self.window_increment = _STRUCT_L.unpack(data)[0]
        except struct.error:
            raise InvalidFrameError("Invalid WINDOW_UPDATE body")

        self.body_len = 4


class HeadersFrame(Padding, Priority, Frame):
    """
    The HEADERS frame carries name-value pairs. It is used to open a stream.
    HEADERS frames can be sent on a stream in the "open" or "half closed
    (remote)" states.

    The HeadersFrame class is actually basically a data frame in this
    implementation, because of the requirement to control the sizes of frames.
    A header block fragment that doesn't fit in an entire HEADERS frame needs
    to be followed with CONTINUATION frames. From the perspective of the frame
    building code the header block is an opaque data segment.
    """
    #: The flags defined for HEADERS frames.
    defined_flags = [
        Flag('END_STREAM', 0x01),
        Flag('END_HEADERS', 0x04),
        Flag('PADDED', 0x08),
        Flag('PRIORITY', 0x20),
    ]

    #: The type byte defined for HEADERS frames.
    type = 0x01

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def __init__(self, stream_id, data=b'', **kwargs):
        super(HeadersFrame, self).__init__(stream_id, **kwargs)

        #: The HPACK-encoded header block.
        self.data = data

    def serialize_body(self):
        padding_data = self.serialize_padding_data()
        padding = b'\0' * self.total_padding

        if 'PRIORITY' in self.flags:
            priority_data = self.serialize_priority_data()
        else:
            priority_data = b''

        return b''.join([padding_data, priority_data, self.data, padding])

    def parse_body(self, data):
        padding_data_length = self.parse_padding_data(data)
        data = data[padding_data_length:]

        if 'PRIORITY' in self.flags:
            priority_data_length = self.parse_priority_data(data)
        else:
            priority_data_length = 0

        self.body_len = len(data)
        self.data = (
            data[priority_data_length:len(data)-self.total_padding].tobytes()
        )

        if self.total_padding and self.total_padding >= self.body_len:
            raise InvalidPaddingError("Padding is too long.")


class ContinuationFrame(Frame):
    """
    The CONTINUATION frame is used to continue a sequence of header block
    fragments. Any number of CONTINUATION frames can be sent on an existing
    stream, as long as the preceding frame on the same stream is one of
    HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set.

    Much like the HEADERS frame, hyper treats this as an opaque data frame with
    different flags and a different type.
    """
    #: The flags defined for CONTINUATION frames.
    defined_flags = [Flag('END_HEADERS', 0x04)]

    #: The type byte defined for CONTINUATION frames.
    type = 0x09

    stream_association = _STREAM_ASSOC_HAS_STREAM

    def __init__(self, stream_id, data=b'', **kwargs):
        super(ContinuationFrame, self).__init__(stream_id, **kwargs)

        #: The HPACK-encoded header block.
        self.data = data

    def serialize_body(self):
        return self.data

    def parse_body(self, data):
        self.data = data.tobytes()
        self.body_len = len(data)


class AltSvcFrame(Frame):
    """
    The ALTSVC frame is used to advertise alternate services that the current
    host, or a different one, can understand. This frame is standardised as
    part of RFC 7838.

    This frame does no work to validate that the ALTSVC field parameter is
    acceptable per the rules of RFC 7838.

    .. note:: If the ``stream_id`` of this frame is nonzero, the origin field
              must have zero length. Conversely, if the ``stream_id`` of this
              frame is zero, the origin field must have nonzero length. Put
              another way, a valid ALTSVC frame has ``stream_id != 0`` XOR
              ``len(origin) != 0``.
    """
    type = 0xA

    stream_association = _STREAM_ASSOC_EITHER

    def __init__(self, stream_id, origin=b'', field=b'', **kwargs):
        super(AltSvcFrame, self).__init__(stream_id, **kwargs)

        if not isinstance(origin, bytes):
            raise ValueError("AltSvc origin must be bytestring.")
        if not isinstance(field, bytes):
            raise ValueError("AltSvc field must be a bytestring.")
        self.origin = origin
        self.field = field

    def serialize_body(self):
        origin_len = _STRUCT_H.pack(len(self.origin))
        return b''.join([origin_len, self.origin, self.field])

    def parse_body(self, data):
        try:
            origin_len = _STRUCT_H.unpack(data[0:2])[0]
            self.origin = data[2:2+origin_len].tobytes()

            if len(self.origin) != origin_len:
                raise InvalidFrameError("Invalid ALTSVC frame body.")

            self.field = data[2+origin_len:].tobytes()
        except (struct.error, ValueError):
            raise InvalidFrameError("Invalid ALTSVC frame body.")

        self.body_len = len(data)


class ExtensionFrame(Frame):
    """
    ExtensionFrame is used to wrap frames which are not natively interpretable
    by hyperframe.

    Although certain byte prefixes are ordained by specification to have
    certain contextual meanings, frames with other prefixes are not prohibited,
    and may be used to communicate arbitrary meaning between HTTP/2 peers.

    Thus, hyperframe, rather than raising an exception when such a frame is
    encountered, wraps it in a generic frame to be properly acted upon by
    upstream consumers which might have additional context on how to use it.

    .. versionadded:: 5.0.0
    """

    stream_association = _STREAM_ASSOC_EITHER

    def __init__(self, type, stream_id, **kwargs):
        super(ExtensionFrame, self).__init__(stream_id, **kwargs)
        self.type = type
        self.flag_byte = None

    def parse_flags(self, flag_byte):
        """
        For extension frames, we parse the flags by just storing a flag byte.
        """
        self.flag_byte = flag_byte

    def parse_body(self, data):
        self.body = data.tobytes()
        self.body_len = len(data)

    def serialize(self):
        """
        A broad override of the serialize method that ensures that the data
        comes back out exactly as it came in. This should not be used in most
        user code: it exists only as a helper method if frames need to be
        reconstituted.
        """
        # Build the frame header.
        # First, get the flags.
        flags = self.flag_byte

        header = _STRUCT_HBBBL.pack(
            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
            self.body_len & 0xFF,
            self.type,
            flags,
            self.stream_id & 0x7FFFFFFF  # Stream ID is 32 bits.
        )

        return header + self.body


_FRAME_CLASSES = [
    DataFrame,
    HeadersFrame,
    PriorityFrame,
    RstStreamFrame,
    SettingsFrame,
    PushPromiseFrame,
    PingFrame,
    GoAwayFrame,
    WindowUpdateFrame,
    ContinuationFrame,
    AltSvcFrame,
]
#: FRAMES maps the type byte for each frame to the class used to represent that
#: frame.
FRAMES = dict((cls.type, cls) for cls in _FRAME_CLASSES)
0707010001f480000081a40000000000000000000000016a100daf00000088000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hyperframe/__init__.py # -*- coding: utf-8 -*-
"""
hyperframe
~~~~~~~~~~

A module for providing a pure-Python HTTP/2 framing layer.
"""
__version__ = '5.2.0'
0707010001f481000081a40000000000000000000000016a100daf000003a8000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyperframe/exceptions.py   # -*- coding: utf-8 -*-
"""
hyperframe/exceptions
~~~~~~~~~~~~~~~~~~~~~

Defines the exceptions that can be thrown by hyperframe.
"""


class UnknownFrameError(ValueError):
    """
    An frame of unknown type was received.
    """
    def __init__(self, frame_type, length):
        #: The type byte of the unknown frame that was received.
        self.frame_type = frame_type

        #: The length of the data portion of the unknown frame.
        self.length = length

    def __str__(self):
        return (
            "UnknownFrameError: Unknown frame type 0x%X received, "
            "length %d bytes" % (self.frame_type, self.length)
        )


class InvalidPaddingError(ValueError):
    """
    A frame with invalid padding was received.
    """
    pass


class InvalidFrameError(ValueError):
    """
    Parsing a frame failed because the data was not laid out appropriately.

    .. versionadded:: 3.0.2
    """
    pass
0707010001f4a5000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002b00000000root/usr/share/opensvc/opensvc/foreign/ply    0707010001f4a6000081a40000000000000000000000016a100daf00000067000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/ply/__init__.py    # PLY package
# Author: David Beazley (dave@dabeaz.com)

__version__ = '3.11'
__all__ = ['lex','yacc']
 0707010001f4ab000081a40000000000000000000000016a100daf000008c6000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/ply/ygen.py    # ply: ygen.py
#
# This is a support program that auto-generates different versions of the YACC parsing
# function with different features removed for the purposes of performance.
#
# Users should edit the method LRParser.parsedebug() in yacc.py.   The source code
# for that method is then used to create the other methods.   See the comments in
# yacc.py for further details.

import os.path
import shutil

def get_source_range(lines, tag):
    srclines = enumerate(lines)
    start_tag = '#--! %s-start' % tag
    end_tag = '#--! %s-end' % tag

    for start_index, line in srclines:
        if line.strip().startswith(start_tag):
            break

    for end_index, line in srclines:
        if line.strip().endswith(end_tag):
            break

    return (start_index + 1, end_index)

def filter_section(lines, tag):
    filtered_lines = []
    include = True
    tag_text = '#--! %s' % tag
    for line in lines:
        if line.strip().startswith(tag_text):
            include = not include
        elif include:
            filtered_lines.append(line)
    return filtered_lines

def main():
    dirname = os.path.dirname(__file__)
    shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak'))
    with open(os.path.join(dirname, 'yacc.py'), 'r') as f:
        lines = f.readlines()

    parse_start, parse_end = get_source_range(lines, 'parsedebug')
    parseopt_start, parseopt_end = get_source_range(lines, 'parseopt')
    parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack')

    # Get the original source
    orig_lines = lines[parse_start:parse_end]

    # Filter the DEBUG sections out
    parseopt_lines = filter_section(orig_lines, 'DEBUG')

    # Filter the TRACKING sections out
    parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING')

    # Replace the parser source sections with updated versions
    lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines
    lines[parseopt_start:parseopt_end] = parseopt_lines

    lines = [line.rstrip()+'\n' for line in lines]
    with open(os.path.join(dirname, 'yacc.py'), 'w') as f:
        f.writelines(lines)

    print('Updated yacc.py')

if __name__ == '__main__':
    main()
  0707010001f4aa000081a40000000000000000000000016a100daf00021a08000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/ply/yacc.py    # -----------------------------------------------------------------------------
# ply: yacc.py
#
# Copyright (C) 2001-2018
# David M. Beazley (Dabeaz LLC)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the David Beazley or Dabeaz LLC may be used to
#   endorse or promote products derived from this software without
#  specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------
#
# This implements an LR parser that is constructed from grammar rules defined
# as Python functions. The grammar is specified by supplying the BNF inside
# Python documentation strings.  The inspiration for this technique was borrowed
# from John Aycock's Spark parsing system.  PLY might be viewed as cross between
# Spark and the GNU bison utility.
#
# The current implementation is only somewhat object-oriented. The
# LR parser itself is defined in terms of an object (which allows multiple
# parsers to co-exist).  However, most of the variables used during table
# construction are defined in terms of global variables.  Users shouldn't
# notice unless they are trying to define multiple parsers at the same
# time using threads (in which case they should have their head examined).
#
# This implementation supports both SLR and LALR(1) parsing.  LALR(1)
# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
# Techniques, and Tools" (The Dragon Book).  LALR(1) has since been replaced
# by the more efficient DeRemer and Pennello algorithm.
#
# :::::::: WARNING :::::::
#
# Construction of LR parsing tables is fairly complicated and expensive.
# To make this module run fast, a *LOT* of work has been put into
# optimization---often at the expensive of readability and what might
# consider to be good Python "coding style."   Modify the code at your
# own risk!
# ----------------------------------------------------------------------------

import re
import types
import sys
import os.path
import inspect
import warnings

__version__    = '3.11'
__tabversion__ = '3.10'

#-----------------------------------------------------------------------------
#                     === User configurable parameters ===
#
# Change these to modify the default behavior of yacc (if you wish)
#-----------------------------------------------------------------------------

yaccdebug   = True             # Debugging mode.  If set, yacc generates a
                               # a 'parser.out' file in the current directory

debug_file  = 'parser.out'     # Default name of the debugging file
tab_module  = 'parsetab'       # Default name of the table module
default_lr  = 'LALR'           # Default LR table generation method

error_count = 3                # Number of symbols that must be shifted to leave recovery mode

yaccdevel   = False            # Set to True if developing yacc.  This turns off optimized
                               # implementations of certain functions.

resultlimit = 40               # Size limit of results when running in debug mode.

pickle_protocol = 0            # Protocol to use when writing pickle files

# String type-checking compatibility
if sys.version_info[0] < 3:
    string_types = basestring
else:
    string_types = str

MAXINT = sys.maxsize

# This object is a stand-in for a logging object created by the
# logging module.   PLY will use this by default to create things
# such as the parser.out file.  If a user wants more detailed
# information, they can create their own logging object and pass
# it into PLY.

class PlyLogger(object):
    def __init__(self, f):
        self.f = f

    def debug(self, msg, *args, **kwargs):
        self.f.write((msg % args) + '\n')

    info = debug

    def warning(self, msg, *args, **kwargs):
        self.f.write('WARNING: ' + (msg % args) + '\n')

    def error(self, msg, *args, **kwargs):
        self.f.write('ERROR: ' + (msg % args) + '\n')

    critical = debug

# Null logger is used when no output is generated. Does nothing.
class NullLogger(object):
    def __getattribute__(self, name):
        return self

    def __call__(self, *args, **kwargs):
        return self

# Exception raised for yacc-related errors
class YaccError(Exception):
    pass

# Format the result message that the parser produces when running in debug mode.
def format_result(r):
    repr_str = repr(r)
    if '\n' in repr_str:
        repr_str = repr(repr_str)
    if len(repr_str) > resultlimit:
        repr_str = repr_str[:resultlimit] + ' ...'
    result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str)
    return result

# Format stack entries when the parser is running in debug mode
def format_stack_entry(r):
    repr_str = repr(r)
    if '\n' in repr_str:
        repr_str = repr(repr_str)
    if len(repr_str) < 16:
        return repr_str
    else:
        return '<%s @ 0x%x>' % (type(r).__name__, id(r))

# Panic mode error recovery support.   This feature is being reworked--much of the
# code here is to offer a deprecation/backwards compatible transition

_errok = None
_token = None
_restart = None
_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error().
Instead, invoke the methods on the associated parser instance:

    def p_error(p):
        ...
        # Use parser.errok(), parser.token(), parser.restart()
        ...

    parser = yacc.yacc()
'''

def errok():
    warnings.warn(_warnmsg)
    return _errok()

def restart():
    warnings.warn(_warnmsg)
    return _restart()

def token():
    warnings.warn(_warnmsg)
    return _token()

# Utility function to call the p_error() function with some deprecation hacks
def call_errorfunc(errorfunc, token, parser):
    global _errok, _token, _restart
    _errok = parser.errok
    _token = parser.token
    _restart = parser.restart
    r = errorfunc(token)
    try:
        del _errok, _token, _restart
    except NameError:
        pass
    return r

#-----------------------------------------------------------------------------
#                        ===  LR Parsing Engine ===
#
# The following classes are used for the LR parser itself.  These are not
# used during table construction and are independent of the actual LR
# table generation algorithm
#-----------------------------------------------------------------------------

# This class is used to hold non-terminal grammar symbols during parsing.
# It normally has the following attributes set:
#        .type       = Grammar symbol type
#        .value      = Symbol value
#        .lineno     = Starting line number
#        .endlineno  = Ending line number (optional, set automatically)
#        .lexpos     = Starting lex position
#        .endlexpos  = Ending lex position (optional, set automatically)

class YaccSymbol:
    def __str__(self):
        return self.type

    def __repr__(self):
        return str(self)

# This class is a wrapper around the objects actually passed to each
# grammar rule.   Index lookup and assignment actually assign the
# .value attribute of the underlying YaccSymbol object.
# The lineno() method returns the line number of a given
# item (or 0 if not defined).   The linespan() method returns
# a tuple of (startline,endline) representing the range of lines
# for a symbol.  The lexspan() method returns a tuple (lexpos,endlexpos)
# representing the range of positional information for a symbol.

class YaccProduction:
    def __init__(self, s, stack=None):
        self.slice = s
        self.stack = stack
        self.lexer = None
        self.parser = None

    def __getitem__(self, n):
        if isinstance(n, slice):
            return [s.value for s in self.slice[n]]
        elif n >= 0:
            return self.slice[n].value
        else:
            return self.stack[n].value

    def __setitem__(self, n, v):
        self.slice[n].value = v

    def __getslice__(self, i, j):
        return [s.value for s in self.slice[i:j]]

    def __len__(self):
        return len(self.slice)

    def lineno(self, n):
        return getattr(self.slice[n], 'lineno', 0)

    def set_lineno(self, n, lineno):
        self.slice[n].lineno = lineno

    def linespan(self, n):
        startline = getattr(self.slice[n], 'lineno', 0)
        endline = getattr(self.slice[n], 'endlineno', startline)
        return startline, endline

    def lexpos(self, n):
        return getattr(self.slice[n], 'lexpos', 0)

    def set_lexpos(self, n, lexpos):
        self.slice[n].lexpos = lexpos

    def lexspan(self, n):
        startpos = getattr(self.slice[n], 'lexpos', 0)
        endpos = getattr(self.slice[n], 'endlexpos', startpos)
        return startpos, endpos

    def error(self):
        raise SyntaxError

# -----------------------------------------------------------------------------
#                               == LRParser ==
#
# The LR Parsing engine.
# -----------------------------------------------------------------------------

class LRParser:
    def __init__(self, lrtab, errorf):
        self.productions = lrtab.lr_productions
        self.action = lrtab.lr_action
        self.goto = lrtab.lr_goto
        self.errorfunc = errorf
        self.set_defaulted_states()
        self.errorok = True

    def errok(self):
        self.errorok = True

    def restart(self):
        del self.statestack[:]
        del self.symstack[:]
        sym = YaccSymbol()
        sym.type = '$end'
        self.symstack.append(sym)
        self.statestack.append(0)

    # Defaulted state support.
    # This method identifies parser states where there is only one possible reduction action.
    # For such states, the parser can make a choose to make a rule reduction without consuming
    # the next look-ahead token.  This delayed invocation of the tokenizer can be useful in
    # certain kinds of advanced parsing situations where the lexer and parser interact with
    # each other or change states (i.e., manipulation of scope, lexer states, etc.).
    #
    # See:  http://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions
    def set_defaulted_states(self):
        self.defaulted_states = {}
        for state, actions in self.action.items():
            rules = list(actions.values())
            if len(rules) == 1 and rules[0] < 0:
                self.defaulted_states[state] = rules[0]

    def disable_defaulted_states(self):
        self.defaulted_states = {}

    def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
        if debug or yaccdevel:
            if isinstance(debug, int):
                debug = PlyLogger(sys.stderr)
            return self.parsedebug(input, lexer, debug, tracking, tokenfunc)
        elif tracking:
            return self.parseopt(input, lexer, debug, tracking, tokenfunc)
        else:
            return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)


    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # parsedebug().
    #
    # This is the debugging enabled version of parse().  All changes made to the
    # parsing engine should be made here.   Optimized versions of this function
    # are automatically created by the ply/ygen.py script.  This script cuts out
    # sections enclosed in markers such as this:
    #
    #      #--! DEBUG
    #      statements
    #      #--! DEBUG
    #
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
        #--! parsedebug-start
        lookahead = None                         # Current lookahead symbol
        lookaheadstack = []                      # Stack of lookahead symbols
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
        defaulted_states = self.defaulted_states # Local reference to defaulted states
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
        errorcount = 0                           # Used during error recovery

        #--! DEBUG
        debug.info('PLY: PARSE DEBUG START')
        #--! DEBUG

        # If no lexer was given, we will try to use the lex module
        if not lexer:
            from . import lex
            lexer = lex.lexer

        # Set up the lexer and parser objects on pslice
        pslice.lexer = lexer
        pslice.parser = self

        # If input was supplied, pass to lexer
        if input is not None:
            lexer.input(input)

        if tokenfunc is None:
            # Tokenize function
            get_token = lexer.token
        else:
            get_token = tokenfunc

        # Set the parser() token method (sometimes used in error recovery)
        self.token = get_token

        # Set up the state and symbol stacks

        statestack = []                # Stack of parsing states
        self.statestack = statestack
        symstack   = []                # Stack of grammar symbols
        self.symstack = symstack

        pslice.stack = symstack         # Put in the production
        errtoken   = None               # Err token

        # The start state is assumed to be (0,$end)

        statestack.append(0)
        sym = YaccSymbol()
        sym.type = '$end'
        symstack.append(sym)
        state = 0
        while True:
            # Get the next symbol on the input.  If a lookahead symbol
            # is already set, we just use that. Otherwise, we'll pull
            # the next token off of the lookaheadstack or from the lexer

            #--! DEBUG
            debug.debug('')
            debug.debug('State  : %s', state)
            #--! DEBUG

            if state not in defaulted_states:
                if not lookahead:
                    if not lookaheadstack:
                        lookahead = get_token()     # Get the next token
                    else:
                        lookahead = lookaheadstack.pop()
                    if not lookahead:
                        lookahead = YaccSymbol()
                        lookahead.type = '$end'

                # Check the action table
                ltype = lookahead.type
                t = actions[state].get(ltype)
            else:
                t = defaulted_states[state]
                #--! DEBUG
                debug.debug('Defaulted state %s: Reduce using %d', state, -t)
                #--! DEBUG

            #--! DEBUG
            debug.debug('Stack  : %s',
                        ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
            #--! DEBUG

            if t is not None:
                if t > 0:
                    # shift a symbol on the stack
                    statestack.append(t)
                    state = t

                    #--! DEBUG
                    debug.debug('Action : Shift and goto state %s', t)
                    #--! DEBUG

                    symstack.append(lookahead)
                    lookahead = None

                    # Decrease error count on successful shift
                    if errorcount:
                        errorcount -= 1
                    continue

                if t < 0:
                    # reduce a symbol on the stack, emit a production
                    p = prod[-t]
                    pname = p.name
                    plen  = p.len

                    # Get production function
                    sym = YaccSymbol()
                    sym.type = pname       # Production name
                    sym.value = None

                    #--! DEBUG
                    if plen:
                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str,
                                   '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']',
                                   goto[statestack[-1-plen]][pname])
                    else:
                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [],
                                   goto[statestack[-1]][pname])

                    #--! DEBUG

                    if plen:
                        targ = symstack[-plen-1:]
                        targ[0] = sym

                        #--! TRACKING
                        if tracking:
                            t1 = targ[1]
                            sym.lineno = t1.lineno
                            sym.lexpos = t1.lexpos
                            t1 = targ[-1]
                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
                        #--! TRACKING

                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # below as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            del symstack[-plen:]
                            self.state = state
                            p.callable(pslice)
                            del statestack[-plen:]
                            #--! DEBUG
                            debug.info('Result : %s', format_result(pslice[0]))
                            #--! DEBUG
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                    else:

                        #--! TRACKING
                        if tracking:
                            sym.lineno = lexer.lineno
                            sym.lexpos = lexer.lexpos
                        #--! TRACKING

                        targ = [sym]

                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # above as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            self.state = state
                            p.callable(pslice)
                            #--! DEBUG
                            debug.info('Result : %s', format_result(pslice[0]))
                            #--! DEBUG
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                if t == 0:
                    n = symstack[-1]
                    result = getattr(n, 'value', None)
                    #--! DEBUG
                    debug.info('Done   : Returning %s', format_result(result))
                    debug.info('PLY: PARSE DEBUG END')
                    #--! DEBUG
                    return result

            if t is None:

                #--! DEBUG
                debug.error('Error  : %s',
                            ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
                #--! DEBUG

                # We have some kind of parsing error here.  To handle
                # this, we are going to push the current token onto
                # the tokenstack and replace it with an 'error' token.
                # If there are any synchronization rules, they may
                # catch it.
                #
                # In addition to pushing the error token, we call call
                # the user defined p_error() function if this is the
                # first syntax error.  This function is only called if
                # errorcount == 0.
                if errorcount == 0 or self.errorok:
                    errorcount = error_count
                    self.errorok = False
                    errtoken = lookahead
                    if errtoken.type == '$end':
                        errtoken = None               # End of file!
                    if self.errorfunc:
                        if errtoken and not hasattr(errtoken, 'lexer'):
                            errtoken.lexer = lexer
                        self.state = state
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                        if self.errorok:
                            # User must have done some kind of panic
                            # mode recovery on their own.  The
                            # returned token is the next lookahead
                            lookahead = tok
                            errtoken = None
                            continue
                    else:
                        if errtoken:
                            if hasattr(errtoken, 'lineno'):
                                lineno = lookahead.lineno
                            else:
                                lineno = 0
                            if lineno:
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
                            else:
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                        else:
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                            return

                else:
                    errorcount = error_count

                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
                # entire parse has been rolled back and we're completely hosed.   The token is
                # discarded and we just keep going.

                if len(statestack) <= 1 and lookahead.type != '$end':
                    lookahead = None
                    errtoken = None
                    state = 0
                    # Nuke the pushback stack
                    del lookaheadstack[:]
                    continue

                # case 2: the statestack has a couple of entries on it, but we're
                # at the end of the file. nuke the top entry and generate an error token

                # Start nuking entries on the stack
                if lookahead.type == '$end':
                    # Whoa. We're really hosed here. Bail out
                    return

                if lookahead.type != 'error':
                    sym = symstack[-1]
                    if sym.type == 'error':
                        # Hmmm. Error is on top of stack, we'll just nuke input
                        # symbol and continue
                        #--! TRACKING
                        if tracking:
                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
                        #--! TRACKING
                        lookahead = None
                        continue

                    # Create the error symbol for the first time and make it the new lookahead symbol
                    t = YaccSymbol()
                    t.type = 'error'

                    if hasattr(lookahead, 'lineno'):
                        t.lineno = t.endlineno = lookahead.lineno
                    if hasattr(lookahead, 'lexpos'):
                        t.lexpos = t.endlexpos = lookahead.lexpos
                    t.value = lookahead
                    lookaheadstack.append(lookahead)
                    lookahead = t
                else:
                    sym = symstack.pop()
                    #--! TRACKING
                    if tracking:
                        lookahead.lineno = sym.lineno
                        lookahead.lexpos = sym.lexpos
                    #--! TRACKING
                    statestack.pop()
                    state = statestack[-1]

                continue

            # Call an error function here
            raise RuntimeError('yacc: internal parser error!!!\n')

        #--! parsedebug-end

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # parseopt().
    #
    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY!
    # This code is automatically generated by the ply/ygen.py script. Make
    # changes to the parsedebug() method instead.
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
        #--! parseopt-start
        lookahead = None                         # Current lookahead symbol
        lookaheadstack = []                      # Stack of lookahead symbols
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
        defaulted_states = self.defaulted_states # Local reference to defaulted states
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
        errorcount = 0                           # Used during error recovery


        # If no lexer was given, we will try to use the lex module
        if not lexer:
            from . import lex
            lexer = lex.lexer

        # Set up the lexer and parser objects on pslice
        pslice.lexer = lexer
        pslice.parser = self

        # If input was supplied, pass to lexer
        if input is not None:
            lexer.input(input)

        if tokenfunc is None:
            # Tokenize function
            get_token = lexer.token
        else:
            get_token = tokenfunc

        # Set the parser() token method (sometimes used in error recovery)
        self.token = get_token

        # Set up the state and symbol stacks

        statestack = []                # Stack of parsing states
        self.statestack = statestack
        symstack   = []                # Stack of grammar symbols
        self.symstack = symstack

        pslice.stack = symstack         # Put in the production
        errtoken   = None               # Err token

        # The start state is assumed to be (0,$end)

        statestack.append(0)
        sym = YaccSymbol()
        sym.type = '$end'
        symstack.append(sym)
        state = 0
        while True:
            # Get the next symbol on the input.  If a lookahead symbol
            # is already set, we just use that. Otherwise, we'll pull
            # the next token off of the lookaheadstack or from the lexer


            if state not in defaulted_states:
                if not lookahead:
                    if not lookaheadstack:
                        lookahead = get_token()     # Get the next token
                    else:
                        lookahead = lookaheadstack.pop()
                    if not lookahead:
                        lookahead = YaccSymbol()
                        lookahead.type = '$end'

                # Check the action table
                ltype = lookahead.type
                t = actions[state].get(ltype)
            else:
                t = defaulted_states[state]


            if t is not None:
                if t > 0:
                    # shift a symbol on the stack
                    statestack.append(t)
                    state = t


                    symstack.append(lookahead)
                    lookahead = None

                    # Decrease error count on successful shift
                    if errorcount:
                        errorcount -= 1
                    continue

                if t < 0:
                    # reduce a symbol on the stack, emit a production
                    p = prod[-t]
                    pname = p.name
                    plen  = p.len

                    # Get production function
                    sym = YaccSymbol()
                    sym.type = pname       # Production name
                    sym.value = None


                    if plen:
                        targ = symstack[-plen-1:]
                        targ[0] = sym

                        #--! TRACKING
                        if tracking:
                            t1 = targ[1]
                            sym.lineno = t1.lineno
                            sym.lexpos = t1.lexpos
                            t1 = targ[-1]
                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
                        #--! TRACKING

                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # below as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            del symstack[-plen:]
                            self.state = state
                            p.callable(pslice)
                            del statestack[-plen:]
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                    else:

                        #--! TRACKING
                        if tracking:
                            sym.lineno = lexer.lineno
                            sym.lexpos = lexer.lexpos
                        #--! TRACKING

                        targ = [sym]

                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # above as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            self.state = state
                            p.callable(pslice)
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                if t == 0:
                    n = symstack[-1]
                    result = getattr(n, 'value', None)
                    return result

            if t is None:


                # We have some kind of parsing error here.  To handle
                # this, we are going to push the current token onto
                # the tokenstack and replace it with an 'error' token.
                # If there are any synchronization rules, they may
                # catch it.
                #
                # In addition to pushing the error token, we call call
                # the user defined p_error() function if this is the
                # first syntax error.  This function is only called if
                # errorcount == 0.
                if errorcount == 0 or self.errorok:
                    errorcount = error_count
                    self.errorok = False
                    errtoken = lookahead
                    if errtoken.type == '$end':
                        errtoken = None               # End of file!
                    if self.errorfunc:
                        if errtoken and not hasattr(errtoken, 'lexer'):
                            errtoken.lexer = lexer
                        self.state = state
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                        if self.errorok:
                            # User must have done some kind of panic
                            # mode recovery on their own.  The
                            # returned token is the next lookahead
                            lookahead = tok
                            errtoken = None
                            continue
                    else:
                        if errtoken:
                            if hasattr(errtoken, 'lineno'):
                                lineno = lookahead.lineno
                            else:
                                lineno = 0
                            if lineno:
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
                            else:
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                        else:
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                            return

                else:
                    errorcount = error_count

                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
                # entire parse has been rolled back and we're completely hosed.   The token is
                # discarded and we just keep going.

                if len(statestack) <= 1 and lookahead.type != '$end':
                    lookahead = None
                    errtoken = None
                    state = 0
                    # Nuke the pushback stack
                    del lookaheadstack[:]
                    continue

                # case 2: the statestack has a couple of entries on it, but we're
                # at the end of the file. nuke the top entry and generate an error token

                # Start nuking entries on the stack
                if lookahead.type == '$end':
                    # Whoa. We're really hosed here. Bail out
                    return

                if lookahead.type != 'error':
                    sym = symstack[-1]
                    if sym.type == 'error':
                        # Hmmm. Error is on top of stack, we'll just nuke input
                        # symbol and continue
                        #--! TRACKING
                        if tracking:
                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
                        #--! TRACKING
                        lookahead = None
                        continue

                    # Create the error symbol for the first time and make it the new lookahead symbol
                    t = YaccSymbol()
                    t.type = 'error'

                    if hasattr(lookahead, 'lineno'):
                        t.lineno = t.endlineno = lookahead.lineno
                    if hasattr(lookahead, 'lexpos'):
                        t.lexpos = t.endlexpos = lookahead.lexpos
                    t.value = lookahead
                    lookaheadstack.append(lookahead)
                    lookahead = t
                else:
                    sym = symstack.pop()
                    #--! TRACKING
                    if tracking:
                        lookahead.lineno = sym.lineno
                        lookahead.lexpos = sym.lexpos
                    #--! TRACKING
                    statestack.pop()
                    state = statestack[-1]

                continue

            # Call an error function here
            raise RuntimeError('yacc: internal parser error!!!\n')

        #--! parseopt-end

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # parseopt_notrack().
    #
    # Optimized version of parseopt() with line number tracking removed.
    # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated
    # by the ply/ygen.py script. Make changes to the parsedebug() method instead.
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
        #--! parseopt-notrack-start
        lookahead = None                         # Current lookahead symbol
        lookaheadstack = []                      # Stack of lookahead symbols
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
        defaulted_states = self.defaulted_states # Local reference to defaulted states
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
        errorcount = 0                           # Used during error recovery


        # If no lexer was given, we will try to use the lex module
        if not lexer:
            from . import lex
            lexer = lex.lexer

        # Set up the lexer and parser objects on pslice
        pslice.lexer = lexer
        pslice.parser = self

        # If input was supplied, pass to lexer
        if input is not None:
            lexer.input(input)

        if tokenfunc is None:
            # Tokenize function
            get_token = lexer.token
        else:
            get_token = tokenfunc

        # Set the parser() token method (sometimes used in error recovery)
        self.token = get_token

        # Set up the state and symbol stacks

        statestack = []                # Stack of parsing states
        self.statestack = statestack
        symstack   = []                # Stack of grammar symbols
        self.symstack = symstack

        pslice.stack = symstack         # Put in the production
        errtoken   = None               # Err token

        # The start state is assumed to be (0,$end)

        statestack.append(0)
        sym = YaccSymbol()
        sym.type = '$end'
        symstack.append(sym)
        state = 0
        while True:
            # Get the next symbol on the input.  If a lookahead symbol
            # is already set, we just use that. Otherwise, we'll pull
            # the next token off of the lookaheadstack or from the lexer


            if state not in defaulted_states:
                if not lookahead:
                    if not lookaheadstack:
                        lookahead = get_token()     # Get the next token
                    else:
                        lookahead = lookaheadstack.pop()
                    if not lookahead:
                        lookahead = YaccSymbol()
                        lookahead.type = '$end'

                # Check the action table
                ltype = lookahead.type
                t = actions[state].get(ltype)
            else:
                t = defaulted_states[state]


            if t is not None:
                if t > 0:
                    # shift a symbol on the stack
                    statestack.append(t)
                    state = t


                    symstack.append(lookahead)
                    lookahead = None

                    # Decrease error count on successful shift
                    if errorcount:
                        errorcount -= 1
                    continue

                if t < 0:
                    # reduce a symbol on the stack, emit a production
                    p = prod[-t]
                    pname = p.name
                    plen  = p.len

                    # Get production function
                    sym = YaccSymbol()
                    sym.type = pname       # Production name
                    sym.value = None


                    if plen:
                        targ = symstack[-plen-1:]
                        targ[0] = sym


                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # below as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            del symstack[-plen:]
                            self.state = state
                            p.callable(pslice)
                            del statestack[-plen:]
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                    else:


                        targ = [sym]

                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                        # The code enclosed in this section is duplicated
                        # above as a performance optimization.  Make sure
                        # changes get made in both locations.

                        pslice.slice = targ

                        try:
                            # Call the grammar rule with our special slice object
                            self.state = state
                            p.callable(pslice)
                            symstack.append(sym)
                            state = goto[statestack[-1]][pname]
                            statestack.append(state)
                        except SyntaxError:
                            # If an error was set. Enter error recovery state
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
                            statestack.pop()                    # Pop back one state (before the reduce)
                            state = statestack[-1]
                            sym.type = 'error'
                            sym.value = 'error'
                            lookahead = sym
                            errorcount = error_count
                            self.errorok = False

                        continue
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                if t == 0:
                    n = symstack[-1]
                    result = getattr(n, 'value', None)
                    return result

            if t is None:


                # We have some kind of parsing error here.  To handle
                # this, we are going to push the current token onto
                # the tokenstack and replace it with an 'error' token.
                # If there are any synchronization rules, they may
                # catch it.
                #
                # In addition to pushing the error token, we call call
                # the user defined p_error() function if this is the
                # first syntax error.  This function is only called if
                # errorcount == 0.
                if errorcount == 0 or self.errorok:
                    errorcount = error_count
                    self.errorok = False
                    errtoken = lookahead
                    if errtoken.type == '$end':
                        errtoken = None               # End of file!
                    if self.errorfunc:
                        if errtoken and not hasattr(errtoken, 'lexer'):
                            errtoken.lexer = lexer
                        self.state = state
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
                        if self.errorok:
                            # User must have done some kind of panic
                            # mode recovery on their own.  The
                            # returned token is the next lookahead
                            lookahead = tok
                            errtoken = None
                            continue
                    else:
                        if errtoken:
                            if hasattr(errtoken, 'lineno'):
                                lineno = lookahead.lineno
                            else:
                                lineno = 0
                            if lineno:
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
                            else:
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
                        else:
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
                            return

                else:
                    errorcount = error_count

                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
                # entire parse has been rolled back and we're completely hosed.   The token is
                # discarded and we just keep going.

                if len(statestack) <= 1 and lookahead.type != '$end':
                    lookahead = None
                    errtoken = None
                    state = 0
                    # Nuke the pushback stack
                    del lookaheadstack[:]
                    continue

                # case 2: the statestack has a couple of entries on it, but we're
                # at the end of the file. nuke the top entry and generate an error token

                # Start nuking entries on the stack
                if lookahead.type == '$end':
                    # Whoa. We're really hosed here. Bail out
                    return

                if lookahead.type != 'error':
                    sym = symstack[-1]
                    if sym.type == 'error':
                        # Hmmm. Error is on top of stack, we'll just nuke input
                        # symbol and continue
                        lookahead = None
                        continue

                    # Create the error symbol for the first time and make it the new lookahead symbol
                    t = YaccSymbol()
                    t.type = 'error'

                    if hasattr(lookahead, 'lineno'):
                        t.lineno = t.endlineno = lookahead.lineno
                    if hasattr(lookahead, 'lexpos'):
                        t.lexpos = t.endlexpos = lookahead.lexpos
                    t.value = lookahead
                    lookaheadstack.append(lookahead)
                    lookahead = t
                else:
                    sym = symstack.pop()
                    statestack.pop()
                    state = statestack[-1]

                continue

            # Call an error function here
            raise RuntimeError('yacc: internal parser error!!!\n')

        #--! parseopt-notrack-end

# -----------------------------------------------------------------------------
#                          === Grammar Representation ===
#
# The following functions, classes, and variables are used to represent and
# manipulate the rules that make up a grammar.
# -----------------------------------------------------------------------------

# regex matching identifiers
_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')

# -----------------------------------------------------------------------------
# class Production:
#
# This class stores the raw information about a single production or grammar rule.
# A grammar rule refers to a specification such as this:
#
#       expr : expr PLUS term
#
# Here are the basic attributes defined on all productions
#
#       name     - Name of the production.  For example 'expr'
#       prod     - A list of symbols on the right side ['expr','PLUS','term']
#       prec     - Production precedence level
#       number   - Production number.
#       func     - Function that executes on reduce
#       file     - File where production function is defined
#       lineno   - Line number where production function is defined
#
# The following attributes are defined or optional.
#
#       len       - Length of the production (number of symbols on right hand side)
#       usyms     - Set of unique symbols found in the production
# -----------------------------------------------------------------------------

class Production(object):
    reduced = 0
    def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0):
        self.name     = name
        self.prod     = tuple(prod)
        self.number   = number
        self.func     = func
        self.callable = None
        self.file     = file
        self.line     = line
        self.prec     = precedence

        # Internal settings used during table construction

        self.len  = len(self.prod)   # Length of the production

        # Create a list of unique production symbols used in the production
        self.usyms = []
        for s in self.prod:
            if s not in self.usyms:
                self.usyms.append(s)

        # List of all LR items for the production
        self.lr_items = []
        self.lr_next = None

        # Create a string representation
        if self.prod:
            self.str = '%s -> %s' % (self.name, ' '.join(self.prod))
        else:
            self.str = '%s -> <empty>' % self.name

    def __str__(self):
        return self.str

    def __repr__(self):
        return 'Production(' + str(self) + ')'

    def __len__(self):
        return len(self.prod)

    def __nonzero__(self):
        return 1

    def __getitem__(self, index):
        return self.prod[index]

    # Return the nth lr_item from the production (or None if at the end)
    def lr_item(self, n):
        if n > len(self.prod):
            return None
        p = LRItem(self, n)
        # Precompute the list of productions immediately following.
        try:
            p.lr_after = self.Prodnames[p.prod[n+1]]
        except (IndexError, KeyError):
            p.lr_after = []
        try:
            p.lr_before = p.prod[n-1]
        except IndexError:
            p.lr_before = None
        return p

    # Bind the production function name to a callable
    def bind(self, pdict):
        if self.func:
            self.callable = pdict[self.func]

# This class serves as a minimal standin for Production objects when
# reading table data from files.   It only contains information
# actually used by the LR parsing engine, plus some additional
# debugging information.
class MiniProduction(object):
    def __init__(self, str, name, len, func, file, line):
        self.name     = name
        self.len      = len
        self.func     = func
        self.callable = None
        self.file     = file
        self.line     = line
        self.str      = str

    def __str__(self):
        return self.str

    def __repr__(self):
        return 'MiniProduction(%s)' % self.str

    # Bind the production function name to a callable
    def bind(self, pdict):
        if self.func:
            self.callable = pdict[self.func]


# -----------------------------------------------------------------------------
# class LRItem
#
# This class represents a specific stage of parsing a production rule.  For
# example:
#
#       expr : expr . PLUS term
#
# In the above, the "." represents the current location of the parse.  Here
# basic attributes:
#
#       name       - Name of the production.  For example 'expr'
#       prod       - A list of symbols on the right side ['expr','.', 'PLUS','term']
#       number     - Production number.
#
#       lr_next      Next LR item. Example, if we are ' expr -> expr . PLUS term'
#                    then lr_next refers to 'expr -> expr PLUS . term'
#       lr_index   - LR item index (location of the ".") in the prod list.
#       lookaheads - LALR lookahead symbols for this item
#       len        - Length of the production (number of symbols on right hand side)
#       lr_after    - List of all productions that immediately follow
#       lr_before   - Grammar symbol immediately before
# -----------------------------------------------------------------------------

class LRItem(object):
    def __init__(self, p, n):
        self.name       = p.name
        self.prod       = list(p.prod)
        self.number     = p.number
        self.lr_index   = n
        self.lookaheads = {}
        self.prod.insert(n, '.')
        self.prod       = tuple(self.prod)
        self.len        = len(self.prod)
        self.usyms      = p.usyms

    def __str__(self):
        if self.prod:
            s = '%s -> %s' % (self.name, ' '.join(self.prod))
        else:
            s = '%s -> <empty>' % self.name
        return s

    def __repr__(self):
        return 'LRItem(' + str(self) + ')'

# -----------------------------------------------------------------------------
# rightmost_terminal()
#
# Return the rightmost terminal from a list of symbols.  Used in add_production()
# -----------------------------------------------------------------------------
def rightmost_terminal(symbols, terminals):
    i = len(symbols) - 1
    while i >= 0:
        if symbols[i] in terminals:
            return symbols[i]
        i -= 1
    return None

# -----------------------------------------------------------------------------
#                           === GRAMMAR CLASS ===
#
# The following class represents the contents of the specified grammar along
# with various computed properties such as first sets, follow sets, LR items, etc.
# This data is used for critical parts of the table generation process later.
# -----------------------------------------------------------------------------

class GrammarError(YaccError):
    pass

class Grammar(object):
    def __init__(self, terminals):
        self.Productions  = [None]  # A list of all of the productions.  The first
                                    # entry is always reserved for the purpose of
                                    # building an augmented grammar

        self.Prodnames    = {}      # A dictionary mapping the names of nonterminals to a list of all
                                    # productions of that nonterminal.

        self.Prodmap      = {}      # A dictionary that is only used to detect duplicate
                                    # productions.

        self.Terminals    = {}      # A dictionary mapping the names of terminal symbols to a
                                    # list of the rules where they are used.

        for term in terminals:
            self.Terminals[term] = []

        self.Terminals['error'] = []

        self.Nonterminals = {}      # A dictionary mapping names of nonterminals to a list
                                    # of rule numbers where they are used.

        self.First        = {}      # A dictionary of precomputed FIRST(x) symbols

        self.Follow       = {}      # A dictionary of precomputed FOLLOW(x) symbols

        self.Precedence   = {}      # Precedence rules for each terminal. Contains tuples of the
                                    # form ('right',level) or ('nonassoc', level) or ('left',level)

        self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer.
                                    # This is only used to provide error checking and to generate
                                    # a warning about unused precedence rules.

        self.Start = None           # Starting symbol for the grammar


    def __len__(self):
        return len(self.Productions)

    def __getitem__(self, index):
        return self.Productions[index]

    # -----------------------------------------------------------------------------
    # set_precedence()
    #
    # Sets the precedence for a given terminal. assoc is the associativity such as
    # 'left','right', or 'nonassoc'.  level is a numeric level.
    #
    # -----------------------------------------------------------------------------

    def set_precedence(self, term, assoc, level):
        assert self.Productions == [None], 'Must call set_precedence() before add_production()'
        if term in self.Precedence:
            raise GrammarError('Precedence already specified for terminal %r' % term)
        if assoc not in ['left', 'right', 'nonassoc']:
            raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
        self.Precedence[term] = (assoc, level)

    # -----------------------------------------------------------------------------
    # add_production()
    #
    # Given an action function, this function assembles a production rule and
    # computes its precedence level.
    #
    # The production rule is supplied as a list of symbols.   For example,
    # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
    # symbols ['expr','PLUS','term'].
    #
    # Precedence is determined by the precedence of the right-most non-terminal
    # or the precedence of a terminal specified by %prec.
    #
    # A variety of error checks are performed to make sure production symbols
    # are valid and that %prec is used correctly.
    # -----------------------------------------------------------------------------

    def add_production(self, prodname, syms, func=None, file='', line=0):

        if prodname in self.Terminals:
            raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname))
        if prodname == 'error':
            raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname))
        if not _is_identifier.match(prodname):
            raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname))

        # Look for literal tokens
        for n, s in enumerate(syms):
            if s[0] in "'\"":
                try:
                    c = eval(s)
                    if (len(c) > 1):
                        raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
                                           (file, line, s, prodname))
                    if c not in self.Terminals:
                        self.Terminals[c] = []
                    syms[n] = c
                    continue
                except SyntaxError:
                    pass
            if not _is_identifier.match(s) and s != '%prec':
                raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname))

        # Determine the precedence level
        if '%prec' in syms:
            if syms[-1] == '%prec':
                raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line))
            if syms[-2] != '%prec':
                raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' %
                                   (file, line))
            precname = syms[-1]
            prodprec = self.Precedence.get(precname)
            if not prodprec:
                raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname))
            else:
                self.UsedPrecedence.add(precname)
            del syms[-2:]     # Drop %prec from the rule
        else:
            # If no %prec, precedence is determined by the rightmost terminal symbol
            precname = rightmost_terminal(syms, self.Terminals)
            prodprec = self.Precedence.get(precname, ('right', 0))

        # See if the rule is already in the rulemap
        map = '%s -> %s' % (prodname, syms)
        if map in self.Prodmap:
            m = self.Prodmap[map]
            raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) +
                               'Previous definition at %s:%d' % (m.file, m.line))

        # From this point on, everything is valid.  Create a new Production instance
        pnumber  = len(self.Productions)
        if prodname not in self.Nonterminals:
            self.Nonterminals[prodname] = []

        # Add the production number to Terminals and Nonterminals
        for t in syms:
            if t in self.Terminals:
                self.Terminals[t].append(pnumber)
            else:
                if t not in self.Nonterminals:
                    self.Nonterminals[t] = []
                self.Nonterminals[t].append(pnumber)

        # Create a production and add it to the list of productions
        p = Production(pnumber, prodname, syms, prodprec, func, file, line)
        self.Productions.append(p)
        self.Prodmap[map] = p

        # Add to the global productions list
        try:
            self.Prodnames[prodname].append(p)
        except KeyError:
            self.Prodnames[prodname] = [p]

    # -----------------------------------------------------------------------------
    # set_start()
    #
    # Sets the starting symbol and creates the augmented grammar.  Production
    # rule 0 is S' -> start where start is the start symbol.
    # -----------------------------------------------------------------------------

    def set_start(self, start=None):
        if not start:
            start = self.Productions[1].name
        if start not in self.Nonterminals:
            raise GrammarError('start symbol %s undefined' % start)
        self.Productions[0] = Production(0, "S'", [start])
        self.Nonterminals[start].append(0)
        self.Start = start

    # -----------------------------------------------------------------------------
    # find_unreachable()
    #
    # Find all of the nonterminal symbols that can't be reached from the starting
    # symbol.  Returns a list of nonterminals that can't be reached.
    # -----------------------------------------------------------------------------

    def find_unreachable(self):

        # Mark all symbols that are reachable from a symbol s
        def mark_reachable_from(s):
            if s in reachable:
                return
            reachable.add(s)
            for p in self.Prodnames.get(s, []):
                for r in p.prod:
                    mark_reachable_from(r)

        reachable = set()
        mark_reachable_from(self.Productions[0].prod[0])
        return [s for s in self.Nonterminals if s not in reachable]

    # -----------------------------------------------------------------------------
    # infinite_cycles()
    #
    # This function looks at the various parsing rules and tries to detect
    # infinite recursion cycles (grammar rules where there is no possible way
    # to derive a string of only terminals).
    # -----------------------------------------------------------------------------

    def infinite_cycles(self):
        terminates = {}

        # Terminals:
        for t in self.Terminals:
            terminates[t] = True

        terminates['$end'] = True

        # Nonterminals:

        # Initialize to false:
        for n in self.Nonterminals:
            terminates[n] = False

        # Then propagate termination until no change:
        while True:
            some_change = False
            for (n, pl) in self.Prodnames.items():
                # Nonterminal n terminates iff any of its productions terminates.
                for p in pl:
                    # Production p terminates iff all of its rhs symbols terminate.
                    for s in p.prod:
                        if not terminates[s]:
                            # The symbol s does not terminate,
                            # so production p does not terminate.
                            p_terminates = False
                            break
                    else:
                        # didn't break from the loop,
                        # so every symbol s terminates
                        # so production p terminates.
                        p_terminates = True

                    if p_terminates:
                        # symbol n terminates!
                        if not terminates[n]:
                            terminates[n] = True
                            some_change = True
                        # Don't need to consider any more productions for this n.
                        break

            if not some_change:
                break

        infinite = []
        for (s, term) in terminates.items():
            if not term:
                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
                    # s is used-but-not-defined, and we've already warned of that,
                    # so it would be overkill to say that it's also non-terminating.
                    pass
                else:
                    infinite.append(s)

        return infinite

    # -----------------------------------------------------------------------------
    # undefined_symbols()
    #
    # Find all symbols that were used the grammar, but not defined as tokens or
    # grammar rules.  Returns a list of tuples (sym, prod) where sym in the symbol
    # and prod is the production where the symbol was used.
    # -----------------------------------------------------------------------------
    def undefined_symbols(self):
        result = []
        for p in self.Productions:
            if not p:
                continue

            for s in p.prod:
                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
                    result.append((s, p))
        return result

    # -----------------------------------------------------------------------------
    # unused_terminals()
    #
    # Find all terminals that were defined, but not used by the grammar.  Returns
    # a list of all symbols.
    # -----------------------------------------------------------------------------
    def unused_terminals(self):
        unused_tok = []
        for s, v in self.Terminals.items():
            if s != 'error' and not v:
                unused_tok.append(s)

        return unused_tok

    # ------------------------------------------------------------------------------
    # unused_rules()
    #
    # Find all grammar rules that were defined,  but not used (maybe not reachable)
    # Returns a list of productions.
    # ------------------------------------------------------------------------------

    def unused_rules(self):
        unused_prod = []
        for s, v in self.Nonterminals.items():
            if not v:
                p = self.Prodnames[s][0]
                unused_prod.append(p)
        return unused_prod

    # -----------------------------------------------------------------------------
    # unused_precedence()
    #
    # Returns a list of tuples (term,precedence) corresponding to precedence
    # rules that were never used by the grammar.  term is the name of the terminal
    # on which precedence was applied and precedence is a string such as 'left' or
    # 'right' corresponding to the type of precedence.
    # -----------------------------------------------------------------------------

    def unused_precedence(self):
        unused = []
        for termname in self.Precedence:
            if not (termname in self.Terminals or termname in self.UsedPrecedence):
                unused.append((termname, self.Precedence[termname][0]))

        return unused

    # -------------------------------------------------------------------------
    # _first()
    #
    # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
    #
    # During execution of compute_first1, the result may be incomplete.
    # Afterward (e.g., when called from compute_follow()), it will be complete.
    # -------------------------------------------------------------------------
    def _first(self, beta):

        # We are computing First(x1,x2,x3,...,xn)
        result = []
        for x in beta:
            x_produces_empty = False

            # Add all the non-<empty> symbols of First[x] to the result.
            for f in self.First[x]:
                if f == '<empty>':
                    x_produces_empty = True
                else:
                    if f not in result:
                        result.append(f)

            if x_produces_empty:
                # We have to consider the next x in beta,
                # i.e. stay in the loop.
                pass
            else:
                # We don't have to consider any further symbols in beta.
                break
        else:
            # There was no 'break' from the loop,
            # so x_produces_empty was true for all x in beta,
            # so beta produces empty as well.
            result.append('<empty>')

        return result

    # -------------------------------------------------------------------------
    # compute_first()
    #
    # Compute the value of FIRST1(X) for all symbols
    # -------------------------------------------------------------------------
    def compute_first(self):
        if self.First:
            return self.First

        # Terminals:
        for t in self.Terminals:
            self.First[t] = [t]

        self.First['$end'] = ['$end']

        # Nonterminals:

        # Initialize to the empty set:
        for n in self.Nonterminals:
            self.First[n] = []

        # Then propagate symbols until no change:
        while True:
            some_change = False
            for n in self.Nonterminals:
                for p in self.Prodnames[n]:
                    for f in self._first(p.prod):
                        if f not in self.First[n]:
                            self.First[n].append(f)
                            some_change = True
            if not some_change:
                break

        return self.First

    # ---------------------------------------------------------------------
    # compute_follow()
    #
    # Computes all of the follow sets for every non-terminal symbol.  The
    # follow set is the set of all symbols that might follow a given
    # non-terminal.  See the Dragon book, 2nd Ed. p. 189.
    # ---------------------------------------------------------------------
    def compute_follow(self, start=None):
        # If already computed, return the result
        if self.Follow:
            return self.Follow

        # If first sets not computed yet, do that first.
        if not self.First:
            self.compute_first()

        # Add '$end' to the follow list of the start symbol
        for k in self.Nonterminals:
            self.Follow[k] = []

        if not start:
            start = self.Productions[1].name

        self.Follow[start] = ['$end']

        while True:
            didadd = False
            for p in self.Productions[1:]:
                # Here is the production set
                for i, B in enumerate(p.prod):
                    if B in self.Nonterminals:
                        # Okay. We got a non-terminal in a production
                        fst = self._first(p.prod[i+1:])
                        hasempty = False
                        for f in fst:
                            if f != '<empty>' and f not in self.Follow[B]:
                                self.Follow[B].append(f)
                                didadd = True
                            if f == '<empty>':
                                hasempty = True
                        if hasempty or i == (len(p.prod)-1):
                            # Add elements of follow(a) to follow(b)
                            for f in self.Follow[p.name]:
                                if f not in self.Follow[B]:
                                    self.Follow[B].append(f)
                                    didadd = True
            if not didadd:
                break
        return self.Follow


    # -----------------------------------------------------------------------------
    # build_lritems()
    #
    # This function walks the list of productions and builds a complete set of the
    # LR items.  The LR items are stored in two ways:  First, they are uniquely
    # numbered and placed in the list _lritems.  Second, a linked list of LR items
    # is built for each production.  For example:
    #
    #   E -> E PLUS E
    #
    # Creates the list
    #
    #  [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
    # -----------------------------------------------------------------------------

    def build_lritems(self):
        for p in self.Productions:
            lastlri = p
            i = 0
            lr_items = []
            while True:
                if i > len(p):
                    lri = None
                else:
                    lri = LRItem(p, i)
                    # Precompute the list of productions immediately following
                    try:
                        lri.lr_after = self.Prodnames[lri.prod[i+1]]
                    except (IndexError, KeyError):
                        lri.lr_after = []
                    try:
                        lri.lr_before = lri.prod[i-1]
                    except IndexError:
                        lri.lr_before = None

                lastlri.lr_next = lri
                if not lri:
                    break
                lr_items.append(lri)
                lastlri = lri
                i += 1
            p.lr_items = lr_items

# -----------------------------------------------------------------------------
#                            == Class LRTable ==
#
# This basic class represents a basic table of LR parsing information.
# Methods for generating the tables are not defined here.  They are defined
# in the derived class LRGeneratedTable.
# -----------------------------------------------------------------------------

class VersionError(YaccError):
    pass

class LRTable(object):
    def __init__(self):
        self.lr_action = None
        self.lr_goto = None
        self.lr_productions = None
        self.lr_method = None

    def read_table(self, module):
        if isinstance(module, types.ModuleType):
            parsetab = module
        else:
            exec('import %s' % module)
            parsetab = sys.modules[module]

        if parsetab._tabversion != __tabversion__:
            raise VersionError('yacc table file version is out of date')

        self.lr_action = parsetab._lr_action
        self.lr_goto = parsetab._lr_goto

        self.lr_productions = []
        for p in parsetab._lr_productions:
            self.lr_productions.append(MiniProduction(*p))

        self.lr_method = parsetab._lr_method
        return parsetab._lr_signature

    def read_pickle(self, filename):
        try:
            import cPickle as pickle
        except ImportError:
            import pickle

        if not os.path.exists(filename):
          raise ImportError

        in_f = open(filename, 'rb')

        tabversion = pickle.load(in_f)
        if tabversion != __tabversion__:
            raise VersionError('yacc table file version is out of date')
        self.lr_method = pickle.load(in_f)
        signature      = pickle.load(in_f)
        self.lr_action = pickle.load(in_f)
        self.lr_goto   = pickle.load(in_f)
        productions    = pickle.load(in_f)

        self.lr_productions = []
        for p in productions:
            self.lr_productions.append(MiniProduction(*p))

        in_f.close()
        return signature

    # Bind all production function names to callable objects in pdict
    def bind_callables(self, pdict):
        for p in self.lr_productions:
            p.bind(pdict)


# -----------------------------------------------------------------------------
#                           === LR Generator ===
#
# The following classes and functions are used to generate LR parsing tables on
# a grammar.
# -----------------------------------------------------------------------------

# -----------------------------------------------------------------------------
# digraph()
# traverse()
#
# The following two functions are used to compute set valued functions
# of the form:
#
#     F(x) = F'(x) U U{F(y) | x R y}
#
# This is used to compute the values of Read() sets as well as FOLLOW sets
# in LALR(1) generation.
#
# Inputs:  X    - An input set
#          R    - A relation
#          FP   - Set-valued function
# ------------------------------------------------------------------------------

def digraph(X, R, FP):
    N = {}
    for x in X:
        N[x] = 0
    stack = []
    F = {}
    for x in X:
        if N[x] == 0:
            traverse(x, N, stack, F, X, R, FP)
    return F

def traverse(x, N, stack, F, X, R, FP):
    stack.append(x)
    d = len(stack)
    N[x] = d
    F[x] = FP(x)             # F(X) <- F'(x)

    rel = R(x)               # Get y's related to x
    for y in rel:
        if N[y] == 0:
            traverse(y, N, stack, F, X, R, FP)
        N[x] = min(N[x], N[y])
        for a in F.get(y, []):
            if a not in F[x]:
                F[x].append(a)
    if N[x] == d:
        N[stack[-1]] = MAXINT
        F[stack[-1]] = F[x]
        element = stack.pop()
        while element != x:
            N[stack[-1]] = MAXINT
            F[stack[-1]] = F[x]
            element = stack.pop()

class LALRError(YaccError):
    pass

# -----------------------------------------------------------------------------
#                             == LRGeneratedTable ==
#
# This class implements the LR table generation algorithm.  There are no
# public methods except for write()
# -----------------------------------------------------------------------------

class LRGeneratedTable(LRTable):
    def __init__(self, grammar, method='LALR', log=None):
        if method not in ['SLR', 'LALR']:
            raise LALRError('Unsupported method %s' % method)

        self.grammar = grammar
        self.lr_method = method

        # Set up the logger
        if not log:
            log = NullLogger()
        self.log = log

        # Internal attributes
        self.lr_action     = {}        # Action table
        self.lr_goto       = {}        # Goto table
        self.lr_productions  = grammar.Productions    # Copy of grammar Production array
        self.lr_goto_cache = {}        # Cache of computed gotos
        self.lr0_cidhash   = {}        # Cache of closures

        self._add_count    = 0         # Internal counter used to detect cycles

        # Diagonistic information filled in by the table generator
        self.sr_conflict   = 0
        self.rr_conflict   = 0
        self.conflicts     = []        # List of conflicts

        self.sr_conflicts  = []
        self.rr_conflicts  = []

        # Build the tables
        self.grammar.build_lritems()
        self.grammar.compute_first()
        self.grammar.compute_follow()
        self.lr_parse_table()

    # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.

    def lr0_closure(self, I):
        self._add_count += 1

        # Add everything in I to J
        J = I[:]
        didadd = True
        while didadd:
            didadd = False
            for j in J:
                for x in j.lr_after:
                    if getattr(x, 'lr0_added', 0) == self._add_count:
                        continue
                    # Add B --> .G to J
                    J.append(x.lr_next)
                    x.lr0_added = self._add_count
                    didadd = True

        return J

    # Compute the LR(0) goto function goto(I,X) where I is a set
    # of LR(0) items and X is a grammar symbol.   This function is written
    # in a way that guarantees uniqueness of the generated goto sets
    # (i.e. the same goto set will never be returned as two different Python
    # objects).  With uniqueness, we can later do fast set comparisons using
    # id(obj) instead of element-wise comparison.

    def lr0_goto(self, I, x):
        # First we look for a previously cached entry
        g = self.lr_goto_cache.get((id(I), x))
        if g:
            return g

        # Now we generate the goto set in a way that guarantees uniqueness
        # of the result

        s = self.lr_goto_cache.get(x)
        if not s:
            s = {}
            self.lr_goto_cache[x] = s

        gs = []
        for p in I:
            n = p.lr_next
            if n and n.lr_before == x:
                s1 = s.get(id(n))
                if not s1:
                    s1 = {}
                    s[id(n)] = s1
                gs.append(n)
                s = s1
        g = s.get('$end')
        if not g:
            if gs:
                g = self.lr0_closure(gs)
                s['$end'] = g
            else:
                s['$end'] = gs
        self.lr_goto_cache[(id(I), x)] = g
        return g

    # Compute the LR(0) sets of item function
    def lr0_items(self):
        C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
        i = 0
        for I in C:
            self.lr0_cidhash[id(I)] = i
            i += 1

        # Loop over the items in C and each grammar symbols
        i = 0
        while i < len(C):
            I = C[i]
            i += 1

            # Collect all of the symbols that could possibly be in the goto(I,X) sets
            asyms = {}
            for ii in I:
                for s in ii.usyms:
                    asyms[s] = None

            for x in asyms:
                g = self.lr0_goto(I, x)
                if not g or id(g) in self.lr0_cidhash:
                    continue
                self.lr0_cidhash[id(g)] = len(C)
                C.append(g)

        return C

    # -----------------------------------------------------------------------------
    #                       ==== LALR(1) Parsing ====
    #
    # LALR(1) parsing is almost exactly the same as SLR except that instead of
    # relying upon Follow() sets when performing reductions, a more selective
    # lookahead set that incorporates the state of the LR(0) machine is utilized.
    # Thus, we mainly just have to focus on calculating the lookahead sets.
    #
    # The method used here is due to DeRemer and Pennelo (1982).
    #
    # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
    #     Lookahead Sets", ACM Transactions on Programming Languages and Systems,
    #     Vol. 4, No. 4, Oct. 1982, pp. 615-649
    #
    # Further details can also be found in:
    #
    #  J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
    #      McGraw-Hill Book Company, (1985).
    #
    # -----------------------------------------------------------------------------

    # -----------------------------------------------------------------------------
    # compute_nullable_nonterminals()
    #
    # Creates a dictionary containing all of the non-terminals that might produce
    # an empty production.
    # -----------------------------------------------------------------------------

    def compute_nullable_nonterminals(self):
        nullable = set()
        num_nullable = 0
        while True:
            for p in self.grammar.Productions[1:]:
                if p.len == 0:
                    nullable.add(p.name)
                    continue
                for t in p.prod:
                    if t not in nullable:
                        break
                else:
                    nullable.add(p.name)
            if len(nullable) == num_nullable:
                break
            num_nullable = len(nullable)
        return nullable

    # -----------------------------------------------------------------------------
    # find_nonterminal_trans(C)
    #
    # Given a set of LR(0) items, this functions finds all of the non-terminal
    # transitions.    These are transitions in which a dot appears immediately before
    # a non-terminal.   Returns a list of tuples of the form (state,N) where state
    # is the state number and N is the nonterminal symbol.
    #
    # The input C is the set of LR(0) items.
    # -----------------------------------------------------------------------------

    def find_nonterminal_transitions(self, C):
        trans = []
        for stateno, state in enumerate(C):
            for p in state:
                if p.lr_index < p.len - 1:
                    t = (stateno, p.prod[p.lr_index+1])
                    if t[1] in self.grammar.Nonterminals:
                        if t not in trans:
                            trans.append(t)
        return trans

    # -----------------------------------------------------------------------------
    # dr_relation()
    #
    # Computes the DR(p,A) relationships for non-terminal transitions.  The input
    # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
    #
    # Returns a list of terminals.
    # -----------------------------------------------------------------------------

    def dr_relation(self, C, trans, nullable):
        state, N = trans
        terms = []

        g = self.lr0_goto(C[state], N)
        for p in g:
            if p.lr_index < p.len - 1:
                a = p.prod[p.lr_index+1]
                if a in self.grammar.Terminals:
                    if a not in terms:
                        terms.append(a)

        # This extra bit is to handle the start state
        if state == 0 and N == self.grammar.Productions[0].prod[0]:
            terms.append('$end')

        return terms

    # -----------------------------------------------------------------------------
    # reads_relation()
    #
    # Computes the READS() relation (p,A) READS (t,C).
    # -----------------------------------------------------------------------------

    def reads_relation(self, C, trans, empty):
        # Look for empty transitions
        rel = []
        state, N = trans

        g = self.lr0_goto(C[state], N)
        j = self.lr0_cidhash.get(id(g), -1)
        for p in g:
            if p.lr_index < p.len - 1:
                a = p.prod[p.lr_index + 1]
                if a in empty:
                    rel.append((j, a))

        return rel

    # -----------------------------------------------------------------------------
    # compute_lookback_includes()
    #
    # Determines the lookback and includes relations
    #
    # LOOKBACK:
    #
    # This relation is determined by running the LR(0) state machine forward.
    # For example, starting with a production "N : . A B C", we run it forward
    # to obtain "N : A B C ."   We then build a relationship between this final
    # state and the starting state.   These relationships are stored in a dictionary
    # lookdict.
    #
    # INCLUDES:
    #
    # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
    #
    # This relation is used to determine non-terminal transitions that occur
    # inside of other non-terminal transition states.   (p,A) INCLUDES (p', B)
    # if the following holds:
    #
    #       B -> LAT, where T -> epsilon and p' -L-> p
    #
    # L is essentially a prefix (which may be empty), T is a suffix that must be
    # able to derive an empty string.  State p' must lead to state p with the string L.
    #
    # -----------------------------------------------------------------------------

    def compute_lookback_includes(self, C, trans, nullable):
        lookdict = {}          # Dictionary of lookback relations
        includedict = {}       # Dictionary of include relations

        # Make a dictionary of non-terminal transitions
        dtrans = {}
        for t in trans:
            dtrans[t] = 1

        # Loop over all transitions and compute lookbacks and includes
        for state, N in trans:
            lookb = []
            includes = []
            for p in C[state]:
                if p.name != N:
                    continue

                # Okay, we have a name match.  We now follow the production all the way
                # through the state machine until we get the . on the right hand side

                lr_index = p.lr_index
                j = state
                while lr_index < p.len - 1:
                    lr_index = lr_index + 1
                    t = p.prod[lr_index]

                    # Check to see if this symbol and state are a non-terminal transition
                    if (j, t) in dtrans:
                        # Yes.  Okay, there is some chance that this is an includes relation
                        # the only way to know for certain is whether the rest of the
                        # production derives empty

                        li = lr_index + 1
                        while li < p.len:
                            if p.prod[li] in self.grammar.Terminals:
                                break      # No forget it
                            if p.prod[li] not in nullable:
                                break
                            li = li + 1
                        else:
                            # Appears to be a relation between (j,t) and (state,N)
                            includes.append((j, t))

                    g = self.lr0_goto(C[j], t)               # Go to next set
                    j = self.lr0_cidhash.get(id(g), -1)      # Go to next state

                # When we get here, j is the final state, now we have to locate the production
                for r in C[j]:
                    if r.name != p.name:
                        continue
                    if r.len != p.len:
                        continue
                    i = 0
                    # This look is comparing a production ". A B C" with "A B C ."
                    while i < r.lr_index:
                        if r.prod[i] != p.prod[i+1]:
                            break
                        i = i + 1
                    else:
                        lookb.append((j, r))
            for i in includes:
                if i not in includedict:
                    includedict[i] = []
                includedict[i].append((state, N))
            lookdict[(state, N)] = lookb

        return lookdict, includedict

    # -----------------------------------------------------------------------------
    # compute_read_sets()
    #
    # Given a set of LR(0) items, this function computes the read sets.
    #
    # Inputs:  C        =  Set of LR(0) items
    #          ntrans   = Set of nonterminal transitions
    #          nullable = Set of empty transitions
    #
    # Returns a set containing the read sets
    # -----------------------------------------------------------------------------

    def compute_read_sets(self, C, ntrans, nullable):
        FP = lambda x: self.dr_relation(C, x, nullable)
        R =  lambda x: self.reads_relation(C, x, nullable)
        F = digraph(ntrans, R, FP)
        return F

    # -----------------------------------------------------------------------------
    # compute_follow_sets()
    #
    # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
    # and an include set, this function computes the follow sets
    #
    # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
    #
    # Inputs:
    #            ntrans     = Set of nonterminal transitions
    #            readsets   = Readset (previously computed)
    #            inclsets   = Include sets (previously computed)
    #
    # Returns a set containing the follow sets
    # -----------------------------------------------------------------------------

    def compute_follow_sets(self, ntrans, readsets, inclsets):
        FP = lambda x: readsets[x]
        R  = lambda x: inclsets.get(x, [])
        F = digraph(ntrans, R, FP)
        return F

    # -----------------------------------------------------------------------------
    # add_lookaheads()
    #
    # Attaches the lookahead symbols to grammar rules.
    #
    # Inputs:    lookbacks         -  Set of lookback relations
    #            followset         -  Computed follow set
    #
    # This function directly attaches the lookaheads to productions contained
    # in the lookbacks set
    # -----------------------------------------------------------------------------

    def add_lookaheads(self, lookbacks, followset):
        for trans, lb in lookbacks.items():
            # Loop over productions in lookback
            for state, p in lb:
                if state not in p.lookaheads:
                    p.lookaheads[state] = []
                f = followset.get(trans, [])
                for a in f:
                    if a not in p.lookaheads[state]:
                        p.lookaheads[state].append(a)

    # -----------------------------------------------------------------------------
    # add_lalr_lookaheads()
    #
    # This function does all of the work of adding lookahead information for use
    # with LALR parsing
    # -----------------------------------------------------------------------------

    def add_lalr_lookaheads(self, C):
        # Determine all of the nullable nonterminals
        nullable = self.compute_nullable_nonterminals()

        # Find all non-terminal transitions
        trans = self.find_nonterminal_transitions(C)

        # Compute read sets
        readsets = self.compute_read_sets(C, trans, nullable)

        # Compute lookback/includes relations
        lookd, included = self.compute_lookback_includes(C, trans, nullable)

        # Compute LALR FOLLOW sets
        followsets = self.compute_follow_sets(trans, readsets, included)

        # Add all of the lookaheads
        self.add_lookaheads(lookd, followsets)

    # -----------------------------------------------------------------------------
    # lr_parse_table()
    #
    # This function constructs the parse tables for SLR or LALR
    # -----------------------------------------------------------------------------
    def lr_parse_table(self):
        Productions = self.grammar.Productions
        Precedence  = self.grammar.Precedence
        goto   = self.lr_goto         # Goto array
        action = self.lr_action       # Action array
        log    = self.log             # Logger for output

        actionp = {}                  # Action production array (temporary)

        log.info('Parsing method: %s', self.lr_method)

        # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
        # This determines the number of states

        C = self.lr0_items()

        if self.lr_method == 'LALR':
            self.add_lalr_lookaheads(C)

        # Build the parser table, state by state
        st = 0
        for I in C:
            # Loop over each production in I
            actlist = []              # List of actions
            st_action  = {}
            st_actionp = {}
            st_goto    = {}
            log.info('')
            log.info('state %d', st)
            log.info('')
            for p in I:
                log.info('    (%d) %s', p.number, p)
            log.info('')

            for p in I:
                    if p.len == p.lr_index + 1:
                        if p.name == "S'":
                            # Start symbol. Accept!
                            st_action['$end'] = 0
                            st_actionp['$end'] = p
                        else:
                            # We are at the end of a production.  Reduce!
                            if self.lr_method == 'LALR':
                                laheads = p.lookaheads[st]
                            else:
                                laheads = self.grammar.Follow[p.name]
                            for a in laheads:
                                actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p)))
                                r = st_action.get(a)
                                if r is not None:
                                    # Whoa. Have a shift/reduce or reduce/reduce conflict
                                    if r > 0:
                                        # Need to decide on shift or reduce here
                                        # By default we favor shifting. Need to add
                                        # some precedence rules here.

                                        # Shift precedence comes from the token
                                        sprec, slevel = Precedence.get(a, ('right', 0))

                                        # Reduce precedence comes from rule being reduced (p)
                                        rprec, rlevel = Productions[p.number].prec

                                        if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
                                            # We really need to reduce here.
                                            st_action[a] = -p.number
                                            st_actionp[a] = p
                                            if not slevel and not rlevel:
                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
                                                self.sr_conflicts.append((st, a, 'reduce'))
                                            Productions[p.number].reduced += 1
                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
                                            st_action[a] = None
                                        else:
                                            # Hmmm. Guess we'll keep the shift
                                            if not rlevel:
                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
                                                self.sr_conflicts.append((st, a, 'shift'))
                                    elif r < 0:
                                        # Reduce/reduce conflict.   In this case, we favor the rule
                                        # that was defined first in the grammar file
                                        oldp = Productions[-r]
                                        pp = Productions[p.number]
                                        if oldp.line > pp.line:
                                            st_action[a] = -p.number
                                            st_actionp[a] = p
                                            chosenp, rejectp = pp, oldp
                                            Productions[p.number].reduced += 1
                                            Productions[oldp.number].reduced -= 1
                                        else:
                                            chosenp, rejectp = oldp, pp
                                        self.rr_conflicts.append((st, chosenp, rejectp))
                                        log.info('  ! reduce/reduce conflict for %s resolved using rule %d (%s)',
                                                 a, st_actionp[a].number, st_actionp[a])
                                    else:
                                        raise LALRError('Unknown conflict in state %d' % st)
                                else:
                                    st_action[a] = -p.number
                                    st_actionp[a] = p
                                    Productions[p.number].reduced += 1
                    else:
                        i = p.lr_index
                        a = p.prod[i+1]       # Get symbol right after the "."
                        if a in self.grammar.Terminals:
                            g = self.lr0_goto(I, a)
                            j = self.lr0_cidhash.get(id(g), -1)
                            if j >= 0:
                                # We are in a shift state
                                actlist.append((a, p, 'shift and go to state %d' % j))
                                r = st_action.get(a)
                                if r is not None:
                                    # Whoa have a shift/reduce or shift/shift conflict
                                    if r > 0:
                                        if r != j:
                                            raise LALRError('Shift/shift conflict in state %d' % st)
                                    elif r < 0:
                                        # Do a precedence check.
                                        #   -  if precedence of reduce rule is higher, we reduce.
                                        #   -  if precedence of reduce is same and left assoc, we reduce.
                                        #   -  otherwise we shift

                                        # Shift precedence comes from the token
                                        sprec, slevel = Precedence.get(a, ('right', 0))

                                        # Reduce precedence comes from the rule that could have been reduced
                                        rprec, rlevel = Productions[st_actionp[a].number].prec

                                        if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
                                            # We decide to shift here... highest precedence to shift
                                            Productions[st_actionp[a].number].reduced -= 1
                                            st_action[a] = j
                                            st_actionp[a] = p
                                            if not rlevel:
                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
                                                self.sr_conflicts.append((st, a, 'shift'))
                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
                                            st_action[a] = None
                                        else:
                                            # Hmmm. Guess we'll keep the reduce
                                            if not slevel and not rlevel:
                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
                                                self.sr_conflicts.append((st, a, 'reduce'))

                                    else:
                                        raise LALRError('Unknown conflict in state %d' % st)
                                else:
                                    st_action[a] = j
                                    st_actionp[a] = p

            # Print the actions associated with each terminal
            _actprint = {}
            for a, p, m in actlist:
                if a in st_action:
                    if p is st_actionp[a]:
                        log.info('    %-15s %s', a, m)
                        _actprint[(a, m)] = 1
            log.info('')
            # Print the actions that were not used. (debugging)
            not_used = 0
            for a, p, m in actlist:
                if a in st_action:
                    if p is not st_actionp[a]:
                        if not (a, m) in _actprint:
                            log.debug('  ! %-15s [ %s ]', a, m)
                            not_used = 1
                            _actprint[(a, m)] = 1
            if not_used:
                log.debug('')

            # Construct the goto table for this state

            nkeys = {}
            for ii in I:
                for s in ii.usyms:
                    if s in self.grammar.Nonterminals:
                        nkeys[s] = None
            for n in nkeys:
                g = self.lr0_goto(I, n)
                j = self.lr0_cidhash.get(id(g), -1)
                if j >= 0:
                    st_goto[n] = j
                    log.info('    %-30s shift and go to state %d', n, j)

            action[st] = st_action
            actionp[st] = st_actionp
            goto[st] = st_goto
            st += 1

    # -----------------------------------------------------------------------------
    # write()
    #
    # This function writes the LR parsing tables to a file
    # -----------------------------------------------------------------------------

    def write_table(self, tabmodule, outputdir='', signature=''):
        if isinstance(tabmodule, types.ModuleType):
            raise IOError("Won't overwrite existing tabmodule")

        basemodulename = tabmodule.split('.')[-1]
        filename = os.path.join(outputdir, basemodulename) + '.py'
        try:
            f = open(filename, 'w')

            f.write('''
# %s
# This file is automatically generated. Do not edit.
# pylint: disable=W,C,R
_tabversion = %r

_lr_method = %r

_lr_signature = %r
    ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature))

            # Change smaller to 0 to go back to original tables
            smaller = 1

            # Factor out names to try and make smaller
            if smaller:
                items = {}

                for s, nd in self.lr_action.items():
                    for name, v in nd.items():
                        i = items.get(name)
                        if not i:
                            i = ([], [])
                            items[name] = i
                        i[0].append(s)
                        i[1].append(v)

                f.write('\n_lr_action_items = {')
                for k, v in items.items():
                    f.write('%r:([' % k)
                    for i in v[0]:
                        f.write('%r,' % i)
                    f.write('],[')
                    for i in v[1]:
                        f.write('%r,' % i)

                    f.write(']),')
                f.write('}\n')

                f.write('''
_lr_action = {}
for _k, _v in _lr_action_items.items():
   for _x,_y in zip(_v[0],_v[1]):
      if not _x in _lr_action:  _lr_action[_x] = {}
      _lr_action[_x][_k] = _y
del _lr_action_items
''')

            else:
                f.write('\n_lr_action = { ')
                for k, v in self.lr_action.items():
                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
                f.write('}\n')

            if smaller:
                # Factor out names to try and make smaller
                items = {}

                for s, nd in self.lr_goto.items():
                    for name, v in nd.items():
                        i = items.get(name)
                        if not i:
                            i = ([], [])
                            items[name] = i
                        i[0].append(s)
                        i[1].append(v)

                f.write('\n_lr_goto_items = {')
                for k, v in items.items():
                    f.write('%r:([' % k)
                    for i in v[0]:
                        f.write('%r,' % i)
                    f.write('],[')
                    for i in v[1]:
                        f.write('%r,' % i)

                    f.write(']),')
                f.write('}\n')

                f.write('''
_lr_goto = {}
for _k, _v in _lr_goto_items.items():
   for _x, _y in zip(_v[0], _v[1]):
       if not _x in _lr_goto: _lr_goto[_x] = {}
       _lr_goto[_x][_k] = _y
del _lr_goto_items
''')
            else:
                f.write('\n_lr_goto = { ')
                for k, v in self.lr_goto.items():
                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
                f.write('}\n')

            # Write production table
            f.write('_lr_productions = [\n')
            for p in self.lr_productions:
                if p.func:
                    f.write('  (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len,
                                                          p.func, os.path.basename(p.file), p.line))
                else:
                    f.write('  (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len))
            f.write(']\n')
            f.close()

        except IOError as e:
            raise


    # -----------------------------------------------------------------------------
    # pickle_table()
    #
    # This function pickles the LR parsing tables to a supplied file object
    # -----------------------------------------------------------------------------

    def pickle_table(self, filename, signature=''):
        try:
            import cPickle as pickle
        except ImportError:
            import pickle
        with open(filename, 'wb') as outf:
            pickle.dump(__tabversion__, outf, pickle_protocol)
            pickle.dump(self.lr_method, outf, pickle_protocol)
            pickle.dump(signature, outf, pickle_protocol)
            pickle.dump(self.lr_action, outf, pickle_protocol)
            pickle.dump(self.lr_goto, outf, pickle_protocol)

            outp = []
            for p in self.lr_productions:
                if p.func:
                    outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line))
                else:
                    outp.append((str(p), p.name, p.len, None, None, None))
            pickle.dump(outp, outf, pickle_protocol)

# -----------------------------------------------------------------------------
#                            === INTROSPECTION ===
#
# The following functions and classes are used to implement the PLY
# introspection features followed by the yacc() function itself.
# -----------------------------------------------------------------------------

# -----------------------------------------------------------------------------
# get_caller_module_dict()
#
# This function returns a dictionary containing all of the symbols defined within
# a caller further down the call stack.  This is used to get the environment
# associated with the yacc() call if none was provided.
# -----------------------------------------------------------------------------

def get_caller_module_dict(levels):
    f = sys._getframe(levels)
    ldict = f.f_globals.copy()
    if f.f_globals != f.f_locals:
        ldict.update(f.f_locals)
    return ldict

# -----------------------------------------------------------------------------
# parse_grammar()
#
# This takes a raw grammar rule string and parses it into production data
# -----------------------------------------------------------------------------
def parse_grammar(doc, file, line):
    grammar = []
    # Split the doc string into lines
    pstrings = doc.splitlines()
    lastp = None
    dline = line
    for ps in pstrings:
        dline += 1
        p = ps.split()
        if not p:
            continue
        try:
            if p[0] == '|':
                # This is a continuation of a previous rule
                if not lastp:
                    raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline))
                prodname = lastp
                syms = p[1:]
            else:
                prodname = p[0]
                lastp = prodname
                syms   = p[2:]
                assign = p[1]
                if assign != ':' and assign != '::=':
                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline))

            grammar.append((file, dline, prodname, syms))
        except SyntaxError:
            raise
        except Exception:
            raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip()))

    return grammar

# -----------------------------------------------------------------------------
# ParserReflect()
#
# This class represents information extracted for building a parser including
# start symbol, error function, tokens, precedence list, action functions,
# etc.
# -----------------------------------------------------------------------------
class ParserReflect(object):
    def __init__(self, pdict, log=None):
        self.pdict      = pdict
        self.start      = None
        self.error_func = None
        self.tokens     = None
        self.modules    = set()
        self.grammar    = []
        self.error      = False

        if log is None:
            self.log = PlyLogger(sys.stderr)
        else:
            self.log = log

    # Get all of the basic information
    def get_all(self):
        self.get_start()
        self.get_error_func()
        self.get_tokens()
        self.get_precedence()
        self.get_pfunctions()

    # Validate all of the information
    def validate_all(self):
        self.validate_start()
        self.validate_error_func()
        self.validate_tokens()
        self.validate_precedence()
        self.validate_pfunctions()
        self.validate_modules()
        return self.error

    # Compute a signature over the grammar
    def signature(self):
        parts = []
        try:
            if self.start:
                parts.append(self.start)
            if self.prec:
                parts.append(''.join([''.join(p) for p in self.prec]))
            if self.tokens:
                parts.append(' '.join(self.tokens))
            for f in self.pfuncs:
                if f[3]:
                    parts.append(f[3])
        except (TypeError, ValueError):
            pass
        return ''.join(parts)

    # -----------------------------------------------------------------------------
    # validate_modules()
    #
    # This method checks to see if there are duplicated p_rulename() functions
    # in the parser module file.  Without this function, it is really easy for
    # users to make mistakes by cutting and pasting code fragments (and it's a real
    # bugger to try and figure out why the resulting parser doesn't work).  Therefore,
    # we just do a little regular expression pattern matching of def statements
    # to try and detect duplicates.
    # -----------------------------------------------------------------------------

    def validate_modules(self):
        # Match def p_funcname(
        fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')

        for module in self.modules:
            try:
                lines, linen = inspect.getsourcelines(module)
            except IOError:
                continue

            counthash = {}
            for linen, line in enumerate(lines):
                linen += 1
                m = fre.match(line)
                if m:
                    name = m.group(1)
                    prev = counthash.get(name)
                    if not prev:
                        counthash[name] = linen
                    else:
                        filename = inspect.getsourcefile(module)
                        self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d',
                                         filename, linen, name, prev)

    # Get the start symbol
    def get_start(self):
        self.start = self.pdict.get('start')

    # Validate the start symbol
    def validate_start(self):
        if self.start is not None:
            if not isinstance(self.start, string_types):
                self.log.error("'start' must be a string")

    # Look for error handler
    def get_error_func(self):
        self.error_func = self.pdict.get('p_error')

    # Validate the error function
    def validate_error_func(self):
        if self.error_func:
            if isinstance(self.error_func, types.FunctionType):
                ismethod = 0
            elif isinstance(self.error_func, types.MethodType):
                ismethod = 1
            else:
                self.log.error("'p_error' defined, but is not a function or method")
                self.error = True
                return

            eline = self.error_func.__code__.co_firstlineno
            efile = self.error_func.__code__.co_filename
            module = inspect.getmodule(self.error_func)
            self.modules.add(module)

            argcount = self.error_func.__code__.co_argcount - ismethod
            if argcount != 1:
                self.log.error('%s:%d: p_error() requires 1 argument', efile, eline)
                self.error = True

    # Get the tokens map
    def get_tokens(self):
        tokens = self.pdict.get('tokens')
        if not tokens:
            self.log.error('No token list is defined')
            self.error = True
            return

        if not isinstance(tokens, (list, tuple)):
            self.log.error('tokens must be a list or tuple')
            self.error = True
            return

        if not tokens:
            self.log.error('tokens is empty')
            self.error = True
            return

        self.tokens = sorted(tokens)

    # Validate the tokens
    def validate_tokens(self):
        # Validate the tokens.
        if 'error' in self.tokens:
            self.log.error("Illegal token name 'error'. Is a reserved word")
            self.error = True
            return

        terminals = set()
        for n in self.tokens:
            if n in terminals:
                self.log.warning('Token %r multiply defined', n)
            terminals.add(n)

    # Get the precedence map (if any)
    def get_precedence(self):
        self.prec = self.pdict.get('precedence')

    # Validate and parse the precedence map
    def validate_precedence(self):
        preclist = []
        if self.prec:
            if not isinstance(self.prec, (list, tuple)):
                self.log.error('precedence must be a list or tuple')
                self.error = True
                return
            for level, p in enumerate(self.prec):
                if not isinstance(p, (list, tuple)):
                    self.log.error('Bad precedence table')
                    self.error = True
                    return

                if len(p) < 2:
                    self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p)
                    self.error = True
                    return
                assoc = p[0]
                if not isinstance(assoc, string_types):
                    self.log.error('precedence associativity must be a string')
                    self.error = True
                    return
                for term in p[1:]:
                    if not isinstance(term, string_types):
                        self.log.error('precedence items must be strings')
                        self.error = True
                        return
                    preclist.append((term, assoc, level+1))
        self.preclist = preclist

    # Get all p_functions from the grammar
    def get_pfunctions(self):
        p_functions = []
        for name, item in self.pdict.items():
            if not name.startswith('p_') or name == 'p_error':
                continue
            if isinstance(item, (types.FunctionType, types.MethodType)):
                line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno)
                module = inspect.getmodule(item)
                p_functions.append((line, module, name, item.__doc__))

        # Sort all of the actions by line number; make sure to stringify
        # modules to make them sortable, since `line` may not uniquely sort all
        # p functions
        p_functions.sort(key=lambda p_function: (
            p_function[0],
            str(p_function[1]),
            p_function[2],
            p_function[3]))
        self.pfuncs = p_functions

    # Validate all of the p_functions
    def validate_pfunctions(self):
        grammar = []
        # Check for non-empty symbols
        if len(self.pfuncs) == 0:
            self.log.error('no rules of the form p_rulename are defined')
            self.error = True
            return

        for line, module, name, doc in self.pfuncs:
            file = inspect.getsourcefile(module)
            func = self.pdict[name]
            if isinstance(func, types.MethodType):
                reqargs = 2
            else:
                reqargs = 1
            if func.__code__.co_argcount > reqargs:
                self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__)
                self.error = True
            elif func.__code__.co_argcount < reqargs:
                self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__)
                self.error = True
            elif not func.__doc__:
                self.log.warning('%s:%d: No documentation string specified in function %r (ignored)',
                                 file, line, func.__name__)
            else:
                try:
                    parsed_g = parse_grammar(doc, file, line)
                    for g in parsed_g:
                        grammar.append((name, g))
                except SyntaxError as e:
                    self.log.error(str(e))
                    self.error = True

                # Looks like a valid grammar rule
                # Mark the file in which defined.
                self.modules.add(module)

        # Secondary validation step that looks for p_ definitions that are not functions
        # or functions that look like they might be grammar rules.

        for n, v in self.pdict.items():
            if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)):
                continue
            if n.startswith('t_'):
                continue
            if n.startswith('p_') and n != 'p_error':
                self.log.warning('%r not defined as a function', n)
            if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or
                   (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)):
                if v.__doc__:
                    try:
                        doc = v.__doc__.split(' ')
                        if doc[1] == ':':
                            self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix',
                                             v.__code__.co_filename, v.__code__.co_firstlineno, n)
                    except IndexError:
                        pass

        self.grammar = grammar

# -----------------------------------------------------------------------------
# yacc(module)
#
# Build a parser
# -----------------------------------------------------------------------------

def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
         check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file,
         outputdir=None, debuglog=None, errorlog=None, picklefile=None):

    if tabmodule is None:
        tabmodule = tab_module

    # Reference to the parsing method of the last built parser
    global parse

    # If pickling is enabled, table files are not created
    if picklefile:
        write_tables = 0

    if errorlog is None:
        errorlog = PlyLogger(sys.stderr)

    # Get the module dictionary used for the parser
    if module:
        _items = [(k, getattr(module, k)) for k in dir(module)]
        pdict = dict(_items)
        # If no __file__ or __package__ attributes are available, try to obtain them
        # from the __module__ instead
        if '__file__' not in pdict:
            pdict['__file__'] = sys.modules[pdict['__module__']].__file__
        if '__package__' not in pdict and '__module__' in pdict:
            if hasattr(sys.modules[pdict['__module__']], '__package__'):
                pdict['__package__'] = sys.modules[pdict['__module__']].__package__
    else:
        pdict = get_caller_module_dict(2)

    if outputdir is None:
        # If no output directory is set, the location of the output files
        # is determined according to the following rules:
        #     - If tabmodule specifies a package, files go into that package directory
        #     - Otherwise, files go in the same directory as the specifying module
        if isinstance(tabmodule, types.ModuleType):
            srcfile = tabmodule.__file__
        else:
            if '.' not in tabmodule:
                srcfile = pdict['__file__']
            else:
                parts = tabmodule.split('.')
                pkgname = '.'.join(parts[:-1])
                exec('import %s' % pkgname)
                srcfile = getattr(sys.modules[pkgname], '__file__', '')
        outputdir = os.path.dirname(srcfile)

    # Determine if the module is package of a package or not.
    # If so, fix the tabmodule setting so that tables load correctly
    pkg = pdict.get('__package__')
    if pkg and isinstance(tabmodule, str):
        if '.' not in tabmodule:
            tabmodule = pkg + '.' + tabmodule



    # Set start symbol if it's specified directly using an argument
    if start is not None:
        pdict['start'] = start

    # Collect parser information from the dictionary
    pinfo = ParserReflect(pdict, log=errorlog)
    pinfo.get_all()

    if pinfo.error:
        raise YaccError('Unable to build parser')

    # Check signature against table files (if any)
    signature = pinfo.signature()

    # Read the tables
    try:
        lr = LRTable()
        if picklefile:
            read_signature = lr.read_pickle(picklefile)
        else:
            read_signature = lr.read_table(tabmodule)
        if optimize or (read_signature == signature):
            try:
                lr.bind_callables(pinfo.pdict)
                parser = LRParser(lr, pinfo.error_func)
                parse = parser.parse
                return parser
            except Exception as e:
                errorlog.warning('There was a problem loading the table file: %r', e)
    except VersionError as e:
        errorlog.warning(str(e))
    except ImportError:
        pass

    if debuglog is None:
        if debug:
            try:
                debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w'))
            except IOError as e:
                errorlog.warning("Couldn't open %r. %s" % (debugfile, e))
                debuglog = NullLogger()
        else:
            debuglog = NullLogger()

    debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__)

    errors = False

    # Validate the parser information
    if pinfo.validate_all():
        raise YaccError('Unable to build parser')

    if not pinfo.error_func:
        errorlog.warning('no p_error() function is defined')

    # Create a grammar object
    grammar = Grammar(pinfo.tokens)

    # Set precedence level for terminals
    for term, assoc, level in pinfo.preclist:
        try:
            grammar.set_precedence(term, assoc, level)
        except GrammarError as e:
            errorlog.warning('%s', e)

    # Add productions to the grammar
    for funcname, gram in pinfo.grammar:
        file, line, prodname, syms = gram
        try:
            grammar.add_production(prodname, syms, funcname, file, line)
        except GrammarError as e:
            errorlog.error('%s', e)
            errors = True

    # Set the grammar start symbols
    try:
        if start is None:
            grammar.set_start(pinfo.start)
        else:
            grammar.set_start(start)
    except GrammarError as e:
        errorlog.error(str(e))
        errors = True

    if errors:
        raise YaccError('Unable to build parser')

    # Verify the grammar structure
    undefined_symbols = grammar.undefined_symbols()
    for sym, prod in undefined_symbols:
        errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
        errors = True

    unused_terminals = grammar.unused_terminals()
    if unused_terminals:
        debuglog.info('')
        debuglog.info('Unused terminals:')
        debuglog.info('')
        for term in unused_terminals:
            errorlog.warning('Token %r defined, but not used', term)
            debuglog.info('    %s', term)

    # Print out all productions to the debug log
    if debug:
        debuglog.info('')
        debuglog.info('Grammar')
        debuglog.info('')
        for n, p in enumerate(grammar.Productions):
            debuglog.info('Rule %-5d %s', n, p)

    # Find unused non-terminals
    unused_rules = grammar.unused_rules()
    for prod in unused_rules:
        errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name)

    if len(unused_terminals) == 1:
        errorlog.warning('There is 1 unused token')
    if len(unused_terminals) > 1:
        errorlog.warning('There are %d unused tokens', len(unused_terminals))

    if len(unused_rules) == 1:
        errorlog.warning('There is 1 unused rule')
    if len(unused_rules) > 1:
        errorlog.warning('There are %d unused rules', len(unused_rules))

    if debug:
        debuglog.info('')
        debuglog.info('Terminals, with rules where they appear')
        debuglog.info('')
        terms = list(grammar.Terminals)
        terms.sort()
        for term in terms:
            debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]]))

        debuglog.info('')
        debuglog.info('Nonterminals, with rules where they appear')
        debuglog.info('')
        nonterms = list(grammar.Nonterminals)
        nonterms.sort()
        for nonterm in nonterms:
            debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]]))
        debuglog.info('')

    if check_recursion:
        unreachable = grammar.find_unreachable()
        for u in unreachable:
            errorlog.warning('Symbol %r is unreachable', u)

        infinite = grammar.infinite_cycles()
        for inf in infinite:
            errorlog.error('Infinite recursion detected for symbol %r', inf)
            errors = True

    unused_prec = grammar.unused_precedence()
    for term, assoc in unused_prec:
        errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
        errors = True

    if errors:
        raise YaccError('Unable to build parser')

    # Run the LRGeneratedTable on the grammar
    if debug:
        errorlog.debug('Generating %s tables', method)

    lr = LRGeneratedTable(grammar, method, debuglog)

    if debug:
        num_sr = len(lr.sr_conflicts)

        # Report shift/reduce and reduce/reduce conflicts
        if num_sr == 1:
            errorlog.warning('1 shift/reduce conflict')
        elif num_sr > 1:
            errorlog.warning('%d shift/reduce conflicts', num_sr)

        num_rr = len(lr.rr_conflicts)
        if num_rr == 1:
            errorlog.warning('1 reduce/reduce conflict')
        elif num_rr > 1:
            errorlog.warning('%d reduce/reduce conflicts', num_rr)

    # Write out conflicts to the output file
    if debug and (lr.sr_conflicts or lr.rr_conflicts):
        debuglog.warning('')
        debuglog.warning('Conflicts:')
        debuglog.warning('')

        for state, tok, resolution in lr.sr_conflicts:
            debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s',  tok, state, resolution)

        already_reported = set()
        for state, rule, rejected in lr.rr_conflicts:
            if (state, id(rule), id(rejected)) in already_reported:
                continue
            debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
            debuglog.warning('rejected rule (%s) in state %d', rejected, state)
            errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
            errorlog.warning('rejected rule (%s) in state %d', rejected, state)
            already_reported.add((state, id(rule), id(rejected)))

        warned_never = []
        for state, rule, rejected in lr.rr_conflicts:
            if not rejected.reduced and (rejected not in warned_never):
                debuglog.warning('Rule (%s) is never reduced', rejected)
                errorlog.warning('Rule (%s) is never reduced', rejected)
                warned_never.append(rejected)

    # Write the table file if requested
    if write_tables:
        try:
            lr.write_table(tabmodule, outputdir, signature)
            if tabmodule in sys.modules:
                del sys.modules[tabmodule]
        except IOError as e:
            errorlog.warning("Couldn't create %r. %s" % (tabmodule, e))

    # Write a pickled version of the tables
    if picklefile:
        try:
            lr.pickle_table(picklefile, signature)
        except IOError as e:
            errorlog.warning("Couldn't create %r. %s" % (picklefile, e))

    # Build the parser
    lr.bind_callables(pinfo.pdict)
    parser = LRParser(lr, pinfo.error_func)

    parse = parser.parse
    return parser
0707010001f4a8000081a40000000000000000000000016a100daf00000c53000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/ply/ctokens.py # ----------------------------------------------------------------------
# ctokens.py
#
# Token specifications for symbols in ANSI C and C++.  This file is
# meant to be used as a library in other tokenizers.
# ----------------------------------------------------------------------

# Reserved words

tokens = [
    # Literals (identifier, integer constant, float constant, string constant, char const)
    'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER',

    # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO',
    'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
    'LOR', 'LAND', 'LNOT',
    'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',

    # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
    'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
    'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',

    # Increment/decrement (++,--)
    'INCREMENT', 'DECREMENT',

    # Structure dereference (->)
    'ARROW',

    # Ternary operator (?)
    'TERNARY',

    # Delimeters ( ) [ ] { } , . ; :
    'LPAREN', 'RPAREN',
    'LBRACKET', 'RBRACKET',
    'LBRACE', 'RBRACE',
    'COMMA', 'PERIOD', 'SEMI', 'COLON',

    # Ellipsis (...)
    'ELLIPSIS',
]

# Operators
t_PLUS             = r'\+'
t_MINUS            = r'-'
t_TIMES            = r'\*'
t_DIVIDE           = r'/'
t_MODULO           = r'%'
t_OR               = r'\|'
t_AND              = r'&'
t_NOT              = r'~'
t_XOR              = r'\^'
t_LSHIFT           = r'<<'
t_RSHIFT           = r'>>'
t_LOR              = r'\|\|'
t_LAND             = r'&&'
t_LNOT             = r'!'
t_LT               = r'<'
t_GT               = r'>'
t_LE               = r'<='
t_GE               = r'>='
t_EQ               = r'=='
t_NE               = r'!='

# Assignment operators

t_EQUALS           = r'='
t_TIMESEQUAL       = r'\*='
t_DIVEQUAL         = r'/='
t_MODEQUAL         = r'%='
t_PLUSEQUAL        = r'\+='
t_MINUSEQUAL       = r'-='
t_LSHIFTEQUAL      = r'<<='
t_RSHIFTEQUAL      = r'>>='
t_ANDEQUAL         = r'&='
t_OREQUAL          = r'\|='
t_XOREQUAL         = r'\^='

# Increment/decrement
t_INCREMENT        = r'\+\+'
t_DECREMENT        = r'--'

# ->
t_ARROW            = r'->'

# ?
t_TERNARY          = r'\?'

# Delimeters
t_LPAREN           = r'\('
t_RPAREN           = r'\)'
t_LBRACKET         = r'\['
t_RBRACKET         = r'\]'
t_LBRACE           = r'\{'
t_RBRACE           = r'\}'
t_COMMA            = r','
t_PERIOD           = r'\.'
t_SEMI             = r';'
t_COLON            = r':'
t_ELLIPSIS         = r'\.\.\.'

# Identifiers
t_ID = r'[A-Za-z_][A-Za-z0-9_]*'

# Integer literal
t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?'

# Floating literal
t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'

# String literal
t_STRING = r'\"([^\\\n]|(\\.))*?\"'

# Character constant 'c' or L'c'
t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\''

# Comment (C-Style)
def t_COMMENT(t):
    r'/\*(.|\n)*?\*/'
    t.lexer.lineno += t.value.count('\n')
    return t

# Comment (C++-Style)
def t_CPPCOMMENT(t):
    r'//.*\n'
    t.lexer.lineno += 1
    return t
 0707010001f4a9000081a40000000000000000000000016a100daf0000a799000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/foreign/ply/lex.py # -----------------------------------------------------------------------------
# ply: lex.py
#
# Copyright (C) 2001-2018
# David M. Beazley (Dabeaz LLC)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the David Beazley or Dabeaz LLC may be used to
#   endorse or promote products derived from this software without
#  specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# -----------------------------------------------------------------------------

__version__    = '3.11'
__tabversion__ = '3.10'

import re
import sys
import types
import copy
import os
import inspect

# This tuple contains known string types
try:
    # Python 2.6
    StringTypes = (types.StringType, types.UnicodeType)
except AttributeError:
    # Python 3.0
    StringTypes = (str, bytes)

# This regular expression is used to match valid token names
_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')

# Exception thrown when invalid token encountered and no default error
# handler is defined.
class LexError(Exception):
    def __init__(self, message, s):
        self.args = (message,)
        self.text = s


# Token class.  This class is used to represent the tokens produced.
class LexToken(object):
    def __str__(self):
        return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos)

    def __repr__(self):
        return str(self)


# This object is a stand-in for a logging object created by the
# logging module.

class PlyLogger(object):
    def __init__(self, f):
        self.f = f

    def critical(self, msg, *args, **kwargs):
        self.f.write((msg % args) + '\n')

    def warning(self, msg, *args, **kwargs):
        self.f.write('WARNING: ' + (msg % args) + '\n')

    def error(self, msg, *args, **kwargs):
        self.f.write('ERROR: ' + (msg % args) + '\n')

    info = critical
    debug = critical


# Null logger is used when no output is generated. Does nothing.
class NullLogger(object):
    def __getattribute__(self, name):
        return self

    def __call__(self, *args, **kwargs):
        return self


# -----------------------------------------------------------------------------
#                        === Lexing Engine ===
#
# The following Lexer class implements the lexer runtime.   There are only
# a few public methods and attributes:
#
#    input()          -  Store a new string in the lexer
#    token()          -  Get the next token
#    clone()          -  Clone the lexer
#
#    lineno           -  Current line number
#    lexpos           -  Current position in the input string
# -----------------------------------------------------------------------------

class Lexer:
    def __init__(self):
        self.lexre = None             # Master regular expression. This is a list of
                                      # tuples (re, findex) where re is a compiled
                                      # regular expression and findex is a list
                                      # mapping regex group numbers to rules
        self.lexretext = None         # Current regular expression strings
        self.lexstatere = {}          # Dictionary mapping lexer states to master regexs
        self.lexstateretext = {}      # Dictionary mapping lexer states to regex strings
        self.lexstaterenames = {}     # Dictionary mapping lexer states to symbol names
        self.lexstate = 'INITIAL'     # Current lexer state
        self.lexstatestack = []       # Stack of lexer states
        self.lexstateinfo = None      # State information
        self.lexstateignore = {}      # Dictionary of ignored characters for each state
        self.lexstateerrorf = {}      # Dictionary of error functions for each state
        self.lexstateeoff = {}        # Dictionary of eof functions for each state
        self.lexreflags = 0           # Optional re compile flags
        self.lexdata = None           # Actual input data (as a string)
        self.lexpos = 0               # Current position in input text
        self.lexlen = 0               # Length of the input text
        self.lexerrorf = None         # Error rule (if any)
        self.lexeoff = None           # EOF rule (if any)
        self.lextokens = None         # List of valid tokens
        self.lexignore = ''           # Ignored characters
        self.lexliterals = ''         # Literal characters that can be passed through
        self.lexmodule = None         # Module
        self.lineno = 1               # Current line number
        self.lexoptimize = False      # Optimized mode

    def clone(self, object=None):
        c = copy.copy(self)

        # If the object parameter has been supplied, it means we are attaching the
        # lexer to a new object.  In this case, we have to rebind all methods in
        # the lexstatere and lexstateerrorf tables.

        if object:
            newtab = {}
            for key, ritem in self.lexstatere.items():
                newre = []
                for cre, findex in ritem:
                    newfindex = []
                    for f in findex:
                        if not f or not f[0]:
                            newfindex.append(f)
                            continue
                        newfindex.append((getattr(object, f[0].__name__), f[1]))
                newre.append((cre, newfindex))
                newtab[key] = newre
            c.lexstatere = newtab
            c.lexstateerrorf = {}
            for key, ef in self.lexstateerrorf.items():
                c.lexstateerrorf[key] = getattr(object, ef.__name__)
            c.lexmodule = object
        return c

    # ------------------------------------------------------------
    # writetab() - Write lexer information to a table file
    # ------------------------------------------------------------
    def writetab(self, lextab, outputdir=''):
        if isinstance(lextab, types.ModuleType):
            raise IOError("Won't overwrite existing lextab module")
        basetabmodule = lextab.split('.')[-1]
        filename = os.path.join(outputdir, basetabmodule) + '.py'
        with open(filename, 'w') as tf:
            tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__))
            tf.write('_tabversion   = %s\n' % repr(__tabversion__))
            tf.write('_lextokens    = set(%s)\n' % repr(tuple(sorted(self.lextokens))))
            tf.write('_lexreflags   = %s\n' % repr(int(self.lexreflags)))
            tf.write('_lexliterals  = %s\n' % repr(self.lexliterals))
            tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo))

            # Rewrite the lexstatere table, replacing function objects with function names
            tabre = {}
            for statename, lre in self.lexstatere.items():
                titem = []
                for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]):
                    titem.append((retext, _funcs_to_names(func, renames)))
                tabre[statename] = titem

            tf.write('_lexstatere   = %s\n' % repr(tabre))
            tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore))

            taberr = {}
            for statename, ef in self.lexstateerrorf.items():
                taberr[statename] = ef.__name__ if ef else None
            tf.write('_lexstateerrorf = %s\n' % repr(taberr))

            tabeof = {}
            for statename, ef in self.lexstateeoff.items():
                tabeof[statename] = ef.__name__ if ef else None
            tf.write('_lexstateeoff = %s\n' % repr(tabeof))

    # ------------------------------------------------------------
    # readtab() - Read lexer information from a tab file
    # ------------------------------------------------------------
    def readtab(self, tabfile, fdict):
        if isinstance(tabfile, types.ModuleType):
            lextab = tabfile
        else:
            exec('import %s' % tabfile)
            lextab = sys.modules[tabfile]

        if getattr(lextab, '_tabversion', '0.0') != __tabversion__:
            raise ImportError('Inconsistent PLY version')

        self.lextokens      = lextab._lextokens
        self.lexreflags     = lextab._lexreflags
        self.lexliterals    = lextab._lexliterals
        self.lextokens_all  = self.lextokens | set(self.lexliterals)
        self.lexstateinfo   = lextab._lexstateinfo
        self.lexstateignore = lextab._lexstateignore
        self.lexstatere     = {}
        self.lexstateretext = {}
        for statename, lre in lextab._lexstatere.items():
            titem = []
            txtitem = []
            for pat, func_name in lre:
                titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict)))

            self.lexstatere[statename] = titem
            self.lexstateretext[statename] = txtitem

        self.lexstateerrorf = {}
        for statename, ef in lextab._lexstateerrorf.items():
            self.lexstateerrorf[statename] = fdict[ef]

        self.lexstateeoff = {}
        for statename, ef in lextab._lexstateeoff.items():
            self.lexstateeoff[statename] = fdict[ef]

        self.begin('INITIAL')

    # ------------------------------------------------------------
    # input() - Push a new string into the lexer
    # ------------------------------------------------------------
    def input(self, s):
        # Pull off the first character to see if s looks like a string
        c = s[:1]
        if not isinstance(c, StringTypes):
            raise ValueError('Expected a string')
        self.lexdata = s
        self.lexpos = 0
        self.lexlen = len(s)

    # ------------------------------------------------------------
    # begin() - Changes the lexing state
    # ------------------------------------------------------------
    def begin(self, state):
        if state not in self.lexstatere:
            raise ValueError('Undefined state')
        self.lexre = self.lexstatere[state]
        self.lexretext = self.lexstateretext[state]
        self.lexignore = self.lexstateignore.get(state, '')
        self.lexerrorf = self.lexstateerrorf.get(state, None)
        self.lexeoff = self.lexstateeoff.get(state, None)
        self.lexstate = state

    # ------------------------------------------------------------
    # push_state() - Changes the lexing state and saves old on stack
    # ------------------------------------------------------------
    def push_state(self, state):
        self.lexstatestack.append(self.lexstate)
        self.begin(state)

    # ------------------------------------------------------------
    # pop_state() - Restores the previous state
    # ------------------------------------------------------------
    def pop_state(self):
        self.begin(self.lexstatestack.pop())

    # ------------------------------------------------------------
    # current_state() - Returns the current lexing state
    # ------------------------------------------------------------
    def current_state(self):
        return self.lexstate

    # ------------------------------------------------------------
    # skip() - Skip ahead n characters
    # ------------------------------------------------------------
    def skip(self, n):
        self.lexpos += n

    # ------------------------------------------------------------
    # opttoken() - Return the next token from the Lexer
    #
    # Note: This function has been carefully implemented to be as fast
    # as possible.  Don't make changes unless you really know what
    # you are doing
    # ------------------------------------------------------------
    def token(self):
        # Make local copies of frequently referenced attributes
        lexpos    = self.lexpos
        lexlen    = self.lexlen
        lexignore = self.lexignore
        lexdata   = self.lexdata

        while lexpos < lexlen:
            # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
            if lexdata[lexpos] in lexignore:
                lexpos += 1
                continue

            # Look for a regular expression match
            for lexre, lexindexfunc in self.lexre:
                m = lexre.match(lexdata, lexpos)
                if not m:
                    continue

                # Create a token for return
                tok = LexToken()
                tok.value = m.group()
                tok.lineno = self.lineno
                tok.lexpos = lexpos

                i = m.lastindex
                func, tok.type = lexindexfunc[i]

                if not func:
                    # If no token type was set, it's an ignored token
                    if tok.type:
                        self.lexpos = m.end()
                        return tok
                    else:
                        lexpos = m.end()
                        break

                lexpos = m.end()

                # If token is processed by a function, call it

                tok.lexer = self      # Set additional attributes useful in token rules
                self.lexmatch = m
                self.lexpos = lexpos

                newtok = func(tok)

                # Every function must return a token, if nothing, we just move to next token
                if not newtok:
                    lexpos    = self.lexpos         # This is here in case user has updated lexpos.
                    lexignore = self.lexignore      # This is here in case there was a state change
                    break

                # Verify type of the token.  If not in the token map, raise an error
                if not self.lexoptimize:
                    if newtok.type not in self.lextokens_all:
                        raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
                            func.__code__.co_filename, func.__code__.co_firstlineno,
                            func.__name__, newtok.type), lexdata[lexpos:])

                return newtok
            else:
                # No match, see if in literals
                if lexdata[lexpos] in self.lexliterals:
                    tok = LexToken()
                    tok.value = lexdata[lexpos]
                    tok.lineno = self.lineno
                    tok.type = tok.value
                    tok.lexpos = lexpos
                    self.lexpos = lexpos + 1
                    return tok

                # No match. Call t_error() if defined.
                if self.lexerrorf:
                    tok = LexToken()
                    tok.value = self.lexdata[lexpos:]
                    tok.lineno = self.lineno
                    tok.type = 'error'
                    tok.lexer = self
                    tok.lexpos = lexpos
                    self.lexpos = lexpos
                    newtok = self.lexerrorf(tok)
                    if lexpos == self.lexpos:
                        # Error method didn't change text position at all. This is an error.
                        raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
                    lexpos = self.lexpos
                    if not newtok:
                        continue
                    return newtok

                self.lexpos = lexpos
                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:])

        if self.lexeoff:
            tok = LexToken()
            tok.type = 'eof'
            tok.value = ''
            tok.lineno = self.lineno
            tok.lexpos = lexpos
            tok.lexer = self
            self.lexpos = lexpos
            newtok = self.lexeoff(tok)
            return newtok

        self.lexpos = lexpos + 1
        if self.lexdata is None:
            raise RuntimeError('No input string given with input()')
        return None

    # Iterator interface
    def __iter__(self):
        return self

    def next(self):
        t = self.token()
        if t is None:
            raise StopIteration
        return t

    __next__ = next

# -----------------------------------------------------------------------------
#                           ==== Lex Builder ===
#
# The functions and classes below are used to collect lexing information
# and build a Lexer object from it.
# -----------------------------------------------------------------------------

# -----------------------------------------------------------------------------
# _get_regex(func)
#
# Returns the regular expression assigned to a function either as a doc string
# or as a .regex attribute attached by the @TOKEN decorator.
# -----------------------------------------------------------------------------
def _get_regex(func):
    return getattr(func, 'regex', func.__doc__)

# -----------------------------------------------------------------------------
# get_caller_module_dict()
#
# This function returns a dictionary containing all of the symbols defined within
# a caller further down the call stack.  This is used to get the environment
# associated with the yacc() call if none was provided.
# -----------------------------------------------------------------------------
def get_caller_module_dict(levels):
    f = sys._getframe(levels)
    ldict = f.f_globals.copy()
    if f.f_globals != f.f_locals:
        ldict.update(f.f_locals)
    return ldict

# -----------------------------------------------------------------------------
# _funcs_to_names()
#
# Given a list of regular expression functions, this converts it to a list
# suitable for output to a table file
# -----------------------------------------------------------------------------
def _funcs_to_names(funclist, namelist):
    result = []
    for f, name in zip(funclist, namelist):
        if f and f[0]:
            result.append((name, f[1]))
        else:
            result.append(f)
    return result

# -----------------------------------------------------------------------------
# _names_to_funcs()
#
# Given a list of regular expression function names, this converts it back to
# functions.
# -----------------------------------------------------------------------------
def _names_to_funcs(namelist, fdict):
    result = []
    for n in namelist:
        if n and n[0]:
            result.append((fdict[n[0]], n[1]))
        else:
            result.append(n)
    return result

# -----------------------------------------------------------------------------
# _form_master_re()
#
# This function takes a list of all of the regex components and attempts to
# form the master regular expression.  Given limitations in the Python re
# module, it may be necessary to break the master regex into separate expressions.
# -----------------------------------------------------------------------------
def _form_master_re(relist, reflags, ldict, toknames):
    if not relist:
        return []
    regex = '|'.join(relist)
    try:
        lexre = re.compile(regex, reflags)

        # Build the index to function map for the matching engine
        lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1)
        lexindexnames = lexindexfunc[:]

        for f, i in lexre.groupindex.items():
            handle = ldict.get(f, None)
            if type(handle) in (types.FunctionType, types.MethodType):
                lexindexfunc[i] = (handle, toknames[f])
                lexindexnames[i] = f
            elif handle is not None:
                lexindexnames[i] = f
                if f.find('ignore_') > 0:
                    lexindexfunc[i] = (None, None)
                else:
                    lexindexfunc[i] = (None, toknames[f])

        return [(lexre, lexindexfunc)], [regex], [lexindexnames]
    except Exception:
        m = int(len(relist)/2)
        if m == 0:
            m = 1
        llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames)
        rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames)
        return (llist+rlist), (lre+rre), (lnames+rnames)

# -----------------------------------------------------------------------------
# def _statetoken(s,names)
#
# Given a declaration name s of the form "t_" and a dictionary whose keys are
# state names, this function returns a tuple (states,tokenname) where states
# is a tuple of state names and tokenname is the name of the token.  For example,
# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
# -----------------------------------------------------------------------------
def _statetoken(s, names):
    parts = s.split('_')
    for i, part in enumerate(parts[1:], 1):
        if part not in names and part != 'ANY':
            break

    if i > 1:
        states = tuple(parts[1:i])
    else:
        states = ('INITIAL',)

    if 'ANY' in states:
        states = tuple(names)

    tokenname = '_'.join(parts[i:])
    return (states, tokenname)


# -----------------------------------------------------------------------------
# LexerReflect()
#
# This class represents information needed to build a lexer as extracted from a
# user's input file.
# -----------------------------------------------------------------------------
class LexerReflect(object):
    def __init__(self, ldict, log=None, reflags=0):
        self.ldict      = ldict
        self.error_func = None
        self.tokens     = []
        self.reflags    = reflags
        self.stateinfo  = {'INITIAL': 'inclusive'}
        self.modules    = set()
        self.error      = False
        self.log        = PlyLogger(sys.stderr) if log is None else log

    # Get all of the basic information
    def get_all(self):
        self.get_tokens()
        self.get_literals()
        self.get_states()
        self.get_rules()

    # Validate all of the information
    def validate_all(self):
        self.validate_tokens()
        self.validate_literals()
        self.validate_rules()
        return self.error

    # Get the tokens map
    def get_tokens(self):
        tokens = self.ldict.get('tokens', None)
        if not tokens:
            self.log.error('No token list is defined')
            self.error = True
            return

        if not isinstance(tokens, (list, tuple)):
            self.log.error('tokens must be a list or tuple')
            self.error = True
            return

        if not tokens:
            self.log.error('tokens is empty')
            self.error = True
            return

        self.tokens = tokens

    # Validate the tokens
    def validate_tokens(self):
        terminals = {}
        for n in self.tokens:
            if not _is_identifier.match(n):
                self.log.error("Bad token name '%s'", n)
                self.error = True
            if n in terminals:
                self.log.warning("Token '%s' multiply defined", n)
            terminals[n] = 1

    # Get the literals specifier
    def get_literals(self):
        self.literals = self.ldict.get('literals', '')
        if not self.literals:
            self.literals = ''

    # Validate literals
    def validate_literals(self):
        try:
            for c in self.literals:
                if not isinstance(c, StringTypes) or len(c) > 1:
                    self.log.error('Invalid literal %s. Must be a single character', repr(c))
                    self.error = True

        except TypeError:
            self.log.error('Invalid literals specification. literals must be a sequence of characters')
            self.error = True

    def get_states(self):
        self.states = self.ldict.get('states', None)
        # Build statemap
        if self.states:
            if not isinstance(self.states, (tuple, list)):
                self.log.error('states must be defined as a tuple or list')
                self.error = True
            else:
                for s in self.states:
                    if not isinstance(s, tuple) or len(s) != 2:
                        self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s))
                        self.error = True
                        continue
                    name, statetype = s
                    if not isinstance(name, StringTypes):
                        self.log.error('State name %s must be a string', repr(name))
                        self.error = True
                        continue
                    if not (statetype == 'inclusive' or statetype == 'exclusive'):
                        self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name)
                        self.error = True
                        continue
                    if name in self.stateinfo:
                        self.log.error("State '%s' already defined", name)
                        self.error = True
                        continue
                    self.stateinfo[name] = statetype

    # Get all of the symbols with a t_ prefix and sort them into various
    # categories (functions, strings, error functions, and ignore characters)

    def get_rules(self):
        tsymbols = [f for f in self.ldict if f[:2] == 't_']

        # Now build up a list of functions and a list of strings
        self.toknames = {}        # Mapping of symbols to token names
        self.funcsym  = {}        # Symbols defined as functions
        self.strsym   = {}        # Symbols defined as strings
        self.ignore   = {}        # Ignore strings by state
        self.errorf   = {}        # Error functions by state
        self.eoff     = {}        # EOF functions by state

        for s in self.stateinfo:
            self.funcsym[s] = []
            self.strsym[s] = []

        if len(tsymbols) == 0:
            self.log.error('No rules of the form t_rulename are defined')
            self.error = True
            return

        for f in tsymbols:
            t = self.ldict[f]
            states, tokname = _statetoken(f, self.stateinfo)
            self.toknames[f] = tokname

            if hasattr(t, '__call__'):
                if tokname == 'error':
                    for s in states:
                        self.errorf[s] = t
                elif tokname == 'eof':
                    for s in states:
                        self.eoff[s] = t
                elif tokname == 'ignore':
                    line = t.__code__.co_firstlineno
                    file = t.__code__.co_filename
                    self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__)
                    self.error = True
                else:
                    for s in states:
                        self.funcsym[s].append((f, t))
            elif isinstance(t, StringTypes):
                if tokname == 'ignore':
                    for s in states:
                        self.ignore[s] = t
                    if '\\' in t:
                        self.log.warning("%s contains a literal backslash '\\'", f)

                elif tokname == 'error':
                    self.log.error("Rule '%s' must be defined as a function", f)
                    self.error = True
                else:
                    for s in states:
                        self.strsym[s].append((f, t))
            else:
                self.log.error('%s not defined as a function or string', f)
                self.error = True

        # Sort the functions by line number
        for f in self.funcsym.values():
            f.sort(key=lambda x: x[1].__code__.co_firstlineno)

        # Sort the strings by regular expression length
        for s in self.strsym.values():
            s.sort(key=lambda x: len(x[1]), reverse=True)

    # Validate all of the t_rules collected
    def validate_rules(self):
        for state in self.stateinfo:
            # Validate all rules defined by functions

            for fname, f in self.funcsym[state]:
                line = f.__code__.co_firstlineno
                file = f.__code__.co_filename
                module = inspect.getmodule(f)
                self.modules.add(module)

                tokname = self.toknames[fname]
                if isinstance(f, types.MethodType):
                    reqargs = 2
                else:
                    reqargs = 1
                nargs = f.__code__.co_argcount
                if nargs > reqargs:
                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
                    self.error = True
                    continue

                if nargs < reqargs:
                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
                    self.error = True
                    continue

                if not _get_regex(f):
                    self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__)
                    self.error = True
                    continue

                try:
                    c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags)
                    if c.match(''):
                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__)
                        self.error = True
                except re.error as e:
                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e)
                    if '#' in _get_regex(f):
                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__)
                    self.error = True

            # Validate all rules defined by strings
            for name, r in self.strsym[state]:
                tokname = self.toknames[name]
                if tokname == 'error':
                    self.log.error("Rule '%s' must be defined as a function", name)
                    self.error = True
                    continue

                if tokname not in self.tokens and tokname.find('ignore_') < 0:
                    self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname)
                    self.error = True
                    continue

                try:
                    c = re.compile('(?P<%s>%s)' % (name, r), self.reflags)
                    if (c.match('')):
                        self.log.error("Regular expression for rule '%s' matches empty string", name)
                        self.error = True
                except re.error as e:
                    self.log.error("Invalid regular expression for rule '%s'. %s", name, e)
                    if '#' in r:
                        self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name)
                    self.error = True

            if not self.funcsym[state] and not self.strsym[state]:
                self.log.error("No rules defined for state '%s'", state)
                self.error = True

            # Validate the error function
            efunc = self.errorf.get(state, None)
            if efunc:
                f = efunc
                line = f.__code__.co_firstlineno
                file = f.__code__.co_filename
                module = inspect.getmodule(f)
                self.modules.add(module)

                if isinstance(f, types.MethodType):
                    reqargs = 2
                else:
                    reqargs = 1
                nargs = f.__code__.co_argcount
                if nargs > reqargs:
                    self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__)
                    self.error = True

                if nargs < reqargs:
                    self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__)
                    self.error = True

        for module in self.modules:
            self.validate_module(module)

    # -----------------------------------------------------------------------------
    # validate_module()
    #
    # This checks to see if there are duplicated t_rulename() functions or strings
    # in the parser input file.  This is done using a simple regular expression
    # match on each line in the source code of the given module.
    # -----------------------------------------------------------------------------

    def validate_module(self, module):
        try:
            lines, linen = inspect.getsourcelines(module)
        except IOError:
            return

        fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
        sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')

        counthash = {}
        linen += 1
        for line in lines:
            m = fre.match(line)
            if not m:
                m = sre.match(line)
            if m:
                name = m.group(1)
                prev = counthash.get(name)
                if not prev:
                    counthash[name] = linen
                else:
                    filename = inspect.getsourcefile(module)
                    self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev)
                    self.error = True
            linen += 1

# -----------------------------------------------------------------------------
# lex(module)
#
# Build all of the regular expression rules from definitions in the supplied module
# -----------------------------------------------------------------------------
def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab',
        reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None):

    if lextab is None:
        lextab = 'lextab'

    global lexer

    ldict = None
    stateinfo  = {'INITIAL': 'inclusive'}
    lexobj = Lexer()
    lexobj.lexoptimize = optimize
    global token, input

    if errorlog is None:
        errorlog = PlyLogger(sys.stderr)

    if debug:
        if debuglog is None:
            debuglog = PlyLogger(sys.stderr)

    # Get the module dictionary used for the lexer
    if object:
        module = object

    # Get the module dictionary used for the parser
    if module:
        _items = [(k, getattr(module, k)) for k in dir(module)]
        ldict = dict(_items)
        # If no __file__ attribute is available, try to obtain it from the __module__ instead
        if '__file__' not in ldict:
            ldict['__file__'] = sys.modules[ldict['__module__']].__file__
    else:
        ldict = get_caller_module_dict(2)

    # Determine if the module is package of a package or not.
    # If so, fix the tabmodule setting so that tables load correctly
    pkg = ldict.get('__package__')
    if pkg and isinstance(lextab, str):
        if '.' not in lextab:
            lextab = pkg + '.' + lextab

    # Collect parser information from the dictionary
    linfo = LexerReflect(ldict, log=errorlog, reflags=reflags)
    linfo.get_all()
    if not optimize:
        if linfo.validate_all():
            raise SyntaxError("Can't build lexer")

    if optimize and lextab:
        try:
            lexobj.readtab(lextab, ldict)
            token = lexobj.token
            input = lexobj.input
            lexer = lexobj
            return lexobj

        except ImportError:
            pass

    # Dump some basic debugging information
    if debug:
        debuglog.info('lex: tokens   = %r', linfo.tokens)
        debuglog.info('lex: literals = %r', linfo.literals)
        debuglog.info('lex: states   = %r', linfo.stateinfo)

    # Build a dictionary of valid token names
    lexobj.lextokens = set()
    for n in linfo.tokens:
        lexobj.lextokens.add(n)

    # Get literals specification
    if isinstance(linfo.literals, (list, tuple)):
        lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
    else:
        lexobj.lexliterals = linfo.literals

    lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals)

    # Get the stateinfo dictionary
    stateinfo = linfo.stateinfo

    regexs = {}
    # Build the master regular expressions
    for state in stateinfo:
        regex_list = []

        # Add rules defined by functions first
        for fname, f in linfo.funcsym[state]:
            regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f)))
            if debug:
                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state)

        # Now add all of the simple rules
        for name, r in linfo.strsym[state]:
            regex_list.append('(?P<%s>%s)' % (name, r))
            if debug:
                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state)

        regexs[state] = regex_list

    # Build the master regular expressions

    if debug:
        debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====')

    for state in regexs:
        lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames)
        lexobj.lexstatere[state] = lexre
        lexobj.lexstateretext[state] = re_text
        lexobj.lexstaterenames[state] = re_names
        if debug:
            for i, text in enumerate(re_text):
                debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text)

    # For inclusive states, we need to add the regular expressions from the INITIAL state
    for state, stype in stateinfo.items():
        if state != 'INITIAL' and stype == 'inclusive':
            lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
            lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
            lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])

    lexobj.lexstateinfo = stateinfo
    lexobj.lexre = lexobj.lexstatere['INITIAL']
    lexobj.lexretext = lexobj.lexstateretext['INITIAL']
    lexobj.lexreflags = reflags

    # Set up ignore variables
    lexobj.lexstateignore = linfo.ignore
    lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '')

    # Set up error functions
    lexobj.lexstateerrorf = linfo.errorf
    lexobj.lexerrorf = linfo.errorf.get('INITIAL', None)
    if not lexobj.lexerrorf:
        errorlog.warning('No t_error rule is defined')

    # Set up eof functions
    lexobj.lexstateeoff = linfo.eoff
    lexobj.lexeoff = linfo.eoff.get('INITIAL', None)

    # Check state information for ignore and error rules
    for s, stype in stateinfo.items():
        if stype == 'exclusive':
            if s not in linfo.errorf:
                errorlog.warning("No error rule is defined for exclusive state '%s'", s)
            if s not in linfo.ignore and lexobj.lexignore:
                errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
        elif stype == 'inclusive':
            if s not in linfo.errorf:
                linfo.errorf[s] = linfo.errorf.get('INITIAL', None)
            if s not in linfo.ignore:
                linfo.ignore[s] = linfo.ignore.get('INITIAL', '')

    # Create global versions of the token() and input() functions
    token = lexobj.token
    input = lexobj.input
    lexer = lexobj

    # If in optimize mode, we write the lextab
    if lextab and optimize:
        if outputdir is None:
            # If no output directory is set, the location of the output files
            # is determined according to the following rules:
            #     - If lextab specifies a package, files go into that package directory
            #     - Otherwise, files go in the same directory as the specifying module
            if isinstance(lextab, types.ModuleType):
                srcfile = lextab.__file__
            else:
                if '.' not in lextab:
                    srcfile = ldict['__file__']
                else:
                    parts = lextab.split('.')
                    pkgname = '.'.join(parts[:-1])
                    exec('import %s' % pkgname)
                    srcfile = getattr(sys.modules[pkgname], '__file__', '')
            outputdir = os.path.dirname(srcfile)
        try:
            lexobj.writetab(lextab, outputdir)
            if lextab in sys.modules:
                del sys.modules[lextab]
        except IOError as e:
            errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e))

    return lexobj

# -----------------------------------------------------------------------------
# runmain()
#
# This runs the lexer as a main program
# -----------------------------------------------------------------------------

def runmain(lexer=None, data=None):
    if not data:
        try:
            filename = sys.argv[1]
            f = open(filename)
            data = f.read()
            f.close()
        except IndexError:
            sys.stdout.write('Reading from standard input (type EOF to end):\n')
            data = sys.stdin.read()

    if lexer:
        _input = lexer.input
    else:
        _input = input
    _input(data)
    if lexer:
        _token = lexer.token
    else:
        _token = token

    while True:
        tok = _token()
        if not tok:
            break
        sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos))

# -----------------------------------------------------------------------------
# @TOKEN(regex)
#
# This decorator function can be used to set the regex expression on a function
# when its docstring might need to be set in an alternative way
# -----------------------------------------------------------------------------

def TOKEN(r):
    def set_regex(f):
        if hasattr(r, '__call__'):
            f.regex = _get_regex(r)
        else:
            f.regex = r
        return f
    return set_regex

# Alternative spelling of the TOKEN decorator
Token = TOKEN
   0707010001f4a7000081a40000000000000000000000016a100daf00008367000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/foreign/ply/cpp.py # -----------------------------------------------------------------------------
# cpp.py
#
# Author:  David Beazley (http://www.dabeaz.com)
# Copyright (C) 2007
# All rights reserved
#
# This module implements an ANSI-C style lexical preprocessor for PLY.
# -----------------------------------------------------------------------------
from __future__ import generators

import sys

# Some Python 3 compatibility shims
if sys.version_info.major < 3:
    STRING_TYPES = (str, unicode)
else:
    STRING_TYPES = str
    xrange = range

# -----------------------------------------------------------------------------
# Default preprocessor lexer definitions.   These tokens are enough to get
# a basic preprocessor working.   Other modules may import these if they want
# -----------------------------------------------------------------------------

tokens = (
   'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND'
)

literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""

# Whitespace
def t_CPP_WS(t):
    r'\s+'
    t.lexer.lineno += t.value.count("\n")
    return t

t_CPP_POUND = r'\#'
t_CPP_DPOUND = r'\#\#'

# Identifier
t_CPP_ID = r'[A-Za-z_][\w_]*'

# Integer literal
def CPP_INTEGER(t):
    r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)'
    return t

t_CPP_INTEGER = CPP_INTEGER

# Floating literal
t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'

# String literal
def t_CPP_STRING(t):
    r'\"([^\\\n]|(\\(.|\n)))*?\"'
    t.lexer.lineno += t.value.count("\n")
    return t

# Character constant 'c' or L'c'
def t_CPP_CHAR(t):
    r'(L)?\'([^\\\n]|(\\(.|\n)))*?\''
    t.lexer.lineno += t.value.count("\n")
    return t

# Comment
def t_CPP_COMMENT1(t):
    r'(/\*(.|\n)*?\*/)'
    ncr = t.value.count("\n")
    t.lexer.lineno += ncr
    # replace with one space or a number of '\n'
    t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' '
    return t

# Line comment
def t_CPP_COMMENT2(t):
    r'(//.*?(\n|$))'
    # replace with '/n'
    t.type = 'CPP_WS'; t.value = '\n'
    return t

def t_error(t):
    t.type = t.value[0]
    t.value = t.value[0]
    t.lexer.skip(1)
    return t

import re
import copy
import time
import os.path

# -----------------------------------------------------------------------------
# trigraph()
#
# Given an input string, this function replaces all trigraph sequences.
# The following mapping is used:
#
#     ??=    #
#     ??/    \
#     ??'    ^
#     ??(    [
#     ??)    ]
#     ??!    |
#     ??<    {
#     ??>    }
#     ??-    ~
# -----------------------------------------------------------------------------

_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''')
_trigraph_rep = {
    '=':'#',
    '/':'\\',
    "'":'^',
    '(':'[',
    ')':']',
    '!':'|',
    '<':'{',
    '>':'}',
    '-':'~'
}

def trigraph(input):
    return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input)

# ------------------------------------------------------------------
# Macro object
#
# This object holds information about preprocessor macros
#
#    .name      - Macro name (string)
#    .value     - Macro value (a list of tokens)
#    .arglist   - List of argument names
#    .variadic  - Boolean indicating whether or not variadic macro
#    .vararg    - Name of the variadic parameter
#
# When a macro is created, the macro replacement token sequence is
# pre-scanned and used to create patch lists that are later used
# during macro expansion
# ------------------------------------------------------------------

class Macro(object):
    def __init__(self,name,value,arglist=None,variadic=False):
        self.name = name
        self.value = value
        self.arglist = arglist
        self.variadic = variadic
        if variadic:
            self.vararg = arglist[-1]
        self.source = None

# ------------------------------------------------------------------
# Preprocessor object
#
# Object representing a preprocessor.  Contains macro definitions,
# include directories, and other information
# ------------------------------------------------------------------

class Preprocessor(object):
    def __init__(self,lexer=None):
        if lexer is None:
            lexer = lex.lexer
        self.lexer = lexer
        self.macros = { }
        self.path = []
        self.temp_path = []

        # Probe the lexer for selected tokens
        self.lexprobe()

        tm = time.localtime()
        self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm))
        self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm))
        self.parser = None

    # -----------------------------------------------------------------------------
    # tokenize()
    #
    # Utility function. Given a string of text, tokenize into a list of tokens
    # -----------------------------------------------------------------------------

    def tokenize(self,text):
        tokens = []
        self.lexer.input(text)
        while True:
            tok = self.lexer.token()
            if not tok: break
            tokens.append(tok)
        return tokens

    # ---------------------------------------------------------------------
    # error()
    #
    # Report a preprocessor error/warning of some kind
    # ----------------------------------------------------------------------

    def error(self,file,line,msg):
        print("%s:%d %s" % (file,line,msg))

    # ----------------------------------------------------------------------
    # lexprobe()
    #
    # This method probes the preprocessor lexer object to discover
    # the token types of symbols that are important to the preprocessor.
    # If this works right, the preprocessor will simply "work"
    # with any suitable lexer regardless of how tokens have been named.
    # ----------------------------------------------------------------------

    def lexprobe(self):

        # Determine the token type for identifiers
        self.lexer.input("identifier")
        tok = self.lexer.token()
        if not tok or tok.value != "identifier":
            print("Couldn't determine identifier type")
        else:
            self.t_ID = tok.type

        # Determine the token type for integers
        self.lexer.input("12345")
        tok = self.lexer.token()
        if not tok or int(tok.value) != 12345:
            print("Couldn't determine integer type")
        else:
            self.t_INTEGER = tok.type
            self.t_INTEGER_TYPE = type(tok.value)

        # Determine the token type for strings enclosed in double quotes
        self.lexer.input("\"filename\"")
        tok = self.lexer.token()
        if not tok or tok.value != "\"filename\"":
            print("Couldn't determine string type")
        else:
            self.t_STRING = tok.type

        # Determine the token type for whitespace--if any
        self.lexer.input("  ")
        tok = self.lexer.token()
        if not tok or tok.value != "  ":
            self.t_SPACE = None
        else:
            self.t_SPACE = tok.type

        # Determine the token type for newlines
        self.lexer.input("\n")
        tok = self.lexer.token()
        if not tok or tok.value != "\n":
            self.t_NEWLINE = None
            print("Couldn't determine token for newlines")
        else:
            self.t_NEWLINE = tok.type

        self.t_WS = (self.t_SPACE, self.t_NEWLINE)

        # Check for other characters used by the preprocessor
        chars = [ '<','>','#','##','\\','(',')',',','.']
        for c in chars:
            self.lexer.input(c)
            tok = self.lexer.token()
            if not tok or tok.value != c:
                print("Unable to lex '%s' required for preprocessor" % c)

    # ----------------------------------------------------------------------
    # add_path()
    #
    # Adds a search path to the preprocessor.
    # ----------------------------------------------------------------------

    def add_path(self,path):
        self.path.append(path)

    # ----------------------------------------------------------------------
    # group_lines()
    #
    # Given an input string, this function splits it into lines.  Trailing whitespace
    # is removed.   Any line ending with \ is grouped with the next line.  This
    # function forms the lowest level of the preprocessor---grouping into text into
    # a line-by-line format.
    # ----------------------------------------------------------------------

    def group_lines(self,input):
        lex = self.lexer.clone()
        lines = [x.rstrip() for x in input.splitlines()]
        for i in xrange(len(lines)):
            j = i+1
            while lines[i].endswith('\\') and (j < len(lines)):
                lines[i] = lines[i][:-1]+lines[j]
                lines[j] = ""
                j += 1

        input = "\n".join(lines)
        lex.input(input)
        lex.lineno = 1

        current_line = []
        while True:
            tok = lex.token()
            if not tok:
                break
            current_line.append(tok)
            if tok.type in self.t_WS and '\n' in tok.value:
                yield current_line
                current_line = []

        if current_line:
            yield current_line

    # ----------------------------------------------------------------------
    # tokenstrip()
    #
    # Remove leading/trailing whitespace tokens from a token list
    # ----------------------------------------------------------------------

    def tokenstrip(self,tokens):
        i = 0
        while i < len(tokens) and tokens[i].type in self.t_WS:
            i += 1
        del tokens[:i]
        i = len(tokens)-1
        while i >= 0 and tokens[i].type in self.t_WS:
            i -= 1
        del tokens[i+1:]
        return tokens


    # ----------------------------------------------------------------------
    # collect_args()
    #
    # Collects comma separated arguments from a list of tokens.   The arguments
    # must be enclosed in parenthesis.  Returns a tuple (tokencount,args,positions)
    # where tokencount is the number of tokens consumed, args is a list of arguments,
    # and positions is a list of integers containing the starting index of each
    # argument.  Each argument is represented by a list of tokens.
    #
    # When collecting arguments, leading and trailing whitespace is removed
    # from each argument.
    #
    # This function properly handles nested parenthesis and commas---these do not
    # define new arguments.
    # ----------------------------------------------------------------------

    def collect_args(self,tokenlist):
        args = []
        positions = []
        current_arg = []
        nesting = 1
        tokenlen = len(tokenlist)

        # Search for the opening '('.
        i = 0
        while (i < tokenlen) and (tokenlist[i].type in self.t_WS):
            i += 1

        if (i < tokenlen) and (tokenlist[i].value == '('):
            positions.append(i+1)
        else:
            self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments")
            return 0, [], []

        i += 1

        while i < tokenlen:
            t = tokenlist[i]
            if t.value == '(':
                current_arg.append(t)
                nesting += 1
            elif t.value == ')':
                nesting -= 1
                if nesting == 0:
                    if current_arg:
                        args.append(self.tokenstrip(current_arg))
                        positions.append(i)
                    return i+1,args,positions
                current_arg.append(t)
            elif t.value == ',' and nesting == 1:
                args.append(self.tokenstrip(current_arg))
                positions.append(i+1)
                current_arg = []
            else:
                current_arg.append(t)
            i += 1

        # Missing end argument
        self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments")
        return 0, [],[]

    # ----------------------------------------------------------------------
    # macro_prescan()
    #
    # Examine the macro value (token sequence) and identify patch points
    # This is used to speed up macro expansion later on---we'll know
    # right away where to apply patches to the value to form the expansion
    # ----------------------------------------------------------------------

    def macro_prescan(self,macro):
        macro.patch     = []             # Standard macro arguments
        macro.str_patch = []             # String conversion expansion
        macro.var_comma_patch = []       # Variadic macro comma patch
        i = 0
        while i < len(macro.value):
            if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist:
                argnum = macro.arglist.index(macro.value[i].value)
                # Conversion of argument to a string
                if i > 0 and macro.value[i-1].value == '#':
                    macro.value[i] = copy.copy(macro.value[i])
                    macro.value[i].type = self.t_STRING
                    del macro.value[i-1]
                    macro.str_patch.append((argnum,i-1))
                    continue
                # Concatenation
                elif (i > 0 and macro.value[i-1].value == '##'):
                    macro.patch.append(('c',argnum,i-1))
                    del macro.value[i-1]
                    i -= 1
                    continue
                elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
                    macro.patch.append(('c',argnum,i))
                    del macro.value[i + 1]
                    continue
                # Standard expansion
                else:
                    macro.patch.append(('e',argnum,i))
            elif macro.value[i].value == '##':
                if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \
                        ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \
                        (macro.value[i+1].value == macro.vararg):
                    macro.var_comma_patch.append(i-1)
            i += 1
        macro.patch.sort(key=lambda x: x[2],reverse=True)

    # ----------------------------------------------------------------------
    # macro_expand_args()
    #
    # Given a Macro and list of arguments (each a token list), this method
    # returns an expanded version of a macro.  The return value is a token sequence
    # representing the replacement macro tokens
    # ----------------------------------------------------------------------

    def macro_expand_args(self,macro,args):
        # Make a copy of the macro token sequence
        rep = [copy.copy(_x) for _x in macro.value]

        # Make string expansion patches.  These do not alter the length of the replacement sequence

        str_expansion = {}
        for argnum, i in macro.str_patch:
            if argnum not in str_expansion:
                str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\")
            rep[i] = copy.copy(rep[i])
            rep[i].value = str_expansion[argnum]

        # Make the variadic macro comma patch.  If the variadic macro argument is empty, we get rid
        comma_patch = False
        if macro.variadic and not args[-1]:
            for i in macro.var_comma_patch:
                rep[i] = None
                comma_patch = True

        # Make all other patches.   The order of these matters.  It is assumed that the patch list
        # has been sorted in reverse order of patch location since replacements will cause the
        # size of the replacement sequence to expand from the patch point.

        expanded = { }
        for ptype, argnum, i in macro.patch:
            # Concatenation.   Argument is left unexpanded
            if ptype == 'c':
                rep[i:i+1] = args[argnum]
            # Normal expansion.  Argument is macro expanded first
            elif ptype == 'e':
                if argnum not in expanded:
                    expanded[argnum] = self.expand_macros(args[argnum])
                rep[i:i+1] = expanded[argnum]

        # Get rid of removed comma if necessary
        if comma_patch:
            rep = [_i for _i in rep if _i]

        return rep


    # ----------------------------------------------------------------------
    # expand_macros()
    #
    # Given a list of tokens, this function performs macro expansion.
    # The expanded argument is a dictionary that contains macros already
    # expanded.  This is used to prevent infinite recursion.
    # ----------------------------------------------------------------------

    def expand_macros(self,tokens,expanded=None):
        if expanded is None:
            expanded = {}
        i = 0
        while i < len(tokens):
            t = tokens[i]
            if t.type == self.t_ID:
                if t.value in self.macros and t.value not in expanded:
                    # Yes, we found a macro match
                    expanded[t.value] = True

                    m = self.macros[t.value]
                    if not m.arglist:
                        # A simple macro
                        ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded)
                        for e in ex:
                            e.lineno = t.lineno
                        tokens[i:i+1] = ex
                        i += len(ex)
                    else:
                        # A macro with arguments
                        j = i + 1
                        while j < len(tokens) and tokens[j].type in self.t_WS:
                            j += 1
                        if j < len(tokens) and tokens[j].value == '(':
                            tokcount,args,positions = self.collect_args(tokens[j:])
                            if not m.variadic and len(args) !=  len(m.arglist):
                                self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
                                i = j + tokcount
                            elif m.variadic and len(args) < len(m.arglist)-1:
                                if len(m.arglist) > 2:
                                    self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1))
                                else:
                                    self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1))
                                i = j + tokcount
                            else:
                                if m.variadic:
                                    if len(args) == len(m.arglist)-1:
                                        args.append([])
                                    else:
                                        args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1]
                                        del args[len(m.arglist):]

                                # Get macro replacement text
                                rep = self.macro_expand_args(m,args)
                                rep = self.expand_macros(rep,expanded)
                                for r in rep:
                                    r.lineno = t.lineno
                                tokens[i:j+tokcount] = rep
                                i += len(rep)
                        else:
                            # This is not a macro. It is just a word which
                            # equals to name of the macro. Hence, go to the
                            # next token.
                            i += 1

                    del expanded[t.value]
                    continue
                elif t.value == '__LINE__':
                    t.type = self.t_INTEGER
                    t.value = self.t_INTEGER_TYPE(t.lineno)

            i += 1
        return tokens

    # ----------------------------------------------------------------------
    # evalexpr()
    #
    # Evaluate an expression token sequence for the purposes of evaluating
    # integral expressions.
    # ----------------------------------------------------------------------

    def evalexpr(self,tokens):
        # tokens = tokenize(line)
        # Search for defined macros
        i = 0
        while i < len(tokens):
            if tokens[i].type == self.t_ID and tokens[i].value == 'defined':
                j = i + 1
                needparen = False
                result = "0L"
                while j < len(tokens):
                    if tokens[j].type in self.t_WS:
                        j += 1
                        continue
                    elif tokens[j].type == self.t_ID:
                        if tokens[j].value in self.macros:
                            result = "1L"
                        else:
                            result = "0L"
                        if not needparen: break
                    elif tokens[j].value == '(':
                        needparen = True
                    elif tokens[j].value == ')':
                        break
                    else:
                        self.error(self.source,tokens[i].lineno,"Malformed defined()")
                    j += 1
                tokens[i].type = self.t_INTEGER
                tokens[i].value = self.t_INTEGER_TYPE(result)
                del tokens[i+1:j+1]
            i += 1
        tokens = self.expand_macros(tokens)
        for i,t in enumerate(tokens):
            if t.type == self.t_ID:
                tokens[i] = copy.copy(t)
                tokens[i].type = self.t_INTEGER
                tokens[i].value = self.t_INTEGER_TYPE("0L")
            elif t.type == self.t_INTEGER:
                tokens[i] = copy.copy(t)
                # Strip off any trailing suffixes
                tokens[i].value = str(tokens[i].value)
                while tokens[i].value[-1] not in "0123456789abcdefABCDEF":
                    tokens[i].value = tokens[i].value[:-1]

        expr = "".join([str(x.value) for x in tokens])
        expr = expr.replace("&&"," and ")
        expr = expr.replace("||"," or ")
        expr = expr.replace("!"," not ")
        try:
            result = eval(expr)
        except Exception:
            self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression")
            result = 0
        return result

    # ----------------------------------------------------------------------
    # parsegen()
    #
    # Parse an input string/
    # ----------------------------------------------------------------------
    def parsegen(self,input,source=None):

        # Replace trigraph sequences
        t = trigraph(input)
        lines = self.group_lines(t)

        if not source:
            source = ""

        self.define("__FILE__ \"%s\"" % source)

        self.source = source
        chunk = []
        enable = True
        iftrigger = False
        ifstack = []

        for x in lines:
            for i,tok in enumerate(x):
                if tok.type not in self.t_WS: break
            if tok.value == '#':
                # Preprocessor directive

                # insert necessary whitespace instead of eaten tokens
                for tok in x:
                    if tok.type in self.t_WS and '\n' in tok.value:
                        chunk.append(tok)

                dirtokens = self.tokenstrip(x[i+1:])
                if dirtokens:
                    name = dirtokens[0].value
                    args = self.tokenstrip(dirtokens[1:])
                else:
                    name = ""
                    args = []

                if name == 'define':
                    if enable:
                        for tok in self.expand_macros(chunk):
                            yield tok
                        chunk = []
                        self.define(args)
                elif name == 'include':
                    if enable:
                        for tok in self.expand_macros(chunk):
                            yield tok
                        chunk = []
                        oldfile = self.macros['__FILE__']
                        for tok in self.include(args):
                            yield tok
                        self.macros['__FILE__'] = oldfile
                        self.source = source
                elif name == 'undef':
                    if enable:
                        for tok in self.expand_macros(chunk):
                            yield tok
                        chunk = []
                        self.undef(args)
                elif name == 'ifdef':
                    ifstack.append((enable,iftrigger))
                    if enable:
                        if not args[0].value in self.macros:
                            enable = False
                            iftrigger = False
                        else:
                            iftrigger = True
                elif name == 'ifndef':
                    ifstack.append((enable,iftrigger))
                    if enable:
                        if args[0].value in self.macros:
                            enable = False
                            iftrigger = False
                        else:
                            iftrigger = True
                elif name == 'if':
                    ifstack.append((enable,iftrigger))
                    if enable:
                        result = self.evalexpr(args)
                        if not result:
                            enable = False
                            iftrigger = False
                        else:
                            iftrigger = True
                elif name == 'elif':
                    if ifstack:
                        if ifstack[-1][0]:     # We only pay attention if outer "if" allows this
                            if enable:         # If already true, we flip enable False
                                enable = False
                            elif not iftrigger:   # If False, but not triggered yet, we'll check expression
                                result = self.evalexpr(args)
                                if result:
                                    enable  = True
                                    iftrigger = True
                    else:
                        self.error(self.source,dirtokens[0].lineno,"Misplaced #elif")

                elif name == 'else':
                    if ifstack:
                        if ifstack[-1][0]:
                            if enable:
                                enable = False
                            elif not iftrigger:
                                enable = True
                                iftrigger = True
                    else:
                        self.error(self.source,dirtokens[0].lineno,"Misplaced #else")

                elif name == 'endif':
                    if ifstack:
                        enable,iftrigger = ifstack.pop()
                    else:
                        self.error(self.source,dirtokens[0].lineno,"Misplaced #endif")
                else:
                    # Unknown preprocessor directive
                    pass

            else:
                # Normal text
                if enable:
                    chunk.extend(x)

        for tok in self.expand_macros(chunk):
            yield tok
        chunk = []

    # ----------------------------------------------------------------------
    # include()
    #
    # Implementation of file-inclusion
    # ----------------------------------------------------------------------

    def include(self,tokens):
        # Try to extract the filename and then process an include file
        if not tokens:
            return
        if tokens:
            if tokens[0].value != '<' and tokens[0].type != self.t_STRING:
                tokens = self.expand_macros(tokens)

            if tokens[0].value == '<':
                # Include <...>
                i = 1
                while i < len(tokens):
                    if tokens[i].value == '>':
                        break
                    i += 1
                else:
                    print("Malformed #include <...>")
                    return
                filename = "".join([x.value for x in tokens[1:i]])
                path = self.path + [""] + self.temp_path
            elif tokens[0].type == self.t_STRING:
                filename = tokens[0].value[1:-1]
                path = self.temp_path + [""] + self.path
            else:
                print("Malformed #include statement")
                return
        for p in path:
            iname = os.path.join(p,filename)
            try:
                data = open(iname,"r").read()
                dname = os.path.dirname(iname)
                if dname:
                    self.temp_path.insert(0,dname)
                for tok in self.parsegen(data,filename):
                    yield tok
                if dname:
                    del self.temp_path[0]
                break
            except IOError:
                pass
        else:
            print("Couldn't find '%s'" % filename)

    # ----------------------------------------------------------------------
    # define()
    #
    # Define a new macro
    # ----------------------------------------------------------------------

    def define(self,tokens):
        if isinstance(tokens,STRING_TYPES):
            tokens = self.tokenize(tokens)

        linetok = tokens
        try:
            name = linetok[0]
            if len(linetok) > 1:
                mtype = linetok[1]
            else:
                mtype = None
            if not mtype:
                m = Macro(name.value,[])
                self.macros[name.value] = m
            elif mtype.type in self.t_WS:
                # A normal macro
                m = Macro(name.value,self.tokenstrip(linetok[2:]))
                self.macros[name.value] = m
            elif mtype.value == '(':
                # A macro with arguments
                tokcount, args, positions = self.collect_args(linetok[1:])
                variadic = False
                for a in args:
                    if variadic:
                        print("No more arguments may follow a variadic argument")
                        break
                    astr = "".join([str(_i.value) for _i in a])
                    if astr == "...":
                        variadic = True
                        a[0].type = self.t_ID
                        a[0].value = '__VA_ARGS__'
                        variadic = True
                        del a[1:]
                        continue
                    elif astr[-3:] == "..." and a[0].type == self.t_ID:
                        variadic = True
                        del a[1:]
                        # If, for some reason, "." is part of the identifier, strip off the name for the purposes
                        # of macro expansion
                        if a[0].value[-3:] == '...':
                            a[0].value = a[0].value[:-3]
                        continue
                    if len(a) > 1 or a[0].type != self.t_ID:
                        print("Invalid macro argument")
                        break
                else:
                    mvalue = self.tokenstrip(linetok[1+tokcount:])
                    i = 0
                    while i < len(mvalue):
                        if i+1 < len(mvalue):
                            if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##':
                                del mvalue[i]
                                continue
                            elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS:
                                del mvalue[i+1]
                        i += 1
                    m = Macro(name.value,mvalue,[x[0].value for x in args],variadic)
                    self.macro_prescan(m)
                    self.macros[name.value] = m
            else:
                print("Bad macro definition")
        except LookupError:
            print("Bad macro definition")

    # ----------------------------------------------------------------------
    # undef()
    #
    # Undefine a macro
    # ----------------------------------------------------------------------

    def undef(self,tokens):
        id = tokens[0].value
        try:
            del self.macros[id]
        except LookupError:
            pass

    # ----------------------------------------------------------------------
    # parse()
    #
    # Parse input text.
    # ----------------------------------------------------------------------
    def parse(self,input,source=None,ignore={}):
        self.ignore = ignore
        self.parser = self.parsegen(input,source)

    # ----------------------------------------------------------------------
    # token()
    #
    # Method to return individual tokens
    # ----------------------------------------------------------------------
    def token(self):
        try:
            while True:
                tok = next(self.parser)
                if tok.type not in self.ignore: return tok
        except StopIteration:
            self.parser = None
            return None

if __name__ == '__main__':
    import ply.lex as lex
    lexer = lex.lex()

    # Run a preprocessor
    import sys
    f = open(sys.argv[1])
    input = f.read()

    p = Preprocessor(lexer)
    p.parse(input,sys.argv[1])
    while True:
        tok = p.token()
        if not tok: break
        print(p.source, tok)
 0707010001f4bf000081a40000000000000000000000016a100daf0000870d000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/foreign/six.py # pylint: disable-all
#
# Copyright (c) 2010-2020 Benjamin Peterson
#
# 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.

"""Utilities for writing code that runs on Python 2 and 3"""

from __future__ import absolute_import

import functools
import itertools
import operator
import sys
import types

__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.16.0"


# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
PY34 = sys.version_info[0:2] >= (3, 4)

if PY3:
    string_types = str,
    integer_types = int,
    class_types = type,
    text_type = str
    binary_type = bytes

    MAXSIZE = sys.maxsize
else:
    string_types = basestring,
    integer_types = (int, long)
    class_types = (type, types.ClassType)
    text_type = unicode
    binary_type = str

    if sys.platform.startswith("java"):
        # Jython always uses 32 bits.
        MAXSIZE = int((1 << 31) - 1)
    else:
        # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
        class X(object):

            def __len__(self):
                return 1 << 31
        try:
            len(X())
        except OverflowError:
            # 32-bit
            MAXSIZE = int((1 << 31) - 1)
        else:
            # 64-bit
            MAXSIZE = int((1 << 63) - 1)
        del X

if PY34:
    from importlib.util import spec_from_loader
else:
    spec_from_loader = None


def _add_doc(func, doc):
    """Add documentation to a function."""
    func.__doc__ = doc


def _import_module(name):
    """Import module, returning the module after the last dot."""
    __import__(name)
    return sys.modules[name]


class _LazyDescr(object):

    def __init__(self, name):
        self.name = name

    def __get__(self, obj, tp):
        result = self._resolve()
        setattr(obj, self.name, result)  # Invokes __set__.
        try:
            # This is a bit ugly, but it avoids running this again by
            # removing this descriptor.
            delattr(obj.__class__, self.name)
        except AttributeError:
            pass
        return result


class MovedModule(_LazyDescr):

    def __init__(self, name, old, new=None):
        super(MovedModule, self).__init__(name)
        if PY3:
            if new is None:
                new = name
            self.mod = new
        else:
            self.mod = old

    def _resolve(self):
        return _import_module(self.mod)

    def __getattr__(self, attr):
        _module = self._resolve()
        value = getattr(_module, attr)
        setattr(self, attr, value)
        return value


class _LazyModule(types.ModuleType):

    def __init__(self, name):
        super(_LazyModule, self).__init__(name)
        self.__doc__ = self.__class__.__doc__

    def __dir__(self):
        attrs = ["__doc__", "__name__"]
        attrs += [attr.name for attr in self._moved_attributes]
        return attrs

    # Subclasses should override this
    _moved_attributes = []


class MovedAttribute(_LazyDescr):

    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
        super(MovedAttribute, self).__init__(name)
        if PY3:
            if new_mod is None:
                new_mod = name
            self.mod = new_mod
            if new_attr is None:
                if old_attr is None:
                    new_attr = name
                else:
                    new_attr = old_attr
            self.attr = new_attr
        else:
            self.mod = old_mod
            if old_attr is None:
                old_attr = name
            self.attr = old_attr

    def _resolve(self):
        module = _import_module(self.mod)
        return getattr(module, self.attr)


class _SixMetaPathImporter(object):

    """
    A meta path importer to import six.moves and its submodules.

    This class implements a PEP302 finder and loader. It should be compatible
    with Python 2.5 and all existing versions of Python3
    """

    def __init__(self, six_module_name):
        self.name = six_module_name
        self.known_modules = {}

    def _add_module(self, mod, *fullnames):
        for fullname in fullnames:
            self.known_modules[self.name + "." + fullname] = mod

    def _get_module(self, fullname):
        return self.known_modules[self.name + "." + fullname]

    def find_module(self, fullname, path=None):
        if fullname in self.known_modules:
            return self
        return None

    def find_spec(self, fullname, path, target=None):
        if fullname in self.known_modules:
            return spec_from_loader(fullname, self)
        return None

    def __get_module(self, fullname):
        try:
            return self.known_modules[fullname]
        except KeyError:
            raise ImportError("This loader does not know module " + fullname)

    def load_module(self, fullname):
        try:
            # in case of a reload
            return sys.modules[fullname]
        except KeyError:
            pass
        mod = self.__get_module(fullname)
        if isinstance(mod, MovedModule):
            mod = mod._resolve()
        else:
            mod.__loader__ = self
        sys.modules[fullname] = mod
        return mod

    def is_package(self, fullname):
        """
        Return true, if the named module is a package.

        We need this method to get correct spec objects with
        Python 3.4 (see PEP451)
        """
        return hasattr(self.__get_module(fullname), "__path__")

    def get_code(self, fullname):
        """Return None

        Required, if is_package is implemented"""
        self.__get_module(fullname)  # eventually raises ImportError
        return None
    get_source = get_code  # same as get_code

    def create_module(self, spec):
        return self.load_module(spec.name)

    def exec_module(self, module):
        pass

_importer = _SixMetaPathImporter(__name__)


class _MovedItems(_LazyModule):

    """Lazy loading of moved objects"""
    __path__ = []  # mark as package


_moved_attributes = [
    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
    MovedAttribute("intern", "__builtin__", "sys"),
    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
    MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
    MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
    MovedAttribute("getoutput", "commands", "subprocess"),
    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
    MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
    MovedAttribute("reduce", "__builtin__", "functools"),
    MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
    MovedAttribute("StringIO", "StringIO", "io"),
    MovedAttribute("UserDict", "UserDict", "collections"),
    MovedAttribute("UserList", "UserList", "collections"),
    MovedAttribute("UserString", "UserString", "collections"),
    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
    MovedModule("builtins", "__builtin__"),
    MovedModule("configparser", "ConfigParser"),
    MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
    MovedModule("copyreg", "copy_reg"),
    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
    MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
    MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
    MovedModule("http_cookies", "Cookie", "http.cookies"),
    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
    MovedModule("html_parser", "HTMLParser", "html.parser"),
    MovedModule("http_client", "httplib", "http.client"),
    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
    MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
    MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
    MovedModule("cPickle", "cPickle", "pickle"),
    MovedModule("queue", "Queue"),
    MovedModule("reprlib", "repr"),
    MovedModule("socketserver", "SocketServer"),
    MovedModule("_thread", "thread", "_thread"),
    MovedModule("tkinter", "Tkinter"),
    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
    MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
    MovedModule("tkinter_colorchooser", "tkColorChooser",
                "tkinter.colorchooser"),
    MovedModule("tkinter_commondialog", "tkCommonDialog",
                "tkinter.commondialog"),
    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
                "tkinter.simpledialog"),
    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
    MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
    MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
]
# Add windows specific modules.
if sys.platform == "win32":
    _moved_attributes += [
        MovedModule("winreg", "_winreg"),
    ]

for attr in _moved_attributes:
    setattr(_MovedItems, attr.name, attr)
    if isinstance(attr, MovedModule):
        _importer._add_module(attr, "moves." + attr.name)
del attr

_MovedItems._moved_attributes = _moved_attributes

moves = _MovedItems(__name__ + ".moves")
_importer._add_module(moves, "moves")


class Module_six_moves_urllib_parse(_LazyModule):

    """Lazy loading of moved objects in six.moves.urllib_parse"""


_urllib_parse_moved_attributes = [
    MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
    MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
    MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
    MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
    MovedAttribute("urljoin", "urlparse", "urllib.parse"),
    MovedAttribute("urlparse", "urlparse", "urllib.parse"),
    MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
    MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
    MovedAttribute("quote", "urllib", "urllib.parse"),
    MovedAttribute("quote_plus", "urllib", "urllib.parse"),
    MovedAttribute("unquote", "urllib", "urllib.parse"),
    MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
    MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
    MovedAttribute("urlencode", "urllib", "urllib.parse"),
    MovedAttribute("splitquery", "urllib", "urllib.parse"),
    MovedAttribute("splittag", "urllib", "urllib.parse"),
    MovedAttribute("splituser", "urllib", "urllib.parse"),
    MovedAttribute("splitvalue", "urllib", "urllib.parse"),
    MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
    MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
    MovedAttribute("uses_params", "urlparse", "urllib.parse"),
    MovedAttribute("uses_query", "urlparse", "urllib.parse"),
    MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
]
for attr in _urllib_parse_moved_attributes:
    setattr(Module_six_moves_urllib_parse, attr.name, attr)
del attr

Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes

_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
                      "moves.urllib_parse", "moves.urllib.parse")


class Module_six_moves_urllib_error(_LazyModule):

    """Lazy loading of moved objects in six.moves.urllib_error"""


_urllib_error_moved_attributes = [
    MovedAttribute("URLError", "urllib2", "urllib.error"),
    MovedAttribute("HTTPError", "urllib2", "urllib.error"),
    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
]
for attr in _urllib_error_moved_attributes:
    setattr(Module_six_moves_urllib_error, attr.name, attr)
del attr

Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes

_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
                      "moves.urllib_error", "moves.urllib.error")


class Module_six_moves_urllib_request(_LazyModule):

    """Lazy loading of moved objects in six.moves.urllib_request"""


_urllib_request_moved_attributes = [
    MovedAttribute("urlopen", "urllib2", "urllib.request"),
    MovedAttribute("install_opener", "urllib2", "urllib.request"),
    MovedAttribute("build_opener", "urllib2", "urllib.request"),
    MovedAttribute("pathname2url", "urllib", "urllib.request"),
    MovedAttribute("url2pathname", "urllib", "urllib.request"),
    MovedAttribute("getproxies", "urllib", "urllib.request"),
    MovedAttribute("Request", "urllib2", "urllib.request"),
    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
    MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
    MovedAttribute("FileHandler", "urllib2", "urllib.request"),
    MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
    MovedAttribute("urlretrieve", "urllib", "urllib.request"),
    MovedAttribute("urlcleanup", "urllib", "urllib.request"),
    MovedAttribute("URLopener", "urllib", "urllib.request"),
    MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
    MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
    MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
    MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
]
for attr in _urllib_request_moved_attributes:
    setattr(Module_six_moves_urllib_request, attr.name, attr)
del attr

Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes

_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
                      "moves.urllib_request", "moves.urllib.request")


class Module_six_moves_urllib_response(_LazyModule):

    """Lazy loading of moved objects in six.moves.urllib_response"""


_urllib_response_moved_attributes = [
    MovedAttribute("addbase", "urllib", "urllib.response"),
    MovedAttribute("addclosehook", "urllib", "urllib.response"),
    MovedAttribute("addinfo", "urllib", "urllib.response"),
    MovedAttribute("addinfourl", "urllib", "urllib.response"),
]
for attr in _urllib_response_moved_attributes:
    setattr(Module_six_moves_urllib_response, attr.name, attr)
del attr

Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes

_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
                      "moves.urllib_response", "moves.urllib.response")


class Module_six_moves_urllib_robotparser(_LazyModule):

    """Lazy loading of moved objects in six.moves.urllib_robotparser"""


_urllib_robotparser_moved_attributes = [
    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
]
for attr in _urllib_robotparser_moved_attributes:
    setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
del attr

Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes

_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
                      "moves.urllib_robotparser", "moves.urllib.robotparser")


class Module_six_moves_urllib(types.ModuleType):

    """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
    __path__ = []  # mark as package
    parse = _importer._get_module("moves.urllib_parse")
    error = _importer._get_module("moves.urllib_error")
    request = _importer._get_module("moves.urllib_request")
    response = _importer._get_module("moves.urllib_response")
    robotparser = _importer._get_module("moves.urllib_robotparser")

    def __dir__(self):
        return ['parse', 'error', 'request', 'response', 'robotparser']

_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
                      "moves.urllib")


def add_move(move):
    """Add an item to six.moves."""
    setattr(_MovedItems, move.name, move)


def remove_move(name):
    """Remove item from six.moves."""
    try:
        delattr(_MovedItems, name)
    except AttributeError:
        try:
            del moves.__dict__[name]
        except KeyError:
            raise AttributeError("no such move, %r" % (name,))


if PY3:
    _meth_func = "__func__"
    _meth_self = "__self__"

    _func_closure = "__closure__"
    _func_code = "__code__"
    _func_defaults = "__defaults__"
    _func_globals = "__globals__"
else:
    _meth_func = "im_func"
    _meth_self = "im_self"

    _func_closure = "func_closure"
    _func_code = "func_code"
    _func_defaults = "func_defaults"
    _func_globals = "func_globals"


try:
    advance_iterator = next
except NameError:
    def advance_iterator(it):
        return it.next()
next = advance_iterator


try:
    callable = callable
except NameError:
    def callable(obj):
        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)


if PY3:
    def get_unbound_function(unbound):
        return unbound

    create_bound_method = types.MethodType

    def create_unbound_method(func, cls):
        return func

    Iterator = object
else:
    def get_unbound_function(unbound):
        return unbound.im_func

    def create_bound_method(func, obj):
        return types.MethodType(func, obj, obj.__class__)

    def create_unbound_method(func, cls):
        return types.MethodType(func, None, cls)

    class Iterator(object):

        def next(self):
            return type(self).__next__(self)

    callable = callable
_add_doc(get_unbound_function,
         """Get the function out of a possibly unbound function""")


get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
get_function_closure = operator.attrgetter(_func_closure)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
get_function_globals = operator.attrgetter(_func_globals)


if PY3:
    def iterkeys(d, **kw):
        return iter(d.keys(**kw))

    def itervalues(d, **kw):
        return iter(d.values(**kw))

    def iteritems(d, **kw):
        return iter(d.items(**kw))

    def iterlists(d, **kw):
        return iter(d.lists(**kw))

    viewkeys = operator.methodcaller("keys")

    viewvalues = operator.methodcaller("values")

    viewitems = operator.methodcaller("items")
else:
    def iterkeys(d, **kw):
        return d.iterkeys(**kw)

    def itervalues(d, **kw):
        return d.itervalues(**kw)

    def iteritems(d, **kw):
        return d.iteritems(**kw)

    def iterlists(d, **kw):
        return d.iterlists(**kw)

    viewkeys = operator.methodcaller("viewkeys")

    viewvalues = operator.methodcaller("viewvalues")

    viewitems = operator.methodcaller("viewitems")

_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
_add_doc(iteritems,
         "Return an iterator over the (key, value) pairs of a dictionary.")
_add_doc(iterlists,
         "Return an iterator over the (key, [values]) pairs of a dictionary.")


if PY3:
    def b(s):
        return s.encode("latin-1")

    def u(s):
        return s
    unichr = chr
    import struct
    int2byte = struct.Struct(">B").pack
    del struct
    byte2int = operator.itemgetter(0)
    indexbytes = operator.getitem
    iterbytes = iter
    import io
    StringIO = io.StringIO
    BytesIO = io.BytesIO
    del io
    _assertCountEqual = "assertCountEqual"
    if sys.version_info[1] <= 1:
        _assertRaisesRegex = "assertRaisesRegexp"
        _assertRegex = "assertRegexpMatches"
        _assertNotRegex = "assertNotRegexpMatches"
    else:
        _assertRaisesRegex = "assertRaisesRegex"
        _assertRegex = "assertRegex"
        _assertNotRegex = "assertNotRegex"
else:
    def b(s):
        return s
    # Workaround for standalone backslash

    def u(s):
        return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
    unichr = unichr
    int2byte = chr

    def byte2int(bs):
        return ord(bs[0])

    def indexbytes(buf, i):
        return ord(buf[i])
    iterbytes = functools.partial(itertools.imap, ord)
    import StringIO
    StringIO = BytesIO = StringIO.StringIO
    _assertCountEqual = "assertItemsEqual"
    _assertRaisesRegex = "assertRaisesRegexp"
    _assertRegex = "assertRegexpMatches"
    _assertNotRegex = "assertNotRegexpMatches"
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")


def assertCountEqual(self, *args, **kwargs):
    return getattr(self, _assertCountEqual)(*args, **kwargs)


def assertRaisesRegex(self, *args, **kwargs):
    return getattr(self, _assertRaisesRegex)(*args, **kwargs)


def assertRegex(self, *args, **kwargs):
    return getattr(self, _assertRegex)(*args, **kwargs)


def assertNotRegex(self, *args, **kwargs):
    return getattr(self, _assertNotRegex)(*args, **kwargs)


if PY3:
    exec_ = getattr(moves.builtins, "exec")

    def reraise(tp, value, tb=None):
        try:
            if value is None:
                value = tp()
            if value.__traceback__ is not tb:
                raise value.with_traceback(tb)
            raise value
        finally:
            value = None
            tb = None

else:
    def exec_(_code_, _globs_=None, _locs_=None):
        """Execute code in a namespace."""
        if _globs_ is None:
            frame = sys._getframe(1)
            _globs_ = frame.f_globals
            if _locs_ is None:
                _locs_ = frame.f_locals
            del frame
        elif _locs_ is None:
            _locs_ = _globs_
        exec("""exec _code_ in _globs_, _locs_""")

    exec_("""def reraise(tp, value, tb=None):
    try:
        raise tp, value, tb
    finally:
        tb = None
""")


if sys.version_info[:2] > (3,):
    exec_("""def raise_from(value, from_value):
    try:
        raise value from from_value
    finally:
        value = None
""")
else:
    def raise_from(value, from_value):
        raise value


print_ = getattr(moves.builtins, "print", None)
if print_ is None:
    def print_(*args, **kwargs):
        """The new-style print function for Python 2.4 and 2.5."""
        fp = kwargs.pop("file", sys.stdout)
        if fp is None:
            return

        def write(data):
            if not isinstance(data, basestring):
                data = str(data)
            # If the file has an encoding, encode unicode with it.
            if (isinstance(fp, file) and
                    isinstance(data, unicode) and
                    fp.encoding is not None):
                errors = getattr(fp, "errors", None)
                if errors is None:
                    errors = "strict"
                data = data.encode(fp.encoding, errors)
            fp.write(data)
        want_unicode = False
        sep = kwargs.pop("sep", None)
        if sep is not None:
            if isinstance(sep, unicode):
                want_unicode = True
            elif not isinstance(sep, str):
                raise TypeError("sep must be None or a string")
        end = kwargs.pop("end", None)
        if end is not None:
            if isinstance(end, unicode):
                want_unicode = True
            elif not isinstance(end, str):
                raise TypeError("end must be None or a string")
        if kwargs:
            raise TypeError("invalid keyword arguments to print()")
        if not want_unicode:
            for arg in args:
                if isinstance(arg, unicode):
                    want_unicode = True
                    break
        if want_unicode:
            newline = unicode("\n")
            space = unicode(" ")
        else:
            newline = "\n"
            space = " "
        if sep is None:
            sep = space
        if end is None:
            end = newline
        for i, arg in enumerate(args):
            if i:
                write(sep)
            write(arg)
        write(end)
if sys.version_info[:2] < (3, 3):
    _print = print_

    def print_(*args, **kwargs):
        fp = kwargs.get("file", sys.stdout)
        flush = kwargs.pop("flush", False)
        _print(*args, **kwargs)
        if flush and fp is not None:
            fp.flush()

_add_doc(reraise, """Reraise an exception.""")

if sys.version_info[0:2] < (3, 4):
    # This does exactly the same what the :func:`py3:functools.update_wrapper`
    # function does on Python versions after 3.2. It sets the ``__wrapped__``
    # attribute on ``wrapper`` object and it doesn't raise an error if any of
    # the attributes mentioned in ``assigned`` and ``updated`` are missing on
    # ``wrapped`` object.
    def _update_wrapper(wrapper, wrapped,
                        assigned=functools.WRAPPER_ASSIGNMENTS,
                        updated=functools.WRAPPER_UPDATES):
        for attr in assigned:
            try:
                value = getattr(wrapped, attr)
            except AttributeError:
                continue
            else:
                setattr(wrapper, attr, value)
        for attr in updated:
            getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
        wrapper.__wrapped__ = wrapped
        return wrapper
    _update_wrapper.__doc__ = functools.update_wrapper.__doc__

    def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
              updated=functools.WRAPPER_UPDATES):
        return functools.partial(_update_wrapper, wrapped=wrapped,
                                 assigned=assigned, updated=updated)
    wraps.__doc__ = functools.wraps.__doc__

else:
    wraps = functools.wraps


def with_metaclass(meta, *bases):
    """Create a base class with a metaclass."""
    # This requires a bit of explanation: the basic idea is to make a dummy
    # metaclass for one level of class instantiation that replaces itself with
    # the actual metaclass.
    class metaclass(type):

        def __new__(cls, name, this_bases, d):
            if sys.version_info[:2] >= (3, 7):
                # This version introduced PEP 560 that requires a bit
                # of extra care (we mimic what is done by __build_class__).
                resolved_bases = types.resolve_bases(bases)
                if resolved_bases is not bases:
                    d['__orig_bases__'] = bases
            else:
                resolved_bases = bases
            return meta(name, resolved_bases, d)

        @classmethod
        def __prepare__(cls, name, this_bases):
            return meta.__prepare__(name, bases)
    return type.__new__(metaclass, 'temporary_class', (), {})


def add_metaclass(metaclass):
    """Class decorator for creating a class with a metaclass."""
    def wrapper(cls):
        orig_vars = cls.__dict__.copy()
        slots = orig_vars.get('__slots__')
        if slots is not None:
            if isinstance(slots, str):
                slots = [slots]
            for slots_var in slots:
                orig_vars.pop(slots_var)
        orig_vars.pop('__dict__', None)
        orig_vars.pop('__weakref__', None)
        if hasattr(cls, '__qualname__'):
            orig_vars['__qualname__'] = cls.__qualname__
        return metaclass(cls.__name__, cls.__bases__, orig_vars)
    return wrapper


def ensure_binary(s, encoding='utf-8', errors='strict'):
    """Coerce **s** to six.binary_type.

    For Python 2:
      - `unicode` -> encoded to `str`
      - `str` -> `str`

    For Python 3:
      - `str` -> encoded to `bytes`
      - `bytes` -> `bytes`
    """
    if isinstance(s, binary_type):
        return s
    if isinstance(s, text_type):
        return s.encode(encoding, errors)
    raise TypeError("not expecting type '%s'" % type(s))


def ensure_str(s, encoding='utf-8', errors='strict'):
    """Coerce *s* to `str`.

    For Python 2:
      - `unicode` -> encoded to `str`
      - `str` -> `str`

    For Python 3:
      - `str` -> `str`
      - `bytes` -> decoded to `str`
    """
    # Optimization: Fast return for the common case.
    if type(s) is str:
        return s
    if PY2 and isinstance(s, text_type):
        return s.encode(encoding, errors)
    elif PY3 and isinstance(s, binary_type):
        return s.decode(encoding, errors)
    elif not isinstance(s, (text_type, binary_type)):
        raise TypeError("not expecting type '%s'" % type(s))
    return s


def ensure_text(s, encoding='utf-8', errors='strict'):
    """Coerce *s* to six.text_type.

    For Python 2:
      - `unicode` -> `unicode`
      - `str` -> `unicode`

    For Python 3:
      - `str` -> `str`
      - `bytes` -> decoded to `str`
    """
    if isinstance(s, binary_type):
        return s.decode(encoding, errors)
    elif isinstance(s, text_type):
        return s
    else:
        raise TypeError("not expecting type '%s'" % type(s))


def python_2_unicode_compatible(klass):
    """
    A class decorator that defines __unicode__ and __str__ methods under Python 2.
    Under Python 3 it does nothing.

    To support Python 2 and 3 with a single code base, define a __str__ method
    returning text and apply this decorator to the class.
    """
    if PY2:
        if '__str__' not in klass.__dict__:
            raise ValueError("@python_2_unicode_compatible cannot be applied "
                             "to %s because it doesn't define __str__()." %
                             klass.__name__)
        klass.__unicode__ = klass.__str__
        klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
    return klass


# Complete the moves implementation.
# This code is at the end of this module to speed up module loading.
# Turn this module into a package.
__path__ = []  # required for PEP 302 and PEP 451
__package__ = __name__  # see PEP 366 @ReservedAssignment
if globals().get("__spec__") is not None:
    __spec__.submodule_search_locations = []  # PEP 451 @UndefinedVariable
# Remove other six meta path importers, since they cause problems. This can
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
# this for some reason.)
if sys.meta_path:
    for i, importer in enumerate(sys.meta_path):
        # Here's some real nastiness: Another "instance" of the six module might
        # be floating around. Therefore, we can't use isinstance() to check for
        # the six meta path importer, since the other six instance will have
        # inserted an importer with different class.
        if (type(importer).__name__ == "_SixMetaPathImporter" and
                importer.name == __name__):
            del sys.meta_path[i]
            break
    del i, importer
# Finally, add the importer to the meta path import hook.
sys.meta_path.append(_importer)
   0707010001f4b1000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/foreign/rfc3986    0707010001f4be000081a40000000000000000000000016a100daf0000361e000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/validators.py  # -*- coding: utf-8 -*-
# Copyright (c) 2017 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing the validation logic for rfc3986."""
from . import exceptions
from . import misc
from . import normalizers


class Validator(object):
    """Object used to configure validation of all objects in rfc3986.

    .. versionadded:: 1.0

    Example usage::

         >>> from rfc3986 import api, validators
         >>> uri = api.uri_reference('https://github.com/')
         >>> validator = validators.Validator().require_presence_of(
         ...    'scheme', 'host', 'path',
         ... ).allow_schemes(
         ...    'http', 'https',
         ... ).allow_hosts(
         ...    '127.0.0.1', 'github.com',
         ... )
         >>> validator.validate(uri)
         >>> invalid_uri = rfc3986.uri_reference('imap://mail.google.com')
         >>> validator.validate(invalid_uri)
         Traceback (most recent call last):
         ...
         rfc3986.exceptions.MissingComponentError: ('path was required but
         missing', URIReference(scheme=u'imap', authority=u'mail.google.com',
         path=None, query=None, fragment=None), ['path'])

    """

    COMPONENT_NAMES = frozenset([
        'scheme',
        'userinfo',
        'host',
        'port',
        'path',
        'query',
        'fragment',
    ])

    def __init__(self):
        """Initialize our default validations."""
        self.allowed_schemes = set()
        self.allowed_hosts = set()
        self.allowed_ports = set()
        self.allow_password = True
        self.required_components = {
            'scheme': False,
            'userinfo': False,
            'host': False,
            'port': False,
            'path': False,
            'query': False,
            'fragment': False,
        }
        self.validated_components = self.required_components.copy()

    def allow_schemes(self, *schemes):
        """Require the scheme to be one of the provided schemes.

        .. versionadded:: 1.0

        :param schemes:
            Schemes, without ``://`` that are allowed.
        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        for scheme in schemes:
            self.allowed_schemes.add(normalizers.normalize_scheme(scheme))
        return self

    def allow_hosts(self, *hosts):
        """Require the host to be one of the provided hosts.

        .. versionadded:: 1.0

        :param hosts:
            Hosts that are allowed.
        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        for host in hosts:
            self.allowed_hosts.add(normalizers.normalize_host(host))
        return self

    def allow_ports(self, *ports):
        """Require the port to be one of the provided ports.

        .. versionadded:: 1.0

        :param ports:
            Ports that are allowed.
        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        for port in ports:
            port_int = int(port, base=10)
            if 0 <= port_int <= 65535:
                self.allowed_ports.add(port)
        return self

    def allow_use_of_password(self):
        """Allow passwords to be present in the URI.

        .. versionadded:: 1.0

        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        self.allow_password = True
        return self

    def forbid_use_of_password(self):
        """Prevent passwords from being included in the URI.

        .. versionadded:: 1.0

        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        self.allow_password = False
        return self

    def check_validity_of(self, *components):
        """Check the validity of the components provided.

        This can be specified repeatedly.

        .. versionadded:: 1.1

        :param components:
            Names of components from :attr:`Validator.COMPONENT_NAMES`.
        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        components = [c.lower() for c in components]
        for component in components:
            if component not in self.COMPONENT_NAMES:
                raise ValueError(
                    '"{}" is not a valid component'.format(component)
                )
        self.validated_components.update({
            component: True for component in components
        })
        return self

    def require_presence_of(self, *components):
        """Require the components provided.

        This can be specified repeatedly.

        .. versionadded:: 1.0

        :param components:
            Names of components from :attr:`Validator.COMPONENT_NAMES`.
        :returns:
            The validator instance.
        :rtype:
            Validator
        """
        components = [c.lower() for c in components]
        for component in components:
            if component not in self.COMPONENT_NAMES:
                raise ValueError(
                    '"{}" is not a valid component'.format(component)
                )
        self.required_components.update({
            component: True for component in components
        })
        return self

    def validate(self, uri):
        """Check a URI for conditions specified on this validator.

        .. versionadded:: 1.0

        :param uri:
            Parsed URI to validate.
        :type uri:
            rfc3986.uri.URIReference
        :raises MissingComponentError:
            When a required component is missing.
        :raises UnpermittedComponentError:
            When a component is not one of those allowed.
        :raises PasswordForbidden:
            When a password is present in the userinfo component but is
            not permitted by configuration.
        :raises InvalidComponentsError:
            When a component was found to be invalid.
        """
        if not self.allow_password:
            check_password(uri)

        required_components = [
            component
            for component, required in self.required_components.items()
            if required
        ]
        validated_components = [
            component
            for component, required in self.validated_components.items()
            if required
        ]
        if required_components:
            ensure_required_components_exist(uri, required_components)
        if validated_components:
            ensure_components_are_valid(uri, validated_components)

        ensure_one_of(self.allowed_schemes, uri, 'scheme')
        ensure_one_of(self.allowed_hosts, uri, 'host')
        ensure_one_of(self.allowed_ports, uri, 'port')


def check_password(uri):
    """Assert that there is no password present in the uri."""
    userinfo = uri.userinfo
    if not userinfo:
        return
    credentials = userinfo.split(':', 1)
    if len(credentials) <= 1:
        return
    raise exceptions.PasswordForbidden(uri)


def ensure_one_of(allowed_values, uri, attribute):
    """Assert that the uri's attribute is one of the allowed values."""
    value = getattr(uri, attribute)
    if value is not None and allowed_values and value not in allowed_values:
        raise exceptions.UnpermittedComponentError(
            attribute, value, allowed_values,
        )


def ensure_required_components_exist(uri, required_components):
    """Assert that all required components are present in the URI."""
    missing_components = sorted([
        component
        for component in required_components
        if getattr(uri, component) is None
    ])
    if missing_components:
        raise exceptions.MissingComponentError(uri, *missing_components)


def is_valid(value, matcher, require):
    """Determine if a value is valid based on the provided matcher.

    :param str value:
        Value to validate.
    :param matcher:
        Compiled regular expression to use to validate the value.
    :param require:
        Whether or not the value is required.
    """
    if require:
        return (value is not None
                and matcher.match(value))

    # require is False and value is not None
    return value is None or matcher.match(value)


def authority_is_valid(authority, host=None, require=False):
    """Determine if the authority string is valid.

    :param str authority:
        The authority to validate.
    :param str host:
        (optional) The host portion of the authority to validate.
    :param bool require:
        (optional) Specify if authority must not be None.
    :returns:
        ``True`` if valid, ``False`` otherwise
    :rtype:
        bool
    """
    validated = is_valid(authority, misc.SUBAUTHORITY_MATCHER, require)
    if validated and host is not None:
        return host_is_valid(host, require)
    return validated


def host_is_valid(host, require=False):
    """Determine if the host string is valid.

    :param str host:
        The host to validate.
    :param bool require:
        (optional) Specify if host must not be None.
    :returns:
        ``True`` if valid, ``False`` otherwise
    :rtype:
        bool
    """
    validated = is_valid(host, misc.HOST_MATCHER, require)
    if validated and host is not None and misc.IPv4_MATCHER.match(host):
        return valid_ipv4_host_address(host)
    elif validated and host is not None and misc.IPv6_MATCHER.match(host):
        return misc.IPv6_NO_RFC4007_MATCHER.match(host) is not None
    return validated


def scheme_is_valid(scheme, require=False):
    """Determine if the scheme is valid.

    :param str scheme:
        The scheme string to validate.
    :param bool require:
        (optional) Set to ``True`` to require the presence of a scheme.
    :returns:
        ``True`` if the scheme is valid. ``False`` otherwise.
    :rtype:
        bool
    """
    return is_valid(scheme, misc.SCHEME_MATCHER, require)


def path_is_valid(path, require=False):
    """Determine if the path component is valid.

    :param str path:
        The path string to validate.
    :param bool require:
        (optional) Set to ``True`` to require the presence of a path.
    :returns:
        ``True`` if the path is valid. ``False`` otherwise.
    :rtype:
        bool
    """
    return is_valid(path, misc.PATH_MATCHER, require)


def query_is_valid(query, require=False):
    """Determine if the query component is valid.

    :param str query:
        The query string to validate.
    :param bool require:
        (optional) Set to ``True`` to require the presence of a query.
    :returns:
        ``True`` if the query is valid. ``False`` otherwise.
    :rtype:
        bool
    """
    return is_valid(query, misc.QUERY_MATCHER, require)


def fragment_is_valid(fragment, require=False):
    """Determine if the fragment component is valid.

    :param str fragment:
        The fragment string to validate.
    :param bool require:
        (optional) Set to ``True`` to require the presence of a fragment.
    :returns:
        ``True`` if the fragment is valid. ``False`` otherwise.
    :rtype:
        bool
    """
    return is_valid(fragment, misc.FRAGMENT_MATCHER, require)


def valid_ipv4_host_address(host):
    """Determine if the given host is a valid IPv4 address."""
    # If the host exists, and it might be IPv4, check each byte in the
    # address.
    return all([0 <= int(byte, base=10) <= 255 for byte in host.split('.')])


_COMPONENT_VALIDATORS = {
    'scheme': scheme_is_valid,
    'path': path_is_valid,
    'query': query_is_valid,
    'fragment': fragment_is_valid,
}

_SUBAUTHORITY_VALIDATORS = set(['userinfo', 'host', 'port'])


def subauthority_component_is_valid(uri, component):
    """Determine if the userinfo, host, and port are valid."""
    try:
        subauthority_dict = uri.authority_info()
    except exceptions.InvalidAuthority:
        return False

    # If we can parse the authority into sub-components and we're not
    # validating the port, we can assume it's valid.
    if component == 'host':
        return host_is_valid(subauthority_dict['host'])
    elif component != 'port':
        return True

    try:
        port = int(subauthority_dict['port'])
    except TypeError:
        # If the port wasn't provided it'll be None and int(None) raises a
        # TypeError
        return True

    return (0 <= port <= 65535)


def ensure_components_are_valid(uri, validated_components):
    """Assert that all components are valid in the URI."""
    invalid_components = set([])
    for component in validated_components:
        if component in _SUBAUTHORITY_VALIDATORS:
            if not subauthority_component_is_valid(uri, component):
                invalid_components.add(component)
            # Python's peephole optimizer means that while this continue *is*
            # actually executed, coverage.py cannot detect that. See also,
            # https://bitbucket.org/ned/coveragepy/issues/198/continue-marked-as-not-covered
            continue  # nocov: Python 2.7, 3.3, 3.4

        validator = _COMPONENT_VALIDATORS[component]
        if not validator(getattr(uri, component)):
            invalid_components.add(component)

    if invalid_components:
        raise exceptions.InvalidComponentsError(uri, *invalid_components)
  0707010001f4b2000081a40000000000000000000000016a100daf0000061a000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/__init__.py    # -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
An implementation of semantics and validations described in RFC 3986.

See http://rfc3986.readthedocs.io/ for detailed documentation.

:copyright: (c) 2014 Rackspace
:license: Apache v2.0, see LICENSE for details
"""

from .api import iri_reference
from .api import IRIReference
from .api import is_valid_uri
from .api import normalize_uri
from .api import uri_reference
from .api import URIReference
from .api import urlparse
from .parseresult import ParseResult

__title__ = 'rfc3986'
__author__ = 'Ian Stapleton Cordasco'
__author_email__ = 'graffatcolmingov@gmail.com'
__license__ = 'Apache v2.0'
__copyright__ = 'Copyright 2014 Rackspace'
__version__ = '1.3.2'

__all__ = (
    'ParseResult',
    'URIReference',
    'IRIReference',
    'is_valid_uri',
    'normalize_uri',
    'uri_reference',
    'iri_reference',
    'urlparse',
    '__title__',
    '__author__',
    '__author_email__',
    '__license__',
    '__copyright__',
    '__version__',
)
  0707010001f4ba000081a40000000000000000000000016a100daf00000ffe000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/rfc3986/misc.py    # -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Module containing compiled regular expressions and constants.

This module contains important constants, patterns, and compiled regular
expressions for parsing and validating URIs and their components.
"""

import re

from . import abnf_regexp

# These are enumerated for the named tuple used as a superclass of
# URIReference
URI_COMPONENTS = ['scheme', 'authority', 'path', 'query', 'fragment']

important_characters = {
    'generic_delimiters': abnf_regexp.GENERIC_DELIMITERS,
    'sub_delimiters': abnf_regexp.SUB_DELIMITERS,
    # We need to escape the '*' in this case
    're_sub_delimiters': abnf_regexp.SUB_DELIMITERS_RE,
    'unreserved_chars': abnf_regexp.UNRESERVED_CHARS,
    # We need to escape the '-' in this case:
    're_unreserved': abnf_regexp.UNRESERVED_RE,
}

# For details about delimiters and reserved characters, see:
# http://tools.ietf.org/html/rfc3986#section-2.2
GENERIC_DELIMITERS = abnf_regexp.GENERIC_DELIMITERS_SET
SUB_DELIMITERS = abnf_regexp.SUB_DELIMITERS_SET
RESERVED_CHARS = abnf_regexp.RESERVED_CHARS_SET
# For details about unreserved characters, see:
# http://tools.ietf.org/html/rfc3986#section-2.3
UNRESERVED_CHARS = abnf_regexp.UNRESERVED_CHARS_SET
NON_PCT_ENCODED = abnf_regexp.NON_PCT_ENCODED_SET

URI_MATCHER = re.compile(abnf_regexp.URL_PARSING_RE)

SUBAUTHORITY_MATCHER = re.compile((
    '^(?:(?P<userinfo>{0})@)?'  # userinfo
    '(?P<host>{1})'  # host
    ':?(?P<port>{2})?$'  # port
    ).format(abnf_regexp.USERINFO_RE,
             abnf_regexp.HOST_PATTERN,
             abnf_regexp.PORT_RE))


HOST_MATCHER = re.compile('^' + abnf_regexp.HOST_RE + '$')
IPv4_MATCHER = re.compile('^' + abnf_regexp.IPv4_RE + '$')
IPv6_MATCHER = re.compile(r'^\[' + abnf_regexp.IPv6_ADDRZ_RFC4007_RE + r'\]$')

# Used by host validator
IPv6_NO_RFC4007_MATCHER = re.compile(r'^\[%s\]$' % (
    abnf_regexp.IPv6_ADDRZ_RE
))

# Matcher used to validate path components
PATH_MATCHER = re.compile(abnf_regexp.PATH_RE)


# ##################################
# Query and Fragment Matcher Section
# ##################################

QUERY_MATCHER = re.compile(abnf_regexp.QUERY_RE)

FRAGMENT_MATCHER = QUERY_MATCHER

# Scheme validation, see: http://tools.ietf.org/html/rfc3986#section-3.1
SCHEME_MATCHER = re.compile('^{0}$'.format(abnf_regexp.SCHEME_RE))

RELATIVE_REF_MATCHER = re.compile(r'^%s(\?%s)?(#%s)?$' % (
    abnf_regexp.RELATIVE_PART_RE,
    abnf_regexp.QUERY_RE,
    abnf_regexp.FRAGMENT_RE,
))

# See http://tools.ietf.org/html/rfc3986#section-4.3
ABSOLUTE_URI_MATCHER = re.compile(r'^%s:%s(\?%s)?$' % (
    abnf_regexp.COMPONENT_PATTERN_DICT['scheme'],
    abnf_regexp.HIER_PART_RE,
    abnf_regexp.QUERY_RE[1:-1],
))

# ###############
# IRIs / RFC 3987
# ###############

IRI_MATCHER = re.compile(abnf_regexp.URL_PARSING_RE, re.UNICODE)

ISUBAUTHORITY_MATCHER = re.compile((
    u'^(?:(?P<userinfo>{0})@)?'  # iuserinfo
    u'(?P<host>{1})'  # ihost
    u':?(?P<port>{2})?$'  # port
    ).format(abnf_regexp.IUSERINFO_RE,
             abnf_regexp.IHOST_RE,
             abnf_regexp.PORT_RE), re.UNICODE)


# Path merger as defined in http://tools.ietf.org/html/rfc3986#section-5.2.3
def merge_paths(base_uri, relative_path):
    """Merge a base URI's path with a relative URI's path."""
    if base_uri.path is None and base_uri.authority is not None:
        return '/' + relative_path
    else:
        path = base_uri.path or ''
        index = path.rfind('/')
        return path[:index] + '/' + relative_path


UseExisting = object()
  0707010001f4bd000081a40000000000000000000000016a100daf0000146b000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/rfc3986/uri.py """Module containing the implementation of the URIReference class."""
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Copyright (c) 2015 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import namedtuple

from . import compat
from . import misc
from . import normalizers
from ._mixin import URIMixin


class URIReference(namedtuple('URIReference', misc.URI_COMPONENTS), URIMixin):
    """Immutable object representing a parsed URI Reference.

    .. note::

        This class is not intended to be directly instantiated by the user.

    This object exposes attributes for the following components of a
    URI:

    - scheme
    - authority
    - path
    - query
    - fragment

    .. attribute:: scheme

        The scheme that was parsed for the URI Reference. For example,
        ``http``, ``https``, ``smtp``, ``imap``, etc.

    .. attribute:: authority

        Component of the URI that contains the user information, host,
        and port sub-components. For example,
        ``google.com``, ``127.0.0.1:5000``, ``username@[::1]``,
        ``username:password@example.com:443``, etc.

    .. attribute:: path

        The path that was parsed for the given URI Reference. For example,
        ``/``, ``/index.php``, etc.

    .. attribute:: query

        The query component for a given URI Reference. For example, ``a=b``,
        ``a=b%20c``, ``a=b+c``, ``a=b,c=d,e=%20f``, etc.

    .. attribute:: fragment

        The fragment component of a URI. For example, ``section-3.1``.

    This class also provides extra attributes for easier access to information
    like the subcomponents of the authority component.

    .. attribute:: userinfo

        The user information parsed from the authority.

    .. attribute:: host

        The hostname, IPv4, or IPv6 adddres parsed from the authority.

    .. attribute:: port

        The port parsed from the authority.
    """

    slots = ()

    def __new__(cls, scheme, authority, path, query, fragment,
                encoding='utf-8'):
        """Create a new URIReference."""
        ref = super(URIReference, cls).__new__(
            cls,
            scheme or None,
            authority or None,
            path or None,
            query,
            fragment)
        ref.encoding = encoding
        return ref

    __hash__ = tuple.__hash__

    def __eq__(self, other):
        """Compare this reference to another."""
        other_ref = other
        if isinstance(other, tuple):
            other_ref = URIReference(*other)
        elif not isinstance(other, URIReference):
            try:
                other_ref = URIReference.from_string(other)
            except TypeError:
                raise TypeError(
                    'Unable to compare URIReference() to {0}()'.format(
                        type(other).__name__))

        # See http://tools.ietf.org/html/rfc3986#section-6.2
        naive_equality = tuple(self) == tuple(other_ref)
        return naive_equality or self.normalized_equality(other_ref)

    def normalize(self):
        """Normalize this reference as described in Section 6.2.2.

        This is not an in-place normalization. Instead this creates a new
        URIReference.

        :returns: A new reference object with normalized components.
        :rtype: URIReference
        """
        # See http://tools.ietf.org/html/rfc3986#section-6.2.2 for logic in
        # this method.
        return URIReference(normalizers.normalize_scheme(self.scheme or ''),
                            normalizers.normalize_authority(
                                (self.userinfo, self.host, self.port)),
                            normalizers.normalize_path(self.path or ''),
                            normalizers.normalize_query(self.query),
                            normalizers.normalize_fragment(self.fragment),
                            self.encoding)

    @classmethod
    def from_string(cls, uri_string, encoding='utf-8'):
        """Parse a URI reference from the given unicode URI string.

        :param str uri_string: Unicode URI to be parsed into a reference.
        :param str encoding: The encoding of the string provided
        :returns: :class:`URIReference` or subclass thereof
        """
        uri_string = compat.to_str(uri_string, encoding)

        split_uri = misc.URI_MATCHER.match(uri_string).groupdict()
        return cls(
            split_uri['scheme'], split_uri['authority'],
            normalizers.encode_component(split_uri['path'], encoding),
            normalizers.encode_component(split_uri['query'], encoding),
            normalizers.encode_component(split_uri['fragment'], encoding),
            encoding,
        )
 0707010001f4b4000081a40000000000000000000000016a100daf00002379000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/abnf_regexp.py # -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module for the regular expressions crafted from ABNF."""

import sys

# https://tools.ietf.org/html/rfc3986#page-13
GEN_DELIMS = GENERIC_DELIMITERS = ":/?#[]@"
GENERIC_DELIMITERS_SET = set(GENERIC_DELIMITERS)
# https://tools.ietf.org/html/rfc3986#page-13
SUB_DELIMS = SUB_DELIMITERS = "!$&'()*+,;="
SUB_DELIMITERS_SET = set(SUB_DELIMITERS)
# Escape the '*' for use in regular expressions
SUB_DELIMITERS_RE = r"!$&'()\*+,;="
RESERVED_CHARS_SET = GENERIC_DELIMITERS_SET.union(SUB_DELIMITERS_SET)
ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
DIGIT = '0123456789'
# https://tools.ietf.org/html/rfc3986#section-2.3
UNRESERVED = UNRESERVED_CHARS = ALPHA + DIGIT + r'._!-'
UNRESERVED_CHARS_SET = set(UNRESERVED_CHARS)
NON_PCT_ENCODED_SET = RESERVED_CHARS_SET.union(UNRESERVED_CHARS_SET)
# We need to escape the '-' in this case:
UNRESERVED_RE = r'A-Za-z0-9._~\-'

# Percent encoded character values
PERCENT_ENCODED = PCT_ENCODED = '%[A-Fa-f0-9]{2}'
PCHAR = '([' + UNRESERVED_RE + SUB_DELIMITERS_RE + ':@]|%s)' % PCT_ENCODED

# NOTE(sigmavirus24): We're going to use more strict regular expressions
# than appear in Appendix B for scheme. This will prevent over-eager
# consuming of items that aren't schemes.
SCHEME_RE = '[a-zA-Z][a-zA-Z0-9+.-]*'
_AUTHORITY_RE = '[^/?#]*'
_PATH_RE = '[^?#]*'
_QUERY_RE = '[^#]*'
_FRAGMENT_RE = '.*'

# Extracted from http://tools.ietf.org/html/rfc3986#appendix-B
COMPONENT_PATTERN_DICT = {
    'scheme': SCHEME_RE,
    'authority': _AUTHORITY_RE,
    'path': _PATH_RE,
    'query': _QUERY_RE,
    'fragment': _FRAGMENT_RE,
}

# See http://tools.ietf.org/html/rfc3986#appendix-B
# In this case, we name each of the important matches so we can use
# SRE_Match#groupdict to parse the values out if we so choose. This is also
# modified to ignore other matches that are not important to the parsing of
# the reference so we can also simply use SRE_Match#groups.
URL_PARSING_RE = (
    r'(?:(?P<scheme>{scheme}):)?(?://(?P<authority>{authority}))?'
    r'(?P<path>{path})(?:\?(?P<query>{query}))?'
    r'(?:#(?P<fragment>{fragment}))?'
).format(**COMPONENT_PATTERN_DICT)


# #########################
# Authority Matcher Section
# #########################

# Host patterns, see: http://tools.ietf.org/html/rfc3986#section-3.2.2
# The pattern for a regular name, e.g.,  www.google.com, api.github.com
REGULAR_NAME_RE = REG_NAME = '((?:{0}|[{1}])*)'.format(
    '%[0-9A-Fa-f]{2}', SUB_DELIMITERS_RE + UNRESERVED_RE
)
# The pattern for an IPv4 address, e.g., 192.168.255.255, 127.0.0.1,
IPv4_RE = r'([0-9]{1,3}\.){3}[0-9]{1,3}'
# Hexadecimal characters used in each piece of an IPv6 address
HEXDIG_RE = '[0-9A-Fa-f]{1,4}'
# Least-significant 32 bits of an IPv6 address
LS32_RE = '({hex}:{hex}|{ipv4})'.format(hex=HEXDIG_RE, ipv4=IPv4_RE)
# Substitutions into the following patterns for IPv6 patterns defined
# http://tools.ietf.org/html/rfc3986#page-20
_subs = {'hex': HEXDIG_RE, 'ls32': LS32_RE}

# Below: h16 = hexdig, see: https://tools.ietf.org/html/rfc5234 for details
# about ABNF (Augmented Backus-Naur Form) use in the comments
variations = [
    #                            6( h16 ":" ) ls32
    '(%(hex)s:){6}%(ls32)s' % _subs,
    #                       "::" 5( h16 ":" ) ls32
    '::(%(hex)s:){5}%(ls32)s' % _subs,
    # [               h16 ] "::" 4( h16 ":" ) ls32
    '(%(hex)s)?::(%(hex)s:){4}%(ls32)s' % _subs,
    # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
    '((%(hex)s:)?%(hex)s)?::(%(hex)s:){3}%(ls32)s' % _subs,
    # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
    '((%(hex)s:){0,2}%(hex)s)?::(%(hex)s:){2}%(ls32)s' % _subs,
    # [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
    '((%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s' % _subs,
    # [ *4( h16 ":" ) h16 ] "::"              ls32
    '((%(hex)s:){0,4}%(hex)s)?::%(ls32)s' % _subs,
    # [ *5( h16 ":" ) h16 ] "::"              h16
    '((%(hex)s:){0,5}%(hex)s)?::%(hex)s' % _subs,
    # [ *6( h16 ":" ) h16 ] "::"
    '((%(hex)s:){0,6}%(hex)s)?::' % _subs,
]

IPv6_RE = '(({0})|({1})|({2})|({3})|({4})|({5})|({6})|({7})|({8}))'.format(
    *variations
)

IPv_FUTURE_RE = r'v[0-9A-Fa-f]+\.[%s]+' % (
    UNRESERVED_RE + SUB_DELIMITERS_RE + ':'
)

# RFC 6874 Zone ID ABNF
ZONE_ID = '(?:[' + UNRESERVED_RE + ']|' + PCT_ENCODED + ')+'

IPv6_ADDRZ_RFC4007_RE = IPv6_RE + '(?:(?:%25|%)' + ZONE_ID + ')?'
IPv6_ADDRZ_RE = IPv6_RE + '(?:%25' + ZONE_ID + ')?'

IP_LITERAL_RE = r'\[({0}|{1})\]'.format(
    IPv6_ADDRZ_RFC4007_RE,
    IPv_FUTURE_RE,
)

# Pattern for matching the host piece of the authority
HOST_RE = HOST_PATTERN = '({0}|{1}|{2})'.format(
    REG_NAME,
    IPv4_RE,
    IP_LITERAL_RE,
)
USERINFO_RE = '^([' + UNRESERVED_RE + SUB_DELIMITERS_RE + ':]|%s)+' % (
    PCT_ENCODED
)
PORT_RE = '[0-9]{1,5}'

# ####################
# Path Matcher Section
# ####################

# See http://tools.ietf.org/html/rfc3986#section-3.3 for more information
# about the path patterns defined below.
segments = {
    'segment': PCHAR + '*',
    # Non-zero length segment
    'segment-nz': PCHAR + '+',
    # Non-zero length segment without ":"
    'segment-nz-nc': PCHAR.replace(':', '') + '+'
}

# Path types taken from Section 3.3 (linked above)
PATH_EMPTY = '^$'
PATH_ROOTLESS = '%(segment-nz)s(/%(segment)s)*' % segments
PATH_NOSCHEME = '%(segment-nz-nc)s(/%(segment)s)*' % segments
PATH_ABSOLUTE = '/(%s)?' % PATH_ROOTLESS
PATH_ABEMPTY = '(/%(segment)s)*' % segments
PATH_RE = '^(%s|%s|%s|%s|%s)$' % (
    PATH_ABEMPTY, PATH_ABSOLUTE, PATH_NOSCHEME, PATH_ROOTLESS, PATH_EMPTY
)

FRAGMENT_RE = QUERY_RE = (
    '^([/?:@' + UNRESERVED_RE + SUB_DELIMITERS_RE + ']|%s)*$' % PCT_ENCODED
)

# ##########################
# Relative reference matcher
# ##########################

# See http://tools.ietf.org/html/rfc3986#section-4.2 for details
RELATIVE_PART_RE = '(//%s%s|%s|%s|%s)' % (
    COMPONENT_PATTERN_DICT['authority'],
    PATH_ABEMPTY,
    PATH_ABSOLUTE,
    PATH_NOSCHEME,
    PATH_EMPTY,
)

# See http://tools.ietf.org/html/rfc3986#section-3 for definition
HIER_PART_RE = '(//%s%s|%s|%s|%s)' % (
    COMPONENT_PATTERN_DICT['authority'],
    PATH_ABEMPTY,
    PATH_ABSOLUTE,
    PATH_ROOTLESS,
    PATH_EMPTY,
)

# ###############
# IRIs / RFC 3987
# ###############

# Only wide-unicode gets the high-ranges of UCSCHAR
if sys.maxunicode > 0xFFFF:  # pragma: no cover
    IPRIVATE = u'\uE000-\uF8FF\U000F0000-\U000FFFFD\U00100000-\U0010FFFD'
    UCSCHAR_RE = (
        u'\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF'
        u'\U00010000-\U0001FFFD\U00020000-\U0002FFFD'
        u'\U00030000-\U0003FFFD\U00040000-\U0004FFFD'
        u'\U00050000-\U0005FFFD\U00060000-\U0006FFFD'
        u'\U00070000-\U0007FFFD\U00080000-\U0008FFFD'
        u'\U00090000-\U0009FFFD\U000A0000-\U000AFFFD'
        u'\U000B0000-\U000BFFFD\U000C0000-\U000CFFFD'
        u'\U000D0000-\U000DFFFD\U000E1000-\U000EFFFD'
    )
else:  # pragma: no cover
    IPRIVATE = u'\uE000-\uF8FF'
    UCSCHAR_RE = (
        u'\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF'
    )

IUNRESERVED_RE = u'A-Za-z0-9\\._~\\-' + UCSCHAR_RE
IPCHAR = u'([' + IUNRESERVED_RE + SUB_DELIMITERS_RE + u':@]|%s)' % PCT_ENCODED

isegments = {
    'isegment': IPCHAR + u'*',
    # Non-zero length segment
    'isegment-nz': IPCHAR + u'+',
    # Non-zero length segment without ":"
    'isegment-nz-nc': IPCHAR.replace(':', '') + u'+'
}

IPATH_ROOTLESS = u'%(isegment-nz)s(/%(isegment)s)*' % isegments
IPATH_NOSCHEME = u'%(isegment-nz-nc)s(/%(isegment)s)*' % isegments
IPATH_ABSOLUTE = u'/(?:%s)?' % IPATH_ROOTLESS
IPATH_ABEMPTY = u'(?:/%(isegment)s)*' % isegments
IPATH_RE = u'^(?:%s|%s|%s|%s|%s)$' % (
    IPATH_ABEMPTY, IPATH_ABSOLUTE, IPATH_NOSCHEME, IPATH_ROOTLESS, PATH_EMPTY
)

IREGULAR_NAME_RE = IREG_NAME = u'(?:{0}|[{1}])*'.format(
    u'%[0-9A-Fa-f]{2}', SUB_DELIMITERS_RE + IUNRESERVED_RE
)

IHOST_RE = IHOST_PATTERN = u'({0}|{1}|{2})'.format(
    IREG_NAME,
    IPv4_RE,
    IP_LITERAL_RE,
)

IUSERINFO_RE = u'^(?:[' + IUNRESERVED_RE + SUB_DELIMITERS_RE + u':]|%s)+' % (
    PCT_ENCODED
)

IFRAGMENT_RE = (u'^(?:[/?:@' + IUNRESERVED_RE + SUB_DELIMITERS_RE
                + u']|%s)*$' % PCT_ENCODED)
IQUERY_RE = (u'^(?:[/?:@' + IUNRESERVED_RE + SUB_DELIMITERS_RE
             + IPRIVATE + u']|%s)*$' % PCT_ENCODED)

IRELATIVE_PART_RE = u'(//%s%s|%s|%s|%s)' % (
    COMPONENT_PATTERN_DICT['authority'],
    IPATH_ABEMPTY,
    IPATH_ABSOLUTE,
    IPATH_NOSCHEME,
    PATH_EMPTY,
)

IHIER_PART_RE = u'(//%s%s|%s|%s|%s)' % (
    COMPONENT_PATTERN_DICT['authority'],
    IPATH_ABEMPTY,
    IPATH_ABSOLUTE,
    IPATH_ROOTLESS,
    PATH_EMPTY,
)
   0707010001f4b9000081a40000000000000000000000016a100daf0000155a000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/rfc3986/iri.py """Module containing the implementation of the IRIReference class."""
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Copyright (c) 2015 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import namedtuple

from . import compat
from . import exceptions
from . import misc
from . import normalizers
from . import uri


try:
    import idna
except ImportError:  # pragma: no cover
    idna = None


class IRIReference(namedtuple('IRIReference', misc.URI_COMPONENTS),
                   uri.URIMixin):
    """Immutable object representing a parsed IRI Reference.

    Can be encoded into an URIReference object via the procedure
    specified in RFC 3987 Section 3.1

     .. note::
        The IRI submodule is a new interface and may possibly change in
        the future. Check for changes to the interface when upgrading.
    """

    slots = ()

    def __new__(cls, scheme, authority, path, query, fragment,
                encoding='utf-8'):
        """Create a new IRIReference."""
        ref = super(IRIReference, cls).__new__(
            cls,
            scheme or None,
            authority or None,
            path or None,
            query,
            fragment)
        ref.encoding = encoding
        return ref

    def __eq__(self, other):
        """Compare this reference to another."""
        other_ref = other
        if isinstance(other, tuple):
            other_ref = self.__class__(*other)
        elif not isinstance(other, IRIReference):
            try:
                other_ref = self.__class__.from_string(other)
            except TypeError:
                raise TypeError(
                    'Unable to compare {0}() to {1}()'.format(
                        type(self).__name__, type(other).__name__))

        # See http://tools.ietf.org/html/rfc3986#section-6.2
        return tuple(self) == tuple(other_ref)

    def _match_subauthority(self):
        return misc.ISUBAUTHORITY_MATCHER.match(self.authority)

    @classmethod
    def from_string(cls, iri_string, encoding='utf-8'):
        """Parse a IRI reference from the given unicode IRI string.

        :param str iri_string: Unicode IRI to be parsed into a reference.
        :param str encoding: The encoding of the string provided
        :returns: :class:`IRIReference` or subclass thereof
        """
        iri_string = compat.to_str(iri_string, encoding)

        split_iri = misc.IRI_MATCHER.match(iri_string).groupdict()
        return cls(
            split_iri['scheme'], split_iri['authority'],
            normalizers.encode_component(split_iri['path'], encoding),
            normalizers.encode_component(split_iri['query'], encoding),
            normalizers.encode_component(split_iri['fragment'], encoding),
            encoding,
        )

    def encode(self, idna_encoder=None):  # noqa: C901
        """Encode an IRIReference into a URIReference instance.

        If the ``idna`` module is installed or the ``rfc3986[idna]``
        extra is used then unicode characters in the IRI host
        component will be encoded with IDNA2008.

        :param idna_encoder:
            Function that encodes each part of the host component
            If not given will raise an exception if the IRI
            contains a host component.
        :rtype: uri.URIReference
        :returns: A URI reference
        """
        authority = self.authority
        if authority:
            if idna_encoder is None:
                if idna is None:  # pragma: no cover
                    raise exceptions.MissingDependencyError(
                        "Could not import the 'idna' module "
                        "and the IRI hostname requires encoding"
                    )

                def idna_encoder(name):
                    if any(ord(c) > 128 for c in name):
                        try:
                            return idna.encode(name.lower(),
                                               strict=True,
                                               std3_rules=True)
                        except idna.IDNAError:
                            raise exceptions.InvalidAuthority(self.authority)
                    return name

            authority = ""
            if self.host:
                authority = ".".join([compat.to_str(idna_encoder(part))
                                      for part in self.host.split(".")])

            if self.userinfo is not None:
                authority = (normalizers.encode_component(
                             self.userinfo, self.encoding) + '@' + authority)

            if self.port is not None:
                authority += ":" + str(self.port)

        return uri.URIReference(self.scheme,
                                authority,
                                path=self.path,
                                query=self.query,
                                fragment=self.fragment,
                                encoding=self.encoding)
  0707010001f4b8000081a40000000000000000000000016a100daf00000ebf000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/exceptions.py  # -*- coding: utf-8 -*-
"""Exceptions module for rfc3986."""

from . import compat


class RFC3986Exception(Exception):
    """Base class for all rfc3986 exception classes."""

    pass


class InvalidAuthority(RFC3986Exception):
    """Exception when the authority string is invalid."""

    def __init__(self, authority):
        """Initialize the exception with the invalid authority."""
        super(InvalidAuthority, self).__init__(
            u"The authority ({0}) is not valid.".format(
                compat.to_str(authority)))


class InvalidPort(RFC3986Exception):
    """Exception when the port is invalid."""

    def __init__(self, port):
        """Initialize the exception with the invalid port."""
        super(InvalidPort, self).__init__(
            'The port ("{0}") is not valid.'.format(port))


class ResolutionError(RFC3986Exception):
    """Exception to indicate a failure to resolve a URI."""

    def __init__(self, uri):
        """Initialize the error with the failed URI."""
        super(ResolutionError, self).__init__(
            "{0} is not an absolute URI.".format(uri.unsplit()))


class ValidationError(RFC3986Exception):
    """Exception raised during Validation of a URI."""

    pass


class MissingComponentError(ValidationError):
    """Exception raised when a required component is missing."""

    def __init__(self, uri, *component_names):
        """Initialize the error with the missing component name."""
        verb = 'was'
        if len(component_names) > 1:
            verb = 'were'

        self.uri = uri
        self.components = sorted(component_names)
        components = ', '.join(self.components)
        super(MissingComponentError, self).__init__(
            "{} {} required but missing".format(components, verb),
            uri,
            self.components,
        )


class UnpermittedComponentError(ValidationError):
    """Exception raised when a component has an unpermitted value."""

    def __init__(self, component_name, component_value, allowed_values):
        """Initialize the error with the unpermitted component."""
        super(UnpermittedComponentError, self).__init__(
            "{} was required to be one of {!r} but was {!r}".format(
                component_name, list(sorted(allowed_values)), component_value,
            ),
            component_name,
            component_value,
            allowed_values,
        )
        self.component_name = component_name
        self.component_value = component_value
        self.allowed_values = allowed_values


class PasswordForbidden(ValidationError):
    """Exception raised when a URL has a password in the userinfo section."""

    def __init__(self, uri):
        """Initialize the error with the URI that failed validation."""
        unsplit = getattr(uri, 'unsplit', lambda: uri)
        super(PasswordForbidden, self).__init__(
            '"{}" contained a password when validation forbade it'.format(
                unsplit()
            )
        )
        self.uri = uri


class InvalidComponentsError(ValidationError):
    """Exception raised when one or more components are invalid."""

    def __init__(self, uri, *component_names):
        """Initialize the error with the invalid component name(s)."""
        verb = 'was'
        if len(component_names) > 1:
            verb = 'were'

        self.uri = uri
        self.components = sorted(component_names)
        components = ', '.join(self.components)
        super(InvalidComponentsError, self).__init__(
            "{} {} found to be invalid".format(components, verb),
            uri,
            self.components,
        )


class MissingDependencyError(RFC3986Exception):
    """Exception raised when an IRI is encoded without the 'idna' module."""
 0707010001f4b6000081a40000000000000000000000016a100daf00002569000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/builder.py # -*- coding: utf-8 -*-
# Copyright (c) 2017 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing the logic for the URIBuilder object."""
from . import compat
from . import normalizers
from . import uri


class URIBuilder(object):
    """Object to aid in building up a URI Reference from parts.

    .. note::

        This object should be instantiated by the user, but it's recommended
        that it is not provided with arguments. Instead, use the available
        method to populate the fields.

    """

    def __init__(self, scheme=None, userinfo=None, host=None, port=None,
                 path=None, query=None, fragment=None):
        """Initialize our URI builder.

        :param str scheme:
            (optional)
        :param str userinfo:
            (optional)
        :param str host:
            (optional)
        :param int port:
            (optional)
        :param str path:
            (optional)
        :param str query:
            (optional)
        :param str fragment:
            (optional)
        """
        self.scheme = scheme
        self.userinfo = userinfo
        self.host = host
        self.port = port
        self.path = path
        self.query = query
        self.fragment = fragment

    def __repr__(self):
        """Provide a convenient view of our builder object."""
        formatstr = ('URIBuilder(scheme={b.scheme}, userinfo={b.userinfo}, '
                     'host={b.host}, port={b.port}, path={b.path}, '
                     'query={b.query}, fragment={b.fragment})')
        return formatstr.format(b=self)

    def add_scheme(self, scheme):
        """Add a scheme to our builder object.

        After normalizing, this will generate a new URIBuilder instance with
        the specified scheme and all other attributes the same.

        .. code-block:: python

            >>> URIBuilder().add_scheme('HTTPS')
            URIBuilder(scheme='https', userinfo=None, host=None, port=None,
                    path=None, query=None, fragment=None)

        """
        scheme = normalizers.normalize_scheme(scheme)
        return URIBuilder(
            scheme=scheme,
            userinfo=self.userinfo,
            host=self.host,
            port=self.port,
            path=self.path,
            query=self.query,
            fragment=self.fragment,
        )

    def add_credentials(self, username, password):
        """Add credentials as the userinfo portion of the URI.

        .. code-block:: python

            >>> URIBuilder().add_credentials('root', 's3crete')
            URIBuilder(scheme=None, userinfo='root:s3crete', host=None,
                    port=None, path=None, query=None, fragment=None)

            >>> URIBuilder().add_credentials('root', None)
            URIBuilder(scheme=None, userinfo='root', host=None,
                    port=None, path=None, query=None, fragment=None)
        """
        if username is None:
            raise ValueError('Username cannot be None')
        userinfo = normalizers.normalize_username(username)

        if password is not None:
            userinfo = '{}:{}'.format(
                userinfo,
                normalizers.normalize_password(password),
            )

        return URIBuilder(
            scheme=self.scheme,
            userinfo=userinfo,
            host=self.host,
            port=self.port,
            path=self.path,
            query=self.query,
            fragment=self.fragment,
        )

    def add_host(self, host):
        """Add hostname to the URI.

        .. code-block:: python

            >>> URIBuilder().add_host('google.com')
            URIBuilder(scheme=None, userinfo=None, host='google.com',
                    port=None, path=None, query=None, fragment=None)

        """
        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=normalizers.normalize_host(host),
            port=self.port,
            path=self.path,
            query=self.query,
            fragment=self.fragment,
        )

    def add_port(self, port):
        """Add port to the URI.

        .. code-block:: python

            >>> URIBuilder().add_port(80)
            URIBuilder(scheme=None, userinfo=None, host=None, port='80',
                    path=None, query=None, fragment=None)

            >>> URIBuilder().add_port(443)
            URIBuilder(scheme=None, userinfo=None, host=None, port='443',
                    path=None, query=None, fragment=None)

        """
        port_int = int(port)
        if port_int < 0:
            raise ValueError(
                'ports are not allowed to be negative. You provided {}'.format(
                    port_int,
                )
            )
        if port_int > 65535:
            raise ValueError(
                'ports are not allowed to be larger than 65535. '
                'You provided {}'.format(
                    port_int,
                )
            )

        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=self.host,
            port='{}'.format(port_int),
            path=self.path,
            query=self.query,
            fragment=self.fragment,
        )

    def add_path(self, path):
        """Add a path to the URI.

        .. code-block:: python

            >>> URIBuilder().add_path('sigmavirus24/rfc3985')
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path='/sigmavirus24/rfc3986', query=None, fragment=None)

            >>> URIBuilder().add_path('/checkout.php')
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path='/checkout.php', query=None, fragment=None)

        """
        if not path.startswith('/'):
            path = '/{}'.format(path)

        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=self.host,
            port=self.port,
            path=normalizers.normalize_path(path),
            query=self.query,
            fragment=self.fragment,
        )

    def add_query_from(self, query_items):
        """Generate and add a query a dictionary or list of tuples.

        .. code-block:: python

            >>> URIBuilder().add_query_from({'a': 'b c'})
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path=None, query='a=b+c', fragment=None)

            >>> URIBuilder().add_query_from([('a', 'b c')])
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path=None, query='a=b+c', fragment=None)

        """
        query = normalizers.normalize_query(compat.urlencode(query_items))

        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=self.host,
            port=self.port,
            path=self.path,
            query=query,
            fragment=self.fragment,
        )

    def add_query(self, query):
        """Add a pre-formated query string to the URI.

        .. code-block:: python

            >>> URIBuilder().add_query('a=b&c=d')
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path=None, query='a=b&c=d', fragment=None)

        """
        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=self.host,
            port=self.port,
            path=self.path,
            query=normalizers.normalize_query(query),
            fragment=self.fragment,
        )

    def add_fragment(self, fragment):
        """Add a fragment to the URI.

        .. code-block:: python

            >>> URIBuilder().add_fragment('section-2.6.1')
            URIBuilder(scheme=None, userinfo=None, host=None, port=None,
                    path=None, query=None, fragment='section-2.6.1')

        """
        return URIBuilder(
            scheme=self.scheme,
            userinfo=self.userinfo,
            host=self.host,
            port=self.port,
            path=self.path,
            query=self.query,
            fragment=normalizers.normalize_fragment(fragment),
        )

    def finalize(self):
        """Create a URIReference from our builder.

        .. code-block:: python

            >>> URIBuilder().add_scheme('https').add_host('github.com'
            ...     ).add_path('sigmavirus24/rfc3986').finalize().unsplit()
            'https://github.com/sigmavirus24/rfc3986'

            >>> URIBuilder().add_scheme('https').add_host('github.com'
            ...     ).add_path('sigmavirus24/rfc3986').add_credentials(
            ...     'sigmavirus24', 'not-re@l').finalize().unsplit()
            'https://sigmavirus24:not-re%40l@github.com/sigmavirus24/rfc3986'

        """
        return uri.URIReference(
            self.scheme,
            normalizers.normalize_authority(
                (self.userinfo, self.host, self.port)
            ),
            self.path,
            self.query,
            self.fragment,
        )
   0707010001f4b7000081a40000000000000000000000016a100daf000005e9000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/rfc3986/compat.py  # -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Compatibility module for Python 2 and 3 support."""
import sys

try:
    from urllib.parse import quote as urlquote
except ImportError:  # Python 2.x
    from urllib import quote as urlquote

try:
    from urllib.parse import urlencode
except ImportError:  # Python 2.x
    from urllib import urlencode

__all__ = (
    'to_bytes',
    'to_str',
    'urlquote',
    'urlencode',
)

PY3 = (3, 0) <= sys.version_info < (4, 0)
PY2 = (2, 6) <= sys.version_info < (2, 8)


if PY3:
    unicode = str  # Python 3.x


def to_str(b, encoding='utf-8'):
    """Ensure that b is text in the specified encoding."""
    if hasattr(b, 'decode') and not isinstance(b, unicode):
        b = b.decode(encoding)
    return b


def to_bytes(s, encoding='utf-8'):
    """Ensure that s is converted to bytes from the encoding."""
    if hasattr(s, 'encode') and not isinstance(s, bytes):
        s = s.encode(encoding)
    return s
   0707010001f4bc000081a40000000000000000000000016a100daf0000393e000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/parseresult.py # -*- coding: utf-8 -*-
# Copyright (c) 2015 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing the urlparse compatibility logic."""
from collections import namedtuple

from . import compat
from . import exceptions
from . import misc
from . import normalizers
from . import uri

__all__ = ('ParseResult', 'ParseResultBytes')

PARSED_COMPONENTS = ('scheme', 'userinfo', 'host', 'port', 'path', 'query',
                     'fragment')


class ParseResultMixin(object):
    def _generate_authority(self, attributes):
        # I swear I did not align the comparisons below. That's just how they
        # happened to align based on pep8 and attribute lengths.
        userinfo, host, port = (attributes[p]
                                for p in ('userinfo', 'host', 'port'))
        if (self.userinfo != userinfo or
                self.host != host or
                self.port != port):
            if port:
                port = '{0}'.format(port)
            return normalizers.normalize_authority(
                (compat.to_str(userinfo, self.encoding),
                 compat.to_str(host, self.encoding),
                 port)
            )
        return self.authority

    def geturl(self):
        """Shim to match the standard library method."""
        return self.unsplit()

    @property
    def hostname(self):
        """Shim to match the standard library."""
        return self.host

    @property
    def netloc(self):
        """Shim to match the standard library."""
        return self.authority

    @property
    def params(self):
        """Shim to match the standard library."""
        return self.query


class ParseResult(namedtuple('ParseResult', PARSED_COMPONENTS),
                  ParseResultMixin):
    """Implementation of urlparse compatibility class.

    This uses the URIReference logic to handle compatibility with the
    urlparse.ParseResult class.
    """

    slots = ()

    def __new__(cls, scheme, userinfo, host, port, path, query, fragment,
                uri_ref, encoding='utf-8'):
        """Create a new ParseResult."""
        parse_result = super(ParseResult, cls).__new__(
            cls,
            scheme or None,
            userinfo or None,
            host,
            port or None,
            path or None,
            query,
            fragment)
        parse_result.encoding = encoding
        parse_result.reference = uri_ref
        return parse_result

    @classmethod
    def from_parts(cls, scheme=None, userinfo=None, host=None, port=None,
                   path=None, query=None, fragment=None, encoding='utf-8'):
        """Create a ParseResult instance from its parts."""
        authority = ''
        if userinfo is not None:
            authority += userinfo + '@'
        if host is not None:
            authority += host
        if port is not None:
            authority += ':{0}'.format(port)
        uri_ref = uri.URIReference(scheme=scheme,
                                   authority=authority,
                                   path=path,
                                   query=query,
                                   fragment=fragment,
                                   encoding=encoding).normalize()
        userinfo, host, port = authority_from(uri_ref, strict=True)
        return cls(scheme=uri_ref.scheme,
                   userinfo=userinfo,
                   host=host,
                   port=port,
                   path=uri_ref.path,
                   query=uri_ref.query,
                   fragment=uri_ref.fragment,
                   uri_ref=uri_ref,
                   encoding=encoding)

    @classmethod
    def from_string(cls, uri_string, encoding='utf-8', strict=True,
                    lazy_normalize=True):
        """Parse a URI from the given unicode URI string.

        :param str uri_string: Unicode URI to be parsed into a reference.
        :param str encoding: The encoding of the string provided
        :param bool strict: Parse strictly according to :rfc:`3986` if True.
            If False, parse similarly to the standard library's urlparse
            function.
        :returns: :class:`ParseResult` or subclass thereof
        """
        reference = uri.URIReference.from_string(uri_string, encoding)
        if not lazy_normalize:
            reference = reference.normalize()
        userinfo, host, port = authority_from(reference, strict)

        return cls(scheme=reference.scheme,
                   userinfo=userinfo,
                   host=host,
                   port=port,
                   path=reference.path,
                   query=reference.query,
                   fragment=reference.fragment,
                   uri_ref=reference,
                   encoding=encoding)

    @property
    def authority(self):
        """Return the normalized authority."""
        return self.reference.authority

    def copy_with(self, scheme=misc.UseExisting, userinfo=misc.UseExisting,
                  host=misc.UseExisting, port=misc.UseExisting,
                  path=misc.UseExisting, query=misc.UseExisting,
                  fragment=misc.UseExisting):
        """Create a copy of this instance replacing with specified parts."""
        attributes = zip(PARSED_COMPONENTS,
                         (scheme, userinfo, host, port, path, query, fragment))
        attrs_dict = {}
        for name, value in attributes:
            if value is misc.UseExisting:
                value = getattr(self, name)
            attrs_dict[name] = value
        authority = self._generate_authority(attrs_dict)
        ref = self.reference.copy_with(scheme=attrs_dict['scheme'],
                                       authority=authority,
                                       path=attrs_dict['path'],
                                       query=attrs_dict['query'],
                                       fragment=attrs_dict['fragment'])
        return ParseResult(uri_ref=ref, encoding=self.encoding, **attrs_dict)

    def encode(self, encoding=None):
        """Convert to an instance of ParseResultBytes."""
        encoding = encoding or self.encoding
        attrs = dict(
            zip(PARSED_COMPONENTS,
                (attr.encode(encoding) if hasattr(attr, 'encode') else attr
                 for attr in self)))
        return ParseResultBytes(
            uri_ref=self.reference,
            encoding=encoding,
            **attrs
        )

    def unsplit(self, use_idna=False):
        """Create a URI string from the components.

        :returns: The parsed URI reconstituted as a string.
        :rtype: str
        """
        parse_result = self
        if use_idna and self.host:
            hostbytes = self.host.encode('idna')
            host = hostbytes.decode(self.encoding)
            parse_result = self.copy_with(host=host)
        return parse_result.reference.unsplit()


class ParseResultBytes(namedtuple('ParseResultBytes', PARSED_COMPONENTS),
                       ParseResultMixin):
    """Compatibility shim for the urlparse.ParseResultBytes object."""

    def __new__(cls, scheme, userinfo, host, port, path, query, fragment,
                uri_ref, encoding='utf-8', lazy_normalize=True):
        """Create a new ParseResultBytes instance."""
        parse_result = super(ParseResultBytes, cls).__new__(
            cls,
            scheme or None,
            userinfo or None,
            host,
            port or None,
            path or None,
            query or None,
            fragment or None)
        parse_result.encoding = encoding
        parse_result.reference = uri_ref
        parse_result.lazy_normalize = lazy_normalize
        return parse_result

    @classmethod
    def from_parts(cls, scheme=None, userinfo=None, host=None, port=None,
                   path=None, query=None, fragment=None, encoding='utf-8',
                   lazy_normalize=True):
        """Create a ParseResult instance from its parts."""
        authority = ''
        if userinfo is not None:
            authority += userinfo + '@'
        if host is not None:
            authority += host
        if port is not None:
            authority += ':{0}'.format(int(port))
        uri_ref = uri.URIReference(scheme=scheme,
                                   authority=authority,
                                   path=path,
                                   query=query,
                                   fragment=fragment,
                                   encoding=encoding)
        if not lazy_normalize:
            uri_ref = uri_ref.normalize()
        to_bytes = compat.to_bytes
        userinfo, host, port = authority_from(uri_ref, strict=True)
        return cls(scheme=to_bytes(scheme, encoding),
                   userinfo=to_bytes(userinfo, encoding),
                   host=to_bytes(host, encoding),
                   port=port,
                   path=to_bytes(path, encoding),
                   query=to_bytes(query, encoding),
                   fragment=to_bytes(fragment, encoding),
                   uri_ref=uri_ref,
                   encoding=encoding,
                   lazy_normalize=lazy_normalize)

    @classmethod
    def from_string(cls, uri_string, encoding='utf-8', strict=True,
                    lazy_normalize=True):
        """Parse a URI from the given unicode URI string.

        :param str uri_string: Unicode URI to be parsed into a reference.
        :param str encoding: The encoding of the string provided
        :param bool strict: Parse strictly according to :rfc:`3986` if True.
            If False, parse similarly to the standard library's urlparse
            function.
        :returns: :class:`ParseResultBytes` or subclass thereof
        """
        reference = uri.URIReference.from_string(uri_string, encoding)
        if not lazy_normalize:
            reference = reference.normalize()
        userinfo, host, port = authority_from(reference, strict)

        to_bytes = compat.to_bytes
        return cls(scheme=to_bytes(reference.scheme, encoding),
                   userinfo=to_bytes(userinfo, encoding),
                   host=to_bytes(host, encoding),
                   port=port,
                   path=to_bytes(reference.path, encoding),
                   query=to_bytes(reference.query, encoding),
                   fragment=to_bytes(reference.fragment, encoding),
                   uri_ref=reference,
                   encoding=encoding,
                   lazy_normalize=lazy_normalize)

    @property
    def authority(self):
        """Return the normalized authority."""
        return self.reference.authority.encode(self.encoding)

    def copy_with(self, scheme=misc.UseExisting, userinfo=misc.UseExisting,
                  host=misc.UseExisting, port=misc.UseExisting,
                  path=misc.UseExisting, query=misc.UseExisting,
                  fragment=misc.UseExisting, lazy_normalize=True):
        """Create a copy of this instance replacing with specified parts."""
        attributes = zip(PARSED_COMPONENTS,
                         (scheme, userinfo, host, port, path, query, fragment))
        attrs_dict = {}
        for name, value in attributes:
            if value is misc.UseExisting:
                value = getattr(self, name)
            if not isinstance(value, bytes) and hasattr(value, 'encode'):
                value = value.encode(self.encoding)
            attrs_dict[name] = value
        authority = self._generate_authority(attrs_dict)
        to_str = compat.to_str
        ref = self.reference.copy_with(
            scheme=to_str(attrs_dict['scheme'], self.encoding),
            authority=to_str(authority, self.encoding),
            path=to_str(attrs_dict['path'], self.encoding),
            query=to_str(attrs_dict['query'], self.encoding),
            fragment=to_str(attrs_dict['fragment'], self.encoding)
        )
        if not lazy_normalize:
            ref = ref.normalize()
        return ParseResultBytes(
            uri_ref=ref,
            encoding=self.encoding,
            lazy_normalize=lazy_normalize,
            **attrs_dict
        )

    def unsplit(self, use_idna=False):
        """Create a URI bytes object from the components.

        :returns: The parsed URI reconstituted as a string.
        :rtype: bytes
        """
        parse_result = self
        if use_idna and self.host:
            # self.host is bytes, to encode to idna, we need to decode it
            # first
            host = self.host.decode(self.encoding)
            hostbytes = host.encode('idna')
            parse_result = self.copy_with(host=hostbytes)
        if self.lazy_normalize:
            parse_result = parse_result.copy_with(lazy_normalize=False)
        uri = parse_result.reference.unsplit()
        return uri.encode(self.encoding)


def split_authority(authority):
    # Initialize our expected return values
    userinfo = host = port = None
    # Initialize an extra var we may need to use
    extra_host = None
    # Set-up rest in case there is no userinfo portion
    rest = authority

    if '@' in authority:
        userinfo, rest = authority.rsplit('@', 1)

    # Handle IPv6 host addresses
    if rest.startswith('['):
        host, rest = rest.split(']', 1)
        host += ']'

    if ':' in rest:
        extra_host, port = rest.split(':', 1)
    elif not host and rest:
        host = rest

    if extra_host and not host:
        host = extra_host

    return userinfo, host, port


def authority_from(reference, strict):
    try:
        subauthority = reference.authority_info()
    except exceptions.InvalidAuthority:
        if strict:
            raise
        userinfo, host, port = split_authority(reference.authority)
    else:
        # Thanks to Richard Barrell for this idea:
        # https://twitter.com/0x2ba22e11/status/617338811975139328
        userinfo, host, port = (subauthority.get(p)
                                for p in ('userinfo', 'host', 'port'))

    if port:
        try:
            port = int(port)
        except ValueError:
            raise exceptions.InvalidPort(port)
    return userinfo, host, port
  0707010001f4b5000081a40000000000000000000000016a100daf00000f2f000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/rfc3986/api.py # -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Module containing the simple and functional API for rfc3986.

This module defines functions and provides access to the public attributes
and classes of rfc3986.
"""

from .iri import IRIReference
from .parseresult import ParseResult
from .uri import URIReference


def uri_reference(uri, encoding='utf-8'):
    """Parse a URI string into a URIReference.

    This is a convenience function. You could achieve the same end by using
    ``URIReference.from_string(uri)``.

    :param str uri: The URI which needs to be parsed into a reference.
    :param str encoding: The encoding of the string provided
    :returns: A parsed URI
    :rtype: :class:`URIReference`
    """
    return URIReference.from_string(uri, encoding)


def iri_reference(iri, encoding='utf-8'):
    """Parse a IRI string into an IRIReference.

    This is a convenience function. You could achieve the same end by using
    ``IRIReference.from_string(iri)``.

    :param str iri: The IRI which needs to be parsed into a reference.
    :param str encoding: The encoding of the string provided
    :returns: A parsed IRI
    :rtype: :class:`IRIReference`
    """
    return IRIReference.from_string(iri, encoding)


def is_valid_uri(uri, encoding='utf-8', **kwargs):
    """Determine if the URI given is valid.

    This is a convenience function. You could use either
    ``uri_reference(uri).is_valid()`` or
    ``URIReference.from_string(uri).is_valid()`` to achieve the same result.

    :param str uri: The URI to be validated.
    :param str encoding: The encoding of the string provided
    :param bool require_scheme: Set to ``True`` if you wish to require the
        presence of the scheme component.
    :param bool require_authority: Set to ``True`` if you wish to require the
        presence of the authority component.
    :param bool require_path: Set to ``True`` if you wish to require the
        presence of the path component.
    :param bool require_query: Set to ``True`` if you wish to require the
        presence of the query component.
    :param bool require_fragment: Set to ``True`` if you wish to require the
        presence of the fragment component.
    :returns: ``True`` if the URI is valid, ``False`` otherwise.
    :rtype: bool
    """
    return URIReference.from_string(uri, encoding).is_valid(**kwargs)


def normalize_uri(uri, encoding='utf-8'):
    """Normalize the given URI.

    This is a convenience function. You could use either
    ``uri_reference(uri).normalize().unsplit()`` or
    ``URIReference.from_string(uri).normalize().unsplit()`` instead.

    :param str uri: The URI to be normalized.
    :param str encoding: The encoding of the string provided
    :returns: The normalized URI.
    :rtype: str
    """
    normalized_reference = URIReference.from_string(uri, encoding).normalize()
    return normalized_reference.unsplit()


def urlparse(uri, encoding='utf-8'):
    """Parse a given URI and return a ParseResult.

    This is a partial replacement of the standard library's urlparse function.

    :param str uri: The URI to be parsed.
    :param str encoding: The encoding of the string provided.
    :returns: A parsed URI
    :rtype: :class:`~rfc3986.parseresult.ParseResult`
    """
    return ParseResult.from_string(uri, encoding, strict=False)
 0707010001f4bb000081a40000000000000000000000016a100daf0000148b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/rfc3986/normalizers.py # -*- coding: utf-8 -*-
# Copyright (c) 2014 Rackspace
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module with functions to normalize components."""
import re

from . import compat
from . import misc


def normalize_scheme(scheme):
    """Normalize the scheme component."""
    return scheme.lower()


def normalize_authority(authority):
    """Normalize an authority tuple to a string."""
    userinfo, host, port = authority
    result = ''
    if userinfo:
        result += normalize_percent_characters(userinfo) + '@'
    if host:
        result += normalize_host(host)
    if port:
        result += ':' + port
    return result


def normalize_username(username):
    """Normalize a username to make it safe to include in userinfo."""
    return compat.urlquote(username)


def normalize_password(password):
    """Normalize a password to make safe for userinfo."""
    return compat.urlquote(password)


def normalize_host(host):
    """Normalize a host string."""
    if misc.IPv6_MATCHER.match(host):
        percent = host.find('%')
        if percent != -1:
            percent_25 = host.find('%25')

            # Replace RFC 4007 IPv6 Zone ID delimiter '%' with '%25'
            # from RFC 6874. If the host is '[<IPv6 addr>%25]' then we
            # assume RFC 4007 and normalize to '[<IPV6 addr>%2525]'
            if percent_25 == -1 or percent < percent_25 or \
                    (percent == percent_25 and percent_25 == len(host) - 4):
                host = host.replace('%', '%25', 1)

            # Don't normalize the casing of the Zone ID
            return host[:percent].lower() + host[percent:]

    return host.lower()


def normalize_path(path):
    """Normalize the path string."""
    if not path:
        return path

    path = normalize_percent_characters(path)
    return remove_dot_segments(path)


def normalize_query(query):
    """Normalize the query string."""
    if not query:
        return query
    return normalize_percent_characters(query)


def normalize_fragment(fragment):
    """Normalize the fragment string."""
    if not fragment:
        return fragment
    return normalize_percent_characters(fragment)


PERCENT_MATCHER = re.compile('%[A-Fa-f0-9]{2}')


def normalize_percent_characters(s):
    """All percent characters should be upper-cased.

    For example, ``"%3afoo%DF%ab"`` should be turned into ``"%3Afoo%DF%AB"``.
    """
    matches = set(PERCENT_MATCHER.findall(s))
    for m in matches:
        if not m.isupper():
            s = s.replace(m, m.upper())
    return s


def remove_dot_segments(s):
    """Remove dot segments from the string.

    See also Section 5.2.4 of :rfc:`3986`.
    """
    # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code
    segments = s.split('/')  # Turn the path into a list of segments
    output = []  # Initialize the variable to use to store output

    for segment in segments:
        # '.' is the current directory, so ignore it, it is superfluous
        if segment == '.':
            continue
        # Anything other than '..', should be appended to the output
        elif segment != '..':
            output.append(segment)
        # In this case segment == '..', if we can, we should pop the last
        # element
        elif output:
            output.pop()

    # If the path starts with '/' and the output is empty or the first string
    # is non-empty
    if s.startswith('/') and (not output or output[0]):
        output.insert(0, '')

    # If the path starts with '/.' or '/..' ensure we add one more empty
    # string to add a trailing '/'
    if s.endswith(('/.', '/..')):
        output.append('')

    return '/'.join(output)


def encode_component(uri_component, encoding):
    """Encode the specific component in the provided encoding."""
    if uri_component is None:
        return uri_component

    # Try to see if the component we're encoding is already percent-encoded
    # so we can skip all '%' characters but still encode all others.
    percent_encodings = len(PERCENT_MATCHER.findall(
                            compat.to_str(uri_component, encoding)))

    uri_bytes = compat.to_bytes(uri_component, encoding)
    is_percent_encoded = percent_encodings == uri_bytes.count(b'%')

    encoded_uri = bytearray()

    for i in range(0, len(uri_bytes)):
        # Will return a single character bytestring on both Python 2 & 3
        byte = uri_bytes[i:i+1]
        byte_ord = ord(byte)
        if ((is_percent_encoded and byte == b'%')
                or (byte_ord < 128 and byte.decode() in misc.NON_PCT_ENCODED)):
            encoded_uri.extend(byte)
            continue
        encoded_uri.extend('%{0:02x}'.format(byte_ord).encode().upper())

    return encoded_uri.decode(encoding)
 0707010001f4b3000081a40000000000000000000000016a100daf0000339e000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/rfc3986/_mixin.py  """Module containing the implementation of the URIMixin class."""
import warnings

from . import exceptions as exc
from . import misc
from . import normalizers
from . import validators


class URIMixin(object):
    """Mixin with all shared methods for URIs and IRIs."""

    __hash__ = tuple.__hash__

    def authority_info(self):
        """Return a dictionary with the ``userinfo``, ``host``, and ``port``.

        If the authority is not valid, it will raise a
        :class:`~rfc3986.exceptions.InvalidAuthority` Exception.

        :returns:
            ``{'userinfo': 'username:password', 'host': 'www.example.com',
            'port': '80'}``
        :rtype: dict
        :raises rfc3986.exceptions.InvalidAuthority:
            If the authority is not ``None`` and can not be parsed.
        """
        if not self.authority:
            return {'userinfo': None, 'host': None, 'port': None}

        match = self._match_subauthority()

        if match is None:
            # In this case, we have an authority that was parsed from the URI
            # Reference, but it cannot be further parsed by our
            # misc.SUBAUTHORITY_MATCHER. In this case it must not be a valid
            # authority.
            raise exc.InvalidAuthority(self.authority.encode(self.encoding))

        # We had a match, now let's ensure that it is actually a valid host
        # address if it is IPv4
        matches = match.groupdict()
        host = matches.get('host')

        if (host and misc.IPv4_MATCHER.match(host) and not
                validators.valid_ipv4_host_address(host)):
            # If we have a host, it appears to be IPv4 and it does not have
            # valid bytes, it is an InvalidAuthority.
            raise exc.InvalidAuthority(self.authority.encode(self.encoding))

        return matches

    def _match_subauthority(self):
        return misc.SUBAUTHORITY_MATCHER.match(self.authority)

    @property
    def host(self):
        """If present, a string representing the host."""
        try:
            authority = self.authority_info()
        except exc.InvalidAuthority:
            return None
        return authority['host']

    @property
    def port(self):
        """If present, the port extracted from the authority."""
        try:
            authority = self.authority_info()
        except exc.InvalidAuthority:
            return None
        return authority['port']

    @property
    def userinfo(self):
        """If present, the userinfo extracted from the authority."""
        try:
            authority = self.authority_info()
        except exc.InvalidAuthority:
            return None
        return authority['userinfo']

    def is_absolute(self):
        """Determine if this URI Reference is an absolute URI.

        See http://tools.ietf.org/html/rfc3986#section-4.3 for explanation.

        :returns: ``True`` if it is an absolute URI, ``False`` otherwise.
        :rtype: bool
        """
        return bool(misc.ABSOLUTE_URI_MATCHER.match(self.unsplit()))

    def is_valid(self, **kwargs):
        """Determine if the URI is valid.

        .. deprecated:: 1.1.0

            Use the :class:`~rfc3986.validators.Validator` object instead.

        :param bool require_scheme: Set to ``True`` if you wish to require the
            presence of the scheme component.
        :param bool require_authority: Set to ``True`` if you wish to require
            the presence of the authority component.
        :param bool require_path: Set to ``True`` if you wish to require the
            presence of the path component.
        :param bool require_query: Set to ``True`` if you wish to require the
            presence of the query component.
        :param bool require_fragment: Set to ``True`` if you wish to require
            the presence of the fragment component.
        :returns: ``True`` if the URI is valid. ``False`` otherwise.
        :rtype: bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        validators = [
            (self.scheme_is_valid, kwargs.get('require_scheme', False)),
            (self.authority_is_valid, kwargs.get('require_authority', False)),
            (self.path_is_valid, kwargs.get('require_path', False)),
            (self.query_is_valid, kwargs.get('require_query', False)),
            (self.fragment_is_valid, kwargs.get('require_fragment', False)),
            ]
        return all(v(r) for v, r in validators)

    def authority_is_valid(self, require=False):
        """Determine if the authority component is valid.

        .. deprecated:: 1.1.0

            Use the :class:`~rfc3986.validators.Validator` object instead.

        :param bool require:
            Set to ``True`` to require the presence of this component.
        :returns:
            ``True`` if the authority is valid. ``False`` otherwise.
        :rtype:
            bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        try:
            self.authority_info()
        except exc.InvalidAuthority:
            return False

        return validators.authority_is_valid(
            self.authority,
            host=self.host,
            require=require,
        )

    def scheme_is_valid(self, require=False):
        """Determine if the scheme component is valid.

        .. deprecated:: 1.1.0

            Use the :class:`~rfc3986.validators.Validator` object instead.

        :param str require: Set to ``True`` to require the presence of this
            component.
        :returns: ``True`` if the scheme is valid. ``False`` otherwise.
        :rtype: bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        return validators.scheme_is_valid(self.scheme, require)

    def path_is_valid(self, require=False):
        """Determine if the path component is valid.

        .. deprecated:: 1.1.0

            Use the :class:`~rfc3986.validators.Validator` object instead.

        :param str require: Set to ``True`` to require the presence of this
            component.
        :returns: ``True`` if the path is valid. ``False`` otherwise.
        :rtype: bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        return validators.path_is_valid(self.path, require)

    def query_is_valid(self, require=False):
        """Determine if the query component is valid.

        .. deprecated:: 1.1.0

            Use the :class:`~rfc3986.validators.Validator` object instead.

        :param str require: Set to ``True`` to require the presence of this
            component.
        :returns: ``True`` if the query is valid. ``False`` otherwise.
        :rtype: bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        return validators.query_is_valid(self.query, require)

    def fragment_is_valid(self, require=False):
        """Determine if the fragment component is valid.

        .. deprecated:: 1.1.0

            Use the Validator object instead.

        :param str require: Set to ``True`` to require the presence of this
            component.
        :returns: ``True`` if the fragment is valid. ``False`` otherwise.
        :rtype: bool
        """
        warnings.warn("Please use rfc3986.validators.Validator instead. "
                      "This method will be eventually removed.",
                      DeprecationWarning)
        return validators.fragment_is_valid(self.fragment, require)

    def normalized_equality(self, other_ref):
        """Compare this URIReference to another URIReference.

        :param URIReference other_ref: (required), The reference with which
            we're comparing.
        :returns: ``True`` if the references are equal, ``False`` otherwise.
        :rtype: bool
        """
        return tuple(self.normalize()) == tuple(other_ref.normalize())

    def resolve_with(self, base_uri, strict=False):
        """Use an absolute URI Reference to resolve this relative reference.

        Assuming this is a relative reference that you would like to resolve,
        use the provided base URI to resolve it.

        See http://tools.ietf.org/html/rfc3986#section-5 for more information.

        :param base_uri: Either a string or URIReference. It must be an
            absolute URI or it will raise an exception.
        :returns: A new URIReference which is the result of resolving this
            reference using ``base_uri``.
        :rtype: :class:`URIReference`
        :raises rfc3986.exceptions.ResolutionError:
            If the ``base_uri`` is not an absolute URI.
        """
        if not isinstance(base_uri, URIMixin):
            base_uri = type(self).from_string(base_uri)

        if not base_uri.is_absolute():
            raise exc.ResolutionError(base_uri)

        # This is optional per
        # http://tools.ietf.org/html/rfc3986#section-5.2.1
        base_uri = base_uri.normalize()

        # The reference we're resolving
        resolving = self

        if not strict and resolving.scheme == base_uri.scheme:
            resolving = resolving.copy_with(scheme=None)

        # http://tools.ietf.org/html/rfc3986#page-32
        if resolving.scheme is not None:
            target = resolving.copy_with(
                path=normalizers.normalize_path(resolving.path)
            )
        else:
            if resolving.authority is not None:
                target = resolving.copy_with(
                    scheme=base_uri.scheme,
                    path=normalizers.normalize_path(resolving.path)
                )
            else:
                if resolving.path is None:
                    if resolving.query is not None:
                        query = resolving.query
                    else:
                        query = base_uri.query
                    target = resolving.copy_with(
                        scheme=base_uri.scheme,
                        authority=base_uri.authority,
                        path=base_uri.path,
                        query=query
                    )
                else:
                    if resolving.path.startswith('/'):
                        path = normalizers.normalize_path(resolving.path)
                    else:
                        path = normalizers.normalize_path(
                            misc.merge_paths(base_uri, resolving.path)
                        )
                    target = resolving.copy_with(
                        scheme=base_uri.scheme,
                        authority=base_uri.authority,
                        path=path,
                        query=resolving.query
                    )
        return target

    def unsplit(self):
        """Create a URI string from the components.

        :returns: The URI Reference reconstituted as a string.
        :rtype: str
        """
        # See http://tools.ietf.org/html/rfc3986#section-5.3
        result_list = []
        if self.scheme:
            result_list.extend([self.scheme, ':'])
        if self.authority:
            result_list.extend(['//', self.authority])
        if self.path:
            result_list.append(self.path)
        if self.query is not None:
            result_list.extend(['?', self.query])
        if self.fragment is not None:
            result_list.extend(['#', self.fragment])
        return ''.join(result_list)

    def copy_with(self, scheme=misc.UseExisting, authority=misc.UseExisting,
                  path=misc.UseExisting, query=misc.UseExisting,
                  fragment=misc.UseExisting):
        """Create a copy of this reference with the new components.

        :param str scheme:
            (optional) The scheme to use for the new reference.
        :param str authority:
            (optional) The authority to use for the new reference.
        :param str path:
            (optional) The path to use for the new reference.
        :param str query:
            (optional) The query to use for the new reference.
        :param str fragment:
            (optional) The fragment to use for the new reference.
        :returns:
            New URIReference with provided components.
        :rtype:
            URIReference
        """
        attributes = {
            'scheme': scheme,
            'authority': authority,
            'path': path,
            'query': query,
            'fragment': fragment,
        }
        for key, value in list(attributes.items()):
            if value is misc.UseExisting:
                del attributes[key]
        uri = self._replace(**attributes)
        uri.encoding = self.encoding
        return uri
  0707010001f4ac000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/foreign/pyaes  0707010001f4af000081a40000000000000000000000016a100daf00001fb7000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/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)
 0707010001f4b0000081a40000000000000000000000016a100daf00000802000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/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]
  0707010001f4ad000081a40000000000000000000000016a100daf00000827000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/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
 0707010001f4ae000081a40000000000000000000000016a100daf0000eb96000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/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,
)
  0707010001f43c000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/foreign/colorama   0707010001f441000081a40000000000000000000000016a100daf0000151c000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/colorama/win32.py  # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.

# from winbase.h
STDOUT = -11
STDERR = -12

try:
    import ctypes
    from ctypes import LibraryLoader
    windll = LibraryLoader(ctypes.WinDLL)
    from ctypes import wintypes
except (AttributeError, ImportError):
    windll = None
    SetConsoleTextAttribute = lambda *_: None
    winapi_test = lambda *_: None
else:
    from ctypes import byref, Structure, c_char, POINTER

    COORD = wintypes._COORD

    class CONSOLE_SCREEN_BUFFER_INFO(Structure):
        """struct in wincon.h."""
        _fields_ = [
            ("dwSize", COORD),
            ("dwCursorPosition", COORD),
            ("wAttributes", wintypes.WORD),
            ("srWindow", wintypes.SMALL_RECT),
            ("dwMaximumWindowSize", COORD),
        ]
        def __str__(self):
            return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
                self.dwSize.Y, self.dwSize.X
                , self.dwCursorPosition.Y, self.dwCursorPosition.X
                , self.wAttributes
                , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
                , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
            )

    _GetStdHandle = windll.kernel32.GetStdHandle
    _GetStdHandle.argtypes = [
        wintypes.DWORD,
    ]
    _GetStdHandle.restype = wintypes.HANDLE

    _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
    _GetConsoleScreenBufferInfo.argtypes = [
        wintypes.HANDLE,
        POINTER(CONSOLE_SCREEN_BUFFER_INFO),
    ]
    _GetConsoleScreenBufferInfo.restype = wintypes.BOOL

    _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
    _SetConsoleTextAttribute.argtypes = [
        wintypes.HANDLE,
        wintypes.WORD,
    ]
    _SetConsoleTextAttribute.restype = wintypes.BOOL

    _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
    _SetConsoleCursorPosition.argtypes = [
        wintypes.HANDLE,
        COORD,
    ]
    _SetConsoleCursorPosition.restype = wintypes.BOOL

    _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA
    _FillConsoleOutputCharacterA.argtypes = [
        wintypes.HANDLE,
        c_char,
        wintypes.DWORD,
        COORD,
        POINTER(wintypes.DWORD),
    ]
    _FillConsoleOutputCharacterA.restype = wintypes.BOOL

    _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
    _FillConsoleOutputAttribute.argtypes = [
        wintypes.HANDLE,
        wintypes.WORD,
        wintypes.DWORD,
        COORD,
        POINTER(wintypes.DWORD),
    ]
    _FillConsoleOutputAttribute.restype = wintypes.BOOL

    _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW
    _SetConsoleTitleW.argtypes = [
        wintypes.LPCWSTR
    ]
    _SetConsoleTitleW.restype = wintypes.BOOL

    def _winapi_test(handle):
        csbi = CONSOLE_SCREEN_BUFFER_INFO()
        success = _GetConsoleScreenBufferInfo(
            handle, byref(csbi))
        return bool(success)

    def winapi_test():
        return any(_winapi_test(h) for h in
                   (_GetStdHandle(STDOUT), _GetStdHandle(STDERR)))

    def GetConsoleScreenBufferInfo(stream_id=STDOUT):
        handle = _GetStdHandle(stream_id)
        csbi = CONSOLE_SCREEN_BUFFER_INFO()
        success = _GetConsoleScreenBufferInfo(
            handle, byref(csbi))
        return csbi

    def SetConsoleTextAttribute(stream_id, attrs):
        handle = _GetStdHandle(stream_id)
        return _SetConsoleTextAttribute(handle, attrs)

    def SetConsoleCursorPosition(stream_id, position, adjust=True):
        position = COORD(*position)
        # If the position is out of range, do nothing.
        if position.Y <= 0 or position.X <= 0:
            return
        # Adjust for Windows' SetConsoleCursorPosition:
        #    1. being 0-based, while ANSI is 1-based.
        #    2. expecting (x,y), while ANSI uses (y,x).
        adjusted_position = COORD(position.Y - 1, position.X - 1)
        if adjust:
            # Adjust for viewport's scroll position
            sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
            adjusted_position.Y += sr.Top
            adjusted_position.X += sr.Left
        # Resume normal processing
        handle = _GetStdHandle(stream_id)
        return _SetConsoleCursorPosition(handle, adjusted_position)

    def FillConsoleOutputCharacter(stream_id, char, length, start):
        handle = _GetStdHandle(stream_id)
        char = c_char(char.encode())
        length = wintypes.DWORD(length)
        num_written = wintypes.DWORD(0)
        # Note that this is hard-coded for ANSI (vs wide) bytes.
        success = _FillConsoleOutputCharacterA(
            handle, char, length, start, byref(num_written))
        return num_written.value

    def FillConsoleOutputAttribute(stream_id, attr, length, start):
        ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
        handle = _GetStdHandle(stream_id)
        attribute = wintypes.WORD(attr)
        length = wintypes.DWORD(length)
        num_written = wintypes.DWORD(0)
        # Note that this is hard-coded for ANSI (vs wide) bytes.
        return _FillConsoleOutputAttribute(
            handle, attribute, length, start, byref(num_written))

    def SetConsoleTitle(title):
        return _SetConsoleTitleW(title)
0707010001f43f000081a40000000000000000000000016a100daf00002856000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/colorama/ansitowin32.py    # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
import re
import sys
import os

from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
from .winterm import WinTerm, WinColor, WinStyle
from .win32 import windll, winapi_test


winterm = None
if windll is not None:
    winterm = WinTerm()


class StreamWrapper(object):
    '''
    Wraps a stream (such as stdout), acting as a transparent proxy for all
    attribute access apart from method 'write()', which is delegated to our
    Converter instance.
    '''
    def __init__(self, wrapped, converter):
        # double-underscore everything to prevent clashes with names of
        # attributes on the wrapped stream object.
        self.__wrapped = wrapped
        self.__convertor = converter

    def __getattr__(self, name):
        return getattr(self.__wrapped, name)

    def __enter__(self, *args, **kwargs):
        # special method lookup bypasses __getattr__/__getattribute__, see
        # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit
        # thus, contextlib magic methods are not proxied via __getattr__
        return self.__wrapped.__enter__(*args, **kwargs)

    def __exit__(self, *args, **kwargs):
        return self.__wrapped.__exit__(*args, **kwargs)

    def write(self, text):
        self.__convertor.write(text)

    def isatty(self):
        stream = self.__wrapped
        if 'PYCHARM_HOSTED' in os.environ:
            if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__):
                return True
        return (hasattr(stream, 'isatty') and stream.isatty())

    @property
    def closed(self):
        stream = self.__wrapped
        return not hasattr(stream, 'closed') or stream.closed


class AnsiToWin32(object):
    '''
    Implements a 'write()' method which, on Windows, will strip ANSI character
    sequences from the text, and if outputting to a tty, will convert them into
    win32 function calls.
    '''
    ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?')   # Control Sequence Introducer
    ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?')        # Operating System Command

    def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
        # The wrapped stream (normally sys.stdout or sys.stderr)
        self.wrapped = wrapped

        # should we reset colors to defaults after every .write()
        self.autoreset = autoreset

        # create the proxy wrapping our output stream
        self.stream = StreamWrapper(wrapped, self)

        on_windows = os.name == 'nt'
        # We test if the WinAPI works, because even if we are on Windows
        # we may be using a terminal that doesn't support the WinAPI
        # (e.g. Cygwin Terminal). In this case it's up to the terminal
        # to support the ANSI codes.
        conversion_supported = on_windows and winapi_test()

        # should we strip ANSI sequences from our output?
        if strip is None:
            strip = conversion_supported or (not self.stream.closed and not self.stream.isatty())
        self.strip = strip

        # should we should convert ANSI sequences into win32 calls?
        if convert is None:
            convert = conversion_supported and not self.stream.closed and self.stream.isatty()
        self.convert = convert

        # dict of ansi codes to win32 functions and parameters
        self.win32_calls = self.get_win32_calls()

        # are we wrapping stderr?
        self.on_stderr = self.wrapped is sys.stderr

    def should_wrap(self):
        '''
        True if this class is actually needed. If false, then the output
        stream will not be affected, nor will win32 calls be issued, so
        wrapping stdout is not actually required. This will generally be
        False on non-Windows platforms, unless optional functionality like
        autoreset has been requested using kwargs to init()
        '''
        return self.convert or self.strip or self.autoreset

    def get_win32_calls(self):
        if self.convert and winterm:
            return {
                AnsiStyle.RESET_ALL: (winterm.reset_all, ),
                AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
                AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
                AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
                AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
                AnsiFore.RED: (winterm.fore, WinColor.RED),
                AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
                AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
                AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
                AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
                AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
                AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
                AnsiFore.RESET: (winterm.fore, ),
                AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
                AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
                AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
                AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
                AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
                AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
                AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
                AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
                AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
                AnsiBack.RED: (winterm.back, WinColor.RED),
                AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
                AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
                AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
                AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
                AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
                AnsiBack.WHITE: (winterm.back, WinColor.GREY),
                AnsiBack.RESET: (winterm.back, ),
                AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
                AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
                AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
                AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
                AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
                AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
                AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
                AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
            }
        return dict()

    def write(self, text):
        if self.strip or self.convert:
            self.write_and_convert(text)
        else:
            self.wrapped.write(text)
            self.wrapped.flush()
        if self.autoreset:
            self.reset_all()


    def reset_all(self):
        if self.convert:
            self.call_win32('m', (0,))
        elif not self.strip and not self.stream.closed:
            self.wrapped.write(Style.RESET_ALL)


    def write_and_convert(self, text):
        '''
        Write the given text to our wrapped stream, stripping any ANSI
        sequences from the text, and optionally converting them into win32
        calls.
        '''
        cursor = 0
        text = self.convert_osc(text)
        for match in self.ANSI_CSI_RE.finditer(text):
            start, end = match.span()
            self.write_plain_text(text, cursor, start)
            self.convert_ansi(*match.groups())
            cursor = end
        self.write_plain_text(text, cursor, len(text))


    def write_plain_text(self, text, start, end):
        if start < end:
            self.wrapped.write(text[start:end])
            self.wrapped.flush()


    def convert_ansi(self, paramstring, command):
        if self.convert:
            params = self.extract_params(command, paramstring)
            self.call_win32(command, params)


    def extract_params(self, command, paramstring):
        if command in 'Hf':
            params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
            while len(params) < 2:
                # defaults:
                params = params + (1,)
        else:
            params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
            if len(params) == 0:
                # defaults:
                if command in 'JKm':
                    params = (0,)
                elif command in 'ABCD':
                    params = (1,)

        return params


    def call_win32(self, command, params):
        if command == 'm':
            for param in params:
                if param in self.win32_calls:
                    func_args = self.win32_calls[param]
                    func = func_args[0]
                    args = func_args[1:]
                    kwargs = dict(on_stderr=self.on_stderr)
                    func(*args, **kwargs)
        elif command in 'J':
            winterm.erase_screen(params[0], on_stderr=self.on_stderr)
        elif command in 'K':
            winterm.erase_line(params[0], on_stderr=self.on_stderr)
        elif command in 'Hf':     # cursor position - absolute
            winterm.set_cursor_position(params, on_stderr=self.on_stderr)
        elif command in 'ABCD':   # cursor position - relative
            n = params[0]
            # A - up, B - down, C - forward, D - back
            x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
            winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)


    def convert_osc(self, text):
        for match in self.ANSI_OSC_RE.finditer(text):
            start, end = match.span()
            text = text[:start] + text[end:]
            paramstring, command = match.groups()
            if command in '\x07':       # \x07 = BEL
                params = paramstring.split(";")
                # 0 - change title and icon (we will only change title)
                # 1 - change icon (we don't support this)
                # 2 - change title
                if params[0] in '02':
                    winterm.set_title(params[1])
        return text
  0707010001f43e000081a40000000000000000000000016a100daf000009dc000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/colorama/ansi.py   # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
'''
This module generates ANSI character codes to printing colors to terminals.
See: http://en.wikipedia.org/wiki/ANSI_escape_code
'''

CSI = '\033['
OSC = '\033]'
BEL = '\007'


def code_to_chars(code):
    return CSI + str(code) + 'm'

def set_title(title):
    return OSC + '2;' + title + BEL

def clear_screen(mode=2):
    return CSI + str(mode) + 'J'

def clear_line(mode=2):
    return CSI + str(mode) + 'K'


class AnsiCodes(object):
    def __init__(self):
        # the subclasses declare class attributes which are numbers.
        # Upon instantiation we define instance attributes, which are the same
        # as the class attributes but wrapped with the ANSI escape sequence
        for name in dir(self):
            if not name.startswith('_'):
                value = getattr(self, name)
                setattr(self, name, code_to_chars(value))


class AnsiCursor(object):
    def UP(self, n=1):
        return CSI + str(n) + 'A'
    def DOWN(self, n=1):
        return CSI + str(n) + 'B'
    def FORWARD(self, n=1):
        return CSI + str(n) + 'C'
    def BACK(self, n=1):
        return CSI + str(n) + 'D'
    def POS(self, x=1, y=1):
        return CSI + str(y) + ';' + str(x) + 'H'


class AnsiFore(AnsiCodes):
    BLACK           = 30
    RED             = 31
    GREEN           = 32
    YELLOW          = 33
    BLUE            = 34
    MAGENTA         = 35
    CYAN            = 36
    WHITE           = 37
    RESET           = 39

    # These are fairly well supported, but not part of the standard.
    LIGHTBLACK_EX   = 90
    LIGHTRED_EX     = 91
    LIGHTGREEN_EX   = 92
    LIGHTYELLOW_EX  = 93
    LIGHTBLUE_EX    = 94
    LIGHTMAGENTA_EX = 95
    LIGHTCYAN_EX    = 96
    LIGHTWHITE_EX   = 97


class AnsiBack(AnsiCodes):
    BLACK           = 40
    RED             = 41
    GREEN           = 42
    YELLOW          = 43
    BLUE            = 44
    MAGENTA         = 45
    CYAN            = 46
    WHITE           = 47
    RESET           = 49

    # These are fairly well supported, but not part of the standard.
    LIGHTBLACK_EX   = 100
    LIGHTRED_EX     = 101
    LIGHTGREEN_EX   = 102
    LIGHTYELLOW_EX  = 103
    LIGHTBLUE_EX    = 104
    LIGHTMAGENTA_EX = 105
    LIGHTCYAN_EX    = 106
    LIGHTWHITE_EX   = 107


class AnsiStyle(AnsiCodes):
    BRIGHT    = 1
    DIM       = 2
    NORMAL    = 22
    RESET_ALL = 0

Fore   = AnsiFore()
Back   = AnsiBack()
Style  = AnsiStyle()
Cursor = AnsiCursor()
0707010001f43d000081a40000000000000000000000016a100daf000000ef000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/colorama/__init__.py   # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
from .initialise import init, deinit, reinit, colorama_text
from .ansi import Fore, Back, Style, Cursor
from .ansitowin32 import AnsiToWin32

__version__ = '0.4.0'
 0707010001f440000081a40000000000000000000000016a100daf0000077b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/colorama/initialise.py # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
import atexit
import contextlib
import sys

from .ansitowin32 import AnsiToWin32


orig_stdout = None
orig_stderr = None

wrapped_stdout = None
wrapped_stderr = None

atexit_done = False


def reset_all():
    if AnsiToWin32 is not None:    # Issue #74: objects might become None at exit
        AnsiToWin32(orig_stdout).reset_all()


def init(autoreset=False, convert=None, strip=None, wrap=True):

    if not wrap and any([autoreset, convert, strip]):
        raise ValueError('wrap=False conflicts with any other arg=True')

    global wrapped_stdout, wrapped_stderr
    global orig_stdout, orig_stderr

    orig_stdout = sys.stdout
    orig_stderr = sys.stderr

    if sys.stdout is None:
        wrapped_stdout = None
    else:
        sys.stdout = wrapped_stdout = \
            wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
    if sys.stderr is None:
        wrapped_stderr = None
    else:
        sys.stderr = wrapped_stderr = \
            wrap_stream(orig_stderr, convert, strip, autoreset, wrap)

    global atexit_done
    if not atexit_done:
        atexit.register(reset_all)
        atexit_done = True


def deinit():
    if orig_stdout is not None:
        sys.stdout = orig_stdout
    if orig_stderr is not None:
        sys.stderr = orig_stderr


@contextlib.contextmanager
def colorama_text(*args, **kwargs):
    init(*args, **kwargs)
    try:
        yield
    finally:
        deinit()


def reinit():
    if wrapped_stdout is not None:
        sys.stdout = wrapped_stdout
    if wrapped_stderr is not None:
        sys.stderr = wrapped_stderr


def wrap_stream(stream, convert, strip, autoreset, wrap):
    if wrap:
        wrapper = AnsiToWin32(stream,
            convert=convert, strip=strip, autoreset=autoreset)
        if wrapper.should_wrap():
            stream = wrapper.stream
    return stream
 0707010001f442000081a40000000000000000000000016a100daf00001926000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/colorama/winterm.py    # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
from . import win32


# from wincon.h
class WinColor(object):
    BLACK   = 0
    BLUE    = 1
    GREEN   = 2
    CYAN    = 3
    RED     = 4
    MAGENTA = 5
    YELLOW  = 6
    GREY    = 7

# from wincon.h
class WinStyle(object):
    NORMAL              = 0x00 # dim text, dim background
    BRIGHT              = 0x08 # bright text, dim background
    BRIGHT_BACKGROUND   = 0x80 # dim text, bright background

class WinTerm(object):

    def __init__(self):
        self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
        self.set_attrs(self._default)
        self._default_fore = self._fore
        self._default_back = self._back
        self._default_style = self._style
        # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
        # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
        # we track them separately, since LIGHT_EX is overwritten by Fore/Back
        # and BRIGHT is overwritten by Style codes.
        self._light = 0

    def get_attrs(self):
        return self._fore + self._back * 16 + (self._style | self._light)

    def set_attrs(self, value):
        self._fore = value & 7
        self._back = (value >> 4) & 7
        self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)

    def reset_all(self, on_stderr=None):
        self.set_attrs(self._default)
        self.set_console(attrs=self._default)
        self._light = 0

    def fore(self, fore=None, light=False, on_stderr=False):
        if fore is None:
            fore = self._default_fore
        self._fore = fore
        # Emulate LIGHT_EX with BRIGHT Style
        if light:
            self._light |= WinStyle.BRIGHT
        else:
            self._light &= ~WinStyle.BRIGHT
        self.set_console(on_stderr=on_stderr)

    def back(self, back=None, light=False, on_stderr=False):
        if back is None:
            back = self._default_back
        self._back = back
        # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
        if light:
            self._light |= WinStyle.BRIGHT_BACKGROUND
        else:
            self._light &= ~WinStyle.BRIGHT_BACKGROUND
        self.set_console(on_stderr=on_stderr)

    def style(self, style=None, on_stderr=False):
        if style is None:
            style = self._default_style
        self._style = style
        self.set_console(on_stderr=on_stderr)

    def set_console(self, attrs=None, on_stderr=False):
        if attrs is None:
            attrs = self.get_attrs()
        handle = win32.STDOUT
        if on_stderr:
            handle = win32.STDERR
        win32.SetConsoleTextAttribute(handle, attrs)

    def get_position(self, handle):
        position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
        # Because Windows coordinates are 0-based,
        # and win32.SetConsoleCursorPosition expects 1-based.
        position.X += 1
        position.Y += 1
        return position

    def set_cursor_position(self, position=None, on_stderr=False):
        if position is None:
            # I'm not currently tracking the position, so there is no default.
            # position = self.get_position()
            return
        handle = win32.STDOUT
        if on_stderr:
            handle = win32.STDERR
        win32.SetConsoleCursorPosition(handle, position)

    def cursor_adjust(self, x, y, on_stderr=False):
        handle = win32.STDOUT
        if on_stderr:
            handle = win32.STDERR
        position = self.get_position(handle)
        adjusted_position = (position.Y + y, position.X + x)
        win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)

    def erase_screen(self, mode=0, on_stderr=False):
        # 0 should clear from the cursor to the end of the screen.
        # 1 should clear from the cursor to the beginning of the screen.
        # 2 should clear the entire screen, and move cursor to (1,1)
        handle = win32.STDOUT
        if on_stderr:
            handle = win32.STDERR
        csbi = win32.GetConsoleScreenBufferInfo(handle)
        # get the number of character cells in the current buffer
        cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
        # get number of character cells before current cursor position
        cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
        if mode == 0:
            from_coord = csbi.dwCursorPosition
            cells_to_erase = cells_in_screen - cells_before_cursor
        elif mode == 1:
            from_coord = win32.COORD(0, 0)
            cells_to_erase = cells_before_cursor
        elif mode == 2:
            from_coord = win32.COORD(0, 0)
            cells_to_erase = cells_in_screen
        else:
            # invalid mode
            return
        # fill the entire screen with blanks
        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
        # now set the buffer's attributes accordingly
        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
        if mode == 2:
            # put the cursor where needed
            win32.SetConsoleCursorPosition(handle, (1, 1))

    def erase_line(self, mode=0, on_stderr=False):
        # 0 should clear from the cursor to the end of the line.
        # 1 should clear from the cursor to the beginning of the line.
        # 2 should clear the entire line.
        handle = win32.STDOUT
        if on_stderr:
            handle = win32.STDERR
        csbi = win32.GetConsoleScreenBufferInfo(handle)
        if mode == 0:
            from_coord = csbi.dwCursorPosition
            cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
        elif mode == 1:
            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
            cells_to_erase = csbi.dwCursorPosition.X
        elif mode == 2:
            from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
            cells_to_erase = csbi.dwSize.X
        else:
            # invalid mode
            return
        # fill the entire screen with blanks
        win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
        # now set the buffer's attributes accordingly
        win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)

    def set_title(self, title):
        win32.SetConsoleTitle(title)
  0707010001f4a1000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/looseversion   0707010001f4a3000081a40000000000000000000000016a100daf0000006b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/looseversion/README.md # Vendored from https://github.com/effigies/looseversion.git
# Do not modify unless syncing with upstream.
 0707010001f4a2000081a40000000000000000000000016a100daf000009ba000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/looseversion/LICENSE   PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------

1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.

2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
All Rights Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.

3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.

4. PSF is making Python available to Licensee on an "AS IS"
basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.

5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.

6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.

7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee.  This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.

8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
  0707010001f4a4000081a40000000000000000000000016a100daf00002472000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/looseversion/__init__.py   """Provides classes to represent module version numbers (one class for
each style of version numbering).  There are currently two such classes
implemented: StrictVersion and LooseVersion.

Every version number class implements the following interface:
  * the 'parse' method takes a string and parses it to some internal
    representation; if the string is an invalid version number,
    'parse' raises a ValueError exception
  * the class constructor takes an optional string argument which,
    if supplied, is passed to 'parse'
  * __str__ reconstructs the string that was passed to 'parse' (or
    an equivalent string -- ie. one that will generate an equivalent
    version number instance)
  * __repr__ generates Python code to recreate the version number instance
  * _cmp compares the current instance with either another instance
    of the same class or a string (which will be parsed to an instance
    of the same class, thus must follow the same rules)
"""

import re
import sys

# The rules according to Greg Stein:
# 1) a version number has 1 or more numbers separated by a period or by
#    sequences of letters. If only periods, then these are compared
#    left-to-right to determine an ordering.
# 2) sequences of letters are part of the tuple for comparison and are
#    compared lexicographically
# 3) recognize the numeric components may have leading zeroes
#
# The LooseVersion class below implements these rules: a version number
# string is split up into a tuple of integer and string components, and
# comparison is a simple tuple comparison.  This means that version
# numbers behave in a predictable and obvious way, but a way that might
# not necessarily be how people *want* version numbers to behave.  There
# wouldn't be a problem if people could stick to purely numeric version
# numbers: just split on period and compare the numbers as tuples.
# However, people insist on putting letters into their version numbers;
# the most common purpose seems to be:
#   - indicating a "pre-release" version
#     ('alpha', 'beta', 'a', 'b', 'pre', 'p')
#   - indicating a post-release patch ('p', 'pl', 'patch')
# but of course this can't cover all version number schemes, and there's
# no way to know what a programmer means without asking him.
#
# The problem is what to do with letters (and other non-numeric
# characters) in a version number.  The current implementation does the
# obvious and predictable thing: keep them as strings and compare
# lexically within a tuple comparison.  This has the desired effect if
# an appended letter sequence implies something "post-release":
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
#
# However, if letters in a version number imply a pre-release version,
# the "obvious" thing isn't correct.  Eg. you would expect that
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
# implemented here, this just isn't so.
#
# Two possible solutions come to mind.  The first is to tie the
# comparison algorithm to a particular set of semantic rules, as has
# been done in the StrictVersion class above.  This works great as long
# as everyone can go along with bondage and discipline.  Hopefully a
# (large) subset of Python module programmers will agree that the
# particular flavour of bondage and discipline provided by StrictVersion
# provides enough benefit to be worth using, and will submit their
# version numbering scheme to its domination.  The free-thinking
# anarchists in the lot will never give in, though, and something needs
# to be done to accommodate them.
#
# Perhaps a "moderately strict" version class could be implemented that
# lets almost anything slide (syntactically), and makes some heuristic
# assumptions about non-digits in version number strings.  This could
# sink into special-case-hell, though; if I was as talented and
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
# just as happy dealing with things like "2g6" and "1.13++".  I don't
# think I'm smart enough to do it right though.
#
# In any case, I've coded the test suite for this module (see
# ../test/test_version.py) specifically to fail on things like comparing
# "1.2a2" and "1.2".  That's not because the *code* is doing anything
# wrong, it's because the simple, obvious design doesn't match my
# complicated, hairy expectations for real-world version numbers.  It
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
# the Right Thing" (ie. the code matches the conception).  But I'd rather
# have a conception that matches common notions about version numbers.


if sys.version_info >= (3,):

    class _Py2Int(int):
        """Integer object that compares < any string"""

        def __gt__(self, other):
            if isinstance(other, str):
                return False
            return super().__gt__(other)

        def __lt__(self, other):
            if isinstance(other, str):
                return True
            return super().__lt__(other)

else:
    _Py2Int = int


class LooseVersion(object):
    """Version numbering for anarchists and software realists.
    Implements the standard interface for version number classes as
    described above.  A version number consists of a series of numbers,
    separated by either periods or strings of letters.  When comparing
    version numbers, the numeric components will be compared
    numerically, and the alphabetic components lexically.  The following
    are all valid version numbers, in no particular order:

        1.5.1
        1.5.2b2
        161
        3.10a
        8.02
        3.4j
        1996.07.12
        3.2.pl0
        3.1.1.6
        2g6
        11g
        0.960923
        2.2beta29
        1.13++
        5.5.kw
        2.0b1pl0

    In fact, there is no such thing as an invalid version number under
    this scheme; the rules for comparison are simple and predictable,
    but may not always give the results you want (for some definition
    of "want").
    """

    component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE)

    def __init__(self, vstring=None):
        if vstring:
            self.parse(vstring)

    def __eq__(self, other):
        c = self._cmp(other)
        if c is NotImplemented:
            return NotImplemented
        return c == 0

    def __lt__(self, other):
        c = self._cmp(other)
        if c is NotImplemented:
            return NotImplemented
        return c < 0

    def __le__(self, other):
        c = self._cmp(other)
        if c is NotImplemented:
            return NotImplemented
        return c <= 0

    def __gt__(self, other):
        c = self._cmp(other)
        if c is NotImplemented:
            return NotImplemented
        return c > 0

    def __ge__(self, other):
        c = self._cmp(other)
        if c is NotImplemented:
            return NotImplemented
        return c >= 0

    def parse(self, vstring):
        # I've given up on thinking I can reconstruct the version string
        # from the parsed tuple -- so I just store the string here for
        # use by __str__
        self.vstring = vstring
        components = [x for x in self.component_re.split(vstring) if x and x != "."]
        for i, obj in enumerate(components):
            try:
                components[i] = int(obj)
            except ValueError:
                pass

        self.version = components

    def __str__(self):
        return self.vstring

    def __repr__(self):
        return "LooseVersion ('%s')" % str(self)

    def _cmp(self, other):
        other = self._coerce(other)
        if other is NotImplemented:
            return NotImplemented

        if self.version == other.version:
            return 0
        if self.version < other.version:
            return -1
        if self.version > other.version:
            return 1
        return NotImplemented

    @classmethod
    def _coerce(cls, other):
        if isinstance(other, cls):
            return other
        elif isinstance(other, str):
            return cls(other)
        elif "distutils" in sys.modules:
            # Using this check to avoid importing distutils and suppressing the warning
            try:
                from distutils.version import LooseVersion as deprecated
            except ImportError:
                return NotImplemented
            if isinstance(other, deprecated):
                return cls(str(other))
        return NotImplemented


class LooseVersion2(LooseVersion):
    """LooseVersion variant that restores Python 2 semantics

    In Python 2, comparing LooseVersions where paired components could be string
    and int always resulted in the string being "greater". In Python 3, this produced
    a TypeError.
    """

    def parse(self, vstring):
        # I've given up on thinking I can reconstruct the version string
        # from the parsed tuple -- so I just store the string here for
        # use by __str__
        self.vstring = vstring
        components = [x for x in self.component_re.split(vstring) if x and x != "."]
        for i, obj in enumerate(components):
            try:
                components[i] = _Py2Int(obj)
            except ValueError:
                pass

        self.version = components
  0707010001f4c1000081a40000000000000000000000016a100daf0000b76d000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/foreign/wmi.py # pylint: disable-all

"""
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 "")

   0707010001f443000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/foreign/concurrent 0707010001f444000081a40000000000000000000000016a100daf0000004c000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/concurrent/__init__.py from pkgutil import extend_path

__path__ = extend_path(__path__, __name__)
0707010001f445000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/foreign/concurrent/futures 0707010001f448000081a40000000000000000000000016a100daf00003a8a000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/foreign/concurrent/futures/process.py  # Copyright 2009 Brian Quinlan. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

"""Implements ProcessPoolExecutor.

The follow diagram and text describe the data-flow through the system:

|======================= In-process =====================|== Out-of-process ==|

+----------+     +----------+       +--------+     +-----------+    +---------+
|          |  => | Work Ids |    => |        |  => | Call Q    | => |         |
|          |     +----------+       |        |     +-----------+    |         |
|          |     | ...      |       |        |     | ...       |    |         |
|          |     | 6        |       |        |     | 5, call() |    |         |
|          |     | 7        |       |        |     | ...       |    |         |
| Process  |     | ...      |       | Local  |     +-----------+    | Process |
|  Pool    |     +----------+       | Worker |                      |  #1..n  |
| Executor |                        | Thread |                      |         |
|          |     +----------- +     |        |     +-----------+    |         |
|          | <=> | Work Items | <=> |        | <=  | Result Q  | <= |         |
|          |     +------------+     |        |     +-----------+    |         |
|          |     | 6: call()  |     |        |     | ...       |    |         |
|          |     |    future  |     |        |     | 4, result |    |         |
|          |     | ...        |     |        |     | 3, except |    |         |
+----------+     +------------+     +--------+     +-----------+    +---------+

Executor.submit() called:
- creates a uniquely numbered _WorkItem and adds it to the "Work Items" dict
- adds the id of the _WorkItem to the "Work Ids" queue

Local worker thread:
- reads work ids from the "Work Ids" queue and looks up the corresponding
  WorkItem from the "Work Items" dict: if the work item has been cancelled then
  it is simply removed from the dict, otherwise it is repackaged as a
  _CallItem and put in the "Call Q". New _CallItems are put in the "Call Q"
  until "Call Q" is full. NOTE: the size of the "Call Q" is kept small because
  calls placed in the "Call Q" can no longer be cancelled with Future.cancel().
- reads _ResultItems from "Result Q", updates the future stored in the
  "Work Items" dict and deletes the dict entry

Process #1..n:
- reads _CallItems from "Call Q", executes the calls, and puts the resulting
  _ResultItems in "Request Q"
"""

import atexit
from concurrent.futures import _base
import Queue as queue
import multiprocessing
import threading
import weakref
import sys

__author__ = 'Brian Quinlan (brian@sweetapp.com)'

# Workers are created as daemon threads and processes. This is done to allow the
# interpreter to exit when there are still idle processes in a
# ProcessPoolExecutor's process pool (i.e. shutdown() was not called). However,
# allowing workers to die with the interpreter has two undesirable properties:
#   - The workers would still be running during interpretor shutdown,
#     meaning that they would fail in unpredictable ways.
#   - The workers could be killed while evaluating a work item, which could
#     be bad if the callable being evaluated has external side-effects e.g.
#     writing to a file.
#
# To work around this problem, an exit handler is installed which tells the
# workers to exit when their work queues are empty and then waits until the
# threads/processes finish.

_threads_queues = weakref.WeakKeyDictionary()
_shutdown = False

def _python_exit():
    global _shutdown
    _shutdown = True
    items = list(_threads_queues.items()) if _threads_queues else ()
    for t, q in items:
        q.put(None)
    for t, q in items:
        t.join(sys.maxint)

# Controls how many more calls than processes will be queued in the call queue.
# A smaller number will mean that processes spend more time idle waiting for
# work while a larger number will make Future.cancel() succeed less frequently
# (Futures in the call queue cannot be cancelled).
EXTRA_QUEUED_CALLS = 1

class _WorkItem(object):
    def __init__(self, future, fn, args, kwargs):
        self.future = future
        self.fn = fn
        self.args = args
        self.kwargs = kwargs

class _ResultItem(object):
    def __init__(self, work_id, exception=None, result=None):
        self.work_id = work_id
        self.exception = exception
        self.result = result

class _CallItem(object):
    def __init__(self, work_id, fn, args, kwargs):
        self.work_id = work_id
        self.fn = fn
        self.args = args
        self.kwargs = kwargs

def _process_worker(call_queue, result_queue):
    """Evaluates calls from call_queue and places the results in result_queue.

    This worker is run in a separate process.

    Args:
        call_queue: A multiprocessing.Queue of _CallItems that will be read and
            evaluated by the worker.
        result_queue: A multiprocessing.Queue of _ResultItems that will written
            to by the worker.
        shutdown: A multiprocessing.Event that will be set as a signal to the
            worker that it should exit when call_queue is empty.
    """
    while True:
        call_item = call_queue.get(block=True)
        if call_item is None:
            # Wake up queue management thread
            result_queue.put(None)
            return
        try:
            r = call_item.fn(*call_item.args, **call_item.kwargs)
        except:
            e = sys.exc_info()[1]
            result_queue.put(_ResultItem(call_item.work_id,
                                         exception=e))
        else:
            result_queue.put(_ResultItem(call_item.work_id,
                                         result=r))

def _add_call_item_to_queue(pending_work_items,
                            work_ids,
                            call_queue):
    """Fills call_queue with _WorkItems from pending_work_items.

    This function never blocks.

    Args:
        pending_work_items: A dict mapping work ids to _WorkItems e.g.
            {5: <_WorkItem...>, 6: <_WorkItem...>, ...}
        work_ids: A queue.Queue of work ids e.g. Queue([5, 6, ...]). Work ids
            are consumed and the corresponding _WorkItems from
            pending_work_items are transformed into _CallItems and put in
            call_queue.
        call_queue: A multiprocessing.Queue that will be filled with _CallItems
            derived from _WorkItems.
    """
    while True:
        if call_queue.full():
            return
        try:
            work_id = work_ids.get(block=False)
        except queue.Empty:
            return
        else:
            work_item = pending_work_items[work_id]

            if work_item.future.set_running_or_notify_cancel():
                call_queue.put(_CallItem(work_id,
                                         work_item.fn,
                                         work_item.args,
                                         work_item.kwargs),
                               block=True)
            else:
                del pending_work_items[work_id]
                continue

def _queue_management_worker(executor_reference,
                             processes,
                             pending_work_items,
                             work_ids_queue,
                             call_queue,
                             result_queue):
    """Manages the communication between this process and the worker processes.

    This function is run in a local thread.

    Args:
        executor_reference: A weakref.ref to the ProcessPoolExecutor that owns
            this thread. Used to determine if the ProcessPoolExecutor has been
            garbage collected and that this function can exit.
        process: A list of the multiprocessing.Process instances used as
            workers.
        pending_work_items: A dict mapping work ids to _WorkItems e.g.
            {5: <_WorkItem...>, 6: <_WorkItem...>, ...}
        work_ids_queue: A queue.Queue of work ids e.g. Queue([5, 6, ...]).
        call_queue: A multiprocessing.Queue that will be filled with _CallItems
            derived from _WorkItems for processing by the process workers.
        result_queue: A multiprocessing.Queue of _ResultItems generated by the
            process workers.
    """
    nb_shutdown_processes = [0]
    def shutdown_one_process():
        """Tell a worker to terminate, which will in turn wake us again"""
        call_queue.put(None)
        nb_shutdown_processes[0] += 1
    while True:
        _add_call_item_to_queue(pending_work_items,
                                work_ids_queue,
                                call_queue)

        result_item = result_queue.get(block=True)
        if result_item is not None:
            work_item = pending_work_items[result_item.work_id]
            del pending_work_items[result_item.work_id]

            if result_item.exception:
                work_item.future.set_exception(result_item.exception)
            else:
                work_item.future.set_result(result_item.result)
            # Delete references to object. See issue16284
            del work_item
        # Check whether we should start shutting down.
        executor = executor_reference()
        # No more work items can be added if:
        #   - The interpreter is shutting down OR
        #   - The executor that owns this worker has been collected OR
        #   - The executor that owns this worker has been shutdown.
        if _shutdown or executor is None or executor._shutdown_thread:
            # Since no new work items can be added, it is safe to shutdown
            # this thread if there are no pending work items.
            if not pending_work_items:
                while nb_shutdown_processes[0] < len(processes):
                    shutdown_one_process()
                # If .join() is not called on the created processes then
                # some multiprocessing.Queue methods may deadlock on Mac OS
                # X.
                for p in processes:
                    p.join()
                call_queue.close()
                return
        del executor

_system_limits_checked = False
_system_limited = None
def _check_system_limits():
    global _system_limits_checked, _system_limited
    if _system_limits_checked:
        if _system_limited:
            raise NotImplementedError(_system_limited)
    _system_limits_checked = True
    try:
        import os
        nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
    except (AttributeError, ValueError):
        # sysconf not available or setting not available
        return
    if nsems_max == -1:
        # indetermine limit, assume that limit is determined
        # by available memory only
        return
    if nsems_max >= 256:
        # minimum number of semaphores available
        # according to POSIX
        return
    _system_limited = "system provides too few semaphores (%d available, 256 necessary)" % nsems_max
    raise NotImplementedError(_system_limited)


class ProcessPoolExecutor(_base.Executor):
    def __init__(self, max_workers=None):
        """Initializes a new ProcessPoolExecutor instance.

        Args:
            max_workers: The maximum number of processes that can be used to
                execute the given calls. If None or not given then as many
                worker processes will be created as the machine has processors.
        """
        _check_system_limits()

        if max_workers is None:
            self._max_workers = multiprocessing.cpu_count()
        else:
            if max_workers <= 0:
                raise ValueError("max_workers must be greater than 0")

            self._max_workers = max_workers

        # Make the call queue slightly larger than the number of processes to
        # prevent the worker processes from idling. But don't make it too big
        # because futures in the call queue cannot be cancelled.
        self._call_queue = multiprocessing.Queue(self._max_workers +
                                                 EXTRA_QUEUED_CALLS)
        self._result_queue = multiprocessing.Queue()
        self._work_ids = queue.Queue()
        self._queue_management_thread = None
        self._processes = set()

        # Shutdown is a two-step process.
        self._shutdown_thread = False
        self._shutdown_lock = threading.Lock()
        self._queue_count = 0
        self._pending_work_items = {}

    def _start_queue_management_thread(self):
        # When the executor gets lost, the weakref callback will wake up
        # the queue management thread.
        def weakref_cb(_, q=self._result_queue):
            q.put(None)
        if self._queue_management_thread is None:
            self._queue_management_thread = threading.Thread(
                    target=_queue_management_worker,
                    args=(weakref.ref(self, weakref_cb),
                          self._processes,
                          self._pending_work_items,
                          self._work_ids,
                          self._call_queue,
                          self._result_queue))
            self._queue_management_thread.daemon = True
            self._queue_management_thread.start()
            _threads_queues[self._queue_management_thread] = self._result_queue

    def _adjust_process_count(self):
        for _ in range(len(self._processes), self._max_workers):
            p = multiprocessing.Process(
                    target=_process_worker,
                    args=(self._call_queue,
                          self._result_queue))
            p.start()
            self._processes.add(p)

    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._shutdown_thread:
                raise RuntimeError('cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._pending_work_items[self._queue_count] = w
            self._work_ids.put(self._queue_count)
            self._queue_count += 1
            # Wake up queue management thread
            self._result_queue.put(None)

            self._start_queue_management_thread()
            self._adjust_process_count()
            return f
    submit.__doc__ = _base.Executor.submit.__doc__

    def shutdown(self, wait=True):
        with self._shutdown_lock:
            self._shutdown_thread = True
        if self._queue_management_thread:
            # Wake up queue management thread
            self._result_queue.put(None)
            if wait:
                self._queue_management_thread.join(sys.maxint)
        # To reduce the risk of openning too many files, remove references to
        # objects that use file descriptors.
        self._queue_management_thread = None
        self._call_queue = None
        self._result_queue = None
        self._processes = None
    shutdown.__doc__ = _base.Executor.shutdown.__doc__

atexit.register(_python_exit)
  0707010001f446000081a40000000000000000000000016a100daf00000377000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/foreign/concurrent/futures/__init__.py # Copyright 2009 Brian Quinlan. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

"""Execute computations asynchronously using threads or processes."""

__author__ = 'Brian Quinlan (brian@sweetapp.com)'

from concurrent.futures._base import (FIRST_COMPLETED,
                                      FIRST_EXCEPTION,
                                      ALL_COMPLETED,
                                      CancelledError,
                                      TimeoutError,
                                      Future,
                                      Executor,
                                      wait,
                                      as_completed)
from concurrent.futures.thread import ThreadPoolExecutor

try:
    from concurrent.futures.process import ProcessPoolExecutor
except ImportError:
    # some platforms don't have multiprocessing
    pass
 0707010001f447000081a40000000000000000000000016a100daf00005d79000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/foreign/concurrent/futures/_base.py    # Copyright 2009 Brian Quinlan. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

import collections
import logging
import threading
import itertools
import time
import types

__author__ = 'Brian Quinlan (brian@sweetapp.com)'

FIRST_COMPLETED = 'FIRST_COMPLETED'
FIRST_EXCEPTION = 'FIRST_EXCEPTION'
ALL_COMPLETED = 'ALL_COMPLETED'
_AS_COMPLETED = '_AS_COMPLETED'

# Possible future states (for internal use by the futures package).
PENDING = 'PENDING'
RUNNING = 'RUNNING'
# The future was cancelled by the user...
CANCELLED = 'CANCELLED'
# ...and _Waiter.add_cancelled() was called by a worker.
CANCELLED_AND_NOTIFIED = 'CANCELLED_AND_NOTIFIED'
FINISHED = 'FINISHED'

_FUTURE_STATES = [
    PENDING,
    RUNNING,
    CANCELLED,
    CANCELLED_AND_NOTIFIED,
    FINISHED
]

_STATE_TO_DESCRIPTION_MAP = {
    PENDING: "pending",
    RUNNING: "running",
    CANCELLED: "cancelled",
    CANCELLED_AND_NOTIFIED: "cancelled",
    FINISHED: "finished"
}

# Logger for internal use by the futures package.
LOGGER = logging.getLogger("concurrent.futures")

class Error(Exception):
    """Base class for all future-related exceptions."""
    pass

class CancelledError(Error):
    """The Future was cancelled."""
    pass

class TimeoutError(Error):
    """The operation exceeded the given deadline."""
    pass

class _Waiter(object):
    """Provides the event that wait() and as_completed() block on."""
    def __init__(self):
        self.event = threading.Event()
        self.finished_futures = []

    def add_result(self, future):
        self.finished_futures.append(future)

    def add_exception(self, future):
        self.finished_futures.append(future)

    def add_cancelled(self, future):
        self.finished_futures.append(future)

class _AsCompletedWaiter(_Waiter):
    """Used by as_completed()."""

    def __init__(self):
        super(_AsCompletedWaiter, self).__init__()
        self.lock = threading.Lock()

    def add_result(self, future):
        with self.lock:
            super(_AsCompletedWaiter, self).add_result(future)
            self.event.set()

    def add_exception(self, future):
        with self.lock:
            super(_AsCompletedWaiter, self).add_exception(future)
            self.event.set()

    def add_cancelled(self, future):
        with self.lock:
            super(_AsCompletedWaiter, self).add_cancelled(future)
            self.event.set()

class _FirstCompletedWaiter(_Waiter):
    """Used by wait(return_when=FIRST_COMPLETED)."""

    def add_result(self, future):
        super(_FirstCompletedWaiter, self).add_result(future)
        self.event.set()

    def add_exception(self, future):
        super(_FirstCompletedWaiter, self).add_exception(future)
        self.event.set()

    def add_cancelled(self, future):
        super(_FirstCompletedWaiter, self).add_cancelled(future)
        self.event.set()

class _AllCompletedWaiter(_Waiter):
    """Used by wait(return_when=FIRST_EXCEPTION and ALL_COMPLETED)."""

    def __init__(self, num_pending_calls, stop_on_exception):
        self.num_pending_calls = num_pending_calls
        self.stop_on_exception = stop_on_exception
        self.lock = threading.Lock()
        super(_AllCompletedWaiter, self).__init__()

    def _decrement_pending_calls(self):
        with self.lock:
            self.num_pending_calls -= 1
            if not self.num_pending_calls:
                self.event.set()

    def add_result(self, future):
        super(_AllCompletedWaiter, self).add_result(future)
        self._decrement_pending_calls()

    def add_exception(self, future):
        super(_AllCompletedWaiter, self).add_exception(future)
        if self.stop_on_exception:
            self.event.set()
        else:
            self._decrement_pending_calls()

    def add_cancelled(self, future):
        super(_AllCompletedWaiter, self).add_cancelled(future)
        self._decrement_pending_calls()

class _AcquireFutures(object):
    """A context manager that does an ordered acquire of Future conditions."""

    def __init__(self, futures):
        self.futures = sorted(futures, key=id)

    def __enter__(self):
        for future in self.futures:
            future._condition.acquire()

    def __exit__(self, *args):
        for future in self.futures:
            future._condition.release()

def _create_and_install_waiters(fs, return_when):
    if return_when == _AS_COMPLETED:
        waiter = _AsCompletedWaiter()
    elif return_when == FIRST_COMPLETED:
        waiter = _FirstCompletedWaiter()
    else:
        pending_count = sum(
                f._state not in [CANCELLED_AND_NOTIFIED, FINISHED] for f in fs)

        if return_when == FIRST_EXCEPTION:
            waiter = _AllCompletedWaiter(pending_count, stop_on_exception=True)
        elif return_when == ALL_COMPLETED:
            waiter = _AllCompletedWaiter(pending_count, stop_on_exception=False)
        else:
            raise ValueError("Invalid return condition: %r" % return_when)

    for f in fs:
        f._waiters.append(waiter)

    return waiter


def _yield_finished_futures(fs, waiter, ref_collect):
    """
    Iterate on the list *fs*, yielding finished futures one by one in
    reverse order.
    Before yielding a future, *waiter* is removed from its waiters
    and the future is removed from each set in the collection of sets
    *ref_collect*.

    The aim of this function is to avoid keeping stale references after
    the future is yielded and before the iterator resumes.
    """
    while fs:
        f = fs[-1]
        for futures_set in ref_collect:
            futures_set.remove(f)
        with f._condition:
            f._waiters.remove(waiter)
        del f
        # Careful not to keep a reference to the popped value
        yield fs.pop()


def as_completed(fs, timeout=None):
    """An iterator over the given futures that yields each as it completes.

    Args:
        fs: The sequence of Futures (possibly created by different Executors) to
            iterate over.
        timeout: The maximum number of seconds to wait. If None, then there
            is no limit on the wait time.

    Returns:
        An iterator that yields the given Futures as they complete (finished or
        cancelled). If any given Futures are duplicated, they will be returned
        once.

    Raises:
        TimeoutError: If the entire result iterator could not be generated
            before the given timeout.
    """
    if timeout is not None:
        end_time = timeout + time.time()

    fs = set(fs)
    total_futures = len(fs)
    with _AcquireFutures(fs):
        finished = set(
                f for f in fs
                if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
        pending = fs - finished
        waiter = _create_and_install_waiters(fs, _AS_COMPLETED)
    finished = list(finished)
    try:
        for f in _yield_finished_futures(finished, waiter,
                                         ref_collect=(fs,)):
            f = [f]
            yield f.pop()

        while pending:
            if timeout is None:
                wait_timeout = None
            else:
                wait_timeout = end_time - time.time()
                if wait_timeout < 0:
                    raise TimeoutError(
                            '%d (of %d) futures unfinished' % (
                            len(pending), total_futures))

            waiter.event.wait(wait_timeout)

            with waiter.lock:
                finished = waiter.finished_futures
                waiter.finished_futures = []
                waiter.event.clear()

            # reverse to keep finishing order
            finished.reverse()
            for f in _yield_finished_futures(finished, waiter,
                                             ref_collect=(fs, pending)):
                f = [f]
                yield f.pop()

    finally:
        # Remove waiter from unfinished futures
        for f in fs:
            with f._condition:
                f._waiters.remove(waiter)

DoneAndNotDoneFutures = collections.namedtuple(
        'DoneAndNotDoneFutures', 'done not_done')
def wait(fs, timeout=None, return_when=ALL_COMPLETED):
    """Wait for the futures in the given sequence to complete.

    Args:
        fs: The sequence of Futures (possibly created by different Executors) to
            wait upon.
        timeout: The maximum number of seconds to wait. If None, then there
            is no limit on the wait time.
        return_when: Indicates when this function should return. The options
            are:

            FIRST_COMPLETED - Return when any future finishes or is
                              cancelled.
            FIRST_EXCEPTION - Return when any future finishes by raising an
                              exception. If no future raises an exception
                              then it is equivalent to ALL_COMPLETED.
            ALL_COMPLETED -   Return when all futures finish or are cancelled.

    Returns:
        A named 2-tuple of sets. The first set, named 'done', contains the
        futures that completed (is finished or cancelled) before the wait
        completed. The second set, named 'not_done', contains uncompleted
        futures.
    """
    with _AcquireFutures(fs):
        done = set(f for f in fs
                   if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
        not_done = set(fs) - done

        if (return_when == FIRST_COMPLETED) and done:
            return DoneAndNotDoneFutures(done, not_done)
        elif (return_when == FIRST_EXCEPTION) and done:
            if any(f for f in done
                   if not f.cancelled() and f.exception() is not None):
                return DoneAndNotDoneFutures(done, not_done)

        if len(done) == len(fs):
            return DoneAndNotDoneFutures(done, not_done)

        waiter = _create_and_install_waiters(fs, return_when)

    waiter.event.wait(timeout)
    for f in fs:
        with f._condition:
            f._waiters.remove(waiter)

    done.update(waiter.finished_futures)
    return DoneAndNotDoneFutures(done, set(fs) - done)

class Future(object):
    """Represents the result of an asynchronous computation."""

    def __init__(self):
        """Initializes the future. Should not be called by clients."""
        self._condition = threading.Condition()
        self._state = PENDING
        self._result = None
        self._exception = None
        self._traceback = None
        self._waiters = []
        self._done_callbacks = []

    def _invoke_callbacks(self):
        for callback in self._done_callbacks:
            try:
                callback(self)
            except Exception:
                LOGGER.exception('exception calling callback for %r', self)
            except BaseException:
                # Explicitly let all other new-style exceptions through so
                # that we can catch all old-style exceptions with a simple
                # "except:" clause below.
                #
                # All old-style exception objects are instances of
                # types.InstanceType, but "except types.InstanceType:" does
                # not catch old-style exceptions for some reason.  Thus, the
                # only way to catch all old-style exceptions without catching
                # any new-style exceptions is to filter out the new-style
                # exceptions, which all derive from BaseException.
                raise
            except:
                # Because of the BaseException clause above, this handler only
                # executes for old-style exception objects.
                LOGGER.exception('exception calling callback for %r', self)

    def __repr__(self):
        with self._condition:
            if self._state == FINISHED:
                if self._exception:
                    return '<%s at %#x state=%s raised %s>' % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[self._state],
                        self._exception.__class__.__name__)
                else:
                    return '<%s at %#x state=%s returned %s>' % (
                        self.__class__.__name__,
                        id(self),
                        _STATE_TO_DESCRIPTION_MAP[self._state],
                        self._result.__class__.__name__)
            return '<%s at %#x state=%s>' % (
                    self.__class__.__name__,
                    id(self),
                   _STATE_TO_DESCRIPTION_MAP[self._state])

    def cancel(self):
        """Cancel the future if possible.

        Returns True if the future was cancelled, False otherwise. A future
        cannot be cancelled if it is running or has already completed.
        """
        with self._condition:
            if self._state in [RUNNING, FINISHED]:
                return False

            if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                return True

            self._state = CANCELLED
            self._condition.notify_all()

        self._invoke_callbacks()
        return True

    def cancelled(self):
        """Return True if the future was cancelled."""
        with self._condition:
            return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]

    def running(self):
        """Return True if the future is currently executing."""
        with self._condition:
            return self._state == RUNNING

    def done(self):
        """Return True of the future was cancelled or finished executing."""
        with self._condition:
            return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]

    def __get_result(self):
        if self._exception:
            if isinstance(self._exception, types.InstanceType):
                # The exception is an instance of an old-style class, which
                # means type(self._exception) returns types.ClassType instead
                # of the exception's actual class type.
                exception_type = self._exception.__class__
            else:
                exception_type = type(self._exception)
            raise exception_type, self._exception, self._traceback
        else:
            return self._result

    def add_done_callback(self, fn):
        """Attaches a callable that will be called when the future finishes.

        Args:
            fn: A callable that will be called with this future as its only
                argument when the future completes or is cancelled. The callable
                will always be called by a thread in the same process in which
                it was added. If the future has already completed or been
                cancelled then the callable will be called immediately. These
                callables are called in the order that they were added.
        """
        with self._condition:
            if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]:
                self._done_callbacks.append(fn)
                return
        fn(self)

    def result(self, timeout=None):
        """Return the result of the call that the future represents.

        Args:
            timeout: The number of seconds to wait for the result if the future
                isn't done. If None, then there is no limit on the wait time.

        Returns:
            The result of the call that the future represents.

        Raises:
            CancelledError: If the future was cancelled.
            TimeoutError: If the future didn't finish executing before the given
                timeout.
            Exception: If the call raised then that exception will be raised.
        """
        with self._condition:
            if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                raise CancelledError()
            elif self._state == FINISHED:
                return self.__get_result()

            self._condition.wait(timeout)

            if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                raise CancelledError()
            elif self._state == FINISHED:
                return self.__get_result()
            else:
                raise TimeoutError()

    def exception_info(self, timeout=None):
        """Return a tuple of (exception, traceback) raised by the call that the
        future represents.

        Args:
            timeout: The number of seconds to wait for the exception if the
                future isn't done. If None, then there is no limit on the wait
                time.

        Returns:
            The exception raised by the call that the future represents or None
            if the call completed without raising.

        Raises:
            CancelledError: If the future was cancelled.
            TimeoutError: If the future didn't finish executing before the given
                timeout.
        """
        with self._condition:
            if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                raise CancelledError()
            elif self._state == FINISHED:
                return self._exception, self._traceback

            self._condition.wait(timeout)

            if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
                raise CancelledError()
            elif self._state == FINISHED:
                return self._exception, self._traceback
            else:
                raise TimeoutError()

    def exception(self, timeout=None):
        """Return the exception raised by the call that the future represents.

        Args:
            timeout: The number of seconds to wait for the exception if the
                future isn't done. If None, then there is no limit on the wait
                time.

        Returns:
            The exception raised by the call that the future represents or None
            if the call completed without raising.

        Raises:
            CancelledError: If the future was cancelled.
            TimeoutError: If the future didn't finish executing before the given
                timeout.
        """
        return self.exception_info(timeout)[0]

    # The following methods should only be used by Executors and in tests.
    def set_running_or_notify_cancel(self):
        """Mark the future as running or process any cancel notifications.

        Should only be used by Executor implementations and unit tests.

        If the future has been cancelled (cancel() was called and returned
        True) then any threads waiting on the future completing (though calls
        to as_completed() or wait()) are notified and False is returned.

        If the future was not cancelled then it is put in the running state
        (future calls to running() will return True) and True is returned.

        This method should be called by Executor implementations before
        executing the work associated with this future. If this method returns
        False then the work should not be executed.

        Returns:
            False if the Future was cancelled, True otherwise.

        Raises:
            RuntimeError: if this method was already called or if set_result()
                or set_exception() was called.
        """
        with self._condition:
            if self._state == CANCELLED:
                self._state = CANCELLED_AND_NOTIFIED
                for waiter in self._waiters:
                    waiter.add_cancelled(self)
                # self._condition.notify_all() is not necessary because
                # self.cancel() triggers a notification.
                return False
            elif self._state == PENDING:
                self._state = RUNNING
                return True
            else:
                LOGGER.critical('Future %s in unexpected state: %s',
                                id(self),
                                self._state)
                raise RuntimeError('Future in unexpected state')

    def set_result(self, result):
        """Sets the return value of work associated with the future.

        Should only be used by Executor implementations and unit tests.
        """
        with self._condition:
            self._result = result
            self._state = FINISHED
            for waiter in self._waiters:
                waiter.add_result(self)
            self._condition.notify_all()
        self._invoke_callbacks()

    def set_exception_info(self, exception, traceback):
        """Sets the result of the future as being the given exception
        and traceback.

        Should only be used by Executor implementations and unit tests.
        """
        with self._condition:
            self._exception = exception
            self._traceback = traceback
            self._state = FINISHED
            for waiter in self._waiters:
                waiter.add_exception(self)
            self._condition.notify_all()
        self._invoke_callbacks()

    def set_exception(self, exception):
        """Sets the result of the future as being the given exception.

        Should only be used by Executor implementations and unit tests.
        """
        self.set_exception_info(exception, None)

class Executor(object):
    """This is an abstract base class for concrete asynchronous executors."""

    def submit(self, fn, *args, **kwargs):
        """Submits a callable to be executed with the given arguments.

        Schedules the callable to be executed as fn(*args, **kwargs) and returns
        a Future instance representing the execution of the callable.

        Returns:
            A Future representing the given call.
        """
        raise NotImplementedError()

    def map(self, fn, *iterables, **kwargs):
        """Returns an iterator equivalent to map(fn, iter).

        Args:
            fn: A callable that will take as many arguments as there are
                passed iterables.
            timeout: The maximum number of seconds to wait. If None, then there
                is no limit on the wait time.

        Returns:
            An iterator equivalent to: map(func, *iterables) but the calls may
            be evaluated out-of-order.

        Raises:
            TimeoutError: If the entire result iterator could not be generated
                before the given timeout.
            Exception: If fn(*args) raises for any values.
        """
        timeout = kwargs.get('timeout')
        if timeout is not None:
            end_time = timeout + time.time()

        fs = [self.submit(fn, *args) for args in itertools.izip(*iterables)]

        # Yield must be hidden in closure so that the futures are submitted
        # before the first iterator value is required.
        def result_iterator():
            try:
                # reverse to keep finishing order
                fs.reverse()
                while fs:
                    # Careful not to keep a reference to the popped future
                    if timeout is None:
                        yield fs.pop().result()
                    else:
                        yield fs.pop().result(end_time - time.time())
            finally:
                for future in fs:
                    future.cancel()
        return result_iterator()

    def shutdown(self, wait=True):
        """Clean-up the resources associated with the Executor.

        It is safe to call this method several times. Otherwise, no other
        methods can be called after this one.

        Args:
            wait: If True then shutdown will not return until all running
                futures have finished executing and the resources used by the
                executor have been reclaimed.
        """
        pass

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.shutdown(wait=True)
        return False


class BrokenExecutor(RuntimeError):
    """
    Raised when a executor has become non-functional after a severe failure.
    """
   0707010001f449000081a40000000000000000000000016a100daf00001c3d000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/foreign/concurrent/futures/thread.py   # Copyright 2009 Brian Quinlan. All Rights Reserved.
# Licensed to PSF under a Contributor Agreement.

"""Implements ThreadPoolExecutor."""

import atexit
from concurrent.futures import _base
import itertools
import Queue as queue
import threading
import weakref
import sys

try:
    from multiprocessing import cpu_count
except ImportError:
    # some platforms don't have multiprocessing
    def cpu_count():
        return None

__author__ = 'Brian Quinlan (brian@sweetapp.com)'

# Workers are created as daemon threads. This is done to allow the interpreter
# to exit when there are still idle threads in a ThreadPoolExecutor's thread
# pool (i.e. shutdown() was not called). However, allowing workers to die with
# the interpreter has two undesirable properties:
#   - The workers would still be running during interpretor shutdown,
#     meaning that they would fail in unpredictable ways.
#   - The workers could be killed while evaluating a work item, which could
#     be bad if the callable being evaluated has external side-effects e.g.
#     writing to a file.
#
# To work around this problem, an exit handler is installed which tells the
# workers to exit when their work queues are empty and then waits until the
# threads finish.

_threads_queues = weakref.WeakKeyDictionary()
_shutdown = False

def _python_exit():
    global _shutdown
    _shutdown = True
    items = list(_threads_queues.items()) if _threads_queues else ()
    for t, q in items:
        q.put(None)
    for t, q in items:
        t.join(sys.maxint)

atexit.register(_python_exit)

class _WorkItem(object):
    def __init__(self, future, fn, args, kwargs):
        self.future = future
        self.fn = fn
        self.args = args
        self.kwargs = kwargs

    def run(self):
        if not self.future.set_running_or_notify_cancel():
            return

        try:
            result = self.fn(*self.args, **self.kwargs)
        except:
            e, tb = sys.exc_info()[1:]
            self.future.set_exception_info(e, tb)
        else:
            self.future.set_result(result)

def _worker(executor_reference, work_queue, initializer, initargs):
    if initializer is not None:
        try:
            initializer(*initargs)
        except BaseException:
            _base.LOGGER.critical('Exception in initializer:', exc_info=True)
            executor = executor_reference()
            if executor is not None:
                executor._initializer_failed()
            return
    try:
        while True:
            work_item = work_queue.get(block=True)
            if work_item is not None:
                work_item.run()
                # Delete references to object. See issue16284
                del work_item

                # attempt to increment idle count
                executor = executor_reference()
                if executor is not None:
                    executor._idle_semaphore.release()
                del executor
                continue
            executor = executor_reference()
            # Exit if:
            #   - The interpreter is shutting down OR
            #   - The executor that owns the worker has been collected OR
            #   - The executor that owns the worker has been shutdown.
            if _shutdown or executor is None or executor._shutdown:
                # Notice other workers
                work_queue.put(None)
                return
            del executor
    except:
        _base.LOGGER.critical('Exception in worker', exc_info=True)


class BrokenThreadPool(_base.BrokenExecutor):
    """
    Raised when a worker thread in a ThreadPoolExecutor failed initializing.
    """


class ThreadPoolExecutor(_base.Executor):

    # Used to assign unique thread names when thread_name_prefix is not supplied.
    _counter = itertools.count().next

    def __init__(self, max_workers=None, thread_name_prefix='', initializer=None, initargs=()):
        """Initializes a new ThreadPoolExecutor instance.

        Args:
            max_workers: The maximum number of threads that can be used to
                execute the given calls.
            thread_name_prefix: An optional name prefix to give our threads.
        """
        if max_workers is None:
            # Use this number because ThreadPoolExecutor is often
            # used to overlap I/O instead of CPU work.
            max_workers = (cpu_count() or 1) * 5
        if max_workers <= 0:
            raise ValueError("max_workers must be greater than 0")

        self._max_workers = max_workers
        self._initializer = initializer
        self._initargs = initargs
        self._work_queue = queue.Queue()
        self._idle_semaphore = threading.Semaphore(0)
        self._threads = set()
        self._broken = False
        self._shutdown = False
        self._shutdown_lock = threading.Lock()
        self._thread_name_prefix = (thread_name_prefix or
                                    ("ThreadPoolExecutor-%d" % self._counter()))

    def submit(self, fn, *args, **kwargs):
        with self._shutdown_lock:
            if self._broken:
                raise BrokenThreadPool(self._broken)
            if self._shutdown:
                raise RuntimeError('cannot schedule new futures after shutdown')

            f = _base.Future()
            w = _WorkItem(f, fn, args, kwargs)

            self._work_queue.put(w)
            self._adjust_thread_count()
            return f
    submit.__doc__ = _base.Executor.submit.__doc__

    def _adjust_thread_count(self):
        # if idle threads are available, don't spin new threads
        if self._idle_semaphore.acquire(False):
            return

        # When the executor gets lost, the weakref callback will wake up
        # the worker threads.
        def weakref_cb(_, q=self._work_queue):
            q.put(None)

        num_threads = len(self._threads)
        if num_threads < self._max_workers:
            thread_name = '%s_%d' % (self._thread_name_prefix or self,
                                     num_threads)
            t = threading.Thread(name=thread_name, target=_worker,
                                 args=(weakref.ref(self, weakref_cb),
                                       self._work_queue, self._initializer, self._initargs))
            t.daemon = True
            t.start()
            self._threads.add(t)
            _threads_queues[t] = self._work_queue

    def _initializer_failed(self):
        with self._shutdown_lock:
            self._broken = ('A thread initializer failed, the thread pool '
                            'is not usable anymore')
            # Drain work queue and mark pending futures failed
            while True:
                try:
                    work_item = self._work_queue.get_nowait()
                except queue.Empty:
                    break
                if work_item is not None:
                    work_item.future.set_exception(BrokenThreadPool(self._broken))

    def shutdown(self, wait=True):
        with self._shutdown_lock:
            self._shutdown = True
            self._work_queue.put(None)
        if wait:
            for t in self._threads:
                t.join(sys.maxint)
    shutdown.__doc__ = _base.Executor.shutdown.__doc__
   0707010001f460000041ed0000000000000000000000056a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/foreign/hyper  0707010001f461000081a40000000000000000000000016a100daf0000037b000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/hyper/__init__.py  # -*- coding: utf-8 -*-
"""
hyper
~~~~~~

A module for providing an abstraction layer over the differences between
HTTP/1.1 and HTTP/2.
"""
import logging

from .common.connection import HTTPConnection
from .http20.connection import HTTP20Connection
from .http20.response import HTTP20Response, HTTP20Push
from .http11.connection import HTTP11Connection
from .http11.response import HTTP11Response

# Throw import errors on Python <2.7 and 3.0-3.2.
import sys as _sys
if _sys.version_info < (2, 7) or (3, 0) <= _sys.version_info < (3, 3):
    raise ImportError(
        "hyper only supports Python 2.7 and Python 3.3 or higher."
    )

__all__ = [
    HTTPConnection,
    HTTP20Response,
    HTTP20Push,
    HTTP20Connection,
    HTTP11Connection,
    HTTP11Response,
]

# Set default logging handler.
logging.getLogger(__name__).addHandler(logging.NullHandler())

__version__ = '0.8.0dev0'
 0707010001f46e000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/hyper/http11   0707010001f46f000081a40000000000000000000000016a100daf00000065000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyper/http11/__init__.py   # -*- coding: utf-8 -*-
"""
hyper/http11
~~~~~~~~~~~~

The HTTP/1.1 submodule that powers hyper.
"""
   0707010001f470000081a40000000000000000000000016a100daf00004269000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hyper/http11/connection.py # -*- coding: utf-8 -*-
"""
hyper/http11/connection
~~~~~~~~~~~~~~~~~~~~~~~

Objects that build hyper's connection-level HTTP/1.1 abstraction.
"""
import logging
import os
import socket
import base64

try:
    from collections.abc import Iterable, Mapping
except ImportError:
    from collections import Iterable, Mapping

import collections
from hyperframe.frame import SettingsFrame

from .response import HTTP11Response
from ..tls import wrap_socket, H2C_PROTOCOL
from ..common.bufsocket import BufferedSocket
from ..common.exceptions import TLSUpgrade, HTTPUpgrade, ProxyError
from ..common.headers import HTTPHeaderMap
from ..common.util import (
    to_bytestring, to_host_port_tuple, to_native_string, HTTPVersion
)
from ..compat import bytes

# We prefer pycohttpparser to the pure-Python interpretation
try:  # pragma: no cover
    from pycohttpparser.api import Parser
except ImportError:  # pragma: no cover
    from .parser import Parser


log = logging.getLogger(__name__)

BODY_CHUNKED = 1
BODY_FLAT = 2


def _create_tunnel(proxy_host, proxy_port, target_host, target_port,
                   proxy_headers=None, timeout=None):
    """
    Sends CONNECT method to a proxy and returns a socket with established
    connection to the target.

    :returns: socket
    """
    conn = HTTP11Connection(proxy_host, proxy_port, timeout=timeout)
    conn.request('CONNECT', '%s:%d' % (target_host, target_port),
                 headers=proxy_headers)

    resp = conn.get_response()
    if resp.status != 200:
        raise ProxyError(
            "Tunnel connection failed: %d %s" %
            (resp.status, to_native_string(resp.reason)),
            response=resp
        )
    return conn._sock


def _headers_to_http_header_map(headers):
    # TODO turn this to a classmethod of HTTPHeaderMap
    headers = headers or {}
    if not isinstance(headers, HTTPHeaderMap):
        if isinstance(headers, Mapping):
            headers = HTTPHeaderMap(headers.items())
        elif isinstance(headers, Iterable):
            headers = HTTPHeaderMap(headers)
        else:
            raise ValueError(
                'Header argument must be a dictionary or an iterable'
            )
    return headers


class HTTP11Connection(object):
    """
    An object representing a single HTTP/1.1 connection to a server.

    :param host: The host to connect to. This may be an IP address or a
        hostname, and optionally may include a port: for example,
        ``'twitter.com'``, ``'twitter.com:443'`` or ``'127.0.0.1'``.
    :param port: (optional) The port to connect to. If not provided and one
        also isn't provided in the ``host`` parameter, defaults to 80.
    :param secure: (optional) Whether the request should use TLS. Defaults to
        ``False`` for most requests, but to ``True`` for any request issued to
        port 443.
    :param ssl_context: (optional) A class with custom certificate settings.
        If not provided then hyper's default ``SSLContext`` is used instead.
    :param proxy_host: (optional) The proxy to connect to.  This can be an IP
        address or a host name and may include a port.
    :param proxy_port: (optional) The proxy port to connect to. If not provided
        and one also isn't provided in the ``proxy_host`` parameter,
        defaults to 8080.
    :param proxy_headers: (optional) The headers to send to a proxy.
    """

    version = HTTPVersion.http11

    def __init__(self, host, port=None, secure=None, ssl_context=None,
                 proxy_host=None, proxy_port=None, proxy_headers=None,
                 timeout=None, **kwargs):
        if port is None:
            self.host, self.port = to_host_port_tuple(host, default_port=80)
        else:
            self.host, self.port = host, port

        # Record whether we plan to secure the request. In future this should
        # be extended to a security profile, but a bool will do for now.
        # TODO: Actually do something with this!
        if secure is not None:
            self.secure = secure
        elif self.port == 443:
            self.secure = True
        else:
            self.secure = False

        # only send http upgrade headers for non-secure connection
        self._send_http_upgrade = not self.secure
        self._enable_push = kwargs.get('enable_push')

        self.ssl_context = ssl_context
        self._sock = None

        # Keep the current request method in order to be able to know
        # in get_response() what was the request verb.
        self._current_request_method = None

        # Setup proxy details if applicable.
        if proxy_host and proxy_port is None:
            self.proxy_host, self.proxy_port = to_host_port_tuple(
                proxy_host, default_port=8080
            )
        elif proxy_host:
            self.proxy_host, self.proxy_port = proxy_host, proxy_port
        else:
            self.proxy_host = None
            self.proxy_port = None
        self.proxy_headers = proxy_headers

        #: The size of the in-memory buffer used to store data from the
        #: network. This is used as a performance optimisation. Increase buffer
        #: size to improve performance: decrease it to conserve memory.
        #: Defaults to 64kB.
        self.network_buffer_size = 65536

        #: The object used to perform HTTP/1.1 parsing. Needs to conform to
        #: the standard hyper parsing interface.
        self.parser = Parser()

        # timeout
        self._timeout = timeout

    def connect(self):
        """
        Connect to the server specified when the object was created. This is a
        no-op if we're already connected.

        :returns: Nothing.
        """
        if self._sock is None:

            if isinstance(self._timeout, tuple):
                connect_timeout = self._timeout[0]
                read_timeout = self._timeout[1]
            else:
                connect_timeout = self._timeout
                read_timeout = self._timeout

            if self.proxy_host and self.secure:
                # Send http CONNECT method to a proxy and acquire the socket
                sock = _create_tunnel(
                    self.proxy_host,
                    self.proxy_port,
                    self.host,
                    self.port,
                    proxy_headers=self.proxy_headers,
                    timeout=self._timeout
                )
            elif self.proxy_host:
                # Simple http proxy
                sock = socket.create_connection(
                    (self.proxy_host, self.proxy_port),
                    timeout=connect_timeout
                )
            else:
                sock = socket.create_connection((self.host, self.port),
                                                timeout=connect_timeout)
            proto = None

            if self.secure:
                sock, proto = wrap_socket(sock, self.host, self.ssl_context)

            log.debug("Selected protocol: %s", proto)
            sock = BufferedSocket(sock, self.network_buffer_size)

            # Set read timeout
            sock.settimeout(read_timeout)

            if proto not in ('http/1.1', None):
                raise TLSUpgrade(proto, sock)

            self._sock = sock

        return

    def request(self, method, url, body=None, headers=None):
        """
        This will send a request to the server using the HTTP request method
        ``method`` and the selector ``url``. If the ``body`` argument is
        present, it should be string or bytes object of data to send after the
        headers are finished. Strings are encoded as UTF-8. To use other
        encodings, pass a bytes object. The Content-Length header is set to the
        length of the body field.

        :param method: The request method, e.g. ``'GET'``.
        :param url: The URL to contact, e.g. ``'/path/segment'``.
        :param body: (optional) The request body to send. Must be a bytestring,
            an iterable of bytestring, or a file-like object.
        :param headers: (optional) The headers to send on the request.
        :returns: Nothing.
        """

        method = to_bytestring(method)
        is_connect_method = b'CONNECT' == method.upper()
        self._current_request_method = method

        if self.proxy_host and not self.secure:
            # As per https://tools.ietf.org/html/rfc2068#section-5.1.2:
            # The absoluteURI form is required when the request is being made
            # to a proxy.
            url = self._absolute_http_url(url)
        url = to_bytestring(url)

        headers = _headers_to_http_header_map(headers)

        # Append proxy headers.
        if self.proxy_host and not self.secure:
            headers.update(
                _headers_to_http_header_map(self.proxy_headers).items()
            )

        if self._sock is None:
            self.connect()

        if not is_connect_method and self._send_http_upgrade:
            self._add_upgrade_headers(headers)
            self._send_http_upgrade = False

        # We may need extra headers.
        if body:
            body_type = self._add_body_headers(headers, body)

        if not is_connect_method and b'host' not in headers:
            headers[b'host'] = self.host

        # Begin by emitting the header block.
        self._send_headers(method, url, headers)

        # Next, send the request body.
        if body:
            self._send_body(body, body_type)

        return

    def _absolute_http_url(self, url):
        port_part = ':%d' % self.port if self.port != 80 else ''
        return 'http://%s%s%s' % (self.host, port_part, url)

    def get_response(self):
        """
        Returns a response object.

        This is an early beta, so the response object is pretty stupid. That's
        ok, we'll fix it later.
        """
        method = self._current_request_method
        self._current_request_method = None

        headers = HTTPHeaderMap()

        response = None
        while response is None:
            # 'encourage' the socket to receive data.
            self._sock.fill()
            response = self.parser.parse_response(self._sock.buffer)

        for n, v in response.headers:
            headers[n.tobytes()] = v.tobytes()

        self._sock.advance_buffer(response.consumed)

        # Check for a successful "switching protocols to h2c" response.
        # "Connection: upgrade" is not strictly necessary on the receiving end,
        # but we want to fail fast on broken servers or intermediaries:
        # https://github.com/Lukasa/hyper/issues/312.
        # Connection options are case-insensitive, while upgrade tokens are
        # case-sensitive: https://github.com/httpwg/http11bis/issues/8.
        if (response.status == 101 and
                b'upgrade' in map(bytes.lower, headers['connection']) and
                H2C_PROTOCOL.encode('utf-8') in headers['upgrade']):
            raise HTTPUpgrade(H2C_PROTOCOL, self._sock)

        return HTTP11Response(
            response.status,
            response.msg.tobytes(),
            headers,
            self._sock,
            self,
            method
        )

    def _send_headers(self, method, url, headers):
        """
        Handles the logic of sending the header block.
        """
        self._sock.send(b' '.join([method, url, b'HTTP/1.1\r\n']))

        for name, value in headers.iter_raw():
            name, value = to_bytestring(name), to_bytestring(value)
            header = b''.join([name, b': ', value, b'\r\n'])
            self._sock.send(header)

        self._sock.send(b'\r\n')

    def _add_body_headers(self, headers, body):
        """
        Adds any headers needed for sending the request body. This will always
        defer to the user-supplied header content.

        :returns: One of (BODY_CHUNKED, BODY_FLAT), indicating what type of
            request body should be used.
        """
        if b'content-length' in headers:
            return BODY_FLAT

        if b'chunked' in headers.get(b'transfer-encoding', []):
            return BODY_CHUNKED

        # For bytestring bodies we upload the content with a fixed length.
        # For file objects, we use the length of the file object.
        if isinstance(body, bytes):
            length = str(len(body)).encode('utf-8')
        elif hasattr(body, 'fileno'):
            length = str(os.fstat(body.fileno()).st_size).encode('utf-8')
        else:
            length = None

        if length:
            headers[b'content-length'] = length
            return BODY_FLAT

        headers[b'transfer-encoding'] = b'chunked'
        return BODY_CHUNKED

    def _add_upgrade_headers(self, headers):
        # Add HTTP Upgrade headers.
        headers[b'connection'] = b'Upgrade, HTTP2-Settings'
        headers[b'upgrade'] = H2C_PROTOCOL

        # Encode SETTINGS frame payload in Base64 and put into the HTTP-2
        # Settings header.
        http2_settings = SettingsFrame(0)
        http2_settings.settings[SettingsFrame.INITIAL_WINDOW_SIZE] = 65535
        if self._enable_push is not None:
            http2_settings.settings[SettingsFrame.ENABLE_PUSH] = (
                int(self._enable_push)
            )
        encoded_settings = base64.urlsafe_b64encode(
            http2_settings.serialize_body()
        )
        headers[b'HTTP2-Settings'] = encoded_settings.rstrip(b'=')

    def _send_body(self, body, body_type):
        """
        Handles the HTTP/1.1 logic for sending HTTP bodies. This does magical
        different things in different cases.
        """
        if body_type == BODY_FLAT:
            # Special case for files and other 'readable' objects.
            if hasattr(body, 'read'):
                return self._send_file_like_obj(body)

            # Case for bytestrings.
            elif isinstance(body, bytes):
                self._sock.send(body)

                return

            # Iterables that set a specific content length.
            elif isinstance(body, Iterable):
                for item in body:
                    try:
                        self._sock.send(item)
                    except TypeError:
                        raise ValueError(
                            "Elements in iterable body must be bytestrings. "
                            "Illegal element: {}".format(item)
                        )
                return

            else:
                raise ValueError(
                    'Request body must be a bytestring, a file-like object '
                    'returning bytestrings or an iterable of bytestrings. '
                    'Got: {}'.format(type(body))
                )

        # Chunked!
        return self._send_chunked(body)

    def _send_chunked(self, body):
        """
        Handles the HTTP/1.1 logic for sending a chunk-encoded body.
        """
        # Chunked! For chunked bodies we don't special-case, we just iterate
        # over what we have and send stuff out.
        for chunk in body:
            length = '{0:x}'.format(len(chunk)).encode('ascii')

            # For now write this as four 'send' calls. That's probably
            # inefficient, let's come back to it.
            try:
                self._sock.send(length)
                self._sock.send(b'\r\n')
                self._sock.send(chunk)
                self._sock.send(b'\r\n')
            except TypeError:
                raise ValueError(
                    "Iterable bodies must always iterate in bytestrings"
                )

        self._sock.send(b'0\r\n\r\n')
        return

    def _send_file_like_obj(self, fobj):
        """
        Handles streaming a file-like object to the network.
        """
        while True:
            block = fobj.read(16*1024)
            if not block:
                break

            try:
                self._sock.send(block)
            except TypeError:
                raise ValueError(
                    "File-like bodies must return bytestrings. Got: "
                    "{}".format(type(block))
                )

        return

    def close(self):
        """
        Closes the connection. This closes the socket and then abandons the
        reference to it. After calling this method, any outstanding
        :class:`Response <hyper.http11.response.Response>` objects will throw
        exceptions if attempts are made to read their bodies.

        In some cases this method will automatically be called.

        .. warning:: This method should absolutely only be called when you are
                     certain the connection object is no longer needed.
        """
        if self._sock is not None:
            self._sock.close()
        self._sock = None

    # The following two methods are the implementation of the context manager
    # protocol.
    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.close()
        return False  # Never swallow exceptions.
   0707010001f471000081a40000000000000000000000016a100daf00000a24000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hyper/http11/parser.py # -*- coding: utf-8 -*-
"""
hyper/http11/parser
~~~~~~~~~~~~~~~~~~~

This module contains hyper's pure-Python HTTP/1.1 parser. This module defines
an abstraction layer for HTTP/1.1 parsing that allows for dropping in other
modules if needed, in order to obtain speedups on your chosen platform.
"""
from collections import namedtuple


Response = namedtuple(
    'Response', ['status', 'msg', 'minor_version', 'headers', 'consumed']
)


class ParseError(Exception):
    """
    An invalid HTTP message was passed to the parser.
    """
    pass


class Parser(object):
    """
    A single HTTP parser object.
    This object is not thread-safe, and it does maintain state that is shared
    across parsing requests. For this reason, make sure that access to this
    object is synchronized if you use it across multiple threads.
    """
    def __init__(self):
        pass

    def parse_response(self, buffer):
        """
        Parses a single HTTP response from a buffer.
        :param buffer: A ``memoryview`` object wrapping a buffer containing a
            HTTP response.
        :returns: A :class:`Response <hyper.http11.parser.Response>` object, or
            ``None`` if there is not enough data in the buffer.
        """
        # Begin by copying the data out of the buffer. This is necessary
        # because as much as possible we want to use the built-in bytestring
        # methods, rather than looping over the data in Python.
        temp_buffer = buffer.tobytes()

        index = temp_buffer.find(b'\n')
        if index == -1:
            return None

        version, status, reason = (
                temp_buffer[0:index].split(None, 2) + [b''])[:3]
        if not version.startswith(b'HTTP/1.'):
            raise ParseError("Not HTTP/1.X!")

        minor_version = int(version[7:])
        status = int(status)
        reason = memoryview(reason.strip())

        # Chomp the newline.
        index += 1

        # Now, parse the headers out.
        end_index = index
        headers = []

        while True:
            end_index = temp_buffer.find(b'\n', index)
            if end_index == -1:
                return None
            elif (end_index - index) <= 1:
                # Chomp the newline
                end_index += 1
                break

            name, value = temp_buffer[index:end_index].split(b':', 1)
            value = value.strip()
            headers.append((memoryview(name), memoryview(value)))
            index = end_index + 1

        resp = Response(status, reason, minor_version, headers, end_index)
        return resp
0707010001f472000081a40000000000000000000000016a100daf00003252000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyper/http11/response.py   # -*- coding: utf-8 -*-
"""
hyper/http11/response
~~~~~~~~~~~~~~~~~~~~~

Contains the HTTP/1.1 equivalent of the HTTPResponse object defined in
httplib/http.client.
"""
import logging
import weakref
import zlib
#import brotli

from ..common.decoder import DeflateDecoder
from ..common.exceptions import ChunkedDecodeError, InvalidResponseError
from ..common.exceptions import ConnectionResetError
from ..common.util import HTTPVersion

log = logging.getLogger(__name__)


class HTTP11Response(object):
    """
    An ``HTTP11Response`` wraps the HTTP/1.1 response from the server. It
    provides access to the response headers and the entity body. The response
    is an iterable object and can be used in a with statement.
    """

    version = HTTPVersion.http11

    def __init__(self, code, reason, headers, sock, connection=None,
                 request_method=None):
        #: The reason phrase returned by the server.
        self.reason = reason

        #: The status code returned by the server.
        self.status = code

        #: The response headers. These are determined upon creation, assigned
        #: once, and never assigned again.
        self.headers = headers

        #: The response trailers. These are always intially ``None``.
        self.trailers = None

        # The socket this response is being sent over.
        self._sock = sock

        # Whether we expect the connection to be closed. If we do, we don't
        # bother checking for content-length, we just keep reading until
        # we no longer can.
        self._expect_close = False
        if b'close' in self.headers.get(b'connection', []):
            self._expect_close = True

        # The expected length of the body.
        if request_method != b'HEAD':
            try:
                self._length = int(self.headers[b'content-length'][0])
            except KeyError:
                self._length = None
        else:
            self._length = 0

        # Whether we expect a chunked response.
        self._chunked = (
            b'chunked' in self.headers.get(b'transfer-encoding', [])
        )

        # When content-length is absent and response is not chunked,
        # body length is determined by connection closure.
        # https://tools.ietf.org/html/rfc7230#section-3.3.3
        if self._length is None and not self._chunked:
            # 200 response to a CONNECT request means that proxy has connected
            # to the target host and it will start forwarding everything sent
            # from the either side. Thus we must not try to read body of this
            # response. Socket of current connection will be taken over by
            # the code that has sent a CONNECT request.
            if not (request_method is not None and
                    b'CONNECT' == request_method.upper() and
                    code == 200):
                self._expect_close = True

        # This object is used for decompressing gzipped request bodies. Right
        # now we only support gzip because that's all the RFC mandates of us.
        # Later we'll add support for more encodings.
        # This 16 + MAX_WBITS nonsense is to force gzip. See this
        # Stack Overflow answer for more:
        # http://stackoverflow.com/a/2695466/1401686
        if b'gzip' in self.headers.get(b'content-encoding', []):
            self._decompressobj = zlib.decompressobj(16 + zlib.MAX_WBITS)
        elif b'br' in self.headers.get(b'content-encoding', []):
            raise RuntimeError("no decoder for brotli")
#            self._decompressobj = brotli.Decompressor()
        elif b'deflate' in self.headers.get(b'content-encoding', []):
            self._decompressobj = DeflateDecoder()
        else:
            self._decompressobj = None

        # This is a reference that allows for the Response class to tell the
        # parent connection object to throw away its socket object. This is to
        # be used when the connection is genuinely closed, so that the user
        # can keep using the Connection object.
        # Strictly, we take a weakreference to this so that we don't set up a
        # reference cycle.
        if connection is not None:
            self._parent = weakref.ref(connection)
        else:
            self._parent = None

        self._buffered_data = b''
        self._chunker = None

    def read(self, amt=None, decode_content=True):
        """
        Reads the response body, or up to the next ``amt`` bytes.

        :param amt: (optional) The amount of data to read. If not provided, all
            the data will be read from the response.
        :param decode_content: (optional) If ``True``, will transparently
            decode the response data.
        :returns: The read data. Note that if ``decode_content`` is set to
            ``True``, the actual amount of data returned may be different to
            the amount requested.
        """
        # Return early if we've lost our connection.
        if self._sock is None:
            return b''

        if self._chunked:
            return self._normal_read_chunked(amt, decode_content)

        # If we're asked to do a read without a length, we need to read
        # everything. That means either the entire content length, or until the
        # socket is closed, depending.
        if amt is None:
            if self._length is not None:
                amt = self._length
            elif self._expect_close:
                return self._read_expect_closed(decode_content)
            else:  # pragma: no cover
                raise InvalidResponseError(
                    "Response must either have length or Connection: close"
                )

        # Otherwise, we've been asked to do a bounded read. We should read no
        # more than the remaining length, obviously.
        # FIXME: Handle cases without _length
        if self._length is not None:
            amt = min(amt, self._length)

        # Now, issue reads until we read that length. This is to account for
        # the fact that it's possible that we'll be asked to read more than
        # 65kB in one shot.
        to_read = amt
        chunks = []

        # Ideally I'd like this to read 'while to_read', but I want to be
        # defensive against the admittedly unlikely case that the socket
        # returns *more* data than I want.
        while to_read > 0:
            chunk = self._sock.recv(amt).tobytes()

            # If we got an empty read, but were expecting more, the remote end
            # has hung up. Raise an exception if we were expecting more data,
            # but if we were expecting the remote end to close then it's ok.
            if not chunk:
                if self._length is not None or not self._expect_close:
                    self.close(socket_close=True)
                    raise ConnectionResetError("Remote end hung up!")

                break

            to_read -= len(chunk)
            chunks.append(chunk)

        data = b''.join(chunks)
        if self._length is not None:
            self._length -= len(data)

        # If we're at the end of the request, we have some cleaning up to do.
        # Close the stream, and if necessary flush the buffer. Checking that
        # we're at the end is actually obscenely complex: either we've read the
        # full content-length or, if we were expecting a closed connection,
        # we've had a read shorter than the requested amount. We also have to
        # do this before we try to decompress the body.
        end_of_request = (self._length == 0 or
                          (self._expect_close and len(data) < amt))

        # We may need to decode the body.
        if decode_content and self._decompressobj and data:
            data = self._decompressobj.decompress(data)

        if decode_content and self._decompressobj and end_of_request:
            data += self._decompressobj.flush()

        # We're at the end. Close the connection. Explicit check for zero here
        # because self._length might be None.
        if end_of_request:
            self.close(socket_close=self._expect_close)

        return data

    def read_chunked(self, decode_content=True):
        """
        Reads chunked transfer encoded bodies. This method returns a generator:
        each iteration of which yields one chunk *unless* the chunks are
        compressed, in which case it yields whatever the decompressor provides
        for each chunk.

        .. warning:: This may yield the empty string, without that being the
                     end of the body!
        """
        if not self._chunked:
            raise ChunkedDecodeError(
                "Attempted chunked read of non-chunked body."
            )

        # Return early if possible.
        if self._sock is None:
            return

        while True:
            # Read to the newline to get the chunk length. This is a
            # hexadecimal integer.
            chunk_length = int(self._sock.readline().tobytes().strip(), 16)
            data = b''

            # If the chunk length is zero, consume the newline and then we're
            # done. If we were decompressing data, return the remaining data.
            if not chunk_length:
                self._sock.readline()

                if decode_content and self._decompressobj:
                    yield self._decompressobj.flush()

                self.close(socket_close=self._expect_close)
                break

            # Then read that many bytes.
            while chunk_length > 0:
                chunk = self._sock.recv(chunk_length).tobytes()
                data += chunk
                chunk_length -= len(chunk)

            assert chunk_length == 0

            # Now, consume the newline.
            self._sock.readline()

            # We may need to decode the body.
            if decode_content and self._decompressobj and data:
                data = self._decompressobj.decompress(data)

            yield data

        return

    def close(self, socket_close=False):
        """
        Close the response. This causes the Response to lose access to the
        backing socket. In some cases, it can also cause the backing connection
        to be torn down.

        :param socket_close: Whether to close the backing socket.
        :returns: Nothing.
        """
        if socket_close and self._parent is not None:
            # The double call is necessary because we need to dereference the
            # weakref. If the weakref is no longer valid, that's fine, there's
            # no connection object to tell.
            parent = self._parent()
            if parent is not None:
                parent.close()

        self._sock = None

    def _read_expect_closed(self, decode_content):
        """
        Implements the logic for an unbounded read on a socket that we expect
        to be closed by the remote end.
        """
        # In this case, just read until we cannot read anymore. Then, close the
        # socket, becuase we know we have to.
        chunks = []
        while True:
            try:
                chunk = self._sock.recv(65535).tobytes()
                if not chunk:
                    break
            except ConnectionResetError:
                break
            else:
                chunks.append(chunk)

        self.close(socket_close=True)

        # We may need to decompress the data.
        data = b''.join(chunks)
        if decode_content and self._decompressobj:
            data = self._decompressobj.decompress(data)
            data += self._decompressobj.flush()

        return data

    def _normal_read_chunked(self, amt, decode_content):
        """
        Implements the logic for calling ``read()`` on a chunked response.
        """
        # If we're doing a full read, read it as chunked and then just join
        # the chunks together!
        if amt is None:
            return self._buffered_data + b''.join(self.read_chunked())

        if self._chunker is None:
            self._chunker = self.read_chunked()

        # Otherwise, we have a certain amount of data we want to read.
        current_amount = len(self._buffered_data)

        extra_data = [self._buffered_data]
        while current_amount < amt:
            try:
                chunk = next(self._chunker)
            except StopIteration:
                self.close(socket_close=self._expect_close)
                break

            current_amount += len(chunk)
            extra_data.append(chunk)

        data = b''.join(extra_data)
        self._buffered_data = data[amt:]
        return data[:amt]

    # The following methods implement the context manager protocol.
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
        return False  # Never swallow exceptions.
  0707010001f463000081a40000000000000000000000016a100daf00001d51000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/hyper/cli.py   # -*- coding: utf-8 -*-
"""
hyper/cli
~~~~~~~~~

Command line interface for Hyper inspired by Httpie.
"""
import json
import locale
import logging
import sys
from argparse import ArgumentParser, RawTextHelpFormatter
from argparse import OPTIONAL, ZERO_OR_MORE
from pprint import pformat
from textwrap import dedent

from hyper import HTTPConnection, HTTP20Connection
from hyper import __version__
from hyper.compat import is_py2, urlencode, urlsplit, write_to_stdout
from hyper.common.util import to_host_port_tuple


log = logging.getLogger('hyper')

PREFERRED_ENCODING = locale.getpreferredencoding()

# Various separators used in args
SEP_HEADERS = ':'
SEP_QUERY = '=='
SEP_DATA = '='

SEP_GROUP_ITEMS = [
    SEP_HEADERS,
    SEP_QUERY,
    SEP_DATA,
]


class KeyValue(object):
    """Base key-value pair parsed from CLI."""

    def __init__(self, key, value, sep, orig):
        self.key = key
        self.value = value
        self.sep = sep
        self.orig = orig


class KeyValueArgType(object):
    """A key-value pair argument type used with `argparse`.

    Parses a key-value arg and constructs a `KeyValue` instance.
    Used for headers, form data, and other key-value pair types.
    This class is inspired by httpie and implements simple tokenizer only.
    """
    def __init__(self, *separators):
        self.separators = separators

    def __call__(self, string):
        for sep in self.separators:
            splitted = string.split(sep, 1)
            if len(splitted) == 2:
                key, value = splitted
                return KeyValue(key, value, sep, string)


def make_positional_argument(parser):
    parser.add_argument(
        'method', metavar='METHOD', nargs=OPTIONAL, default='GET',
        help=dedent("""
        The HTTP method to be used for the request
        (GET, POST, PUT, DELETE, ...).
        """))
    parser.add_argument(
        '_url', metavar='URL',
        help=dedent("""
        The scheme defaults to 'https://' if the URL does not include one.
        """))
    parser.add_argument(
        'items',
        metavar='REQUEST_ITEM',
        nargs=ZERO_OR_MORE,
        type=KeyValueArgType(*SEP_GROUP_ITEMS),
        help=dedent("""
        Optional key-value pairs to be included in the request.
        The separator used determines the type:

        ':' HTTP headers:

            Referer:http://httpie.org  Cookie:foo=bar  User-Agent:bacon/1.0

        '==' URL parameters to be appended to the request URI:

            search==hyper

        '=' Data fields to be serialized into a JSON object:

            name=Hyper  language=Python  description='CLI HTTP client'
        """))


def make_troubleshooting_argument(parser):
    parser.add_argument(
        '--version', action='version', version=__version__,
        help='Show version and exit.')
    parser.add_argument(
        '--debug', action='store_true', default=False,
        help='Show debugging information (loglevel=DEBUG)')
    parser.add_argument(
        '--h2', action='store_true', default=False,
        help="Do HTTP/2 directly, skipping plaintext upgrade and ignoring "
             "NPN/ALPN."
    )


def split_host_and_port(hostname):
    if ':' in hostname:
        return to_host_port_tuple(hostname, default_port=443)
    return hostname, None


class UrlInfo(object):
    def __init__(self):
        self.fragment = None
        self.host = 'localhost'
        self.netloc = None
        self.path = '/'
        self.port = 443
        self.query = None
        self.scheme = 'https'
        self.secure = False


def set_url_info(args):
    info = UrlInfo()
    _result = urlsplit(args._url)
    for attr in vars(info).keys():
        value = getattr(_result, attr, None)
        if value:
            setattr(info, attr, value)

    if info.scheme == 'http' and not _result.port:
        info.port = 80

    # Set the secure arg is the scheme is HTTPS, otherwise do unsecured.
    info.secure = info.scheme == 'https'

    if info.netloc:
        hostname, _ = split_host_and_port(info.netloc)
        info.host = hostname  # ensure stripping port number
    else:
        if _result.path:
            _path = _result.path.split('/', 1)
            hostname, port = split_host_and_port(_path[0])
            info.host = hostname
            if info.path == _path[0]:
                info.path = '/'
            elif len(_path) == 2 and _path[1]:
                info.path = '/' + _path[1]
            if port is not None:
                info.port = port

    log.debug('Url Info: %s', vars(info))
    args.url = info


def set_request_data(args):
    body, headers, params = {}, {}, {}
    for i in args.items:
        if i.sep == SEP_HEADERS:
            if i.key:
                headers[i.key] = i.value
            else:
                # when overriding a HTTP/2 special header there will be a
                # leading colon, which tricks the command line parser into
                # thinking the header is empty
                k, v = i.value.split(':', 1)
                headers[':' + k] = v
        elif i.sep == SEP_QUERY:
            params[i.key] = i.value
        elif i.sep == SEP_DATA:
            value = i.value
            if is_py2:  # pragma: no cover
                value = value.decode(PREFERRED_ENCODING)
            body[i.key] = value

    if params:
        args.url.path += '?' + urlencode(params)

    if body:
        content_type = 'application/json'
        headers.setdefault('content-type', content_type)
        args.body = json.dumps(body)

    if args.method is None:
        args.method = 'POST' if args.body else 'GET'

    args.method = args.method.upper()
    args.headers = headers


def parse_argument(argv=None):
    parser = ArgumentParser(formatter_class=RawTextHelpFormatter)
    parser.set_defaults(body=None, headers={})
    make_positional_argument(parser)
    make_troubleshooting_argument(parser)
    args = parser.parse_args(sys.argv[1:] if argv is None else argv)

    if args.debug:
        handler = logging.StreamHandler()
        handler.setLevel(logging.DEBUG)
        log.addHandler(handler)
        log.setLevel(logging.DEBUG)

    set_url_info(args)
    set_request_data(args)
    return args


def get_content_type_and_charset(response):
    charset = 'utf-8'
    content_type = response.headers.get('content-type')
    if content_type is None:
        return 'unknown', charset

    content_type = content_type[0].decode('utf-8').lower()
    type_and_charset = content_type.split(';', 1)
    ctype = type_and_charset[0].strip()
    if len(type_and_charset) == 2:
        charset = type_and_charset[1].strip().split('=')[1]

    return ctype, charset


def request(args):
    if not args.h2:
        conn = HTTPConnection(
            args.url.host, args.url.port, secure=args.url.secure
        )
    else:  # pragma: no cover
        conn = HTTP20Connection(
            args.url.host,
            args.url.port,
            secure=args.url.secure,
            force_proto='h2'
        )

    conn.request(args.method, args.url.path, args.body, args.headers)
    response = conn.get_response()
    log.debug('Response Headers:\n%s', pformat(response.headers))
    ctype, charset = get_content_type_and_charset(response)
    data = response.read()
    return data


def main(argv=None):
    args = parse_argument(argv)
    log.debug('Commandline Argument: %s', args)
    data = request(args)
    write_to_stdout(data)


if __name__ == '__main__':  # pragma: no cover
    main()
   0707010001f47e000081a40000000000000000000000016a100daf0000141d000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/hyper/tls.py   # -*- coding: utf-8 -*-
"""
hyper/tls
~~~~~~~~~

Contains the TLS/SSL logic for use in hyper.
"""
import os.path as path
from .common.exceptions import MissingCertFile
from .compat import ignore_missing, ssl


NPN_PROTOCOL = 'h2'
H2_NPN_PROTOCOLS = [NPN_PROTOCOL, 'h2-16', 'h2-15', 'h2-14']
SUPPORTED_NPN_PROTOCOLS = H2_NPN_PROTOCOLS + ['http/1.1']

H2C_PROTOCOL = 'h2c'

# We have a singleton SSLContext object. There's no reason to be creating one
# per connection.
_context = None

# Work out where our certificates are.
cert_loc = path.join(path.dirname(__file__), 'certs.pem')


def wrap_socket(sock, server_hostname, ssl_context=None, force_proto=None):
    """
    A vastly simplified SSL wrapping function. We'll probably extend this to
    do more things later.
    """

    global _context

    if ssl_context:
        # if an SSLContext is provided then use it instead of default context
        _ssl_context = ssl_context
    else:
        # create the singleton SSLContext we use
        if _context is None:  # pragma: no cover
            _context = init_context()
        _ssl_context = _context

    # the spec requires SNI support
    ssl_sock = _ssl_context.wrap_socket(sock, server_hostname=server_hostname)
    # Setting SSLContext.check_hostname to True only verifies that the
    # post-handshake servername matches that of the certificate. We also need
    # to check that it matches the requested one.
    if _ssl_context.check_hostname:  # pragma: no cover
        try:
            ssl.match_hostname(ssl_sock.getpeercert(), server_hostname)
        except AttributeError:
            ssl.verify_hostname(ssl_sock, server_hostname)  # pyopenssl

    # Allow for the protocol to be forced externally.
    proto = force_proto

    # ALPN is newer, so we prefer it over NPN. The odds of us getting
    # different answers is pretty low, but let's be sure.
    with ignore_missing():
        if proto is None:
            proto = ssl_sock.selected_alpn_protocol()

    with ignore_missing():
        if proto is None:
            proto = ssl_sock.selected_npn_protocol()

    return (ssl_sock, proto)


def init_context(cert_path=None, cert=None, cert_password=None):
    """
    Create a new ``SSLContext`` that is correctly set up for an HTTP/2
    connection. This SSL context object can be customized and passed as a
    parameter to the :class:`HTTPConnection <hyper.HTTPConnection>` class.
    Provide your own certificate file in case you donâ€™t want to use hyperâ€™s
    default certificate. The path to the certificate can be absolute or
    relative to your working directory.

    :param cert_path: (optional) The path to the certificate file of
        â€œcertification authorityâ€ (CA) certificates
    :param cert: (optional) if string, path to ssl client cert file (.pem).
        If tuple, ('cert', 'key') pair.
        The certfile string must be the path to a single file in PEM format
        containing the certificate as well as any number of CA certificates
        needed to establish the certificateâ€™s authenticity. The keyfile string,
        if present, must point to a file containing the private key in.
        Otherwise the private key will be taken from certfile as well.
    :param cert_password: (optional) The password argument may be a function to
        call to get the password for decrypting the private key. It will only
        be called if the private key is encrypted and a password is necessary.
        It will be called with no arguments, and it should return a string,
        bytes, or bytearray. If the return value is a string it will be
        encoded as UTF-8 before using it to decrypt the key. Alternatively a
        string, bytes, or bytearray value may be supplied directly as the
        password argument. It will be ignored if the private key is not
        encrypted and no password is needed.
    :returns: An ``SSLContext`` correctly set up for HTTP/2.
    """
    cafile = cert_path or cert_loc
    if not cafile or not path.exists(cafile):
        err_msg = ("No certificate found at " + str(cafile) + ". Either " +
                   "ensure the default cert.pem file is included in the " +
                   "distribution or provide a custom certificate when " +
                   "creating the connection.")
        raise MissingCertFile(err_msg)

    context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
    context.set_default_verify_paths()
    context.load_verify_locations(cafile=cafile)
    context.verify_mode = ssl.CERT_REQUIRED
    context.check_hostname = True

    with ignore_missing():
        context.set_npn_protocols(SUPPORTED_NPN_PROTOCOLS)

    with ignore_missing():
        context.set_alpn_protocols(SUPPORTED_NPN_PROTOCOLS)

    # required by the spec
    context.options |= ssl.OP_NO_COMPRESSION

    if cert is not None:
        try:
            basestring
        except NameError:
            basestring = (str, bytes)
        if not isinstance(cert, basestring):
            context.load_cert_chain(cert[0], cert[1], cert_password)
        else:
            context.load_cert_chain(cert, password=cert_password)

    return context
   0707010001f473000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/hyper/http20   0707010001f478000081a40000000000000000000000016a100daf00001f8e000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/response.py   # -*- coding: utf-8 -*-
"""
hyper/http20/response
~~~~~~~~~~~~~~~~~~~~~

Contains the HTTP/2 equivalent of the HTTPResponse object defined in
httplib/http.client.
"""
import logging
import zlib
#import brotli

from ..common.decoder import DeflateDecoder
from ..common.headers import HTTPHeaderMap
from ..common.util import HTTPVersion

log = logging.getLogger(__name__)


def strip_headers(headers):
    """
    Strips the headers attached to the instance of any header beginning
    with a colon that ``hyper`` doesn't understand. This method logs at
    warning level about the deleted headers, for discoverability.
    """
    # Convert to list to ensure that we don't mutate the headers while
    # we iterate over them.
    for name in list(headers.keys()):
        if name.startswith(b':'):
            del headers[name]


decompressors = {
    b'gzip': lambda: zlib.decompressobj(16 + zlib.MAX_WBITS),
#    b'br': brotli.Decompressor,
    b'deflate': DeflateDecoder
}


class HTTP20Response(object):
    """
    An ``HTTP20Response`` wraps the HTTP/2 response from the server. It
    provides access to the response headers and the entity body. The response
    is an iterable object and can be used in a with statement (though due to
    the persistent connections used in HTTP/2 this has no effect, and is done
    soley for compatibility).
    """

    version = HTTPVersion.http20
    _decompressobj = None

    def __init__(self, headers, stream):
        #: The reason phrase returned by the server. This is not used in
        #: HTTP/2, and so is always the empty string.
        self.reason = ''

        status = headers[b':status'][0]
        strip_headers(headers)

        #: The status code returned by the server.
        self.status = int(status)

        #: The response headers. These are determined upon creation, assigned
        #: once, and never assigned again.
        self.headers = headers

        # The response trailers. These are always intially ``None``.
        self._trailers = None

        # The stream this response is being sent over.
        self._stream = stream

        # We always read in one-data-frame increments from the stream, so we
        # may need to buffer some for incomplete reads.
        self._data_buffer = b''

        # This object is used for decompressing gzipped request bodies. Right
        # now we only support gzip because that's all the RFC mandates of us.
        # Later we'll add support for more encodings.
        # This 16 + MAX_WBITS nonsense is to force gzip. See this
        # Stack Overflow answer for more:
        # http://stackoverflow.com/a/2695466/1401686
        for c in self.headers.get(b'content-encoding', []):
            if c in decompressors:
                self._decompressobj = decompressors.get(c)()
                break

    @property
    def trailers(self):
        """
        Trailers on the HTTP message, if any.

        .. warning:: Note that this property requires that the stream is
                     totally exhausted. This means that, if you have not
                     completely read from the stream, all stream data will be
                     read into memory.
        """
        if self._trailers is None:
            self._trailers = self._stream.gettrailers() or HTTPHeaderMap()
            strip_headers(self._trailers)

        return self._trailers

    def read(self, amt=None, decode_content=True):
        """
        Reads the response body, or up to the next ``amt`` bytes.

        :param amt: (optional) The amount of data to read. If not provided, all
            the data will be read from the response.
        :param decode_content: (optional) If ``True``, will transparently
            decode the response data.
        :returns: The read data. Note that if ``decode_content`` is set to
            ``True``, the actual amount of data returned may be different to
            the amount requested.
        """
        if amt is not None and amt <= len(self._data_buffer):
            data = self._data_buffer[:amt]
            self._data_buffer = self._data_buffer[amt:]
            response_complete = False
        elif amt is not None:
            read_amt = amt - len(self._data_buffer)
            self._data_buffer += self._stream._read(read_amt)
            data = self._data_buffer[:amt]
            self._data_buffer = self._data_buffer[amt:]
            response_complete = len(data) < amt
        else:
            data = b''.join([self._data_buffer, self._stream._read()])
            response_complete = True

        # We may need to decode the body.
        if decode_content and self._decompressobj and data:
            data = self._decompressobj.decompress(data)

        # If we're at the end of the request, we have some cleaning up to do.
        # Close the stream, and if necessary flush the buffer.
        if response_complete:
            if decode_content and self._decompressobj:
                data += self._decompressobj.flush()

            if self._stream.response_headers:
                self.headers.merge(self._stream.response_headers)

        # We're at the end, close the connection.
        if response_complete:
            self.close()

        return data

    def read_chunked(self, decode_content=True):
        """
        Reads chunked transfer encoded bodies. This method returns a generator:
        each iteration of which yields one data frame *unless* the frames
        contain compressed data and ``decode_content`` is ``True``, in which
        case it yields whatever the decompressor provides for each chunk.

        .. warning:: This may yield the empty string, without that being the
                     end of the body!
        """
        while True:
            data = self._stream._read_one_frame()

            if data is None:
                break

            if decode_content and self._decompressobj:
                data = self._decompressobj.decompress(data)

            yield data

        if decode_content and self._decompressobj:
            yield self._decompressobj.flush()

        self.close()

        return

    def fileno(self):
        """
        Return the ``fileno`` of the underlying socket. This function is
        currently not implemented.
        """
        raise NotImplementedError("Not currently implemented.")

    def close(self):
        """
        Close the response. In effect this closes the backing HTTP/2 stream.

        :returns: Nothing.
        """
        self._stream.close()

    # The following methods implement the context manager protocol.
    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
        return False  # Never swallow exceptions.


class HTTP20Push(object):
    """
    Represents a request-response pair sent by the server through the server
    push mechanism.
    """
    def __init__(self, request_headers, stream):
        #: The scheme of the simulated request
        self.scheme = request_headers[b':scheme'][0]
        #: The method of the simulated request (must be safe and cacheable,
        #: e.g. GET)
        self.method = request_headers[b':method'][0]
        #: The authority of the simulated request (usually host:port)
        self.authority = request_headers[b':authority'][0]
        #: The path of the simulated request
        self.path = request_headers[b':path'][0]

        strip_headers(request_headers)

        #: The headers the server attached to the simulated request.
        self.request_headers = request_headers

        self._stream = stream

    def get_response(self):
        """
        Get the pushed response provided by the server.

        :returns: A :class:`HTTP20Response <hyper.HTTP20Response>` object
            representing the pushed response.
        """
        return HTTP20Response(self._stream.getheaders(), self._stream)

    def cancel(self):
        """
        Cancel the pushed response and close the stream.

        :returns: Nothing.
        """
        self._stream.close(8)  # CANCEL
  0707010001f475000081a40000000000000000000000016a100daf00008447000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/connection.py # -*- coding: utf-8 -*-
"""
hyper/http20/connection
~~~~~~~~~~~~~~~~~~~~~~~

Objects that build hyper's connection-level HTTP/2 abstraction.
"""
import h2.connection
import h2.events
import h2.settings

from ..compat import ssl
from ..tls import wrap_socket, H2_NPN_PROTOCOLS, H2C_PROTOCOL
from ..common.exceptions import ConnectionResetError
from ..common.bufsocket import BufferedSocket
from ..common.headers import HTTPHeaderMap
from ..common.util import (
    to_host_port_tuple, to_native_string, to_bytestring, HTTPVersion
)
from ..compat import unicode, bytes
from ..http11.connection import _create_tunnel
from .stream import Stream
from .response import HTTP20Response, HTTP20Push
from .window import FlowControlManager
from .exceptions import ConnectionError, StreamResetError
from . import errors

import errno
import logging
import socket
import time
import threading
import itertools

log = logging.getLogger(__name__)

DEFAULT_WINDOW_SIZE = 65535

TRANSIENT_SSL_ERRORS = (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE)


class _LockedObject(object):
    """
    A wrapper class that hides a specific object behind a lock.

    The goal here is to provide a simple way to protect access to an object
    that cannot safely be simultaneously accessed from multiple threads. The
    intended use of this class is simple: take hold of it with a context
    manager, which returns the protected object.
    """
    def __init__(self, obj):
        self.lock = threading.RLock()
        self._obj = obj

    def __enter__(self):
        self.lock.acquire()
        return self._obj

    def __exit__(self, _exc_type, _exc_val, _exc_tb):
        self.lock.release()


class HTTP20Connection(object):
    """
    An object representing a single HTTP/2 connection to a server.

    This object behaves similarly to the Python standard library's
    ``HTTPConnection`` object, with a few critical differences.

    Most of the standard library's arguments to the constructor are irrelevant
    for HTTP/2 or not supported by hyper.

    :param host: The host to connect to. This may be an IP address or a
        hostname, and optionally may include a port: for example,
        ``'http2bin.org'``, ``'http2bin.org:443'`` or ``'127.0.0.1'``.
    :param port: (optional) The port to connect to. If not provided and one
        also isn't provided in the ``host`` parameter, defaults to 443.
    :param secure: (optional) Whether the request should use TLS. Defaults to
        ``False`` for most requests, but to ``True`` for any request issued to
        port 443.
    :param window_manager: (optional) The class to use to manage flow control
        windows. This needs to be a subclass of the
        :class:`BaseFlowControlManager
        <hyper.http20.window.BaseFlowControlManager>`. If not provided,
        :class:`FlowControlManager <hyper.http20.window.FlowControlManager>`
        will be used.
    :param enable_push: (optional) Whether the server is allowed to push
        resources to the client (see
        :meth:`get_pushes() <hyper.HTTP20Connection.get_pushes>`).
    :param ssl_context: (optional) A class with custom certificate settings.
        If not provided then hyper's default ``SSLContext`` is used instead.
    :param proxy_host: (optional) The proxy to connect to.  This can be an IP
        address or a host name and may include a port.
    :param proxy_port: (optional) The proxy port to connect to. If not provided
        and one also isn't provided in the ``proxy_host`` parameter, defaults
        to 8080.
    :param proxy_headers: (optional) The headers to send to a proxy.
    """

    version = HTTPVersion.http20

    def __init__(self, host, port=None, secure=None, window_manager=None,
                 enable_push=False, ssl_context=None, proxy_host=None,
                 proxy_port=None, force_proto=None, proxy_headers=None,
                 timeout=None, **kwargs):
        """
        Creates an HTTP/2 connection to a specific server.
        """
        if port is None:
            self.host, self.port = to_host_port_tuple(host, default_port=443)
        else:
            self.host, self.port = host, port

        if secure is not None:
            self.secure = secure
        elif self.port == 443:
            self.secure = True
        else:
            self.secure = False

        self._enable_push = enable_push
        self.ssl_context = ssl_context

        # Setup proxy details if applicable.
        if proxy_host and proxy_port is None:
            self.proxy_host, self.proxy_port = to_host_port_tuple(
                proxy_host, default_port=8080
            )
        elif proxy_host:
            self.proxy_host, self.proxy_port = proxy_host, proxy_port
        else:
            self.proxy_host = None
            self.proxy_port = None
        self.proxy_headers = proxy_headers

        #: The size of the in-memory buffer used to store data from the
        #: network. This is used as a performance optimisation. Increase buffer
        #: size to improve performance: decrease it to conserve memory.
        #: Defaults to 64kB.
        self.network_buffer_size = 65536

        self.force_proto = force_proto

        # Concurrency
        #
        # Use one universal lock (_lock) to synchronize all interaction
        # with global connection state, _send_cb and _recv_cb.
        self._lock = threading.RLock()

        # Create the mutable state.
        self.__wm_class = window_manager or FlowControlManager
        self.__init_state()

        # timeout
        self._timeout = timeout

        return

    def __init_state(self):
        """
        Initializes the 'mutable state' portions of the HTTP/2 connection
        object.

        This method exists to enable HTTP20Connection objects to be reused if
        they're closed, by resetting the connection object to its basic state
        whenever it ends up closed. Any situation that needs to recreate the
        connection can call this method and it will be done.

        This is one of the only methods in hyper that is truly private, as
        users should be strongly discouraged from messing about with connection
        objects themselves.
        """
        self._conn = _LockedObject(h2.connection.H2Connection())

        # Streams are stored in a dictionary keyed off their stream IDs. We
        # also save the most recent one for easy access without having to walk
        # the dictionary.
        #
        # We add a set of all streams that we or the remote party forcefully
        # closed with RST_STREAM, to avoid encountering issues where frames
        # were already in flight before the RST was processed.
        #
        # Finally, we add a set of streams that recently received data.  When
        # using multiple threads, this avoids reading on threads that have just
        # acquired the I/O lock whose streams have already had their data read
        # for them by prior threads.
        self.streams = {}
        self.recent_stream = None
        self.next_stream_id = 1
        self.reset_streams = set()
        self.recent_recv_streams = set()

        # The socket used to send data.
        self._sock = None

        # Instantiate a window manager.
        self.window_manager = self.__wm_class(65535)

        return

    def ping(self, opaque_data):
        """
        Send a PING frame.

        Concurrency
        -----------

        This method is thread-safe.

        :param opaque_data: A bytestring of length 8 that will be sent in the
                            PING frame.
        :returns: Nothing
        """
        self.connect()
        with self._lock:
            with self._conn as conn:
                conn.ping(to_bytestring(opaque_data))
            self._send_outstanding_data()

    def request(self, method, url, body=None, headers=None):
        """
        This will send a request to the server using the HTTP request method
        ``method`` and the selector ``url``. If the ``body`` argument is
        present, it should be string or bytes object of data to send after the
        headers are finished. Strings are encoded as UTF-8. To use other
        encodings, pass a bytes object. The Content-Length header is set to the
        length of the body field.

        Concurrency
        -----------

        This method is thread-safe.

        :param method: The request method, e.g. ``'GET'``.
        :param url: The URL to contact, e.g. ``'/path/segment'``.
        :param body: (optional) The request body to send. Must be a bytestring
            or a file-like object.
        :param headers: (optional) The headers to send on the request.
        :returns: A stream ID for the request.
        """
        headers = headers or {}

        # Concurrency
        #
        # It's necessary to hold a lock while this method runs to satisfy H2
        # protocol requirements.
        #
        # - putrequest obtains the next valid new stream_id
        # - endheaders sends a http2 message using the new stream_id
        #
        # If threads interleave these operations, it could result in messages
        # being sent in the wrong order, which can lead to the out-of-order
        # messages with lower stream IDs being closed prematurely.
        with self._lock:
            # Unlike HTTP/1.1, HTTP/2 (according to RFC 7540) doesn't require
            # to use absolute URI when proxying.

            stream_id = self.putrequest(method, url)

            default_headers = (':method', ':scheme', ':authority', ':path')
            all_headers = headers.items()
            if self.proxy_host and not self.secure:
                proxy_headers = self.proxy_headers or {}
                all_headers = itertools.chain(all_headers,
                                              proxy_headers.items())
            for name, value in all_headers:
                is_default = to_native_string(name) in default_headers
                self.putheader(name, value, stream_id, replace=is_default)

            # Convert the body to bytes if needed.
            if body and isinstance(body, (unicode, bytes)):
                body = to_bytestring(body)

            self.endheaders(message_body=body, final=True, stream_id=stream_id)

            return stream_id

    def _get_stream(self, stream_id):
        if stream_id is None:
            return self.recent_stream
        elif stream_id in self.reset_streams or stream_id not in self.streams:
            raise StreamResetError("Stream forcefully closed")
        else:
            return self.streams[stream_id]

    def get_response(self, stream_id=None):
        """
        Should be called after a request is sent to get a response from the
        server. If sending multiple parallel requests, pass the stream ID of
        the request whose response you want. Returns a
        :class:`HTTP20Response <hyper.HTTP20Response>` instance.
        If you pass no ``stream_id``, you will receive the oldest
        :class:`HTTPResponse <hyper.HTTP20Response>` still outstanding.

        Concurrency
        -----------

        This method is thread-safe.

        :param stream_id: (optional) The stream ID of the request for which to
            get a response.
        :returns: A :class:`HTTP20Response <hyper.HTTP20Response>` object.
        """
        stream = self._get_stream(stream_id)
        return HTTP20Response(stream.getheaders(), stream)

    def get_pushes(self, stream_id=None, capture_all=False):
        """
        Returns a generator that yields push promises from the server. **Note
        that this method is not idempotent**: promises returned in one call
        will not be returned in subsequent calls. Iterating through generators
        returned by multiple calls to this method simultaneously results in
        undefined behavior.

        :param stream_id: (optional) The stream ID of the request for which to
            get push promises.
        :param capture_all: (optional) If ``False``, the generator will yield
            all buffered push promises without blocking. If ``True``, the
            generator will first yield all buffered push promises, then yield
            additional ones as they arrive, and terminate when the original
            stream closes.
        :returns: A generator of :class:`HTTP20Push <hyper.HTTP20Push>` objects
            corresponding to the streams pushed by the server.
        """
        stream = self._get_stream(stream_id)
        for promised_stream_id, headers in stream.get_pushes(capture_all):
            yield HTTP20Push(
                HTTPHeaderMap(headers), self.streams[promised_stream_id]
            )

    def connect(self):
        """
        Connect to the server specified when the object was created. This is a
        no-op if we're already connected.

        Concurrency
        -----------

        This method is thread-safe. It may be called from multiple threads, and
        is a noop for all threads apart from the first.

        :returns: Nothing.

        """
        with self._lock:
            if self._sock is not None:
                return

            if isinstance(self._timeout, tuple):
                connect_timeout = self._timeout[0]
                read_timeout = self._timeout[1]
            else:
                connect_timeout = self._timeout
                read_timeout = self._timeout

            if self.proxy_host and self.secure:
                # Send http CONNECT method to a proxy and acquire the socket
                sock = _create_tunnel(
                    self.proxy_host,
                    self.proxy_port,
                    self.host,
                    self.port,
                    proxy_headers=self.proxy_headers,
                    timeout=self._timeout
                )
            elif self.proxy_host:
                # Simple http proxy
                sock = socket.create_connection(
                    (self.proxy_host, self.proxy_port),
                    timeout=connect_timeout
                )
            elif self.host.startswith("/"):
                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                sock.settimeout(connect_timeout)
                sock.connect(self.host)
            else:
                sock = socket.create_connection((self.host, self.port),
                                                timeout=connect_timeout)

            if self.secure:
                sock, proto = wrap_socket(sock, self.host, self.ssl_context,
                                          force_proto=self.force_proto)
            else:
                proto = H2C_PROTOCOL

            log.debug("Selected NPN protocol: %s", proto)
            assert proto in H2_NPN_PROTOCOLS or proto == H2C_PROTOCOL, (
                "No suitable protocol found. Supported protocols: %s. "
                "Check your OpenSSL version."
            ) % ','.join(H2_NPN_PROTOCOLS + [H2C_PROTOCOL])

            self._sock = BufferedSocket(sock, self.network_buffer_size)

            # Set read timeout
            self._sock.settimeout(read_timeout)

            self._send_preamble()

    def _connect_upgrade(self, sock):
        """
        Called by the generic HTTP connection when we're being upgraded. Locks
        in a new socket and places the backing state machine into an upgrade
        state, then sends the preamble.
        """
        self._sock = sock

        with self._conn as conn:
            conn.initiate_upgrade_connection()
            conn.update_settings(
                {h2.settings.SettingCodes.ENABLE_PUSH: int(self._enable_push)}
            )
        self._send_outstanding_data()

        # The server will also send an initial settings frame, so get it.
        # However, we need to make sure our stream state is set up properly
        # first, or any extra data we receive might cause us problems.
        s = self._new_stream(local_closed=True)
        self.recent_stream = s

        self._recv_cb()

    def _send_preamble(self):
        """
        Sends the necessary HTTP/2 preamble.
        """
        # We need to send the connection header immediately on this
        # connection, followed by an initial settings frame.
        with self._conn as conn:
            conn.initiate_connection()
            conn.update_settings(
                {h2.settings.SettingCodes.ENABLE_PUSH: int(self._enable_push)}
            )
        self._send_outstanding_data()

        # The server will also send an initial settings frame, so get it.
        self._recv_cb()

    def close(self, error_code=None):
        """
        Close the connection to the server.

        Concurrency
        -----------

        This method is thread-safe.

        :param error_code: (optional) The error code to reset all streams with.
        :returns: Nothing.
        """
        # Concurrency
        #
        # It's necessary to hold the lock here to ensure that threads closing
        # the connection see consistent state, and to prevent creation of
        # of new streams while the connection is being closed.
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            # Close all streams
            for stream in list(self.streams.values()):
                log.debug("Close stream %d" % stream.stream_id)
                stream.close(error_code)

            # Send GoAway frame to the server
            try:
                with self._conn as conn:
                    conn.close_connection(error_code or 0)
                self._send_outstanding_data(tolerate_peer_gone=True)
            except Exception as e:  # pragma: no cover
                log.warn("GoAway frame could not be sent: %s" % e)

            if self._sock is not None:
                self._sock.close()
            self.__init_state()

    def _send_outstanding_data(self, tolerate_peer_gone=False,
                               send_empty=True):
        # Concurrency
        #
        # Hold _lock; getting and writing data from _conn is synchronized
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            with self._conn as conn:
                data = conn.data_to_send()
            if data or send_empty:
                self._send_cb(data, tolerate_peer_gone=tolerate_peer_gone)

    def putrequest(self, method, selector, **kwargs):
        """
        This should be the first call for sending a given HTTP request to a
        server. It returns a stream ID for the given connection that should be
        passed to all subsequent request building calls.

        Concurrency
        -----------

        This method is thread-safe. It can be called from multiple threads,
        and each thread should receive a unique stream ID.

        :param method: The request method, e.g. ``'GET'``.
        :param selector: The path selector.
        :returns: A stream ID for the request.
        """
        # Create a new stream.
        s = self._new_stream()

        # To this stream we need to immediately add a few headers that are
        # HTTP/2 specific. These are: ":method", ":scheme", ":authority" and
        # ":path". We can set all of these now.
        s.add_header(":method", method)
        s.add_header(":scheme", "https" if self.secure else "http")
        s.add_header(":authority", self.host)
        s.add_header(":path", selector)

        # Save the stream.
        self.recent_stream = s

        return s.stream_id

    def putheader(self, header, argument, stream_id=None, replace=False):
        """
        Sends an HTTP header to the server, with name ``header`` and value
        ``argument``.

        Unlike the ``httplib`` version of this function, this version does not
        actually send anything when called. Instead, it queues the headers up
        to be sent when you call
        :meth:`endheaders() <hyper.HTTP20Connection.endheaders>`.

        This method ensures that headers conform to the HTTP/2 specification.
        In particular, it strips out the ``Connection`` header, as that header
        is no longer valid in HTTP/2. This is to make it easy to write code
        that runs correctly in both HTTP/1.1 and HTTP/2.

        :param header: The name of the header.
        :param argument: The value of the header.
        :param stream_id: (optional) The stream ID of the request to add the
            header to.
        :returns: Nothing.
        """
        stream = self._get_stream(stream_id)
        stream.add_header(header, argument, replace)

        return

    def endheaders(self, message_body=None, final=False, stream_id=None):
        """
        Sends the prepared headers to the server. If the ``message_body``
        argument is provided it will also be sent to the server as the body of
        the request, and the stream will immediately be closed. If the
        ``final`` argument is set to True, the stream will also immediately
        be closed: otherwise, the stream will be left open and subsequent calls
        to ``send()`` will be required.

        :param message_body: (optional) The body to send. May not be provided
            assuming that ``send()`` will be called.
        :param final: (optional) If the ``message_body`` parameter is provided,
            should be set to ``True`` if no further data will be provided via
            calls to :meth:`send() <hyper.HTTP20Connection.send>`.
        :param stream_id: (optional) The stream ID of the request to finish
            sending the headers on.
        :returns: Nothing.
        """
        self.connect()

        stream = self._get_stream(stream_id)

        headers_only = (message_body is None and final)

        # Concurrency:
        #
        # Hold _lock: synchronize access to the connection's HPACK
        # encoder and decoder and the subsquent write to the connection
        with self._lock:
            stream.send_headers(headers_only)

            # Send whatever data we have.
            if message_body is not None:
                stream.send_data(message_body, final)

            self._send_outstanding_data()

        return

    def send(self, data, final=False, stream_id=None):
        """
        Sends some data to the server. This data will be sent immediately
        (excluding the normal HTTP/2 flow control rules). If this is the last
        data that will be sent as part of this request, the ``final`` argument
        should be set to ``True``. This will cause the stream to be closed.

        :param data: The data to send.
        :param final: (optional) Whether this is the last bit of data to be
            sent on this request.
        :param stream_id: (optional) The stream ID of the request to send the
            data on.
        :returns: Nothing.
        """
        stream = self._get_stream(stream_id)
        stream.send_data(data, final)

        return

    def _new_stream(self, stream_id=None, local_closed=False):
        """
        Returns a new stream object for this connection.
        """
        # Concurrency
        #
        # Hold _lock: ensure that threads accessing the connection see
        # self.next_stream_id in a consistent state
        #
        # No I/O occurs, the delay in waiting threads depends on their number.
        with self._lock:
            s = Stream(
                stream_id or self.next_stream_id,
                self.__wm_class(DEFAULT_WINDOW_SIZE),
                self._conn,
                self._send_outstanding_data,
                self._recv_cb,
                self._stream_close_cb,
            )
            s.local_closed = local_closed
            self.streams[s.stream_id] = s
            self.next_stream_id += 2

            return s

    def _send_cb(self, data, tolerate_peer_gone=False):
        """
        This is the callback used by streams to send data on the connection.

        This acts as a dumb wrapper around the socket send method.
        """
        # Concurrency
        #
        # Hold _lock: ensures only writer at a time
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            try:
                self._sock.sendall(data)
            except socket.error as e:
                if (not tolerate_peer_gone or
                        e.errno not in (errno.EPIPE, errno.ECONNRESET)):
                    raise

    def _adjust_receive_window(self, frame_len):
        """
        Adjusts the window size in response to receiving a DATA frame of length
        ``frame_len``. May send a WINDOWUPDATE frame if necessary.
        """
        # Concurrency
        #
        # Hold _lock; synchronize the window manager update and the
        # subsequent potential write to the connection
        #
        # I/O may occur while the lock is held; waiting threads may see a
        # delay.
        with self._lock:
            increment = self.window_manager._handle_frame(frame_len)

            if increment:
                with self._conn as conn:
                    conn.increment_flow_control_window(increment)
                self._send_outstanding_data(tolerate_peer_gone=True)

        return

    def _single_read(self):
        """
        Performs a single read from the socket and hands the data off to the
        h2 connection object.
        """
        # Begin by reading what we can from the socket.
        #
        # Concurrency
        #
        # Synchronizes reading the data
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            if self._sock is None:
                raise ConnectionError('tried to read after connection close')
            self._sock.fill()
            data = self._sock.buffer.tobytes()
            self._sock.advance_buffer(len(data))
            with self._conn as conn:
                events = conn.receive_data(data)
            stream_ids = set(getattr(e, 'stream_id', -1) for e in events)
            stream_ids.discard(-1)  # sentinel
            stream_ids.discard(0)  # connection events
            self.recent_recv_streams |= stream_ids

        for event in events:
            if isinstance(event, h2.events.DataReceived):
                self._adjust_receive_window(event.flow_controlled_length)
                self.streams[event.stream_id].receive_data(event)
            elif isinstance(event, h2.events.PushedStreamReceived):
                if self._enable_push:
                    self._new_stream(event.pushed_stream_id, local_closed=True)
                    self.streams[event.parent_stream_id].receive_push(event)
                else:
                    # Servers are forbidden from sending push promises when
                    # the ENABLE_PUSH setting is 0, but the spec leaves the
                    # client action undefined when they do it anyway. So we
                    # just refuse the stream and go about our business.
                    self._send_rst_frame(event.pushed_stream_id, 7)
            elif isinstance(event, h2.events.ResponseReceived):
                self.streams[event.stream_id].receive_response(event)
            elif isinstance(event, h2.events.TrailersReceived):
                self.streams[event.stream_id].receive_trailers(event)
            elif isinstance(event, h2.events.StreamEnded):
                self.streams[event.stream_id].receive_end_stream(event)
            elif isinstance(event, h2.events.StreamReset):
                if event.stream_id not in self.reset_streams:
                    self.reset_streams.add(event.stream_id)
                    self.streams[event.stream_id].receive_reset(event)
            elif isinstance(event, h2.events.ConnectionTerminated):
                # If we get GoAway with error code zero, we are doing a
                # graceful shutdown and all is well. Otherwise, throw an
                # exception.
                self.close()

                # If an error occured, try to read the error description from
                # code registry otherwise use the frame's additional data.
                if event.error_code != 0:
                    try:
                        name, number, description = errors.get_data(
                            event.error_code
                        )
                    except ValueError:
                        error_string = (
                            "Encountered error code %d" % event.error_code
                        )
                    else:
                        error_string = (
                            "Encountered error %s %s: %s" %
                            (name, number, description)
                        )

                    raise ConnectionError(error_string)
            else:
                log.info("Received unhandled event %s", event)

        self._send_outstanding_data(tolerate_peer_gone=True, send_empty=False)

    def _recv_cb(self, stream_id=0):
        """
        This is the callback used by streams to read data from the connection.

        This stream reads what data it can, and throws it into the underlying
        connection, before farming out any events that fire to the relevant
        streams. If the socket remains readable, it will then optimistically
        continue to attempt to read.

        This is generally called by a stream, not by the connection itself, and
        it's likely that streams will read a frame that doesn't belong to them.

        :param stream_id: (optional) The stream ID of the stream reading data
            from the connection.

        """
        # Begin by reading what we can from the socket.
        #
        # Concurrency
        #
        # Ignore this read if some other thread has recently read data from
        # from the requested stream.
        #
        # The lock here looks broad, but is needed to ensure correct behavior
        # when there are multiple readers of the same stream.  It is
        # re-acquired in the calls to self._single_read.
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            log.debug('recv for stream %d with %s already present',
                      stream_id,
                      self.recent_recv_streams)
            if stream_id in self.recent_recv_streams:
                self.recent_recv_streams.discard(stream_id)
                return

            # make sure to validate the stream is readable.
            # if the connection was reset, this stream id won't appear in
            # self.streams and will cause this call to raise an exception.
            if stream_id:
                self._get_stream(stream_id)

            # TODO: Re-evaluate this.
            self._single_read()
            count = 9
            retry_wait = 0.05  # can improve responsiveness to delay the retry

            while count and self._sock is not None and self._sock.can_read:
                # If the connection has been closed, bail out, but retry
                # on transient errors.
                try:
                    self._single_read()
                except ConnectionResetError:
                    break
                except ssl.SSLError as e:  # pragma: no cover
                    # these are transient errors that can occur while reading
                    # from ssl connections.
                    if e.args[0] in TRANSIENT_SSL_ERRORS:
                        continue
                    else:
                        raise
                except socket.error as e:  # pragma: no cover
                    if e.errno in (errno.EINTR, errno.EAGAIN):
                        # if 'interrupted' or 'try again', continue
                        time.sleep(retry_wait)
                        continue
                    elif e.errno == errno.ECONNRESET:
                        break
                    else:
                        raise

                count -= 1

    def _send_rst_frame(self, stream_id, error_code):
        """
        Send reset stream frame with error code and remove stream from map.
        """
        # Concurrency
        #
        # Hold _lock; synchronize generating the reset frame and writing
        # it
        #
        # I/O occurs while the lock is held; waiting threads will see a delay.
        with self._lock:
            with self._conn as conn:
                conn.reset_stream(stream_id, error_code=error_code)
            self._send_outstanding_data()

        # Concurrency
        #
        # Hold _lock; the stream storage is being updated. No I/O occurs, any
        # delay is proportional to the number of waiting threads.
        with self._lock:
            try:
                del self.streams[stream_id]
                self.recent_recv_streams.discard(stream_id)
            except KeyError as e:  # pragma: no cover
                log.warn(
                    "Stream with id %d does not exist: %s",
                    stream_id, e)

            # Keep track of the fact that we reset this stream in case there
            # are other frames in flight.
            self.reset_streams.add(stream_id)

    def _stream_close_cb(self, stream_id):
        """
        Called by a stream when it is closing, so that state can be cleared.
        """
        try:
            del self.streams[stream_id]
            self.recent_recv_streams.discard(stream_id)
        except KeyError:
            pass

    # The following two methods are the implementation of the context manager
    # protocol.
    def __enter__(self):
        return self

    def __exit__(self, type, value, tb):
        self.close()
        return False  # Never swallow exceptions.
 0707010001f479000081a40000000000000000000000016a100daf00002f34000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/stream.py # -*- coding: utf-8 -*-
"""
hyper/http20/stream
~~~~~~~~~~~~~~~~~~~

Objects that make up the stream-level abstraction of hyper's HTTP/2 support.

These objects are not expected to be part of the public HTTP/2 API: they're
intended purely for use inside hyper's HTTP/2 abstraction.

Conceptually, a single HTTP/2 connection is made up of many streams: each
stream is an independent, bi-directional sequence of HTTP headers and data.
Each stream is identified by a monotonically increasing integer, assigned to
the stream by the endpoint that initiated the stream.
"""
import h2.exceptions

from ..common.headers import HTTPHeaderMap
from .util import h2_safe_headers
import logging

log = logging.getLogger(__name__)

# Define the largest chunk of data we'll send in one go. Realistically, we
# should take the MSS into account but that's pretty dull, so let's just say
# 1kB and call it a day.
MAX_CHUNK = 1024


class Stream(object):
    """
    A single HTTP/2 stream.

    A stream is an independent, bi-directional sequence of HTTP headers and
    data. Each stream is identified by a single integer. From a HTTP
    perspective, a stream _approximately_ matches a single request-response
    pair.
    """
    def __init__(self,
                 stream_id,
                 window_manager,
                 connection,
                 send_outstanding_data,
                 recv_cb,
                 close_cb):
        self.stream_id = stream_id
        self.headers = HTTPHeaderMap()

        # Set to a key-value set of the response headers once their
        # HEADERS..CONTINUATION frame sequence finishes.
        self.response_headers = None

        # Set to a key-value set of the response trailers once their
        # HEADERS..CONTINUATION frame sequence finishes.
        self.response_trailers = None

        # A dict mapping the promised stream ID of a pushed resource to a
        # key-value set of its request headers. Entries are added once their
        # PUSH_PROMISE..CONTINUATION frame sequence finishes.
        self.promised_headers = {}

        # Unconsumed response data chunks. Empties after every call to _read().
        self.data = []

        # Whether the remote side has completed the stream.
        self.remote_closed = False

        # Whether we have closed the stream.
        self.local_closed = False

        # There are two flow control windows: one for data we're sending,
        # one for data being sent to us.
        self._in_window_manager = window_manager

        # Save off a reference to the state machine wrapped with lock.
        self._conn = connection

        # Save off a data callback.
        self._send_outstanding_data = send_outstanding_data
        self._recv_cb = recv_cb
        self._close_cb = close_cb

    def add_header(self, name, value, replace=False):
        """
        Adds a single HTTP header to the headers to be sent on the request.
        """
        if not replace:
            self.headers[name] = value
        else:
            self.headers.replace(name, value)

    def send_headers(self, end_stream=False):
        """
        Sends the complete saved header block on the stream.
        """
        headers = self.get_headers()
        with self._conn as conn:
            conn.send_headers(self.stream_id, headers, end_stream)
        self._send_outstanding_data()

        if end_stream:
            self.local_closed = True

    def send_data(self, data, final):
        """
        Send some data on the stream. If this is the end of the data to be
        sent, the ``final`` flag _must_ be set to True. If no data is to be
        sent, set ``data`` to ``None``.
        """
        # Define a utility iterator for file objects.
        def file_iterator(fobj):
            while True:
                data = fobj.read(MAX_CHUNK)
                yield data
                if len(data) < MAX_CHUNK:
                    break

        # Build the appropriate iterator for the data, in chunks of CHUNK_SIZE.
        if hasattr(data, 'read'):
            chunks = file_iterator(data)
        else:
            chunks = (data[i:i+MAX_CHUNK]
                      for i in range(0, len(data), MAX_CHUNK))

        # since we need to know when we have a last package we need to know
        # if there is another package in advance
        cur_chunk = None
        try:
            cur_chunk = next(chunks)
            while True:
                next_chunk = next(chunks)
                self._send_chunk(cur_chunk, False)
                cur_chunk = next_chunk
        except StopIteration:
            if cur_chunk is not None:  # cur_chunk none when no chunks to send
                self._send_chunk(cur_chunk, final)

    def _read(self, amt=None):
        """
        Read data from the stream. Unlike a normal read behaviour, this
        function returns _at least_ ``amt`` data, but may return more.
        """
        def listlen(list):
            return sum(map(len, list))

        # Keep reading until the stream is closed or we get enough data.
        while (not self.remote_closed and
                (amt is None or listlen(self.data) < amt)):
            self._recv_cb(stream_id=self.stream_id)

        result = b''.join(self.data)
        self.data = []
        return result

    def _read_one_frame(self):
        """
        Reads a single data frame from the stream and returns it.
        """
        # Keep reading until the stream is closed or we have a data frame.
        while not self.remote_closed and not self.data:
            self._recv_cb(stream_id=self.stream_id)

        try:
            return self.data.pop(0)
        except IndexError:
            return None

    def receive_response(self, event):
        """
        Receive response headers.
        """
        # TODO: If this is called while we're still sending data, we may want
        # to stop sending that data and check the response. Early responses to
        # big uploads are almost always a problem.
        self.response_headers = HTTPHeaderMap(event.headers)

    def receive_trailers(self, event):
        """
        Receive response trailers.
        """
        self.response_trailers = HTTPHeaderMap(event.headers)

    def receive_push(self, event):
        """
        Receive the request headers for a pushed stream.
        """
        self.promised_headers[event.pushed_stream_id] = event.headers

    def receive_data(self, event):
        """
        Receive a chunk of data.
        """
        size = event.flow_controlled_length
        increment = self._in_window_manager._handle_frame(size)

        # Append the data to the buffer.
        self.data.append(event.data)

        if increment:
            try:
                with self._conn as conn:
                    conn.increment_flow_control_window(
                        increment, stream_id=self.stream_id
                    )
            except h2.exceptions.StreamClosedError:
                # We haven't got to it yet, but the stream is already
                # closed. We don't need to increment the window in this
                # case!
                pass
            else:
                self._send_outstanding_data()

    def receive_end_stream(self, event):
        """
        All of the data is returned now.
        """
        self.remote_closed = True

    def receive_reset(self, event):
        """
        Stream forcefully reset.
        """
        self.remote_closed = True
        self._close_cb(self.stream_id)

    def get_headers(self):
        """
        Provides the headers to the connection object.
        """
        # Strip any headers invalid in H2.
        return h2_safe_headers(self.headers)

    def getheaders(self):
        """
        Once all data has been sent on this connection, returns a key-value set
        of the headers of the response to the original request.
        """
        # Keep reading until all headers are received.
        while self.response_headers is None:
            self._recv_cb(stream_id=self.stream_id)

        # Find the Content-Length header if present.
        self._in_window_manager.document_size = (
            int(self.response_headers.get(b'content-length', [0])[0])
        )

        return self.response_headers

    def gettrailers(self):
        """
        Once all data has been sent on this connection, returns a key-value set
        of the trailers of the response to the original request.

        .. warning:: Note that this method requires that the stream is
                     totally exhausted. This means that, if you have not
                     completely read from the stream, all stream data will be
                     read into memory.

        :returns: The key-value set of the trailers, or ``None`` if no trailers
                  were sent.
        """
        # Keep reading until the stream is done.
        while not self.remote_closed:
            self._recv_cb(stream_id=self.stream_id)

        return self.response_trailers

    def get_pushes(self, capture_all=False):
        """
        Returns a generator that yields push promises from the server. Note
        that this method is not idempotent; promises returned in one call will
        not be returned in subsequent calls. Iterating through generators
        returned by multiple calls to this method simultaneously results in
        undefined behavior.

        :param capture_all: If ``False``, the generator will yield all buffered
            push promises without blocking. If ``True``, the generator will
            first yield all buffered push promises, then yield additional ones
            as they arrive, and terminate when the original stream closes.
        """
        while True:
            for pair in self.promised_headers.items():
                yield pair
            self.promised_headers = {}
            if not capture_all or self.remote_closed:
                break
            self._recv_cb(stream_id=self.stream_id)

    def close(self, error_code=None):
        """
        Closes the stream. If the stream is currently open, attempts to close
        it as gracefully as possible.

        :param error_code: (optional) The error code to reset the stream with.
        :returns: Nothing.
        """
        # FIXME: I think this is overbroad, but for now it's probably ok.
        if not (self.remote_closed and self.local_closed):
            try:
                with self._conn as conn:
                    conn.reset_stream(self.stream_id, error_code or 0)
            except h2.exceptions.ProtocolError:
                # If for any reason we can't reset the stream, just
                # tolerate it.
                pass
            else:
                self._send_outstanding_data(tolerate_peer_gone=True)
            self.remote_closed = True
            self.local_closed = True

        self._close_cb(self.stream_id)

    @property
    def _out_flow_control_window(self):
        """
        The size of our outbound flow control window.
        """

        with self._conn as conn:
            return conn.local_flow_control_window(self.stream_id)

    def _send_chunk(self, data, final):
        """
        Implements most of the sending logic.

        Takes a single chunk of size at most MAX_CHUNK, wraps it in a frame and
        sends it. Optionally sets the END_STREAM flag if this is the last chunk
        (determined by being of size less than MAX_CHUNK) and no more data is
        to be sent.
        """
        # If we don't fit in the connection window, try popping frames off the
        # connection in hope that one might be a window update frame.
        while len(data) > self._out_flow_control_window:
            self._recv_cb()

        # Send the frame and decrement the flow control window.
        with self._conn as conn:
            conn.send_data(
                stream_id=self.stream_id, data=data, end_stream=final
            )
        self._send_outstanding_data()

        if final:
            self.local_closed = True
0707010001f474000081a40000000000000000000000016a100daf00000063000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/__init__.py   # -*- coding: utf-8 -*-
"""
hyper/http20
~~~~~~~~~~~~

The HTTP/2 submodule that powers hyper.
"""
 0707010001f477000081a40000000000000000000000016a100daf00000414000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/exceptions.py # -*- coding: utf-8 -*-
"""
hyper/http20/exceptions
~~~~~~~~~~~~~~~~~~~~~~~

This defines exceptions used in the HTTP/2 portion of hyper.
"""
from ..common.exceptions import ConnectionError as CommonConnectionError


class HTTP20Error(Exception):
    """
    The base class for all of ``hyper``'s HTTP/2-related exceptions.
    """
    pass


class HPACKEncodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK encoding.
    """
    pass


class HPACKDecodingError(HTTP20Error):
    """
    An error has been encountered while performing HPACK decoding.
    """
    pass


class ConnectionError(CommonConnectionError, HTTP20Error):
    """
    The remote party signalled an error affecting the entire HTTP/2
    connection, and the connection has been closed.
    """
    pass


class ProtocolError(HTTP20Error):
    """
    The remote party violated the HTTP/2 protocol.
    """
    pass


class StreamResetError(HTTP20Error):
    """
    A stream was forcefully reset by the remote party.
    """
    pass
0707010001f476000081a40000000000000000000000016a100daf00000cb3000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/errors.py # -*- coding: utf-8 -*-
# flake8: noqa
"""
hyper/http20/errors
~~~~~~~~~~~~~~~~~~~

Global error code registry containing the established HTTP/2 error codes.
The registry is based on a 32-bit space so we use the error code to index into
the array.

The current registry is available at:
https://tools.ietf.org/html/rfc7540#section-11.4
"""

NO_ERROR =            {'Name': 'NO_ERROR',
                       'Code': '0x0',
                       'Description': 'Graceful shutdown'}
PROTOCOL_ERROR =      {'Name': 'PROTOCOL_ERROR',
                       'Code': '0x1',
                       'Description': 'Protocol error detected'}
INTERNAL_ERROR =      {'Name': 'INTERNAL_ERROR',
                       'Code': '0x2',
                       'Description': 'Implementation fault'}
FLOW_CONTROL_ERROR =  {'Name': 'FLOW_CONTROL_ERROR',
                       'Code': '0x3',
                       'Description': 'Flow control limits exceeded'}
SETTINGS_TIMEOUT =    {'Name': 'SETTINGS_TIMEOUT',
                       'Code': '0x4',
                       'Description': 'Settings not acknowledged'}
STREAM_CLOSED =       {'Name': 'STREAM_CLOSED',
                       'Code': '0x5',
                       'Description': 'Frame received for closed stream'}
FRAME_SIZE_ERROR =    {'Name': 'FRAME_SIZE_ERROR',
                       'Code': '0x6',
                       'Description': 'Frame size incorrect'}
REFUSED_STREAM =      {'Name': 'REFUSED_STREAM ',
                       'Code': '0x7',
                       'Description': 'Stream not processed'}
CANCEL =              {'Name': 'CANCEL',
                       'Code': '0x8',
                       'Description': 'Stream cancelled'}
COMPRESSION_ERROR =   {'Name': 'COMPRESSION_ERROR',
                       'Code': '0x9',
                       'Description': 'Compression state not updated'}
CONNECT_ERROR =       {'Name': 'CONNECT_ERROR',
                       'Code': '0xa',
                       'Description':
                       'TCP connection error for CONNECT method'}
ENHANCE_YOUR_CALM =   {'Name': 'ENHANCE_YOUR_CALM',
                       'Code': '0xb',
                       'Description': 'Processing capacity exceeded'}
INADEQUATE_SECURITY = {'Name': 'INADEQUATE_SECURITY',
                       'Code': '0xc',
                       'Description':
                       'Negotiated TLS parameters not acceptable'}
HTTP_1_1_REQUIRED =   {'Name': 'HTTP_1_1_REQUIRED',
                       'Code': '0xd',
                       'Description': 'Use HTTP/1.1 for the request'}

H2_ERRORS = [NO_ERROR, PROTOCOL_ERROR, INTERNAL_ERROR, FLOW_CONTROL_ERROR,
             SETTINGS_TIMEOUT, STREAM_CLOSED, FRAME_SIZE_ERROR, REFUSED_STREAM,
             CANCEL, COMPRESSION_ERROR, CONNECT_ERROR, ENHANCE_YOUR_CALM,
             INADEQUATE_SECURITY, HTTP_1_1_REQUIRED]


def get_data(error_code):
    """
    Lookup the error code description, if not available throw a value error
    """
    if error_code < 0 or error_code >= len(H2_ERRORS):
        raise ValueError("Error code is invalid")

    name = H2_ERRORS[error_code]['Name']
    number = H2_ERRORS[error_code]['Code']
    description = H2_ERRORS[error_code]['Description']

    return name, number, description
 0707010001f47a000081a40000000000000000000000016a100daf00000638000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/util.py   # -*- coding: utf-8 -*-
"""
hyper/http20/util
~~~~~~~~~~~~~~~~~

Utility functions for use with hyper.
"""
from collections import defaultdict


def combine_repeated_headers(kvset):
    """
    Given a list of key-value pairs (like for HTTP headers!), combines pairs
    with the same key together, separating the values with NULL bytes. This
    function maintains the order of input keys, because it's awesome.
    """
    def set_pop(set, item):
        set.remove(item)
        return item

    headers = defaultdict(list)
    keys = set()

    for key, value in kvset:
        headers[key].append(value)
        keys.add(key)

    return [(set_pop(keys, k), b'\x00'.join(headers[k])) for k, v in kvset
            if k in keys]


def split_repeated_headers(kvset):
    """
    Given a set of key-value pairs (like for HTTP headers!), finds values that
    have NULL bytes in them and splits them into a dictionary whose values are
    lists.
    """
    headers = defaultdict(list)

    for key, value in kvset:
        headers[key] = value.split(b'\x00')

    return dict(headers)


def h2_safe_headers(headers):
    """
    This method takes a set of headers that are provided by the user and
    transforms them into a form that is safe for emitting over HTTP/2.

    Currently, this strips the Connection header and any header it refers to.
    """
    stripped = {
        i.lower().strip()
        for k, v in headers if k == b'connection'
        for i in v.split(b',')
    }
    stripped.add(b'connection')

    return [header for header in headers if header[0] not in stripped]
0707010001f47b000081a40000000000000000000000016a100daf0000180b000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hyper/http20/window.py # -*- coding: utf-8 -*-
"""
hyper/http20/window
~~~~~~~~~~~~~~~~~~~

Objects that understand flow control in hyper.

HTTP/2 implements connection- and stream-level flow control. This flow
control is mandatory. Unfortunately, it's difficult for hyper to be
all that intelligent about how it manages flow control in a general case.

This module defines an interface for pluggable flow-control managers. These
managers will define a flow-control policy. This policy will determine when to
send WINDOWUPDATE frames.
"""


class BaseFlowControlManager(object):
    """
    The abstract base class for flow control managers.

    This class defines the interface for pluggable flow-control managers. A
    flow-control manager defines a flow-control policy, which basically boils
    down to deciding when to increase the flow control window.

    This decision can be based on a number of factors:

    - the initial window size,
    - the size of the document being retrieved,
    - the size of the received data frames,
    - any other information the manager can obtain

    A flow-control manager may be defined at the connection level or at the
    stream level. If no stream-level flow-control manager is defined, an
    instance of the connection-level flow control manager is used.

    A class that inherits from this one must not adjust the member variables
    defined in this class. They are updated and set by methods on this class.
    """
    def __init__(self, initial_window_size, document_size=None):
        #: The initial size of the connection window in bytes. This is set at
        #: creation time.
        self.initial_window_size = initial_window_size

        #: The current size of the connection window. Any methods overridden
        #: by the user must not adjust this value.
        self.window_size = initial_window_size

        #: The size of the document being retrieved, in bytes. This is
        #: retrieved from the Content-Length header, if provided. Note that
        #: the total number of bytes that will be received may be larger than
        #: this value due to HTTP/2 padding. It should not be assumed that
        #: simply because the the document size is smaller than the initial
        #: window size that there will never be a need to increase the window
        #: size.
        self.document_size = document_size

    def increase_window_size(self, frame_size):
        """
        Determine whether or not to emit a WINDOWUPDATE frame.

        This method should be overridden to determine, based on the state of
        the system and the size of the received frame, whether or not a
        WindowUpdate frame should be sent for the stream.

        This method should *not* adjust any of the member variables of this
        class.

        Note that this method is called before the window size is decremented
        as a result of the frame being handled.

        :param frame_size: The size of the received frame. Note that this *may*
          be zero. When this parameter is zero, it's possible that a
          WINDOWUPDATE frame may want to be emitted anyway. A zero-length frame
          size is usually associated with a change in the size of the receive
          window due to a SETTINGS frame.
        :returns: The amount to increase the receive window by. Return zero if
          the window should not be increased.
        """
        raise NotImplementedError(
            "FlowControlManager is an abstract base class"
        )

    def blocked(self):
        """
        Called whenever the remote endpoint reports that it is blocked behind
        the flow control window.

        When this method is called the remote endpoint is signaling that it
        has more data to send and that the transport layer is capable of
        transmitting it, but that the HTTP/2 flow control window prevents it
        being sent.

        This method should return the size by which the window should be
        incremented, which may be zero. This method should *not* adjust any
        of the member variables of this class.

        :returns: The amount to increase the receive window by. Return zero if
          the window should not be increased.
        """
        # TODO: Is this method necessary?
        raise NotImplementedError(
            "FlowControlManager is an abstract base class"
        )

    def _handle_frame(self, frame_size):
        """
        This internal method is called by the connection or stream that owns
        the flow control manager. It handles the generic behaviour of flow
        control managers: namely, keeping track of the window size.
        """
        rc = self.increase_window_size(frame_size)
        self.window_size -= frame_size
        self.window_size += rc
        return rc

    def _blocked(self):
        """
        This internal method is called by the connection or stream that owns
        the flow control manager. It handles the generic behaviour of receiving
        BLOCKED frames.
        """
        rc = self.blocked()
        self.window_size += rc
        return rc


class FlowControlManager(BaseFlowControlManager):
    """
    ``hyper``'s default flow control manager.

    This implements hyper's flow control algorithms. This algorithm attempts to
    reduce the number of WINDOWUPDATE frames we send without blocking the
    remote endpoint behind the flow control window.

    This algorithm will become more complicated over time. In the current form,
    the algorithm is very simple:

    - When the flow control window gets less than 1/4 of the maximum size,
      increment back to the maximum.
    - Otherwise, if the flow control window gets to less than 1kB, increment
      back to the maximum.
    """
    def increase_window_size(self, frame_size):
        future_window_size = self.window_size - frame_size

        if ((future_window_size < (self.initial_window_size / 4)) or
                (future_window_size < 1000)):
            return self.initial_window_size - future_window_size

        return 0

    def blocked(self):
        return self.initial_window_size - self.window_size
 0707010001f46c000081a40000000000000000000000016a100daf000006ef000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/hyper/compat.py    # -*- coding: utf-8 -*-
# flake8: noqa
"""
hyper/compat
~~~~~~~~~~~~

Normalizes the Python 2/3 API for internal use.
"""
from contextlib import contextmanager
import sys
import zlib

try:
    from . import ssl_compat
except ImportError:
    # TODO log?
    ssl_compat = None

_ver = sys.version_info
is_py2 = _ver[0] == 2
is_py2_7_9_or_later = _ver[0] >= 2 and _ver[1] >= 7 and _ver[2] >= 9
is_py3 = _ver[0] == 3
is_py3_3 = is_py3 and _ver[1] == 3


@contextmanager
def ignore_missing():
    try:
        yield
    except (AttributeError, NotImplementedError):  # pragma: no cover
        pass

if is_py2:
    if is_py2_7_9_or_later:
        import ssl
    else:
        ssl = ssl_compat

    from urllib import urlencode
    from urlparse import urlparse, urlsplit
    from itertools import imap

    def to_byte(char):
        return ord(char)

    def decode_hex(b):
        return b.decode('hex')

    def write_to_stdout(data):
        sys.stdout.write(data + '\n')
        sys.stdout.flush()

    # The standard zlib.compressobj() accepts only positional arguments.
    def zlib_compressobj(level=6, method=zlib.DEFLATED, wbits=15, memlevel=8,
                         strategy=zlib.Z_DEFAULT_STRATEGY):
        return zlib.compressobj(level, method, wbits, memlevel, strategy)

    unicode = unicode
    bytes = str

elif is_py3:
    from urllib.parse import urlencode, urlparse, urlsplit

    imap = map

    def to_byte(char):
        return char

    def decode_hex(b):
        return bytes.fromhex(b)

    def write_to_stdout(data):
        sys.stdout.buffer.write(data + b'\n')
        sys.stdout.buffer.flush()

    zlib_compressobj = zlib.compressobj

    if is_py3_3:
        ssl = ssl_compat
    else:
        import ssl

    unicode = str
    bytes = bytes
 0707010001f462000081a40000000000000000000000016a100daf0004ee65000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/hyper/certs.pem    
# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
# Label: "GlobalSign Root CA"
# Serial: 4835703278459707669005204
# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a
# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c
# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----

# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R2
# Label: "GlobalSign Root CA - R2"
# Serial: 4835703278459682885658125
# MD5 Fingerprint: 94:14:77:7e:3e:5e:fd:8f:30:bd:41:b0:cf:e7:d0:30
# SHA1 Fingerprint: 75:e0:ab:b6:13:85:12:27:1c:04:f8:5f:dd:de:38:e4:b7:24:2e:fe
# SHA256 Fingerprint: ca:42:dd:41:74:5f:d0:b8:1e:b9:02:36:2c:f9:d8:bf:71:9d:a1:bd:1b:1e:fc:94:6f:5b:4c:99:f4:2c:1b:9e
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----

# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G3 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 1999 VeriSign, Inc. - For authorized use only
# Label: "Verisign Class 3 Public Primary Certification Authority - G3"
# Serial: 206684696279472310254277870180966723415
# MD5 Fingerprint: cd:68:b6:a7:c7:c4:ce:75:e0:1d:4f:57:44:61:92:09
# SHA1 Fingerprint: 13:2d:0d:45:53:4b:69:97:cd:b2:d5:c3:39:e2:55:76:60:9b:5c:c6
# SHA256 Fingerprint: eb:04:cf:5e:b1:f3:9a:fa:76:2f:2b:b1:20:f2:96:cb:a5:20:c1:b9:7d:b1:58:95:65:b8:1c:b9:a1:7b:72:44
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
-----END CERTIFICATE-----

# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited
# Label: "Entrust.net Premium 2048 Secure Server CA"
# Serial: 946069240
# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90
# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31
# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3
MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub
j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo
U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b
u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
fF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----

# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust
# Label: "Baltimore CyberTrust Root"
# Serial: 33554617
# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4
# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74
# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----

# Issuer: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
# Subject: CN=AddTrust Class 1 CA Root O=AddTrust AB OU=AddTrust TTP Network
# Label: "AddTrust Low-Value Services Root"
# Serial: 1
# MD5 Fingerprint: 1e:42:95:02:33:92:6b:b9:5f:c0:7f:da:d6:b2:4b:fc
# SHA1 Fingerprint: cc:ab:0e:a0:4c:23:01:d6:69:7b:dd:37:9f:cd:12:eb:24:e3:94:9d
# SHA256 Fingerprint: 8c:72:09:27:9a:c0:4e:27:5e:16:d0:7f:d3:b7:75:e8:01:54:b5:96:80:46:e3:1f:52:dd:25:76:63:24:e9:a7
-----BEGIN CERTIFICATE-----
MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
-----END CERTIFICATE-----

# Issuer: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
# Subject: CN=AddTrust External CA Root O=AddTrust AB OU=AddTrust External TTP Network
# Label: "AddTrust External Root"
# Serial: 1
# MD5 Fingerprint: 1d:35:54:04:85:78:b0:3f:42:42:4d:bf:20:73:0a:3f
# SHA1 Fingerprint: 02:fa:f3:e2:91:43:54:68:60:78:57:69:4d:f5:e4:5b:68:85:18:68
# SHA256 Fingerprint: 68:7f:a4:51:38:22:78:ff:f0:c8:b1:1f:8d:43:d5:76:67:1c:6e:b2:bc:ea:b4:13:fb:83:d9:65:d0:6d:2f:f2
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----

# Issuer: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
# Subject: CN=AddTrust Public CA Root O=AddTrust AB OU=AddTrust TTP Network
# Label: "AddTrust Public Services Root"
# Serial: 1
# MD5 Fingerprint: c1:62:3e:23:c5:82:73:9c:03:59:4b:2b:e9:77:49:7f
# SHA1 Fingerprint: 2a:b6:28:48:5e:78:fb:f3:ad:9e:79:10:dd:6b:df:99:72:2c:96:e5
# SHA256 Fingerprint: 07:91:ca:07:49:b2:07:82:aa:d3:c7:d7:bd:0c:df:c9:48:58:35:84:3e:b2:d7:99:60:09:ce:43:ab:6c:69:27
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
BAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
-----END CERTIFICATE-----

# Issuer: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
# Subject: CN=AddTrust Qualified CA Root O=AddTrust AB OU=AddTrust TTP Network
# Label: "AddTrust Qualified Certificates Root"
# Serial: 1
# MD5 Fingerprint: 27:ec:39:47:cd:da:5a:af:e2:9a:01:65:21:a9:4c:bb
# SHA1 Fingerprint: 4d:23:78:ec:91:95:39:b5:00:7f:75:8f:03:3b:21:1e:c5:4d:8b:cf
# SHA256 Fingerprint: 80:95:21:08:05:db:4b:bc:35:5e:44:28:d8:fd:6e:c2:cd:e3:ab:5f:b9:7a:99:42:98:8e:b8:f4:dc:d0:60:16
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
xqE=
-----END CERTIFICATE-----

# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc.
# Label: "Entrust Root Certification Authority"
# Serial: 1164660820
# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4
# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9
# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----

# Issuer: O=RSA Security Inc OU=RSA Security 2048 V3
# Subject: O=RSA Security Inc OU=RSA Security 2048 V3
# Label: "RSA Security 2048 v3"
# Serial: 13297492616345471454730593562152402946
# MD5 Fingerprint: 77:0d:19:b1:21:fd:00:42:9c:3e:0c:a5:dd:0b:02:8e
# SHA1 Fingerprint: 25:01:90:19:cf:fb:d9:99:1c:b7:68:25:74:8d:94:5f:30:93:95:42
# SHA256 Fingerprint: af:8b:67:62:a1:e5:28:22:81:61:a9:5d:5c:55:9e:e2:66:27:8f:75:d7:9e:83:01:89:a5:03:50:6a:bd:6b:4c
-----BEGIN CERTIFICATE-----
MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6
MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX
BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy
MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp
eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg
/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl
wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh
AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2
PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu
AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR
MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc
HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/
Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO
rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch
6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3
7CAFYd4=
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Global CA O=GeoTrust Inc.
# Subject: CN=GeoTrust Global CA O=GeoTrust Inc.
# Label: "GeoTrust Global CA"
# Serial: 144470
# MD5 Fingerprint: f7:75:ab:29:fb:51:4e:b7:77:5e:ff:05:3c:99:8e:f5
# SHA1 Fingerprint: de:28:f4:a4:ff:e5:b9:2f:a3:c5:03:d1:a3:49:a7:f9:96:2a:82:12
# SHA256 Fingerprint: ff:85:6a:2d:25:1d:cd:88:d3:66:56:f4:50:12:67:98:cf:ab:aa:de:40:79:9c:72:2d:e4:d2:b5:db:36:a7:3a
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
# Subject: CN=GeoTrust Global CA 2 O=GeoTrust Inc.
# Label: "GeoTrust Global CA 2"
# Serial: 1
# MD5 Fingerprint: 0e:40:a7:6c:de:03:5d:8f:d1:0f:e4:d1:8d:f9:6c:a9
# SHA1 Fingerprint: a9:e9:78:08:14:37:58:88:f2:05:19:b0:6d:2b:0d:2b:60:16:90:7d
# SHA256 Fingerprint: ca:2d:82:a0:86:77:07:2f:8a:b6:76:4f:f0:35:67:6c:fe:3e:5e:32:5e:01:21:72:df:3f:92:09:6d:b7:9b:85
-----BEGIN CERTIFICATE-----
MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
4iIprn2DQKi6bA==
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Universal CA O=GeoTrust Inc.
# Subject: CN=GeoTrust Universal CA O=GeoTrust Inc.
# Label: "GeoTrust Universal CA"
# Serial: 1
# MD5 Fingerprint: 92:65:58:8b:a2:1a:31:72:73:68:5c:b4:a5:7a:07:48
# SHA1 Fingerprint: e6:21:f3:35:43:79:05:9a:4b:68:30:9d:8a:2f:74:22:15:87:ec:79
# SHA256 Fingerprint: a0:45:9b:9f:63:b2:25:59:f5:fa:5d:4c:6d:b3:f9:f7:2f:f1:93:42:03:35:78:f0:73:bf:1d:1b:46:cb:b9:12
-----BEGIN CERTIFICATE-----
MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
# Subject: CN=GeoTrust Universal CA 2 O=GeoTrust Inc.
# Label: "GeoTrust Universal CA 2"
# Serial: 1
# MD5 Fingerprint: 34:fc:b8:d0:36:db:9e:14:b3:c2:f2:db:8f:e4:94:c7
# SHA1 Fingerprint: 37:9a:19:7b:41:85:45:35:0c:a6:03:69:f3:3c:2e:af:47:4f:20:79
# SHA256 Fingerprint: a0:23:4f:3b:c8:52:7c:a5:62:8e:ec:81:ad:5d:69:89:5d:a5:68:0d:c9:1d:1c:b8:47:7f:33:f8:78:b9:5b:0b
-----BEGIN CERTIFICATE-----
MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
-----END CERTIFICATE-----

# Issuer: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association
# Subject: CN=Visa eCommerce Root O=VISA OU=Visa International Service Association
# Label: "Visa eCommerce Root"
# Serial: 25952180776285836048024890241505565794
# MD5 Fingerprint: fc:11:b8:d8:08:93:30:00:6d:23:f9:7e:eb:52:1e:02
# SHA1 Fingerprint: 70:17:9b:86:8c:00:a4:fa:60:91:52:22:3f:9f:3e:32:bd:e0:05:62
# SHA256 Fingerprint: 69:fa:c9:bd:55:fb:0a:c7:8d:53:bb:ee:5c:f1:d5:97:98:9f:d0:aa:ab:20:a2:51:51:bd:f1:73:3e:e7:d1:22
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr
MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl
cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h
dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l
cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h
2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E
lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV
ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq
299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t
vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL
dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF
AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3
LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw
++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
398znM/jra6O1I7mT1GvFpLgXPYHDw==
-----END CERTIFICATE-----

# Issuer: CN=Certum CA O=Unizeto Sp. z o.o.
# Subject: CN=Certum CA O=Unizeto Sp. z o.o.
# Label: "Certum Root CA"
# Serial: 65568
# MD5 Fingerprint: 2c:8f:9f:66:1d:18:90:b1:47:26:9d:8e:86:82:8c:a9
# SHA1 Fingerprint: 62:52:dc:40:f7:11:43:a2:2f:de:9e:f7:34:8e:06:42:51:b1:81:18
# SHA256 Fingerprint: d8:e0:fe:bc:1d:b2:e3:8d:00:94:0f:37:d2:7d:41:34:4d:99:3e:73:4b:99:d5:65:6d:97:78:d4:d8:14:36:24
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
6GAqm4VKQPNriiTsBhYscw==
-----END CERTIFICATE-----

# Issuer: CN=AAA Certificate Services O=Comodo CA Limited
# Subject: CN=AAA Certificate Services O=Comodo CA Limited
# Label: "Comodo AAA Services root"
# Serial: 1
# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----

# Issuer: CN=Secure Certificate Services O=Comodo CA Limited
# Subject: CN=Secure Certificate Services O=Comodo CA Limited
# Label: "Comodo Secure Services root"
# Serial: 1
# MD5 Fingerprint: d3:d9:bd:ae:9f:ac:67:24:b3:c8:1b:52:e1:b9:a9:bd
# SHA1 Fingerprint: 4a:65:d5:f4:1d:ef:39:b8:b8:90:4a:4a:d3:64:81:33:cf:c7:a1:d1
# SHA256 Fingerprint: bd:81:ce:3b:4f:65:91:d1:1a:67:b5:fc:7a:47:fd:ef:25:52:1b:f9:aa:4e:18:b9:e3:df:2e:34:a7:80:3b:e8
-----BEGIN CERTIFICATE-----
MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
-----END CERTIFICATE-----

# Issuer: CN=Trusted Certificate Services O=Comodo CA Limited
# Subject: CN=Trusted Certificate Services O=Comodo CA Limited
# Label: "Comodo Trusted Services root"
# Serial: 1
# MD5 Fingerprint: 91:1b:3f:6e:cd:9e:ab:ee:07:fe:1f:71:d2:b3:61:27
# SHA1 Fingerprint: e1:9f:e3:0e:8b:84:60:9e:80:9b:17:0d:72:a8:c5:ba:6e:14:09:bd
# SHA256 Fingerprint: 3f:06:e5:56:81:d4:96:f5:be:16:9e:b5:38:9f:9f:2b:8f:f6:1e:17:08:df:68:81:72:48:49:cd:5d:27:cb:69
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
# Subject: CN=QuoVadis Root Certification Authority O=QuoVadis Limited OU=Root Certification Authority
# Label: "QuoVadis Root CA"
# Serial: 985026699
# MD5 Fingerprint: 27:de:36:fe:72:b7:00:03:00:9d:f4:f0:1e:6c:04:24
# SHA1 Fingerprint: de:3f:40:bd:50:93:d3:9b:6c:60:f6:da:bc:07:62:01:00:89:76:c9
# SHA256 Fingerprint: a4:5e:de:3b:bb:f0:9c:8a:e1:5c:72:ef:c0:72:68:d6:93:a2:1c:99:6f:d5:1e:67:ca:07:94:60:fd:6d:88:73
-----BEGIN CERTIFICATE-----
MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
SnQ2+Q==
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited
# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited
# Label: "QuoVadis Root CA 2"
# Serial: 1289
# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b
# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7
# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited
# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited
# Label: "QuoVadis Root CA 3"
# Serial: 1478
# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf
# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85
# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----

# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1
# Subject: O=SECOM Trust.net OU=Security Communication RootCA1
# Label: "Security Communication Root CA"
# Serial: 0
# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a
# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7
# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
-----END CERTIFICATE-----

# Issuer: CN=Sonera Class2 CA O=Sonera
# Subject: CN=Sonera Class2 CA O=Sonera
# Label: "Sonera Class 2 Root CA"
# Serial: 29
# MD5 Fingerprint: a3:ec:75:0f:2e:88:df:fa:48:01:4e:0b:5c:48:6f:fb
# SHA1 Fingerprint: 37:f7:6d:e6:07:7c:90:c5:b1:3e:93:1a:b7:41:10:b4:f2:e4:9a:27
# SHA256 Fingerprint: 79:08:b4:03:14:c1:38:10:0b:51:8d:07:35:80:7f:fb:fc:f8:51:8a:00:95:33:71:05:ba:38:6b:15:3d:d9:27
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
-----END CERTIFICATE-----

# Issuer: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
# Subject: CN=UTN-USERFirst-Hardware O=The USERTRUST Network OU=http://www.usertrust.com
# Label: "UTN USERFirst Hardware Root CA"
# Serial: 91374294542884704022267039221184531197
# MD5 Fingerprint: 4c:56:41:e5:0d:bb:2b:e8:ca:a3:ed:18:08:ad:43:39
# SHA1 Fingerprint: 04:83:ed:33:99:ac:36:08:05:87:22:ed:bc:5e:46:00:e3:be:f9:d7
# SHA256 Fingerprint: 6e:a5:47:41:d0:04:66:7e:ed:1b:48:16:63:4a:a3:a7:9e:6e:4b:96:95:0f:82:79:da:fc:8d:9b:d8:81:21:37
-----BEGIN CERTIFICATE-----
MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
-----END CERTIFICATE-----

# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
# Label: "Camerfirma Chambers of Commerce Root"
# Serial: 0
# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84
# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1
# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
tGWaIZDgqtCYvDi1czyL+Nw=
-----END CERTIFICATE-----

# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
# Label: "Camerfirma Global Chambersign Root"
# Serial: 0
# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19
# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9
# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed
-----BEGIN CERTIFICATE-----
MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
-----END CERTIFICATE-----

# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
# Label: "XRamp Global CA Root"
# Serial: 107108908803651509692980124233745014957
# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1
# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6
# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
O+7ETPTsJ3xCwnR8gooJybQDJbw=
-----END CERTIFICATE-----

# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
# Label: "Go Daddy Class 2 CA"
# Serial: 0
# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67
# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4
# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----

# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
# Label: "Starfield Class 2 CA"
# Serial: 0
# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24
# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a
# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----

# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
# Label: "StartCom Certification Authority"
# Serial: 1
# MD5 Fingerprint: 22:4d:8f:8a:fc:f7:35:c2:bb:57:34:90:7b:8b:22:16
# SHA1 Fingerprint: 3e:2b:f7:f2:03:1b:96:f3:8c:e6:c4:d8:a8:5d:3e:2d:58:47:6a:0f
# SHA256 Fingerprint: c7:66:a9:be:f2:d4:07:1c:86:3a:31:aa:49:20:e8:13:b2:d1:98:60:8c:b7:b7:cf:e2:11:43:b8:36:df:09:ea
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----

# Issuer: O=Government Root Certification Authority
# Subject: O=Government Root Certification Authority
# Label: "Taiwan GRCA"
# Serial: 42023070807708724159991140556527066870
# MD5 Fingerprint: 37:85:44:53:32:45:1f:20:f0:f3:95:e1:25:c4:43:4e
# SHA1 Fingerprint: f4:8b:11:bf:de:ab:be:94:54:20:71:e6:41:de:6b:be:88:2b:40:b9
# SHA256 Fingerprint: 76:00:29:5e:ef:e8:5b:9e:1f:d6:24:db:76:06:2a:aa:ae:59:81:8a:54:d2:77:4c:d4:c0:b2:c0:11:31:e1:b3
-----BEGIN CERTIFICATE-----
MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
pYYsfPQS
-----END CERTIFICATE-----

# Issuer: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
# Subject: CN=Swisscom Root CA 1 O=Swisscom OU=Digital Certificate Services
# Label: "Swisscom Root CA 1"
# Serial: 122348795730808398873664200247279986742
# MD5 Fingerprint: f8:38:7c:77:88:df:2c:16:68:2e:c2:e2:52:4b:b8:f9
# SHA1 Fingerprint: 5f:3a:fc:0a:8b:64:f6:86:67:34:74:df:7e:a9:a2:fe:f9:fa:7a:51
# SHA256 Fingerprint: 21:db:20:12:36:60:bb:2e:d4:18:20:5d:a1:1e:e7:a8:5a:65:e2:bc:6e:55:b5:af:7e:78:99:c8:a2:66:d9:2e
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk
MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT
AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9
m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih
FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/
TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F
EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco
kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu
HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF
vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo
19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC
L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW
bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX
JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc
K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf
ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik
Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB
sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e
3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR
ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip
mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH
b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf
rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms
hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y
zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6
MBr1mmz0DlP5OlvRHA==
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Assured ID Root CA"
# Serial: 17154717934120587862167794914071425081
# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72
# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43
# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Global Root CA"
# Serial: 10944719598952040374951832963794454346
# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e
# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36
# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----

# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert High Assurance EV Root CA"
# Serial: 3553400076410547919724730734378100087
# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a
# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25
# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----

# Issuer: CN=Class 2 Primary CA O=Certplus
# Subject: CN=Class 2 Primary CA O=Certplus
# Label: "Certplus Class 2 Primary CA"
# Serial: 177770208045934040241468760488327595043
# MD5 Fingerprint: 88:2c:8c:52:b8:a2:3c:f3:f7:bb:03:ea:ae:ac:42:0b
# SHA1 Fingerprint: 74:20:74:41:72:9c:dd:92:ec:79:31:d8:23:10:8d:c2:81:92:e2:bb
# SHA256 Fingerprint: 0f:99:3c:8a:ef:97:ba:af:56:87:14:0e:d5:9a:d1:82:1b:b4:af:ac:f0:aa:9a:58:b5:d5:7a:33:8a:3a:fb:cb
-----BEGIN CERTIFICATE-----
MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
l7+ijrRU
-----END CERTIFICATE-----

# Issuer: CN=DST Root CA X3 O=Digital Signature Trust Co.
# Subject: CN=DST Root CA X3 O=Digital Signature Trust Co.
# Label: "DST Root CA X3"
# Serial: 91299735575339953335919266965803778155
# MD5 Fingerprint: 41:03:52:dc:0f:f7:50:1b:16:f0:02:8e:ba:6f:45:c5
# SHA1 Fingerprint: da:c9:02:4f:54:d8:f6:df:94:93:5f:b1:73:26:38:ca:6a:d7:7c:13
# SHA256 Fingerprint: 06:87:26:03:31:a7:24:03:d9:09:f1:05:e6:9b:cf:0d:32:e1:bd:24:93:ff:c6:d9:20:6d:11:bc:d6:77:07:39
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----

# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
# Label: "DST ACES CA X6"
# Serial: 17771143917277623872238992636097467865
# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8
# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d
# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40
-----BEGIN CERTIFICATE-----
MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
-----END CERTIFICATE-----

# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG
# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG
# Label: "SwissSign Gold CA - G2"
# Serial: 13492815561806991280
# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93
# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61
# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----

# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG
# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG
# Label: "SwissSign Silver CA - G2"
# Serial: 5700383053117599563
# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13
# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb
# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
# Subject: CN=GeoTrust Primary Certification Authority O=GeoTrust Inc.
# Label: "GeoTrust Primary Certification Authority"
# Serial: 32798226551256963324313806436981982369
# MD5 Fingerprint: 02:26:c3:01:5e:08:30:37:43:a9:d0:7d:cf:37:e6:bf
# SHA1 Fingerprint: 32:3c:11:8e:1b:f7:b8:b6:52:54:e2:e2:10:0d:d6:02:90:37:f0:96
# SHA256 Fingerprint: 37:d5:10:06:c5:12:ea:ab:62:64:21:f1:ec:8c:92:01:3f:c5:f8:2a:e9:8e:e5:33:eb:46:19:b8:de:b4:d0:6c
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
-----END CERTIFICATE-----

# Issuer: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
# Subject: CN=thawte Primary Root CA O=thawte, Inc. OU=Certification Services Division/(c) 2006 thawte, Inc. - For authorized use only
# Label: "thawte Primary Root CA"
# Serial: 69529181992039203566298953787712940909
# MD5 Fingerprint: 8c:ca:dc:0b:22:ce:f5:be:72:ac:41:1a:11:a8:d8:12
# SHA1 Fingerprint: 91:c6:d6:ee:3e:8a:c8:63:84:e5:48:c2:99:29:5c:75:6c:81:7b:81
# SHA256 Fingerprint: 8d:72:2f:81:a9:c1:13:c0:79:1d:f1:36:a2:96:6d:b2:6c:95:0a:97:1d:b4:6b:41:99:f4:ea:54:b7:8b:fb:9f
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----

# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G5 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2006 VeriSign, Inc. - For authorized use only
# Label: "VeriSign Class 3 Public Primary Certification Authority - G5"
# Serial: 33037644167568058970164719475676101450
# MD5 Fingerprint: cb:17:e4:31:67:3e:e2:09:fe:45:57:93:f3:0a:fa:1c
# SHA1 Fingerprint: 4e:b6:d5:78:49:9b:1c:cf:5f:58:1e:ad:56:be:3d:9b:67:44:a5:e5
# SHA256 Fingerprint: 9a:cf:ab:7e:43:c8:d8:80:d0:6b:26:2a:94:de:ee:e4:b4:65:99:89:c3:d0:ca:f1:9b:af:64:05:e4:1a:b7:df
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----

# Issuer: CN=SecureTrust CA O=SecureTrust Corporation
# Subject: CN=SecureTrust CA O=SecureTrust Corporation
# Label: "SecureTrust CA"
# Serial: 17199774589125277788362757014266862032
# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1
# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11
# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----

# Issuer: CN=Secure Global CA O=SecureTrust Corporation
# Subject: CN=Secure Global CA O=SecureTrust Corporation
# Label: "Secure Global CA"
# Serial: 9751836167731051554232119481456978597
# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de
# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b
# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69
-----BEGIN CERTIFICATE-----
MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
-----END CERTIFICATE-----

# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
# Label: "COMODO Certification Authority"
# Serial: 104350513648249232941998508985834464573
# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75
# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b
# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66
-----BEGIN CERTIFICATE-----
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
ZQ==
-----END CERTIFICATE-----

# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C.
# Label: "Network Solutions Certificate Authority"
# Serial: 116697915152937497490437556386812487904
# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e
# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce
# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----

# Issuer: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA
# Subject: CN=WellsSecure Public Root Certificate Authority O=Wells Fargo WellsSecure OU=Wells Fargo Bank NA
# Label: "WellsSecure Public Root Certificate Authority"
# Serial: 1
# MD5 Fingerprint: 15:ac:a5:c2:92:2d:79:bc:e8:7f:cb:67:ed:02:cf:36
# SHA1 Fingerprint: e7:b4:f6:9d:61:ec:90:69:db:7e:90:a7:40:1a:3c:f4:7d:4f:e8:ee
# SHA256 Fingerprint: a7:12:72:ae:aa:a3:cf:e8:72:7f:7f:b3:9f:0f:b3:d1:e5:42:6e:90:60:b0:6e:e6:f1:3e:9a:3c:58:33:cd:43
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx
IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs
cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0
MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl
bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD
DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r
WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU
Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs
HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj
z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf
SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl
AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG
KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P
AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j
BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC
VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX
ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB
ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd
/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB
A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn
k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9
iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv
2G0xffX8oRAHh84vWdw+WNs=
-----END CERTIFICATE-----

# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
# Label: "COMODO ECC Certification Authority"
# Serial: 41578283867086692638256921589707938090
# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23
# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11
# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7
-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----

# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
# Label: "Security Communication EV RootCA1"
# Serial: 0
# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3
# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d
# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37
-----BEGIN CERTIFICATE-----
MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz
MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N
IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11
bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE
RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO
zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5
bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF
MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1
VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC
OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW
tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ
q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb
EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+
Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O
VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
-----END CERTIFICATE-----

# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
# Label: "OISTE WISeKey Global Root GA CA"
# Serial: 86718877871133159090080555911823548314
# MD5 Fingerprint: bc:6c:51:33:a7:e9:d3:66:63:54:15:72:1b:21:92:93
# SHA1 Fingerprint: 59:22:a1:e1:5a:ea:16:35:21:f8:98:39:6a:46:46:b0:44:1b:0f:a9
# SHA256 Fingerprint: 41:c9:23:86:6a:b4:ca:d6:b7:ad:57:80:81:58:2e:02:07:97:a6:cb:df:4f:ff:78:ce:83:96:b3:89:37:d7:f5
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB
ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly
aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w
NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G
A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD
VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX
SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR
VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2
w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF
mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg
4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9
4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw
EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx
SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2
ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8
vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi
Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ
/L7fCg0=
-----END CERTIFICATE-----

# Issuer: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA
# Subject: CN=Microsec e-Szigno Root CA O=Microsec Ltd. OU=e-Szigno CA
# Label: "Microsec e-Szigno Root CA"
# Serial: 272122594155480254301341951808045322001
# MD5 Fingerprint: f0:96:b6:2f:c5:10:d5:67:8e:83:25:32:e8:5e:2e:e5
# SHA1 Fingerprint: 23:88:c9:d3:71:cc:9e:96:3d:ff:7d:3c:a7:ce:fc:d6:25:ec:19:0d
# SHA256 Fingerprint: 32:7a:3d:76:1a:ba:de:a0:34:eb:99:84:06:27:5c:b1:a4:77:6e:fd:ae:2f:df:6d:01:68:ea:1c:4f:55:67:d0
-----BEGIN CERTIFICATE-----
MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw
cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy
b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z
ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4
NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN
TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p
Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u
uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+
LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA
vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770
Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx
62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB
AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw
LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP
BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB
AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov
MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5
ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT
AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh
ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo
AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa
AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln
bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p
Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP
PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv
Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB
EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu
w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj
cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV
HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI
VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS
BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS
b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS
8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds
ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl
7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR
hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/
MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
-----END CERTIFICATE-----

# Issuer: CN=Certigna O=Dhimyotis
# Subject: CN=Certigna O=Dhimyotis
# Label: "Certigna"
# Serial: 18364802974209362175
# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff
# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97
# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X
DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ
BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4
QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny
gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw
zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q
130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2
JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw
ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT
AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj
AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG
9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h
bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc
fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu
HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w
t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----

# Issuer: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
# Subject: CN=Deutsche Telekom Root CA 2 O=Deutsche Telekom AG OU=T-TeleSec Trust Center
# Label: "Deutsche Telekom Root CA 2"
# Serial: 38
# MD5 Fingerprint: 74:01:4a:91:b1:08:c4:58:ce:47:cd:f0:dd:11:53:08
# SHA1 Fingerprint: 85:a4:08:c0:9c:19:3e:5d:51:58:7d:cd:d6:13:30:fd:8c:de:37:bf
# SHA256 Fingerprint: b6:19:1a:50:d0:c3:97:7f:7d:a9:9b:cd:aa:c8:6a:22:7d:ae:b9:67:9e:c7:0b:a3:b0:c9:d9:22:71:c1:70:d3
-----BEGIN CERTIFICATE-----
MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
Cm26OWMohpLzGITY+9HPBVZkVw==
-----END CERTIFICATE-----

# Issuer: CN=Cybertrust Global Root O=Cybertrust, Inc
# Subject: CN=Cybertrust Global Root O=Cybertrust, Inc
# Label: "Cybertrust Global Root"
# Serial: 4835703278459682877484360
# MD5 Fingerprint: 72:e4:4a:87:e3:69:40:80:77:ea:bc:e3:f4:ff:f0:e1
# SHA1 Fingerprint: 5f:43:e5:b1:bf:f8:78:8c:ac:1c:c7:ca:4a:9a:c6:22:2b:cc:34:c6
# SHA256 Fingerprint: 96:0a:df:00:63:e9:63:56:75:0c:29:65:dd:0a:08:67:da:0b:9c:bd:6e:77:71:4a:ea:fb:23:49:ab:39:3d:a3
-----BEGIN CERTIFICATE-----
MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
WL1WMRJOEcgh4LMRkWXbtKaIOM5V
-----END CERTIFICATE-----

# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority
# Label: "ePKI Root Certification Authority"
# Serial: 28956088682735189655030529057352760477
# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3
# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0
# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5
-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw
IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL
SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH
SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh
ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X
DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1
TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ
fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA
sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU
WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS
nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH
dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip
NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC
AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF
MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB
uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl
PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP
JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/
gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2
j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6
5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB
o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS
/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z
Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE
W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D
hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----

# Issuer: CN=TÃœBÄ°TAK UEKAE KÃ¶k Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± - SÃ¼rÃ¼m 3 O=TÃ¼rkiye Bilimsel ve Teknolojik AraÅŸtÄ±rma Kurumu - TÃœBÄ°TAK OU=Ulusal Elektronik ve Kriptoloji AraÅŸtÄ±rma EnstitÃ¼sÃ¼ - UEKAE/Kamu Sertifikasyon Merkezi
# Subject: CN=TÃœBÄ°TAK UEKAE KÃ¶k Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± - SÃ¼rÃ¼m 3 O=TÃ¼rkiye Bilimsel ve Teknolojik AraÅŸtÄ±rma Kurumu - TÃœBÄ°TAK OU=Ulusal Elektronik ve Kriptoloji AraÅŸtÄ±rma EnstitÃ¼sÃ¼ - UEKAE/Kamu Sertifikasyon Merkezi
# Label: "T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3"
# Serial: 17
# MD5 Fingerprint: ed:41:f5:8c:50:c5:2b:9c:73:e6:ee:6c:eb:c2:a8:26
# SHA1 Fingerprint: 1b:4b:39:61:26:27:6b:64:91:a2:68:6d:d7:02:43:21:2d:1f:1d:96
# SHA256 Fingerprint: e4:c7:34:30:d7:a5:b5:09:25:df:43:37:0a:0d:21:6e:9a:79:b9:d6:db:83:73:a0:c6:9e:b1:cc:31:c7:c5:2a
-----BEGIN CERTIFICATE-----
MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS
MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp
bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw
VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy
YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy
dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2
ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe
Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx
GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls
aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU
QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh
xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0
aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr
IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h
gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK
O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO
fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw
lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID
AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP
NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t
wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM
7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh
gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n
oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs
yZyQ2uypQjyttgI=
-----END CERTIFICATE-----

# Issuer: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327
# Subject: CN=Buypass Class 2 CA 1 O=Buypass AS-983163327
# Label: "Buypass Class 2 CA 1"
# Serial: 1
# MD5 Fingerprint: b8:08:9a:f0:03:cc:1b:0d:c8:6c:0b:76:a1:75:64:23
# SHA1 Fingerprint: a0:a1:ab:90:c9:fc:84:7b:3b:12:61:e8:97:7d:5f:d3:22:61:d3:cc
# SHA256 Fingerprint: 0f:4e:9c:dd:26:4b:02:55:50:d1:70:80:63:40:21:4f:e9:44:34:c9:b0:2f:69:7e:c7:10:fc:5f:ea:fb:5e:38
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL
MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0
ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX
l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB
HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B
5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3
WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD
AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP
gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+
DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu
BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs
h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk
LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
-----END CERTIFICATE-----

# Issuer: O=certSIGN OU=certSIGN ROOT CA
# Subject: O=certSIGN OU=certSIGN ROOT CA
# Label: "certSIGN ROOT CA"
# Serial: 35210227249154
# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17
# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b
# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT
AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD
QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP
MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do
0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ
UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d
RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ
OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv
JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C
AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O
BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ
LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY
MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ
44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I
Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw
i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN
9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----

# Issuer: CN=CNNIC ROOT O=CNNIC
# Subject: CN=CNNIC ROOT O=CNNIC
# Label: "CNNIC ROOT"
# Serial: 1228079105
# MD5 Fingerprint: 21:bc:82:ab:49:c4:13:3b:4b:b2:2b:5c:6b:90:9c:19
# SHA1 Fingerprint: 8b:af:4c:9b:1d:f0:2a:92:f7:da:12:8e:b9:1b:ac:f4:98:60:4b:6f
# SHA256 Fingerprint: e2:83:93:77:3d:a8:45:a6:79:f2:08:0c:c7:fb:44:a3:b7:a1:c3:79:2c:b7:eb:77:29:fd:cb:6a:8d:99:ae:a7
-----BEGIN CERTIFICATE-----
MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD
TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2
MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF
Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh
IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6
dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO
V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC
GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN
v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB
AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB
Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO
76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK
OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH
ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi
yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL
buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj
2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE=
-----END CERTIFICATE-----

# Issuer: O=Japanese Government OU=ApplicationCA
# Subject: O=Japanese Government OU=ApplicationCA
# Label: "ApplicationCA - Japanese Government"
# Serial: 49
# MD5 Fingerprint: 7e:23:4e:5b:a7:a5:b4:25:e9:00:07:74:11:62:ae:d6
# SHA1 Fingerprint: 7f:8a:b0:cf:d0:51:87:6a:66:f3:36:0f:47:c8:8d:8c:d3:35:fc:74
# SHA256 Fingerprint: 2d:47:43:7d:e1:79:51:21:5a:12:f3:c5:8e:51:c7:29:a5:80:26:ef:1f:cc:0a:5f:b3:d9:dc:01:2f:60:0d:19
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc
MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp
b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT
AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs
aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H
j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K
f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55
IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw
FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht
QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm
/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ
k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ
MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC
seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ
hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+
eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U
DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj
B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
rosot4LKGAfmt1t06SAZf7IbiVQ=
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
# Subject: CN=GeoTrust Primary Certification Authority - G3 O=GeoTrust Inc. OU=(c) 2008 GeoTrust Inc. - For authorized use only
# Label: "GeoTrust Primary Certification Authority - G3"
# Serial: 28809105769928564313984085209975885599
# MD5 Fingerprint: b5:e8:34:36:c9:10:44:58:48:70:6d:2e:83:d4:b8:05
# SHA1 Fingerprint: 03:9e:ed:b8:0b:e7:a0:3c:69:53:89:3b:20:d2:d9:32:3a:4c:2a:fd
# SHA256 Fingerprint: b4:78:b8:12:25:0d:f8:78:63:5c:2a:a7:ec:7d:15:5e:aa:62:5e:e8:29:16:e2:cd:29:43:61:88:6c:d1:fb:d4
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
spki4cErx5z481+oghLrGREt
-----END CERTIFICATE-----

# Issuer: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
# Subject: CN=thawte Primary Root CA - G2 O=thawte, Inc. OU=(c) 2007 thawte, Inc. - For authorized use only
# Label: "thawte Primary Root CA - G2"
# Serial: 71758320672825410020661621085256472406
# MD5 Fingerprint: 74:9d:ea:60:24:c4:fd:22:53:3e:cc:3a:72:d9:29:4f
# SHA1 Fingerprint: aa:db:bc:22:23:8f:c4:01:a1:27:bb:38:dd:f4:1d:db:08:9e:f0:12
# SHA256 Fingerprint: a4:31:0d:50:af:18:a6:44:71:90:37:2a:86:af:af:8b:95:1f:fb:43:1d:83:7f:1e:56:88:b4:59:71:ed:15:57
-----BEGIN CERTIFICATE-----
MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
-----END CERTIFICATE-----

# Issuer: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
# Subject: CN=thawte Primary Root CA - G3 O=thawte, Inc. OU=Certification Services Division/(c) 2008 thawte, Inc. - For authorized use only
# Label: "thawte Primary Root CA - G3"
# Serial: 127614157056681299805556476275995414779
# MD5 Fingerprint: fb:1b:5d:43:8a:94:cd:44:c6:76:f2:43:4b:47:e7:31
# SHA1 Fingerprint: f1:8b:53:8d:1b:e9:03:b6:a6:f0:56:43:5b:17:15:89:ca:f3:6b:f2
# SHA256 Fingerprint: 4b:03:f4:58:07:ad:70:f2:1b:fc:2c:ae:71:c9:fd:e4:60:4c:06:4c:f5:ff:b6:86:ba:e5:db:aa:d7:fd:d3:4c
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
MdRAGmI0Nj81Aa6sY6A=
-----END CERTIFICATE-----

# Issuer: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
# Subject: CN=GeoTrust Primary Certification Authority - G2 O=GeoTrust Inc. OU=(c) 2007 GeoTrust Inc. - For authorized use only
# Label: "GeoTrust Primary Certification Authority - G2"
# Serial: 80682863203381065782177908751794619243
# MD5 Fingerprint: 01:5e:d8:6b:bd:6f:3d:8e:a1:31:f8:12:e0:98:73:6a
# SHA1 Fingerprint: 8d:17:84:d5:37:f3:03:7d:ec:70:fe:57:8b:51:9a:99:e6:10:d7:b0
# SHA256 Fingerprint: 5e:db:7a:c4:3b:82:a0:6a:87:61:e8:d7:be:49:79:eb:f2:61:1f:7d:d7:9b:f9:1c:1c:6b:56:6a:21:9e:d7:66
-----BEGIN CERTIFICATE-----
MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
rD6ogRLQy7rQkgu2npaqBA+K
-----END CERTIFICATE-----

# Issuer: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
# Subject: CN=VeriSign Universal Root Certification Authority O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2008 VeriSign, Inc. - For authorized use only
# Label: "VeriSign Universal Root Certification Authority"
# Serial: 85209574734084581917763752644031726877
# MD5 Fingerprint: 8e:ad:b5:01:aa:4d:81:e4:8c:1d:d1:e1:14:00:95:19
# SHA1 Fingerprint: 36:79:ca:35:66:87:72:30:4d:30:a5:fb:87:3b:0f:a7:7b:b7:0d:54
# SHA256 Fingerprint: 23:99:56:11:27:a5:71:25:de:8c:ef:ea:61:0d:df:2f:a0:78:b5:c8:06:7f:4e:82:82:90:bf:b8:60:e8:4b:3c
-----BEGIN CERTIFICATE-----
MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
7M2CYfE45k+XmCpajQ==
-----END CERTIFICATE-----

# Issuer: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
# Subject: CN=VeriSign Class 3 Public Primary Certification Authority - G4 O=VeriSign, Inc. OU=VeriSign Trust Network/(c) 2007 VeriSign, Inc. - For authorized use only
# Label: "VeriSign Class 3 Public Primary Certification Authority - G4"
# Serial: 63143484348153506665311985501458640051
# MD5 Fingerprint: 3a:52:e1:e7:fd:6f:3a:e3:6f:f3:6f:99:1b:f9:22:41
# SHA1 Fingerprint: 22:d5:d8:df:8f:02:31:d1:8d:f7:9d:b7:cf:8a:2d:64:c9:3f:6c:3a
# SHA256 Fingerprint: 69:dd:d7:ea:90:bb:57:c9:3e:13:5d:c8:5e:a6:fc:d5:48:0b:60:32:39:bd:c4:54:fc:75:8b:2a:26:cf:7f:79
-----BEGIN CERTIFICATE-----
MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----

# Issuer: CN=NetLock Arany (Class Gold) FÅ‘tanÃºsÃ­tvÃ¡ny O=NetLock Kft. OU=TanÃºsÃ­tvÃ¡nykiadÃ³k (Certification Services)
# Subject: CN=NetLock Arany (Class Gold) FÅ‘tanÃºsÃ­tvÃ¡ny O=NetLock Kft. OU=TanÃºsÃ­tvÃ¡nykiadÃ³k (Certification Services)
# Label: "NetLock Arany (Class Gold) FÅ‘tanÃºsÃ­tvÃ¡ny"
# Serial: 80544274841616
# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88
# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91
# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----

# Issuer: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
# Subject: CN=Staat der Nederlanden Root CA - G2 O=Staat der Nederlanden
# Label: "Staat der Nederlanden Root CA - G2"
# Serial: 10000012
# MD5 Fingerprint: 7c:a5:0f:f8:5b:9a:7d:6d:30:ae:54:5a:e3:42:a2:8a
# SHA1 Fingerprint: 59:af:82:79:91:86:c7:b4:75:07:cb:cf:03:57:46:eb:04:dd:b7:16
# SHA256 Fingerprint: 66:8c:83:94:7d:a6:3b:72:4b:ec:e1:74:3c:31:a0:e6:ae:d0:db:8e:c5:b3:1b:e3:77:bb:78:4f:91:b6:71:6f
-----BEGIN CERTIFICATE-----
MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
-----END CERTIFICATE-----

# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post
# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post
# Label: "Hongkong Post Root CA 1"
# Serial: 1000
# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca
# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58
# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
AmvZWg==
-----END CERTIFICATE-----

# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc.
# Label: "SecureSign RootCA11"
# Serial: 1
# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26
# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3
# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr
MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG
A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0
MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp
Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD
QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz
i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8
h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV
MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9
UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni
8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC
h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD
VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm
KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ
X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr
QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5
pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN
QSdJQO7e5iNEOdyhIta6A/I=
-----END CERTIFICATE-----

# Issuer: CN=ACEDICOM Root O=EDICOM OU=PKI
# Subject: CN=ACEDICOM Root O=EDICOM OU=PKI
# Label: "ACEDICOM Root"
# Serial: 7029493972724711941
# MD5 Fingerprint: 42:81:a0:e2:1c:e3:55:10:de:55:89:42:65:96:22:e6
# SHA1 Fingerprint: e0:b4:32:2e:b2:f6:a5:68:b6:54:53:84:48:18:4a:50:36:87:43:84
# SHA256 Fingerprint: 03:95:0f:b4:9a:53:1f:3e:19:91:94:23:98:df:a9:e0:ea:32:d7:ba:1c:dd:9b:c8:5d:b5:7e:d9:40:0b:43:4a
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE
AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x
CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW
MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF
RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7
09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7
XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P
Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK
t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb
X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28
MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU
fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI
2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH
K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae
ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP
BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw
RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm
fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3
gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe
I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i
5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi
ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn
MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ
o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6
zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN
GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt
r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK
Z05phkOTOPu220+DkdRgfks+KzgHVZhepA==
-----END CERTIFICATE-----

# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd.
# Label: "Microsec e-Szigno Root CA 2009"
# Serial: 14014712776195784473
# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1
# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e
# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78
-----BEGIN CERTIFICATE-----
MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y
OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx
FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp
Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP
kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc
cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U
fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7
N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC
xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1
+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM
Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG
SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h
mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk
ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c
2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t
HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
-----END CERTIFICATE-----

# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
# Label: "GlobalSign Root CA - R3"
# Serial: 4835703278459759426209954
# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28
# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad
# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----

# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068
# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068"
# Serial: 6047274297262753887
# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3
# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa
# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----

# Issuer: CN=Izenpe.com O=IZENPE S.A.
# Subject: CN=Izenpe.com O=IZENPE S.A.
# Label: "Izenpe.com"
# Serial: 917563065490389241595536686991402621
# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73
# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19
# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----

# Issuer: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A.
# Subject: CN=Chambers of Commerce Root - 2008 O=AC Camerfirma S.A.
# Label: "Chambers of Commerce Root - 2008"
# Serial: 11806822484801597146
# MD5 Fingerprint: 5e:80:9e:84:5a:0e:65:0b:17:02:f3:55:18:2a:3e:d7
# SHA1 Fingerprint: 78:6a:74:ac:76:ab:14:7f:9c:6a:30:50:ba:9e:a8:7e:fe:9a:ce:3c
# SHA256 Fingerprint: 06:3e:4a:fa:c4:91:df:d3:32:f3:08:9b:85:42:e9:46:17:d8:93:d7:fe:94:4e:10:a7:93:7e:e2:9d:96:93:c0
-----BEGIN CERTIFICATE-----
MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz
IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz
MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj
dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw
EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp
MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9
28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq
VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q
DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR
5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL
ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a
Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl
UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s
+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5
Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx
hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV
HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1
+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN
YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t
L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy
ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt
IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV
HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w
DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW
PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF
5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1
glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH
FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2
pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD
xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG
tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq
jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De
fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ
d0jQ
-----END CERTIFICATE-----

# Issuer: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A.
# Subject: CN=Global Chambersign Root - 2008 O=AC Camerfirma S.A.
# Label: "Global Chambersign Root - 2008"
# Serial: 14541511773111788494
# MD5 Fingerprint: 9e:80:ff:78:01:0c:2e:c1:36:bd:fe:96:90:6e:08:f3
# SHA1 Fingerprint: 4a:bd:ee:ec:95:0d:35:9c:89:ae:c7:52:a1:2c:5b:29:f6:d6:aa:0c
# SHA256 Fingerprint: 13:63:35:43:93:34:a7:69:80:16:a0:d3:24:de:72:28:4e:07:9d:7b:52:20:bb:8f:bd:74:78:16:ee:be:ba:ca
-----BEGIN CERTIFICATE-----
MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx
MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy
cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG
A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl
BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed
KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7
G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2
zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4
ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG
HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2
Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V
yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e
beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r
6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog
zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW
BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr
ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp
ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk
cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt
YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC
CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow
KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI
hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ
UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz
X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x
fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz
a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd
Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd
SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O
AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso
M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge
v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
-----END CERTIFICATE-----

# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
# Label: "Go Daddy Root Certificate Authority - G2"
# Serial: 0
# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01
# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b
# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
4uJEvlz36hz1
-----END CERTIFICATE-----

# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
# Label: "Starfield Root Certificate Authority - G2"
# Serial: 0
# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96
# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e
# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----

# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc.
# Label: "Starfield Services Root Certificate Authority - G2"
# Serial: 0
# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2
# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f
# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
sSi6
-----END CERTIFICATE-----

# Issuer: CN=AffirmTrust Commercial O=AffirmTrust
# Subject: CN=AffirmTrust Commercial O=AffirmTrust
# Label: "AffirmTrust Commercial"
# Serial: 8608355977964138876
# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7
# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7
# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----

# Issuer: CN=AffirmTrust Networking O=AffirmTrust
# Subject: CN=AffirmTrust Networking O=AffirmTrust
# Label: "AffirmTrust Networking"
# Serial: 8957382827206547757
# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f
# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f
# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----

# Issuer: CN=AffirmTrust Premium O=AffirmTrust
# Subject: CN=AffirmTrust Premium O=AffirmTrust
# Label: "AffirmTrust Premium"
# Serial: 7893706540734352110
# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57
# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27
# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
KeC2uAloGRwYQw==
-----END CERTIFICATE-----

# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust
# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust
# Label: "AffirmTrust Premium ECC"
# Serial: 8401224907861490260
# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d
# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb
# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
-----END CERTIFICATE-----

# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
# Label: "Certum Trusted Network CA"
# Serial: 279744
# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78
# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e
# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e
-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----

# Issuer: CN=Certinomis - AutoritÃ© Racine O=Certinomis OU=0002 433998903
# Subject: CN=Certinomis - AutoritÃ© Racine O=Certinomis OU=0002 433998903
# Label: "Certinomis - AutoritÃ© Racine"
# Serial: 1
# MD5 Fingerprint: 7f:30:78:8c:03:e3:ca:c9:0a:e2:c9:ea:1e:aa:55:1a
# SHA1 Fingerprint: 2e:14:da:ec:28:f0:fa:1e:8e:38:9a:4e:ab:eb:26:c0:0a:d3:83:c3
# SHA256 Fingerprint: fc:bf:e2:88:62:06:f7:2b:27:59:3c:8b:07:02:97:e1:2d:76:9e:d1:0e:d7:93:07:05:a8:09:8e:ff:c1:4d:17
-----BEGIN CERTIFICATE-----
MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET
MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk
BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4
Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl
cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0
aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY
F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N
8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe
rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K
/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu
7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC
28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6
lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E
nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB
0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09
5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj
WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN
jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s
ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM
OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q
619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn
2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj
o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v
nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG
5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq
pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb
dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0
BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5
-----END CERTIFICATE-----

# Issuer: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA
# Subject: CN=Root CA Generalitat Valenciana O=Generalitat Valenciana OU=PKIGVA
# Label: "Root CA Generalitat Valenciana"
# Serial: 994436456
# MD5 Fingerprint: 2c:8c:17:5e:b1:54:ab:93:17:b5:36:5a:db:d1:c6:f2
# SHA1 Fingerprint: a0:73:e5:c5:bd:43:61:0d:86:4c:21:13:0a:85:58:57:cc:9c:ea:46
# SHA256 Fingerprint: 8c:4e:df:d0:43:48:f3:22:96:9e:7e:29:a4:cd:4d:ca:00:46:55:06:1c:16:e1:b0:76:42:2e:f3:42:ad:63:0e
-----BEGIN CERTIFICATE-----
MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF
UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ
R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN
MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G
A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw
JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+
WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj
SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl
u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy
A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk
Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7
MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr
aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC
IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A
cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA
YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA
bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA
bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA
aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA
ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA
YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA
ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA
LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6
Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y
eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw
CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G
A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu
Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn
lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt
b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg
9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF
ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC
IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
-----END CERTIFICATE-----

# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA
# Label: "TWCA Root Certification Authority"
# Serial: 1
# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79
# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48
# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44
-----BEGIN CERTIFICATE-----
MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES
MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU
V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz
WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO
LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE
AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH
K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX
RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z
rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx
3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq
hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC
MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls
XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D
lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn
aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ
YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
-----END CERTIFICATE-----

# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2
# Label: "Security Communication RootCA2"
# Serial: 0
# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43
# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74
# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----

# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
# Subject: CN=Hellenic Academic and Research Institutions RootCA 2011 O=Hellenic Academic and Research Institutions Cert. Authority
# Label: "Hellenic Academic and Research Institutions RootCA 2011"
# Serial: 0
# MD5 Fingerprint: 73:9f:4c:4b:73:5b:79:e9:fa:ba:1c:ef:6e:cb:d5:c9
# SHA1 Fingerprint: fe:45:65:9b:79:03:5b:98:a1:61:b5:51:2e:ac:da:58:09:48:22:4d
# SHA256 Fingerprint: bc:10:4f:15:a4:8b:e7:09:dc:a5:42:a7:e1:d4:b9:df:6f:05:45:27:e8:02:ea:a9:2d:59:54:44:25:8a:fe:71
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
l7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----

# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967
# Label: "Actalis Authentication Root CA"
# Serial: 6271844772424770508
# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6
# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac
# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----

# Issuer: O=Trustis Limited OU=Trustis FPS Root CA
# Subject: O=Trustis Limited OU=Trustis FPS Root CA
# Label: "Trustis FPS Root CA"
# Serial: 36053640375399034304724988975563710553
# MD5 Fingerprint: 30:c9:e7:1e:6b:e6:14:eb:65:b2:16:69:20:31:67:4d
# SHA1 Fingerprint: 3b:c0:38:0b:33:c3:f6:a6:0c:86:15:22:93:d9:df:f5:4b:81:c0:04
# SHA256 Fingerprint: c1:b4:82:99:ab:a5:20:8f:e9:63:0a:ce:55:ca:68:a0:3e:da:5a:51:9c:88:02:a0:d3:a6:73:be:8f:8e:55:7d
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL
ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx
MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc
MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+
AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH
iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj
vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA
0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB
OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/
BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E
FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01
GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW
zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4
1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE
f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F
jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN
ZetX2fNXlrtIzYE=
-----END CERTIFICATE-----

# Issuer: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
# Subject: CN=StartCom Certification Authority O=StartCom Ltd. OU=Secure Digital Certificate Signing
# Label: "StartCom Certification Authority"
# Serial: 45
# MD5 Fingerprint: c9:3b:0d:84:41:fc:a4:76:79:23:08:57:de:10:19:16
# SHA1 Fingerprint: a3:f1:33:3f:e2:42:bf:cf:c5:d1:4e:8f:39:42:98:40:68:10:d1:a0
# SHA256 Fingerprint: e1:78:90:ee:09:a3:fb:f4:f4:8b:9c:41:4a:17:d6:37:b7:a5:06:47:e9:bc:75:23:22:72:7f:cc:17:42:a9:11
-----BEGIN CERTIFICATE-----
MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
fyWl8kgAwKQB2j8=
-----END CERTIFICATE-----

# Issuer: CN=StartCom Certification Authority G2 O=StartCom Ltd.
# Subject: CN=StartCom Certification Authority G2 O=StartCom Ltd.
# Label: "StartCom Certification Authority G2"
# Serial: 59
# MD5 Fingerprint: 78:4b:fb:9e:64:82:0a:d3:b8:4c:62:f3:64:f2:90:64
# SHA1 Fingerprint: 31:f1:fd:68:22:63:20:ee:c6:3b:3f:9d:ea:4a:3e:53:7c:7c:39:17
# SHA256 Fingerprint: c7:ba:65:67:de:93:a7:98:ae:1f:aa:79:1e:71:2d:37:8f:ae:1f:93:c4:39:7f:ea:44:1b:b7:cb:e6:fd:59:95
-----BEGIN CERTIFICATE-----
MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
-----END CERTIFICATE-----

# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327
# Label: "Buypass Class 2 Root CA"
# Serial: 2
# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29
# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99
# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
-----END CERTIFICATE-----

# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327
# Label: "Buypass Class 3 Root CA"
# Serial: 2
# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec
# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57
# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
-----END CERTIFICATE-----

# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Label: "T-TeleSec GlobalRoot Class 3"
# Serial: 1
# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef
# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1
# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
TpPDpFQUWw==
-----END CERTIFICATE-----

# Issuer: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
# Subject: CN=EE Certification Centre Root CA O=AS Sertifitseerimiskeskus
# Label: "EE Certification Centre Root CA"
# Serial: 112324828676200291871926431888494945866
# MD5 Fingerprint: 43:5e:88:d4:7d:1a:4a:7e:fd:84:2e:52:eb:01:d4:6f
# SHA1 Fingerprint: c9:a8:b9:e7:55:80:5e:58:e3:53:77:a7:25:eb:af:c3:7b:27:cc:d7
# SHA256 Fingerprint: 3e:84:ba:43:42:90:85:16:e7:75:73:c0:99:2f:09:79:ca:08:4e:46:85:68:1f:f1:95:cc:ba:8a:22:9b:8a:76
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
-----END CERTIFICATE-----

# Issuer: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž. (c) AralÄ±k 2007
# Subject: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž. (c) AralÄ±k 2007
# Label: "TURKTRUST Certificate Services Provider Root 2007"
# Serial: 1
# MD5 Fingerprint: 2b:70:20:56:86:82:a0:18:c8:07:53:12:28:70:21:72
# SHA1 Fingerprint: f1:7f:6f:b6:31:dc:99:e3:a3:c8:7f:fe:1c:f1:81:10:88:d9:60:33
# SHA256 Fingerprint: 97:8c:d9:66:f2:fa:a0:7b:a7:aa:95:00:d9:c0:2e:9d:77:f2:cd:ad:a6:ad:6b:a7:4a:f4:b9:1c:66:59:3c:50
-----BEGIN CERTIFICATE-----
MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc
UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS
S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx
OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry
b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC
VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE
sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F
ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY
KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG
+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG
HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P
IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M
733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk
Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW
AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5
mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa
XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ
qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9
-----END CERTIFICATE-----

# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH
# Label: "D-TRUST Root Class 3 CA 2 2009"
# Serial: 623603
# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f
# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0
# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
Johw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----

# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH
# Label: "D-TRUST Root Class 3 CA 2 EV 2009"
# Serial: 623604
# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6
# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83
# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
-----END CERTIFICATE-----

# Issuer: CN=Autoridad de Certificacion Raiz del Estado Venezolano O=Sistema Nacional de Certificacion Electronica OU=Superintendencia de Servicios de Certificacion Electronica
# Subject: CN=PSCProcert O=Sistema Nacional de Certificacion Electronica OU=Proveedor de Certificados PROCERT
# Label: "PSCProcert"
# Serial: 11
# MD5 Fingerprint: e6:24:e9:12:01:ae:0c:de:8e:85:c4:ce:a3:12:dd:ec
# SHA1 Fingerprint: 70:c1:8d:74:b4:28:81:0a:e4:fd:a5:75:d7:01:9f:99:b0:3d:50:74
# SHA256 Fingerprint: 3c:fc:3c:14:d1:f6:84:ff:17:e3:8c:43:ca:44:0c:00:b9:67:ec:93:3e:8b:fe:06:4c:a1:d7:2c:90:f2:ad:b0
-----BEGIN CERTIFICATE-----
MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1
dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s
YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz
dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0
aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh
IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ
KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw
MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy
b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx
KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG
A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u
aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9
7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74
BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G
ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9
JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0
PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2
0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/
6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m
v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7
K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev
bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw
MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w
MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD
gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0
b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh
bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0
cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp
ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg
ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq
hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD
AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w
MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag
RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t
UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl
cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG
AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN
AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS
1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB
3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv
Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh
HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm
pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz
sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE
qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb
mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9
opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H
YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
-----END CERTIFICATE-----

# Issuer: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center
# Subject: CN=China Internet Network Information Center EV Certificates Root O=China Internet Network Information Center
# Label: "China Internet Network Information Center EV Certificates Root"
# Serial: 1218379777
# MD5 Fingerprint: 55:5d:63:00:97:bd:6a:97:f5:67:ab:4b:fb:6e:63:15
# SHA1 Fingerprint: 4f:99:aa:93:fb:2b:d1:37:26:a1:99:4a:ce:7f:f0:05:f2:93:5d:1e
# SHA256 Fingerprint: 1c:01:c6:f4:db:b2:fe:fc:22:55:8b:2b:ca:32:56:3f:49:84:4a:cf:c3:2b:7b:e4:b0:ff:59:9f:9e:8c:7a:f7
-----BEGIN CERTIFICATE-----
MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g
Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0
aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa
Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg
SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo
aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp
ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z
7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//
DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx
zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8
hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs
4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u
gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY
NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3
j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG
52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB
echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI
zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy
wy39FCqQmbkHzJ8=
-----END CERTIFICATE-----

# Issuer: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services
# Subject: CN=Swisscom Root CA 2 O=Swisscom OU=Digital Certificate Services
# Label: "Swisscom Root CA 2"
# Serial: 40698052477090394928831521023204026294
# MD5 Fingerprint: 5b:04:69:ec:a5:83:94:63:18:a7:86:d0:e4:f2:6e:19
# SHA1 Fingerprint: 77:47:4f:c6:30:e4:0f:4c:47:64:3f:84:ba:b8:c6:95:4a:8a:41:ec
# SHA256 Fingerprint: f0:9b:12:2c:71:14:f4:a0:9b:d4:ea:4f:4a:99:d5:58:b4:6e:4c:25:cd:81:14:0d:29:c0:56:13:91:4c:38:41
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk
MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT
AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr
jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r
0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f
2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP
ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF
y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA
tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL
6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0
uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL
acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh
k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q
VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh
b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R
fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv
/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI
REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx
srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv
aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT
woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n
Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W
t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N
8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2
9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5
wSsSnqaeG8XmDtkx2Q==
-----END CERTIFICATE-----

# Issuer: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services
# Subject: CN=Swisscom Root EV CA 2 O=Swisscom OU=Digital Certificate Services
# Label: "Swisscom Root EV CA 2"
# Serial: 322973295377129385374608406479535262296
# MD5 Fingerprint: 7b:30:34:9f:dd:0a:4b:6b:35:ca:31:51:28:5d:ae:ec
# SHA1 Fingerprint: e7:a1:90:29:d3:d5:52:dc:0d:0f:c6:92:d3:ea:88:0d:15:2e:1a:6b
# SHA256 Fingerprint: d9:5f:ea:3c:a4:ee:dc:e7:4c:d7:6e:75:fc:6d:1f:f6:2c:44:1f:0f:a8:bc:77:f0:34:b1:9e:5d:b2:58:01:5d
-----BEGIN CERTIFICATE-----
MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw
ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp
dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290
IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD
VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy
dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg
MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx
UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD
1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH
oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR
HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/
5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv
idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL
OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC
NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f
46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB
UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth
7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G
A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB
bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x
XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T
PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0
Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70
WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL
Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm
7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S
nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN
vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB
WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI
fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb
I+2ksx0WckNLIOFZfsLorSa/ovc=
-----END CERTIFICATE-----

# Issuer: CN=CA Disig Root R1 O=Disig a.s.
# Subject: CN=CA Disig Root R1 O=Disig a.s.
# Label: "CA Disig Root R1"
# Serial: 14052245610670616104
# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a
# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6
# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy
MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk
D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o
OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A
fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe
IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n
oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK
/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj
rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD
3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE
7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC
yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd
qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI
hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA
SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo
HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB
emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC
AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb
7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x
DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk
F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF
a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT
Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL
-----END CERTIFICATE-----

# Issuer: CN=CA Disig Root R2 O=Disig a.s.
# Subject: CN=CA Disig Root R2 O=Disig a.s.
# Label: "CA Disig Root R2"
# Serial: 10572350602393338211
# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03
# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71
# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe
NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH
PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I
x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe
QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR
yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO
QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912
H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ
QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD
i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs
nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1
rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI
hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf
GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb
lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka
+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal
TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i
nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3
gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr
G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----

# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV
# Label: "ACCVRAIZ1"
# Serial: 6828503384748696800
# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02
# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17
# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13
-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----

# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA
# Label: "TWCA Global Root CA"
# Serial: 3262
# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96
# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65
# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT
B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF
10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz
0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh
MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH
zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc
46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2
yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi
laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP
oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA
BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE
qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm
4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL
1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF
H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo
RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+
nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh
15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW
6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW
nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j
wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz
aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy
KwbQBM0=
-----END CERTIFICATE-----

# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera
# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera
# Label: "TeliaSonera Root CA v1"
# Serial: 199041966741090107964904287217786801558
# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c
# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37
# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw
NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv
b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD
VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F
VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1
7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X
Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+
/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs
81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm
dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe
Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu
sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4
pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs
slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ
arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD
VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG
9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl
dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj
TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed
Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7
Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI
OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7
vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW
t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn
HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----

# Issuer: CN=E-Tugra Certification Authority O=E-TuÄŸra EBG BiliÅŸim Teknolojileri ve Hizmetleri A.Åž. OU=E-Tugra Sertifikasyon Merkezi
# Subject: CN=E-Tugra Certification Authority O=E-TuÄŸra EBG BiliÅŸim Teknolojileri ve Hizmetleri A.Åž. OU=E-Tugra Sertifikasyon Merkezi
# Label: "E-Tugra Certification Authority"
# Serial: 7667447206703254355
# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49
# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39
# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----

# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center
# Label: "T-TeleSec GlobalRoot Class 2"
# Serial: 1
# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a
# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9
# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd
AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC
FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi
1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq
jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ
wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/
WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy
NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC
uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw
IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6
g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP
BSeOE6Fuwg==
-----END CERTIFICATE-----

# Issuer: CN=Atos TrustedRoot 2011 O=Atos
# Subject: CN=Atos TrustedRoot 2011 O=Atos
# Label: "Atos TrustedRoot 2011"
# Serial: 6643877497813316402
# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56
# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21
# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE
AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG
EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM
FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC
REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp
Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM
VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+
SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ
4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L
cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi
eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV
HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG
A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j
vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP
DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc
maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D
lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited
# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited
# Label: "QuoVadis Root CA 1 G3"
# Serial: 687049649626669250736271037606554624078720034195
# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab
# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67
# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00
MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV
wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe
rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341
68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh
4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp
UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o
abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc
3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G
KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt
hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO
Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt
zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD
ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2
cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN
qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5
YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv
b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2
8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k
NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj
ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp
q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt
nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited
# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited
# Label: "QuoVadis Root CA 2 G3"
# Serial: 390156079458959257446133169266079962026824725800
# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06
# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36
# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
-----END CERTIFICATE-----

# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited
# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited
# Label: "QuoVadis Root CA 3 G3"
# Serial: 268090761170461462463995952157327242137089239581
# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7
# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d
# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR
/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu
FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR
U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c
ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR
FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k
A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw
eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl
sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp
VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q
A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+
ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD
ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI
FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv
oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg
u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP
0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf
3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl
8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+
DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN
PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/
ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Assured ID Root G2"
# Serial: 15385348160840213938643033620894905419
# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d
# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f
# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
IhNzbM8m9Yop5w==
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Assured ID Root G3"
# Serial: 15459312981008553731928384953135426796
# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb
# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89
# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2
-----BEGIN CERTIFICATE-----
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
6pZjamVFkpUBtA==
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Global Root G2"
# Serial: 4293743540046975378534879503202253541
# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44
# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4
# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Global Root G3"
# Serial: 7089244469030293291760083333884364146
# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca
# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e
# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0
-----BEGIN CERTIFICATE-----
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
sycX
-----END CERTIFICATE-----

# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
# Label: "DigiCert Trusted Root G4"
# Serial: 7451500558977370777930084869016614236
# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49
# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4
# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88
-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----

# Issuer: CN=Certification Authority of WoSign O=WoSign CA Limited
# Subject: CN=Certification Authority of WoSign O=WoSign CA Limited
# Label: "WoSign"
# Serial: 125491772294754854453622855443212256657
# MD5 Fingerprint: a1:f2:f9:b5:d2:c8:7a:74:b8:f3:05:f1:d7:e1:84:8d
# SHA1 Fingerprint: b9:42:94:bf:91:ea:8f:b6:4b:e6:10:97:c7:fb:00:13:59:b6:76:cb
# SHA256 Fingerprint: 4b:22:d5:a6:ae:c9:9f:3c:db:79:aa:5e:c0:68:38:47:9c:d5:ec:ba:71:64:f7:f2:2d:c1:d6:5f:63:d8:57:08
-----BEGIN CERTIFICATE-----
MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV
MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV
BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw
MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX
b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN
rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U
fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc
f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2
ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M
x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR
aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch
zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar
uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K
mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA
Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv
HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H
EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1
LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ
MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e
JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN
g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp
dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab
R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ
PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce
xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+
J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl
OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT
ee5Ehr7XHuQe+w==
-----END CERTIFICATE-----

# Issuer: CN=CA æ²ƒé€šæ ¹è¯ä¹¦ O=WoSign CA Limited
# Subject: CN=CA æ²ƒé€šæ ¹è¯ä¹¦ O=WoSign CA Limited
# Label: "WoSign China"
# Serial: 106921963437422998931660691310149453965
# MD5 Fingerprint: 78:83:5b:52:16:76:c4:24:3b:83:78:e8:ac:da:9a:93
# SHA1 Fingerprint: 16:32:47:8d:89:f9:21:3a:92:00:85:63:f5:a4:a7:d3:12:40:8a:d6
# SHA256 Fingerprint: d6:f0:34:bd:94:aa:23:3f:02:97:ec:a4:24:5b:28:39:73:e4:47:aa:59:0f:31:0c:77:f4:8f:df:83:11:22:54
-----BEGIN CERTIFICATE-----
MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV
BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw
MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl
ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r
D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1
9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf
v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk
UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L
NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb
+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V
qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K
yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G
AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK
J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC
AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4
WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6
yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj
/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6
jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2
ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX
X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n
FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D
u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l
O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le
ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1
2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ==
-----END CERTIFICATE-----

# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited
# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited
# Label: "COMODO RSA Certification Authority"
# Serial: 101909084537582093308941363524873193117
# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18
# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4
# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34
-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
NVOFBkpdn627G190
-----END CERTIFICATE-----

# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
# Label: "USERTrust RSA Certification Authority"
# Serial: 2645093764781058787591871645665788717
# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----

# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
# Label: "USERTrust ECC Certification Authority"
# Serial: 123013823720199481456569720443997572134
# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1
# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0
# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a
-----BEGIN CERTIFICATE-----
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----

# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4
# Label: "GlobalSign ECC Root CA - R4"
# Serial: 14367148294922964480859022125800977897474
# MD5 Fingerprint: 20:f0:27:68:d1:7e:a0:9d:0e:e6:2a:ca:df:5c:89:8e
# SHA1 Fingerprint: 69:69:56:2e:40:80:f4:24:a1:e7:19:9f:14:ba:f3:ee:58:ab:6a:bb
# SHA256 Fingerprint: be:c9:49:11:c2:95:56:76:db:6c:0a:55:09:86:d7:6e:3b:a0:05:66:7c:44:2c:97:62:b4:fb:b7:73:de:22:8c
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
ewv4n4Q=
-----END CERTIFICATE-----

# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
# Label: "GlobalSign ECC Root CA - R5"
# Serial: 32785792099990507226680698011560947931244
# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08
# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa
# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24
-----BEGIN CERTIFICATE-----
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
xwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----

# Issuer: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden
# Subject: CN=Staat der Nederlanden Root CA - G3 O=Staat der Nederlanden
# Label: "Staat der Nederlanden Root CA - G3"
# Serial: 10003001
# MD5 Fingerprint: 0b:46:67:07:db:10:2f:19:8c:35:50:60:d1:0b:f4:37
# SHA1 Fingerprint: d8:eb:6b:41:51:92:59:e0:f3:e7:85:00:c0:3d:b6:88:97:c9:ee:fc
# SHA256 Fingerprint: 3c:4f:b0:b9:5a:b8:b3:00:32:f4:32:b8:6f:53:5f:e1:72:c1:85:d0:fd:39:86:58:37:cf:36:18:7f:a6:f4:28
-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX
DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP
cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW
IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX
xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy
KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR
9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az
5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8
6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7
Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP
bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt
BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt
XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd
INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp
LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8
Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp
gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh
/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw
0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A
fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq
4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR
1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/
QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM
94B7IWcnMFk=
-----END CERTIFICATE-----

# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden
# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden
# Label: "Staat der Nederlanden EV Root CA"
# Serial: 10000013
# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba
# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb
# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a
-----BEGIN CERTIFICATE-----
MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y
MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg
TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS
b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS
M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC
UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d
Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p
rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l
pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb
j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC
KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS
/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X
cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH
1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP
px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7
MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u
2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS
v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC
wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy
CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e
vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6
Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa
Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL
eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8
FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc
7uzXLg==
-----END CERTIFICATE-----

# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust
# Label: "IdenTrust Commercial Root CA 1"
# Serial: 13298821034946342390520003877796839426
# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7
# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25
# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw
JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT
3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU
+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp
S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1
bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi
T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL
vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK
Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK
dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT
c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv
l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N
iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD
ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt
LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93
nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3
+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK
W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT
AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq
l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG
4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ
mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H
-----END CERTIFICATE-----

# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust
# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust
# Label: "IdenTrust Public Sector Root CA 1"
# Serial: 13298821034946342390521976156843933698
# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba
# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd
# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu
VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN
MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0
MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7
ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy
RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS
bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF
/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R
3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw
EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy
9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V
GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ
2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV
WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD
W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN
AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV
DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9
TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G
lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW
mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df
WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5
+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ
tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA
GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv
8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c
-----END CERTIFICATE-----

# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only
# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only
# Label: "Entrust Root Certification Authority - G2"
# Serial: 1246989352
# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2
# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4
# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39
-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy
NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T
RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN
cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW
wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1
U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0
jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN
BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/
jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v
1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R
nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH
VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
-----END CERTIFICATE-----

# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only
# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only
# Label: "Entrust Root Certification Authority - EC1"
# Serial: 51543124481930649114116133369
# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc
# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47
# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5
-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----

# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority
# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority
# Label: "CFCA EV ROOT"
# Serial: 407555286
# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30
# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83
# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx
MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP
T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03
sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL
TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5
/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp
7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz
EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt
hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP
a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot
aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg
TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV
PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv
cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL
tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd
BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT
ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL
jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS
ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy
P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19
xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d
Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN
5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe
/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z
AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----

# Issuer: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H5 O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž.
# Subject: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H5 O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž.
# Label: "TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H5"
# Serial: 156233699172481
# MD5 Fingerprint: da:70:8e:f0:22:df:93:26:f6:5f:9f:d3:15:06:52:4e
# SHA1 Fingerprint: c4:18:f6:4d:46:d1:df:00:3d:27:30:13:72:43:a9:12:11:c6:75:fb
# SHA256 Fingerprint: 49:35:1b:90:34:44:c1:85:cc:dc:5c:69:3d:24:d8:55:5c:b2:08:d6:a8:14:13:07:69:9f:4a:f0:63:19:9d:78
-----BEGIN CERTIFICATE-----
MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UE
BhMCVFIxDzANBgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxn
aSDEsGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkg
QS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1QgRWxla3Ryb25payBTZXJ0aWZpa2Eg
SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAwODA3MDFaFw0yMzA0
MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYD
VQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApCUZ4WWe60ghUEoI5RHwWrom
/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537jVJp45wnEFPzpALFp/kR
Gml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1mep5Fimh3
4khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z
5UNP9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0
hO8EuPbJbKoCPrZV4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QID
AQABo0IwQDAdBgNVHQ4EFgQUVpkHHtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJ5FdnsX
SDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPoBP5yCccLqh0l
VX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq
URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nf
peYVhDfwwvJllpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CF
Yv4HAqGEVka+lgqaE9chTLd8B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW
+qtB4Uu2NQvAmxU=
-----END CERTIFICATE-----

# Issuer: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H6 O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž.
# Subject: CN=TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H6 O=TÃœRKTRUST Bilgi Ä°letiÅŸim ve BiliÅŸim GÃ¼venliÄŸi Hizmetleri A.Åž.
# Label: "TÃœRKTRUST Elektronik Sertifika Hizmet SaÄŸlayÄ±cÄ±sÄ± H6"
# Serial: 138134509972618
# MD5 Fingerprint: f8:c5:ee:2a:6b:be:95:8d:08:f7:25:4a:ea:71:3e:46
# SHA1 Fingerprint: 8a:5c:8c:ee:a5:03:e6:05:56:ba:d8:1b:d4:f6:c9:b0:ed:e5:2f:e0
# SHA256 Fingerprint: 8d:e7:86:55:e1:be:7f:78:47:80:0b:93:f6:94:d2:1d:36:8c:c0:6e:03:3e:7f:ab:04:bb:5e:b9:9d:a6:b7:00
-----BEGIN CERTIFICATE-----
MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQG
EwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdp
IMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBB
LsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBI
aXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5MDQxMFoXDTIzMTIx
NjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBLBgNV
BAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2
ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVs
ZWt0cm9uaWsgU2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCdsGjW6L0UlqMACprx9MfMkU1x
eHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a2uqsxgbPJQ1BgfbBOCK9
+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EEDwnS3/faA
z1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0p
u5FbHH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6p
lVxiSvgNZ1GpryHV+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMB
AAGjQjBAMB0GA1UdDgQWBBTdVRcT9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAb1gNl0Oq
FlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3RfdCaqaXKGDsC
QC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKID
gI6tflEATseWhvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm
9ocJV612ph1jmv3XZch4gyt1O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsG
tAuYSyher4hYyw==
-----END CERTIFICATE-----

# Issuer: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
# Subject: CN=Certinomis - Root CA O=Certinomis OU=0002 433998903
# Label: "Certinomis - Root CA"
# Serial: 1
# MD5 Fingerprint: 14:0a:fd:8d:a8:28:b5:38:69:db:56:7e:61:22:03:3f
# SHA1 Fingerprint: 9d:70:bb:01:a5:a4:a0:18:11:2e:f7:1c:01:b9:32:c5:34:e7:88:a8
# SHA256 Fingerprint: 2a:99:f5:bc:11:74:b7:3c:bb:1d:62:08:84:e0:1c:34:e5:1c:cb:39:78:da:12:5f:0e:33:26:88:83:bf:41:58
-----BEGIN CERTIFICATE-----
MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET
MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb
BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz
MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx
FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g
Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2
fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl
LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV
WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF
TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb
5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc
CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri
wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ
wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG
m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4
F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng
WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0
2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF
AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/
0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw
F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS
g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj
qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN
h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/
ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V
btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj
Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ
8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW
gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE=
-----END CERTIFICATE-----

# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed
# Label: "OISTE WISeKey Global Root GB CA"
# Serial: 157768595616588414422159278966750757568
# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d
# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed
# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6
-----BEGIN CERTIFICATE-----
MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt
MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg
Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i
YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x
CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG
b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh
bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3
HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx
WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX
1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk
u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P
99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r
M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB
BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh
cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5
gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO
ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf
aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic
Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM=
-----END CERTIFICATE-----

# Issuer: CN=Certification Authority of WoSign G2 O=WoSign CA Limited
# Subject: CN=Certification Authority of WoSign G2 O=WoSign CA Limited
# Label: "Certification Authority of WoSign G2"
# Serial: 142423943073812161787490648904721057092
# MD5 Fingerprint: c8:1c:7d:19:aa:cb:71:93:f2:50:f8:52:a8:1e:ba:60
# SHA1 Fingerprint: fb:ed:dc:90:65:b7:27:20:37:bc:55:0c:9c:56:de:bb:f2:78:94:e1
# SHA256 Fingerprint: d4:87:a5:6f:83:b0:74:82:e8:5e:96:33:94:c1:ec:c2:c9:e5:1d:09:03:ee:94:6b:02:c3:01:58:1e:d9:9e:16
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIQayXaioidfLwPBbOxemFFRDANBgkqhkiG9w0BAQsFADBY
MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxLTArBgNV
BAMTJENlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbiBHMjAeFw0xNDEx
MDgwMDU4NThaFw00NDExMDgwMDU4NThaMFgxCzAJBgNVBAYTAkNOMRowGAYDVQQK
ExFXb1NpZ24gQ0EgTGltaXRlZDEtMCsGA1UEAxMkQ2VydGlmaWNhdGlvbiBBdXRo
b3JpdHkgb2YgV29TaWduIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAvsXEoCKASU+/2YcRxlPhuw+9YH+v9oIOH9ywjj2X4FA8jzrvZjtFB5sg+OPX
JYY1kBaiXW8wGQiHC38Gsp1ij96vkqVg1CuAmlI/9ZqD6TRay9nVYlzmDuDfBpgO
gHzKtB0TiGsOqCR3A9DuW/PKaZE1OVbFbeP3PU9ekzgkyhjpJMuSA93MHD0JcOQg
5PGurLtzaaNjOg9FD6FKmsLRY6zLEPg95k4ot+vElbGs/V6r+kHLXZ1L3PR8du9n
fwB6jdKgGlxNIuG12t12s9R23164i5jIFFTMaxeSt+BKv0mUYQs4kI9dJGwlezt5
2eJ+na2fmKEG/HgUYFf47oB3sQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU+mCp62XF3RYUCE4MD42b4Pdkr2cwDQYJ
KoZIhvcNAQELBQADggEBAFfDejaCnI2Y4qtAqkePx6db7XznPWZaOzG73/MWM5H8
fHulwqZm46qwtyeYP0nXYGdnPzZPSsvxFPpahygc7Y9BMsaV+X3avXtbwrAh449G
3CE4Q3RM+zD4F3LBMvzIkRfEzFg3TgvMWvchNSiDbGAtROtSjFA9tWwS1/oJu2yy
SrHFieT801LYYRf+epSEj3m2M1m6D8QL4nCgS3gu+sif/a+RZQp4OBXllxcU3fng
LDT4ONCEIgDAFFEYKwLcMFrw6AF8NTojrwjkr6qOKEJJLvD1mTS+7Q9LGOHSJDy7
XUe3IfKN0QqZjuNuPq1w4I+5ysxugTH2e5x6eeRncRg=
-----END CERTIFICATE-----

# Issuer: CN=CA WoSign ECC Root O=WoSign CA Limited
# Subject: CN=CA WoSign ECC Root O=WoSign CA Limited
# Label: "CA WoSign ECC Root"
# Serial: 138625735294506723296996289575837012112
# MD5 Fingerprint: 80:c6:53:ee:61:82:28:72:f0:ff:21:b9:17:ca:b2:20
# SHA1 Fingerprint: d2:7a:d2:be:ed:94:c0:a1:3c:c7:25:21:ea:5d:71:be:81:19:f3:2b
# SHA256 Fingerprint: 8b:45:da:1c:06:f7:91:eb:0c:ab:f2:6b:e5:88:f5:fb:23:16:5c:2e:61:4b:f8:85:56:2d:0d:ce:50:b2:9b:02
-----BEGIN CERTIFICATE-----
MIICCTCCAY+gAwIBAgIQaEpYcIBr8I8C+vbe6LCQkDAKBggqhkjOPQQDAzBGMQsw
CQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNVBAMT
EkNBIFdvU2lnbiBFQ0MgUm9vdDAeFw0xNDExMDgwMDU4NThaFw00NDExMDgwMDU4
NThaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRlZDEb
MBkGA1UEAxMSQ0EgV29TaWduIEVDQyBSb290MHYwEAYHKoZIzj0CAQYFK4EEACID
YgAE4f2OuEMkq5Z7hcK6C62N4DrjJLnSsb6IOsq/Srj57ywvr1FQPEd1bPiUt5v8
KB7FVMxjnRZLU8HnIKvNrCXSf4/CwVqCXjCLelTOA7WRf6qU0NGKSMyCBSah1VES
1ns2o0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQUqv3VWqP2h4syhf3RMluARZPzA7gwCgYIKoZIzj0EAwMDaAAwZQIxAOSkhLCB
1T2wdKyUpOgOPQB0TKGXa/kNUTyh2Tv0Daupn75OcsqF1NnstTJFGG+rrQIwfcf3
aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYua/GRspBl9JrmkO5K
-----END CERTIFICATE-----

# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A.
# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A.
# Label: "SZAFIR ROOT CA2"
# Serial: 357043034767186914217277344587386743377558296292
# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99
# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de
# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe
-----BEGIN CERTIFICATE-----
MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL
BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6
ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw
NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L
cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg
Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN
QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT
3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw
3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6
3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5
BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN
XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF
AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw
8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG
nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP
oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy
d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg
LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
-----END CERTIFICATE-----

# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority
# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority
# Label: "Certum Trusted Network CA 2"
# Serial: 44979900017204383099463764357512596969
# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2
# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92
# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04
-----BEGIN CERTIFICATE-----
MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB
gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG
A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz
OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ
VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3
b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA
DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn
0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB
OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE
fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E
Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m
o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i
sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW
OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez
Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS
adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n
3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC
AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ
F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf
CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29
XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm
djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/
WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb
AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq
P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko
b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj
XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P
5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi
DrW5viSP
-----END CERTIFICATE-----

# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
# Label: "Hellenic Academic and Research Institutions RootCA 2015"
# Serial: 0
# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce
# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6
# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36
-----BEGIN CERTIFICATE-----
MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix
DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k
IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT
N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v
dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG
A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh
ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx
QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA
4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0
AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10
4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C
ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV
9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD
gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6
Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq
NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko
LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd
ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I
XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI
M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot
9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V
Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea
j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh
X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ
l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf
bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4
pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK
e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0
vm9qp/UsQu0yrbYhnr68
-----END CERTIFICATE-----

# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority
# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015"
# Serial: 0
# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef
# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66
# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33
-----BEGIN CERTIFICATE-----
MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN
BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl
bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv
b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ
BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj
YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5
MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0
dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg
QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa
jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC
MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi
C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep
lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof
TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
-----END CERTIFICATE-----

# Issuer: CN=Certplus Root CA G1 O=Certplus
# Subject: CN=Certplus Root CA G1 O=Certplus
# Label: "Certplus Root CA G1"
# Serial: 1491911565779898356709731176965615564637713
# MD5 Fingerprint: 7f:09:9c:f7:d9:b9:5c:69:69:56:d5:37:3e:14:0d:42
# SHA1 Fingerprint: 22:fd:d0:b7:fd:a2:4e:0d:ac:49:2c:a0:ac:a6:7b:6a:1f:e3:f7:66
# SHA256 Fingerprint: 15:2a:40:2b:fc:df:2c:d5:48:05:4d:22:75:b3:9c:7f:ca:3e:c0:97:80:78:b0:f0:ea:76:e5:61:a6:c7:43:3e
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUA
MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
dHBsdXMgUm9vdCBDQSBHMTAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBa
MD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2Vy
dHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
ANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHNr49a
iZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt
6kuJPKNxQv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP
0FG7Yn2ksYyy/yARujVjBYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f
6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTvLRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDE
EW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2z4QTd28n6v+WZxcIbekN
1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc4nBvCGrc
h2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCT
mehd4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV
4EJQeIQEQWGw9CEjjy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPO
WftwenMGE9nTdDckQQoRb5fc5+R+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1Ud
DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSowcCbkahDFXxd
Bie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHYlwuBsTANBgkq
hkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7
/SMNkPX0XtPGYX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BS
S7CTKtQ+FjPlnsZlFT5kOwQ/2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j
2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F6ALEUz65noe8zDUa3qHpimOHZR4R
Kttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilXCNQ314cnrUlZp5Gr
RHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWetUNy
6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEV
V/xuZDDCVRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5
g4VCXA9DO2pJNdWY9BW/+mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl
++O/QmueD6i9a5jc2NvLi6Td11n0bt3+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
-----END CERTIFICATE-----

# Issuer: CN=Certplus Root CA G2 O=Certplus
# Subject: CN=Certplus Root CA G2 O=Certplus
# Label: "Certplus Root CA G2"
# Serial: 1492087096131536844209563509228951875861589
# MD5 Fingerprint: a7:ee:c4:78:2d:1b:ee:2d:b9:29:ce:d6:a7:96:32:31
# SHA1 Fingerprint: 4f:65:8e:1f:e9:06:d8:28:02:e9:54:47:41:c9:54:25:5d:69:cc:1a
# SHA256 Fingerprint: 6c:c0:50:41:e6:44:5e:74:69:6c:4c:fb:c9:f8:0f:54:3b:7e:ab:bb:44:b4:ce:6f:78:7c:6a:99:71:c4:2f:17
-----BEGIN CERTIFICATE-----
MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4x
CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
dXMgUm9vdCBDQSBHMjAeFw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4x
CzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBs
dXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABM0PW1aC3/BFGtat
93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uNAm8x
Ik0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0P
AQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwj
FNiPwyCrKGBZMB8GA1UdIwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqG
SM49BAMDA2gAMGUCMHD+sAvZ94OX7PNVHdTcswYO/jOYnYs5kGuUIe22113WTNch
p+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjlvPl5adytRSv3tjFzzAal
U5ORGpOucGpnutee5WEaXw==
-----END CERTIFICATE-----

# Issuer: CN=OpenTrust Root CA G1 O=OpenTrust
# Subject: CN=OpenTrust Root CA G1 O=OpenTrust
# Label: "OpenTrust Root CA G1"
# Serial: 1492036577811947013770400127034825178844775
# MD5 Fingerprint: 76:00:cc:81:29:cd:55:5e:88:6a:7a:2e:f7:4d:39:da
# SHA1 Fingerprint: 79:91:e8:34:f7:e2:ee:dd:08:95:01:52:e9:55:2d:14:e9:58:d5:7e
# SHA256 Fingerprint: 56:c7:71:28:d9:8c:18:d9:1b:4c:fd:ff:bc:25:ee:91:03:d4:75:8e:a2:ab:ad:82:6a:90:f3:45:7d:46:0e:b4
-----BEGIN CERTIFICATE-----
MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUA
MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
ZW5UcnVzdCBSb290IENBIEcxMB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAw
MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
T3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7faYp6b
wiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX
/uMftk87ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR0
77F9jAHiOH3BX2pfJLKOYheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGP
uY4zbGneWK2gDqdkVBFpRGZPTBKnjix9xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLx
p2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO9z0M+Yo0FMT7MzUj8czx
Kselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq3ywgsNw2
TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+W
G+Oin6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPw
vFEVVJSmdz7QdFG9URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYY
EQRVzXR7z2FwefR7LFxckvzluFqrTJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUl0YhVyE1
2jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/PxN3DlCPaTKbYw
DQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kf
gLMtMrpkZ2CvuVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbS
FXJfLkur1J1juONI5f6ELlgKn0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0
V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLhX4SPgPL0DTatdrOjteFkdjpY3H1P
XlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80nR14SohWZ25g/4/I
i+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcmGS3t
TAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L91
09S5zvE/bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/Ky
Pu1svf0OnWZzsD2097+o4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJ
AwSQiumPv+i2tCqjI40cHLI5kqiPAlxAOXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj
1oxx
-----END CERTIFICATE-----

# Issuer: CN=OpenTrust Root CA G2 O=OpenTrust
# Subject: CN=OpenTrust Root CA G2 O=OpenTrust
# Label: "OpenTrust Root CA G2"
# Serial: 1492012448042702096986875987676935573415441
# MD5 Fingerprint: 57:24:b6:59:24:6b:ae:c8:fe:1c:0c:20:f2:c0:4e:eb
# SHA1 Fingerprint: 79:5f:88:60:c5:ab:7c:3d:92:e6:cb:f4:8d:e1:45:cd:11:ef:60:0b
# SHA256 Fingerprint: 27:99:58:29:fe:6a:75:15:c1:bf:e8:48:f9:c4:76:1d:b1:6c:22:59:29:25:7b:f4:0d:08:94:f2:9e:a8:ba:f2
-----BEGIN CERTIFICATE-----
MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUA
MEAxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9w
ZW5UcnVzdCBSb290IENBIEcyMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAw
MFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwU
T3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+Ntmh
/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78e
CbY2albz4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/6
1UWY0jUJ9gNDlP7ZvyCVeYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fE
FY8ElggGQgT4hNYdvJGmQr5J1WqIP7wtUdGejeBSzFfdNTVY27SPJIjki9/ca1TS
gSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz3GIZ38i1MH/1PCZ1Eb3X
G7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj3CzMpSZy
YhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaH
vGOz9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4
t/bQWVyJ98LVtZR00dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/
gh7PU3+06yzbXfZqfUAkBXKJOAGTy3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUajn6QiL3
5okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59M4PLuG53hq8w
DQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0
nXGEL8pZ0keImUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qT
RmTFAHneIWv2V6CG1wZy7HBGS4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpT
wm+bREx50B1ws9efAvSyB7DH5fitIw6mVskpEndI2S9G/Tvw/HRwkqWOOAgfZDC2
t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ6e18CL13zSdkzJTa
TkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97krgCf2
o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU
3jg9CcCoSmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eA
iN1nE28daCSLT7d0geX0YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14f
WKGVyasvc0rQLW6aWQ9VGHgtPFGml4vmu7JwqkwR3v98KzfUetF3NI/n+UL3PIEM
S1IK
-----END CERTIFICATE-----

# Issuer: CN=OpenTrust Root CA G3 O=OpenTrust
# Subject: CN=OpenTrust Root CA G3 O=OpenTrust
# Label: "OpenTrust Root CA G3"
# Serial: 1492104908271485653071219941864171170455615
# MD5 Fingerprint: 21:37:b4:17:16:92:7b:67:46:70:a9:96:d7:a8:13:24
# SHA1 Fingerprint: 6e:26:64:f3:56:bf:34:55:bf:d1:93:3f:7c:01:de:d8:13:da:8a:a6
# SHA256 Fingerprint: b7:c3:62:31:70:6e:81:07:8c:36:7c:b8:96:19:8f:1e:32:08:dd:92:69:49:dd:8f:57:09:a4:10:f7:5b:62:92
-----BEGIN CERTIFICATE-----
MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAx
CzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5U
cnVzdCBSb290IENBIEczMB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFow
QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9wZW5UcnVzdDEdMBsGA1UEAwwUT3Bl
blRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARK7liuTcpm
3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5Bta1d
oYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4G
A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5
DMlv4VBN0BBY3JWIbTAfBgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAK
BggqhkjOPQQDAwNpADBmAjEAj6jcnboMBBf6Fek9LykBl7+BFjNAk2z8+e2AcG+q
j9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta3U1fJAuwACEl74+nBCZx
4nxp5V2a+EEfOzmTk51V6s2N8fvB
-----END CERTIFICATE-----

# Issuer: CN=ISRG Root X1 O=Internet Security Research Group
# Subject: CN=ISRG Root X1 O=Internet Security Research Group
# Label: "ISRG Root X1"
# Serial: 172886928669790476064670243504169061120
# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e
# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8
# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
   0707010001f47d000081a40000000000000000000000016a100daf000027ac000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/hyper/ssl_compat.py    # -*- coding: utf-8 -*-
"""
hyper/ssl_compat
~~~~~~~~~

Shoves pyOpenSSL into an API that looks like the standard Python 3.x ssl
module.

Currently exposes exactly those attributes, classes, and methods that we
actually use in hyper (all method signatures are complete, however). May be
expanded to something more general-purpose in the future.
"""
try:
    import StringIO as BytesIO
except ImportError:
    from io import BytesIO
import errno
import socket
import time

from OpenSSL import SSL as ossl
from service_identity.pyopenssl import verify_hostname as _verify

CERT_NONE = ossl.VERIFY_NONE
CERT_REQUIRED = ossl.VERIFY_PEER | ossl.VERIFY_FAIL_IF_NO_PEER_CERT

_OPENSSL_ATTRS = dict(
    OP_NO_COMPRESSION='OP_NO_COMPRESSION',
    PROTOCOL_TLSv1_2='TLSv1_2_METHOD',
    PROTOCOL_SSLv23='SSLv23_METHOD',
)

for external, internal in _OPENSSL_ATTRS.items():
    value = getattr(ossl, internal, None)
    if value:
        locals()[external] = value

OP_ALL = 0
# TODO: Find out the names of these other flags.
for bit in [31] + list(range(10)):
    OP_ALL |= 1 << bit

HAS_NPN = True


def _proxy(method):
    def inner(self, *args, **kwargs):
        return getattr(self._conn, method)(*args, **kwargs)
    return inner


# Referenced in hyper/http20/connection.py. These values come
# from the python ssl package, and must be defined in this file
# for hyper to work in python versions <2.7.9
SSL_ERROR_WANT_READ = 2
SSL_ERROR_WANT_WRITE = 3


# TODO missing some attributes
class SSLError(OSError):
    pass


class CertificateError(SSLError):
    pass


def verify_hostname(ssl_sock, server_hostname):
    """
    A method nearly compatible with the stdlib's match_hostname.
    """
    if isinstance(server_hostname, bytes):
        server_hostname = server_hostname.decode('ascii')
    return _verify(ssl_sock._conn, server_hostname)


class SSLSocket(object):
    SSL_TIMEOUT = 3
    SSL_RETRY = .01

    def __init__(self, conn, server_side, do_handshake_on_connect,
                 suppress_ragged_eofs, server_hostname, check_hostname):
        self._conn = conn
        self._do_handshake_on_connect = do_handshake_on_connect
        self._suppress_ragged_eofs = suppress_ragged_eofs
        self._check_hostname = check_hostname

        if server_side:
            self._conn.set_accept_state()
        else:
            if server_hostname:
                self._conn.set_tlsext_host_name(
                    server_hostname.encode('utf-8')
                )
                self._server_hostname = server_hostname
            # FIXME does this override do_handshake_on_connect=False?
            self._conn.set_connect_state()

        if self.connected and self._do_handshake_on_connect:
            self.do_handshake()

    @property
    def connected(self):
        try:
            self._conn.getpeername()
        except socket.error as e:
            if e.errno != errno.ENOTCONN:
                # It's an exception other than the one we expected if we're not
                # connected.
                raise
            return False
        return True

    # Lovingly stolen from CherryPy
    # (http://svn.cherrypy.org/tags/cherrypy-3.2.1/cherrypy/wsgiserver/ssl_pyopenssl.py).
    def _safe_ssl_call(self, suppress_ragged_eofs, call, *args, **kwargs):
        """Wrap the given call with SSL error-trapping."""
        start = time.time()
        while True:
            try:
                return call(*args, **kwargs)
            except (ossl.WantReadError, ossl.WantWriteError):
                # Sleep and try again. This is dangerous, because it means
                # the rest of the stack has no way of differentiating
                # between a "new handshake" error and "client dropped".
                # Note this isn't an endless loop: there's a timeout below.
                time.sleep(self.SSL_RETRY)
            except ossl.Error as e:
                if suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'):
                    return b''
                raise socket.error(e.args[0])

            if time.time() - start > self.SSL_TIMEOUT:
                raise socket.timeout('timed out')

    def connect(self, address):
        self._conn.connect(address)
        if self._do_handshake_on_connect:
            self.do_handshake()

    def do_handshake(self):
        self._safe_ssl_call(False, self._conn.do_handshake)
        if self._check_hostname:
            verify_hostname(self, self._server_hostname)

    def recv(self, bufsize, flags=None):
        return self._safe_ssl_call(
            self._suppress_ragged_eofs,
            self._conn.recv,
            bufsize,
            flags
        )

    def recv_into(self, buffer, bufsize=None, flags=None):
        # A temporary recv_into implementation. Should be replaced when
        # PyOpenSSL has merged pyca/pyopenssl#121.
        if bufsize is None:
            bufsize = len(buffer)

        data = self.recv(bufsize, flags)
        data_len = len(data)
        buffer[0:data_len] = data
        return data_len

    def send(self, data, flags=None):
        return self._safe_ssl_call(False, self._conn.send, data, flags)

    def sendall(self, data, flags=None):
        return self._safe_ssl_call(False, self._conn.sendall, data, flags)

    def selected_npn_protocol(self):
        proto = self._conn.get_next_proto_negotiated()
        if isinstance(proto, bytes):
            proto = proto.decode('ascii')

        return proto if proto else None

    def selected_alpn_protocol(self):
        proto = self._conn.get_alpn_proto_negotiated()
        if isinstance(proto, bytes):
            proto = proto.decode('ascii')

        return proto if proto else None

    def getpeercert(self):
        def resolve_alias(alias):
            return dict(
                C='countryName',
                ST='stateOrProvinceName',
                L='localityName',
                O='organizationName',  # noqa: E741
                OU='organizationalUnitName',
                CN='commonName',
            ).get(alias, alias)

        def to_components(name):
            # TODO Verify that these are actually *supposed* to all be
            # single-element tuples, and that's not just a quirk of the
            # examples I've seen.
            return tuple(
                [
                    (resolve_alias(k.decode('utf-8'), v.decode('utf-8')),)
                    for k, v in name.get_components()
                ]
            )

        # The standard getpeercert() takes the nice X509 object tree returned
        # by OpenSSL and turns it into a dict according to some format it seems
        # to have made up on the spot. Here, we do our best to emulate that.
        cert = self._conn.get_peer_certificate()
        result = dict(
            issuer=to_components(cert.get_issuer()),
            subject=to_components(cert.get_subject()),
            version=cert.get_subject(),
            serialNumber=cert.get_serial_number(),
            notBefore=cert.get_notBefore(),
            notAfter=cert.get_notAfter(),
        )
        # TODO extensions, including subjectAltName
        # (see _decode_certificate in _ssl.c)
        return result

    # a dash of magic to reduce boilerplate
    methods = ['accept', 'bind', 'close', 'getsockname', 'listen', 'fileno']
    for method in methods:
        locals()[method] = _proxy(method)


class SSLContext(object):
    def __init__(self, protocol):
        self.protocol = protocol
        self._ctx = ossl.Context(protocol)
        self.options = OP_ALL
        self.check_hostname = False
        self.npn_protos = []

    @property
    def options(self):
        return self._options

    @options.setter
    def options(self, value):
        self._options = value
        self._ctx.set_options(value)

    @property
    def verify_mode(self):
        return self._ctx.get_verify_mode()

    @verify_mode.setter
    def verify_mode(self, value):
        # TODO verify exception is raised on failure
        self._ctx.set_verify(
            value, lambda conn, cert, errnum, errdepth, ok: ok
        )

    def set_default_verify_paths(self):
        self._ctx.set_default_verify_paths()

    def load_verify_locations(self, cafile=None, capath=None, cadata=None):
        # TODO factor out common code
        if cafile is not None:
            cafile = cafile.encode('utf-8')
        if capath is not None:
            capath = capath.encode('utf-8')
        self._ctx.load_verify_locations(cafile, capath)
        if cadata is not None:
            self._ctx.load_verify_locations(BytesIO(cadata))

    def load_cert_chain(self, certfile, keyfile=None, password=None):
        self._ctx.use_certificate_file(certfile)
        if password is not None:
            self._ctx.set_passwd_cb(
                lambda max_length, prompt_twice, userdata: password
            )
        self._ctx.use_privatekey_file(keyfile or certfile)

    def set_npn_protocols(self, protocols):
        self.protocols = list(map(lambda x: x.encode('ascii'), protocols))

        def cb(conn, protos):
            # Detect the overlapping set of protocols.
            overlap = set(protos) & set(self.protocols)

            # Select the option that comes last in the list in the overlap.
            for p in self.protocols:
                if p in overlap:
                    return p
            else:
                return b''

        self._ctx.set_npn_select_callback(cb)

    def set_alpn_protocols(self, protocols):
        protocols = list(map(lambda x: x.encode('ascii'), protocols))
        self._ctx.set_alpn_protos(protocols)

    def wrap_socket(self,
                    sock,
                    server_side=False,
                    do_handshake_on_connect=True,
                    suppress_ragged_eofs=True,
                    server_hostname=None):
        conn = ossl.Connection(self._ctx, sock)
        return SSLSocket(conn, server_side, do_handshake_on_connect,
                         suppress_ragged_eofs, server_hostname,
                         # TODO what if this is changed after the fact?
                         self.check_hostname)
0707010001f47c000081a40000000000000000000000016a100daf00001164000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/hyper/httplib_compat.py    # -*- coding: utf-8 -*-
"""
hyper/httplib_compat
~~~~~~~~~~~~~~~~~~~~

This file defines the publicly-accessible API for hyper. This API also
constitutes the abstraction layer between HTTP/1.1 and HTTP/2.

This API doesn't currently work, and is a lower priority than the HTTP/2
stack at this time.
"""
import socket
try:
    import http.client as httplib
except ImportError:
    import httplib

from .compat import ssl
from .http20.tls import wrap_socket

# If there's no NPN support, we're going to drop all support for HTTP/2.
try:
    support_20 = ssl.HAS_NPN
except AttributeError:
    support_20 = False

# The HTTPConnection object is currently always the underlying one.
HTTPConnection = httplib.HTTPConnection
HTTPSConnection = httplib.HTTPSConnection

# If we have NPN support, define our custom one, otherwise just use the
# default.
if support_20:
    class HTTPSConnection(object):
        """
        An object representing a single HTTPS connection, whether HTTP/1.1 or
        HTTP/2.

        More specifically, this object represents an abstraction over the
        distinction. This object encapsulates a connection object for one of
        the specific types of connection, and delegates most of the work to
        that object.
        """
        def __init__(self, *args, **kwargs):
            # Whatever arguments and keyword arguments are passed to this
            # object need to be saved off for when we initialise one of our
            # subsidiary objects.
            self._original_args = args
            self._original_kwargs = kwargs

            # Set up some variables we're going to use later.
            self._sock = None
            self._conn = None

            # Prepare our backlog of method calls.
            self._call_queue = []

        def __getattr__(self, name):
            # Anything that can't be found on this instance is presumably a
            # property of underlying connection object.
            # We need to be a little bit careful here. There are a few methods
            # that can act on a HTTPSConnection before it actually connects to
            # the remote server. We don't want to change the semantics of the,
            # HTTPSConnection so we need to spot these and queue them up. When
            # we actually create the backing Connection, we'll apply them
            # immediately. These methods can't throw exceptions, so we should
            # be fine.
            delay_methods = ["set_tunnel", "set_debuglevel"]

            if self._conn is None and name in delay_methods:
                # Return a little closure that saves off the method call to
                # apply later.
                def capture(obj, *args, **kwargs):
                    self._call_queue.append((name, args, kwargs))
                return capture
            elif self._conn is None:
                # We're being told to do something! We can now connect to the
                # remote server and build the connection object.
                self._delayed_connect()

            # Call through to the underlying object.
            return getattr(self._conn, name)

        def _delayed_connect(self):
            """
            Called when we need to work out what kind of HTTPS connection we're
            actually going to use.
            """
            # Because we're ghetto, we're going to quickly create a
            # HTTPConnection object to parse the args and kwargs for us, and
            # grab the values out.
            tempconn = httplib.HTTPConnection(*self._original_args,
                                              **self._original_kwargs)
            host = tempconn.host
            port = tempconn.port
            timeout = tempconn.timeout
            source_address = tempconn.source_address

            # Connect to the remote server.
            sock = socket.create_connection(
                (host, port),
                timeout,
                source_address
            )

            # Wrap it in TLS. This needs to be looked at in future when I pull
            # in the TLS verification logic from urllib3, but right now we
            # accept insecurity because no-one's using this anyway.
            sock = wrap_socket(sock, host)

            # At this early stage the library can't do HTTP/2, so who cares?
            tempconn.sock = sock
            self._sock = sock
            self._conn = tempconn

            return
0707010001f464000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/hyper/common   0707010001f469000081a40000000000000000000000016a100daf000007cc000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hyper/common/exceptions.py # -*- coding: utf-8 -*-
"""
hyper/common/exceptions
~~~~~~~~~~~~~~~~~~~~~~~

Contains hyper's exceptions.
"""


class ChunkedDecodeError(Exception):
    """
    An error was encountered while decoding a chunked response.
    """
    pass


class InvalidResponseError(Exception):
    """
    A problem was found with the response that makes it invalid.
    """
    pass


class SocketError(Exception):
    """
    An error occurred during socket operation.
    """
    pass


class LineTooLongError(Exception):
    """
    An attempt to read a line from a socket failed because no newline was
    found.
    """
    pass


# Create our own ConnectionResetError.
try:  # pragma: no cover
    ConnectionResetError = ConnectionResetError
except NameError:  # pragma: no cover
    class ConnectionResetError(Exception):
        """
        A HTTP connection was unexpectedly reset.
        """


class TLSUpgrade(Exception):
    """
    We upgraded to a new protocol in the NPN/ALPN handshake.
    """
    def __init__(self, negotiated, sock):
        super(TLSUpgrade, self).__init__()
        self.negotiated = negotiated
        self.sock = sock


class HTTPUpgrade(Exception):
    """
    We upgraded to a new protocol via the HTTP Upgrade response.
    """
    def __init__(self, negotiated, sock):
        super(HTTPUpgrade, self).__init__()
        self.negotiated = negotiated
        self.sock = sock


class MissingCertFile(Exception):
    """
    The certificate file could not be found.
    """
    pass


# Create our own ConnectionError.
try:  # pragma: no cover
    ConnectionError = ConnectionError
except NameError:  # pragma: no cover
    class ConnectionError(Exception):
        """
        An error occurred during connection to a host.
        """


class ProxyError(ConnectionError):
    """
    An error occurred during connection to a proxy.
    """
    def __init__(self, message, response):
        self.response = response
        super(ProxyError, self).__init__(message)
0707010001f468000081a40000000000000000000000016a100daf00000618000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/hyper/common/decoder.py    # -*- coding: utf-8 -*-
"""
hyper/common/decoder
~~~~~~~~~~~~~~~~~~~~

Contains hyper's code for handling compressed bodies.
"""
import zlib


class DeflateDecoder(object):
    """
    This is a decoding object that wraps ``zlib`` and is used for decoding
    deflated content.

    This rationale for the existence of this object is pretty unpleasant.
    The HTTP RFC specifies that 'deflate' is a valid content encoding. However,
    the spec _meant_ the zlib encoding form. Unfortunately, people who didn't
    read the RFC very carefully actually implemented a different form of
    'deflate'. Insanely, ``zlib`` handles them using two wbits values. This is
    such a mess it's hard to adequately articulate.

    This class was lovingly borrowed from the excellent urllib3 library under
    license: see NOTICES. If you ever see @shazow, you should probably buy him
    a drink or something.
    """
    def __init__(self):
        self._first_try = True
        self._data = b''
        self._obj = zlib.decompressobj(zlib.MAX_WBITS)

    def __getattr__(self, name):
        return getattr(self._obj, name)

    def decompress(self, data):
        if not self._first_try:
            return self._obj.decompress(data)

        self._data += data
        try:
            return self._obj.decompress(data)
        except zlib.error:
            self._first_try = False
            self._obj = zlib.decompressobj(-zlib.MAX_WBITS)
            try:
                return self.decompress(self._data)
            finally:
                self._data = None
0707010001f46b000081a40000000000000000000000016a100daf000005f3000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/hyper/common/util.py   # -*- coding: utf-8 -*-
"""
hyper/common/util
~~~~~~~~~~~~~~~~~

General utility functions for use with hyper.
"""
from aenum import Enum

from hyper.compat import unicode, bytes, imap
from rfc3986 import URIReference
from ..compat import is_py3


def to_bytestring(element):
    """
    Converts a single string to a bytestring, encoding via UTF-8 if needed.
    """
    if isinstance(element, unicode):
        return element.encode('utf-8')
    elif isinstance(element, bytes):
        return element
    else:
        raise ValueError("Non string type.")


def to_bytestring_tuple(*x):
    """
    Converts the given strings to a bytestring if necessary, returning a
    tuple. Uses ``to_bytestring``.
    """
    return tuple(imap(to_bytestring, x))


def to_host_port_tuple(host_port_str, default_port=80):
    """
    Converts the given string containing a host and possibly a port
    to a tuple.
    """
    uri = URIReference(
        scheme=None,
        authority=host_port_str,
        path=None,
        query=None,
        fragment=None
    )

    host = uri.host.strip('[]')
    if not uri.port:
        port = default_port
    else:
        port = int(uri.port)

    return (host, port)


def to_native_string(string, encoding='utf-8'):
    if isinstance(string, str):
        return string

    return string.decode(encoding) if is_py3 else string.encode(encoding)


class HTTPVersion(Enum):
    """
    Collection of all HTTP versions used in hyper.
    """
    http11 = "HTTP/1.1"
    http20 = "HTTP/2"
 0707010001f46a000081a40000000000000000000000016a100daf0000230c000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/hyper/common/headers.py    # -*- coding: utf-8 -*-
"""
hyper/common/headers
~~~~~~~~~~~~~~~~~~~~~

Contains hyper's structures for storing and working with HTTP headers.
"""
try:
    from collections.abc import MutableMapping
except ImportError:
    from collections import MutableMapping

from hyper.common.util import to_bytestring, to_bytestring_tuple


class HTTPHeaderMap(MutableMapping):
    """
    A structure that contains HTTP headers.

    HTTP headers are a curious beast. At the surface level they look roughly
    like a name-value set, but in practice they have many variations that
    make them tricky:

    - duplicate keys are allowed
    - keys are compared case-insensitively
    - duplicate keys are isomorphic to comma-separated values, *except when
      they aren't*!
    - they logically contain a form of ordering

    This data structure is an attempt to preserve all of that information
    while being as user-friendly as possible. It retains all of the mapping
    convenience methods (allowing by-name indexing), while avoiding using a
    dictionary for storage.

    When iterated over, this structure returns headers in 'canonical form'.
    This form is a tuple, where the first entry is the header name (in
    lower-case), and the second entry is a list of header values (in original
    case).

    The mapping always emits both names and values in the form of bytestrings:
    never unicode strings. It can accept names and values in unicode form, and
    will automatically be encoded to bytestrings using UTF-8. The reason for
    what appears to be a user-unfriendly decision here is primarily to allow
    the broadest-possible compatibility (to make it possible to send headers in
    unusual encodings) while ensuring that users are never confused about what
    type of data they will receive.

    .. warning:: Note that this data structure makes none of the performance
                 guarantees of a dictionary. Lookup and deletion is not an O(1)
                 operation. Inserting a new value *is* O(1), all other
                 operations are O(n), including *replacing* a header entirely.
    """
    def __init__(self, *args, **kwargs):
        # The meat of the structure. In practice, headers are an ordered list
        # of tuples. This early version of the data structure simply uses this
        # directly under the covers.
        #
        # An important curiosity here is that the headers are not stored in
        # 'canonical form', but are instead stored in the form they were
        # provided in. This is to ensure that it is always possible to
        # reproduce the original header structure if necessary. This leads to
        # some unfortunate performance costs on structure access where it is
        # often necessary to transform the data into canonical form on access.
        # This cost is judged acceptable in low-level code like `hyper`, but
        # higher-level abstractions should consider if they really require this
        # logic.
        self._items = []

        for arg in args:
            self._items.extend(map(lambda x: to_bytestring_tuple(*x), arg))

        for k, v in kwargs.items():
            self._items.append(to_bytestring_tuple(k, v))

    def __getitem__(self, key):
        """
        Unlike the dict __getitem__, this returns a list of items in the order
        they were added. These items are returned in 'canonical form', meaning
        that comma-separated values are split into multiple values.
        """
        key = to_bytestring(key)
        values = []

        for k, v in self._items:
            if _keys_equal(k, key):
                values.extend(x[1] for x in canonical_form(k, v))

        if not values:
            raise KeyError("Nonexistent header key: {}".format(key))

        return values

    def __setitem__(self, key, value):
        """
        Unlike the dict __setitem__, this appends to the list of items.
        """
        self._items.append(to_bytestring_tuple(key, value))

    def __delitem__(self, key):
        """
        Sadly, __delitem__ is kind of stupid here, but the best we can do is
        delete all headers with a given key. To correctly achieve the 'KeyError
        on missing key' logic from dictionaries, we need to do this slowly.
        """
        key = to_bytestring(key)
        indices = []
        for (i, (k, v)) in enumerate(self._items):
            if _keys_equal(k, key):
                indices.append(i)

        if not indices:
            raise KeyError("Nonexistent header key: {}".format(key))

        for i in indices[::-1]:
            self._items.pop(i)

    def __iter__(self):
        """
        This mapping iterates like the list of tuples it is. The headers are
        returned in canonical form.
        """
        for pair in self._items:
            for value in canonical_form(*pair):
                yield value

    def __len__(self):
        """
        The length of this mapping is the number of individual headers in
        canonical form. Sadly, this is a somewhat expensive operation.
        """
        size = 0
        for _ in self:
            size += 1

        return size

    def __contains__(self, key):
        """
        If any header is present with this key, returns True.
        """
        key = to_bytestring(key)
        return any(_keys_equal(key, k) for k, _ in self._items)

    def keys(self):
        """
        Returns an iterable of the header keys in the mapping. This explicitly
        does not filter duplicates, ensuring that it's the same length as
        len().
        """
        for n, _ in self:
            yield n

    def items(self):
        """
        This mapping iterates like the list of tuples it is.
        """
        return self.__iter__()

    def values(self):
        """
        This is an almost nonsensical query on a header dictionary, but we
        satisfy it in the exact same way we satisfy 'keys'.
        """
        for _, v in self:
            yield v

    def get(self, name, default=None):
        """
        Unlike the dict get, this returns a list of items in the order
        they were added.
        """
        try:
            return self[name]
        except KeyError:
            return default

    def iter_raw(self):
        """
        Allows iterating over the headers in 'raw' form: that is, the form in
        which they were added to the structure. This iteration is in order,
        and can be used to rebuild the original headers (e.g. to determine
        exactly what a server sent).
        """
        for item in self._items:
            yield item

    def replace(self, key, value):
        """
        Replace existing header with new value. If header doesn't exist this
        method work like ``__setitem__``. Replacing leads to deletion of all
        existing headers with the same name.
        """
        key, value = to_bytestring_tuple(key, value)
        indices = []
        for (i, (k, v)) in enumerate(self._items):
            if _keys_equal(k, key):
                indices.append(i)

        # If the key isn't present, this is easy: just append and abort early.
        if not indices:
            self._items.append((key, value))
            return

        # Delete all but the first. I swear, this is the correct slicing
        # syntax!
        base_index = indices[0]
        for i in indices[:0:-1]:
            self._items.pop(i)

        del self._items[base_index]
        self._items.insert(base_index, (key, value))

    def merge(self, other):
        """
        Merge another header set or any other dict-like into this one.
        """
        # Short circuit to avoid infinite loops in case we try to merge into
        # ourselves.
        if other is self:
            return

        if isinstance(other, HTTPHeaderMap):
            self._items.extend(other.iter_raw())
            return

        for k, v in other.items():
            self._items.append(to_bytestring_tuple(k, v))

    def __eq__(self, other):
        return self._items == other._items

    def __ne__(self, other):
        return self._items != other._items

    def __str__(self):  # pragma: no cover
        return 'HTTPHeaderMap(%s)' % self._items

    def __repr__(self):  # pragma: no cover
        return str(self)


def canonical_form(k, v):
    """
    Returns an iterable of key-value-pairs corresponding to the header in
    canonical form. This means that the header is split on commas unless for
    any reason it's a super-special snowflake (I'm looking at you Set-Cookie).
    """
    SPECIAL_SNOWFLAKES = set([b'set-cookie', b'set-cookie2'])

    k = k.lower()

    if k in SPECIAL_SNOWFLAKES:
        yield k, v
    else:
        for sub_val in v.split(b','):
            yield k, sub_val.strip()


def _keys_equal(x, y):
    """
    Returns 'True' if the two keys are equal by the laws of HTTP headers.
    """
    return x.lower() == y.lower()
0707010001f467000081a40000000000000000000000016a100daf00001988000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hyper/common/connection.py # -*- coding: utf-8 -*-
"""
hyper/common/connection
~~~~~~~~~~~~~~~~~~~~~~~

Hyper's HTTP/1.1 and HTTP/2 abstraction layer.
"""
from .exceptions import TLSUpgrade, HTTPUpgrade
from ..http11.connection import HTTP11Connection
from ..http20.connection import HTTP20Connection
from ..tls import H2_NPN_PROTOCOLS, H2C_PROTOCOL


class HTTPConnection(object):
    """
    An object representing a single HTTP connection to a server.

    This object behaves similarly to the Python standard library's
    ``HTTPConnection`` object, with a few critical differences.

    Most of the standard library's arguments to the constructor are not
    supported by hyper. Most optional parameters apply to *either* HTTP/1.1 or
    HTTP/2.

    :param host: The host to connect to. This may be an IP address or a
        hostname, and optionally may include a port: for example,
        ``'http2bin.org'``, ``'http2bin.org:443'`` or ``'127.0.0.1'``.
    :param port: (optional) The port to connect to. If not provided and one
        also isn't provided in the ``host`` parameter, defaults to 80.
    :param secure: (optional) Whether the request should use TLS.
        Defaults to ``False`` for most requests, but to ``True`` for any
        request issued to port 443.
    :param window_manager: (optional) The class to use to manage flow control
        windows. This needs to be a subclass of the
        :class:`BaseFlowControlManager
        <hyper.http20.window.BaseFlowControlManager>`. If not provided,
        :class:`FlowControlManager <hyper.http20.window.FlowControlManager>`
        will be used.
    :param enable_push: (optional) Whether the server is allowed to push
        resources to the client (see
        :meth:`get_pushes() <hyper.HTTP20Connection.get_pushes>`).
    :param ssl_context: (optional) A class with custom certificate settings.
        If not provided then hyper's default ``SSLContext`` is used instead.
    :param proxy_host: (optional) The proxy to connect to.  This can be an IP
        address or a host name and may include a port.
    :param proxy_port: (optional) The proxy port to connect to. If not provided
        and one also isn't provided in the ``proxy_host`` parameter, defaults
        to 8080.
    :param proxy_headers: (optional) The headers to send to a proxy.
    """
    def __init__(self,
                 host,
                 port=None,
                 secure=None,
                 window_manager=None,
                 enable_push=False,
                 ssl_context=None,
                 proxy_host=None,
                 proxy_port=None,
                 proxy_headers=None,
                 timeout=None,
                 **kwargs):

        self._host = host
        self._port = port
        self._h1_kwargs = {
            'secure': secure, 'ssl_context': ssl_context,
            'proxy_host': proxy_host, 'proxy_port': proxy_port,
            'proxy_headers': proxy_headers, 'enable_push': enable_push,
            'timeout': timeout
        }
        self._h2_kwargs = {
            'window_manager': window_manager, 'enable_push': enable_push,
            'secure': secure, 'ssl_context': ssl_context,
            'proxy_host': proxy_host, 'proxy_port': proxy_port,
            'proxy_headers': proxy_headers,
            'timeout': timeout
        }

        # Add any unexpected kwargs to both dictionaries.
        self._h1_kwargs.update(kwargs)
        self._h2_kwargs.update(kwargs)

        self._conn = HTTP11Connection(
            self._host, self._port, **self._h1_kwargs
        )

    def request(self, method, url, body=None, headers=None):
        """
        This will send a request to the server using the HTTP request method
        ``method`` and the selector ``url``. If the ``body`` argument is
        present, it should be string or bytes object of data to send after the
        headers are finished. Strings are encoded as UTF-8. To use other
        encodings, pass a bytes object. The Content-Length header is set to the
        length of the body field.

        :param method: The request method, e.g. ``'GET'``.
        :param url: The URL to contact, e.g. ``'/path/segment'``.
        :param body: (optional) The request body to send. Must be a bytestring
            or a file-like object.
        :param headers: (optional) The headers to send on the request.
        :returns: A stream ID for the request, or ``None`` if the request is
            made over HTTP/1.1.
        """

        headers = headers or {}

        try:
            return self._conn.request(
                method=method, url=url, body=body, headers=headers
            )
        except TLSUpgrade as e:
            # We upgraded in the NPN/ALPN handshake. We can just go straight to
            # the world of HTTP/2. Replace the backing object and insert the
            # socket into it.
            assert e.negotiated in H2_NPN_PROTOCOLS

            self._conn = HTTP20Connection(
                self._host, self._port, **self._h2_kwargs
            )
            self._conn._sock = e.sock

            # Because we skipped the connecting logic, we need to send the
            # HTTP/2 preamble.
            self._conn._send_preamble()

            return self._conn.request(
                method=method, url=url, body=body, headers=headers
            )

    def get_response(self, *args, **kwargs):
        """
        Returns a response object.
        """
        try:
            return self._conn.get_response(*args, **kwargs)
        except HTTPUpgrade as e:
            # We upgraded via the HTTP Upgrade mechanism. We can just
            # go straight to the world of HTTP/2. Replace the backing object
            # and insert the socket into it.
            assert e.negotiated == H2C_PROTOCOL

            self._conn = HTTP20Connection(
                self._host, self._port, **self._h2_kwargs
            )

            self._conn._connect_upgrade(e.sock)
            # stream id 1 is used by the upgrade request and response
            # and is half-closed by the client

            return self._conn.get_response(1)

    # The following two methods are the implementation of the context manager
    # protocol.
    def __enter__(self):  # pragma: no cover
        return self

    def __exit__(self, type, value, tb):  # pragma: no cover
        self._conn.close()
        return False  # Never swallow exceptions.

    # Can anyone say 'proxy object pattern'?
    def __getattr__(self, name):
        return getattr(self._conn, name)
0707010001f465000081a40000000000000000000000016a100daf00000051000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/foreign/hyper/common/__init__.py   # -*- coding: utf-8 -*-
"""
hyper/common
~~~~~~~~~~~~

Common code in hyper.
"""
   0707010001f466000081a40000000000000000000000016a100daf0000208a000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/foreign/hyper/common/bufsocket.py  # -*- coding: utf-8 -*-
"""
hyper/http20/bufsocket.py
~~~~~~~~~~~~~~~~~~~~~~~~~

This file implements a buffered socket wrapper.

The purpose of this is to avoid the overhead of unnecessary syscalls while
allowing small reads from the network. This represents a potentially massive
performance optimisation at the cost of burning some memory in the userspace
process.
"""
import select
from .exceptions import ConnectionResetError, LineTooLongError


class BufferedSocket(object):
    """
    A buffered socket wrapper.

    The purpose of this is to avoid the overhead of unnecessary syscalls while
    allowing small reads from the network. This represents a potentially
    massive performance optimisation at the cost of burning some memory in the
    userspace process.
    """
    def __init__(self, sck, buffer_size=1000):
        """
        Create the buffered socket.

        :param sck: The socket to wrap.
        :param buffer_size: The size of the backing buffer in bytes. This
            parameter should be set to an appropriate value for your use case.
            Small values of ``buffer_size`` increase the overhead of buffer
            management: large values cause more memory to be used.
        """
        # The wrapped socket.
        self._sck = sck

        # The buffer we're using.
        self._backing_buffer = bytearray(buffer_size)
        self._buffer_view = memoryview(self._backing_buffer)

        # The size of the buffer.
        self._buffer_size = buffer_size

        # The start index in the memory view.
        self._index = 0

        # The number of bytes in the buffer.
        self._bytes_in_buffer = 0

    @property
    def _remaining_capacity(self):
        """
        The maximum number of bytes the buffer could still contain.
        """
        return self._buffer_size - self._index

    @property
    def _buffer_end(self):
        """
        The index of the first free byte in the buffer.
        """
        return self._index + self._bytes_in_buffer

    @property
    def can_read(self):
        """
        Whether or not there is more data to read from the socket.
        """
        read = select.select([self._sck], [], [], 0)[0]
        if read:
            return True

        return False

    @property
    def buffer(self):
        """
        Get access to the buffer itself.
        """
        return self._buffer_view[self._index:self._buffer_end]

    def advance_buffer(self, count):
        """
        Advances the buffer by the amount of data consumed outside the socket.
        """
        self._index += count
        self._bytes_in_buffer -= count

    def new_buffer(self):
        """
        This method moves all the data in the backing buffer to the start of
        a new, fresh buffer. This gives the ability to read much more data.
        """
        def read_all_from_buffer():
            end = self._index + self._bytes_in_buffer
            return self._buffer_view[self._index:end]

        new_buffer = bytearray(self._buffer_size)
        new_buffer_view = memoryview(new_buffer)
        new_buffer_view[0:self._bytes_in_buffer] = read_all_from_buffer()

        self._index = 0
        self._backing_buffer = new_buffer
        self._buffer_view = new_buffer_view

        return

    def recv(self, amt):
        """
        Read some data from the socket.

        :param amt: The amount of data to read.
        :returns: A ``memoryview`` object containing the appropriate number of
            bytes. The data *must* be copied out by the caller before the next
            call to this function.
        """
        # In this implementation you can never read more than the number of
        # bytes in the buffer.
        if amt > self._buffer_size:
            amt = self._buffer_size

        # If the amount of data we've been asked to read is less than the
        # remaining space in the buffer, we need to clear out the buffer and
        # start over.
        if amt > self._remaining_capacity:
            self.new_buffer()

        # If there's still some room in the buffer, opportunistically attempt
        # to read into it.
        # If we don't actually _need_ the data (i.e. there's enough in the
        # buffer to satisfy the request), use select to work out if the read
        # attempt will block. If it will, don't bother reading. If we need the
        # data, always do the read.
        if self._bytes_in_buffer >= amt:
            should_read = select.select([self._sck], [], [], 0)[0]
        else:
            should_read = True

        if should_read:
            count = self._sck.recv_into(self._buffer_view[self._buffer_end:])

            # The socket just got closed. We should throw an exception if we
            # were asked for more data than we can return.
            if not count and amt > self._bytes_in_buffer:
                raise ConnectionResetError()
            self._bytes_in_buffer += count

        # Read out the bytes and update the index.
        amt = min(amt, self._bytes_in_buffer)
        data = self._buffer_view[self._index:self._index+amt]

        self._index += amt
        self._bytes_in_buffer -= amt

        return data

    def fill(self):
        """
        Attempts to fill the buffer as much as possible. It will block for at
        most the time required to have *one* ``recv_into`` call return.
        """
        if not self._remaining_capacity:
            self.new_buffer()

        count = self._sck.recv_into(self._buffer_view[self._buffer_end:])
        if not count:
            raise ConnectionResetError()

        self._bytes_in_buffer += count

        return

    def readline(self):
        """
        Read up to a newline from the network and returns it. The implicit
        maximum line length is the buffer size of the buffered socket.

        Note that, unlike recv, this method absolutely *does* block until it
        can read the line.

        :returns: A ``memoryview`` object containing the appropriate number of
            bytes. The data *must* be copied out by the caller before the next
            call to this function.
        """
        # First, check if there's anything in the buffer. This is one of those
        # rare circumstances where this will work correctly on all platforms.
        index = self._backing_buffer.find(
            b'\n',
            self._index,
            self._index + self._bytes_in_buffer
        )

        if index != -1:
            length = index + 1 - self._index
            data = self._buffer_view[self._index:self._index+length]
            self._index += length
            self._bytes_in_buffer -= length
            return data

        # In this case, we didn't find a newline in the buffer. To fix that,
        # read some data into the buffer. To do our best to satisfy the read,
        # we should shunt the data down in the buffer so that it's right at
        # the start. We don't bother if we're already at the start of the
        # buffer.
        if self._index != 0:
            self.new_buffer()

        while self._bytes_in_buffer < self._buffer_size:
            count = self._sck.recv_into(self._buffer_view[self._buffer_end:])
            if not count:
                raise ConnectionResetError()

            # We have some more data. Again, look for a newline in that gap.
            first_new_byte = self._buffer_end
            self._bytes_in_buffer += count
            index = self._backing_buffer.find(
                b'\n',
                first_new_byte,
                first_new_byte + count,
            )

            if index != -1:
                # The length of the buffer is the index into the
                # buffer at which we found the newline plus 1, minus the start
                # index of the buffer, which really should be zero.
                assert not self._index
                length = index + 1
                data = self._buffer_view[:length]
                self._index += length
                self._bytes_in_buffer -= length
                return data

        # If we got here, it means we filled the buffer without ever getting
        # a newline. Time to throw an exception.
        raise LineTooLongError()

    def __getattr__(self, name):
        return getattr(self._sck, name)
  0707010001f46d000081a40000000000000000000000016a100daf00001bba000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/hyper/contrib.py   # -*- coding: utf-8 -*-
"""
hyper/contrib
~~~~~~~~~~~~~

Contains a few utilities for use with other HTTP libraries.
"""
try:
    from requests.adapters import HTTPAdapter
    from requests.models import Response
    from requests.structures import CaseInsensitiveDict
    from requests.utils import (
        get_encoding_from_headers, select_proxy, prepend_scheme_if_needed
    )
    from requests.cookies import extract_cookies_to_jar
except ImportError:  # pragma: no cover
    HTTPAdapter = object

from hyper.common.connection import HTTPConnection
from hyper.compat import urlparse, ssl
from hyper.tls import init_context
from hyper.common.util import to_native_string


class HTTP20Adapter(HTTPAdapter):
    """
    A Requests Transport Adapter that uses hyper to send requests over
    HTTP/2. This implements some degree of connection pooling to maximise the
    HTTP/2 gain.
    """
    def __init__(self, window_manager=None, *args, **kwargs):
        #: A mapping between HTTP netlocs and ``HTTP20Connection`` objects.
        self.connections = {}
        self.window_manager = window_manager

    def get_connection(self, host, port, scheme, cert=None, verify=True,
                       proxy=None, timeout=None):
        """
        Gets an appropriate HTTP/2 connection object based on
        host/port/scheme/cert tuples.
        """
        secure = (scheme == 'https')

        if port is None:  # pragma: no cover
            port = 80 if not secure else 443

        ssl_context = None
        if not verify:
            verify = False
            ssl_context = init_context(cert=cert)
            ssl_context.check_hostname = False
            ssl_context.verify_mode = ssl.CERT_NONE
        elif verify is True and cert is not None:
            ssl_context = init_context(cert=cert)
        elif verify is not True:
            ssl_context = init_context(cert_path=verify, cert=cert)

        if proxy:
            proxy_headers = self.proxy_headers(proxy)
            proxy_netloc = urlparse(proxy).netloc
        else:
            proxy_headers = None
            proxy_netloc = None

        # We put proxy headers in the connection_key, because
        # ``proxy_headers`` method might be overridden, so we can't
        # rely on proxy headers being the same for the same proxies.
        proxy_headers_key = (frozenset(proxy_headers.items())
                             if proxy_headers else None)
        connection_key = (host, port, scheme, cert, verify,
                          proxy_netloc, proxy_headers_key)
        try:
            conn = self.connections[connection_key]
        except KeyError:
            conn = HTTPConnection(
                host,
                port,
                secure=secure,
                window_manager=self.window_manager,
                ssl_context=ssl_context,
                proxy_host=proxy_netloc,
                proxy_headers=proxy_headers,
                timeout=timeout)
            self.connections[connection_key] = conn

        return conn

    def send(self, request, stream=False, cert=None, verify=True, proxies=None,
             timeout=None, **kwargs):
        """
        Sends a HTTP message to the server.
        """
        proxy = select_proxy(request.url, proxies)
        if proxy:
            proxy = prepend_scheme_if_needed(proxy, 'http')

        parsed = urlparse(request.url)
        conn = self.get_connection(
            parsed.hostname,
            parsed.port,
            parsed.scheme,
            cert=cert,
            verify=verify,
            proxy=proxy,
            timeout=timeout)

        # Build the selector.
        selector = parsed.path
        selector += '?' + parsed.query if parsed.query else ''
        selector += '#' + parsed.fragment if parsed.fragment else ''

        conn.request(
            request.method,
            selector,
            request.body,
            request.headers
        )
        resp = conn.get_response()

        r = self.build_response(request, resp)

        if not stream:
            r.content

        return r

    def build_response(self, request, resp):
        """
        Builds a Requests' response object.  This emulates most of the logic of
        the standard function but deals with the lack of the ``.headers``
        property on the HTTP20Response object.

        Additionally, this function builds in a number of features that are
        purely for HTTPie. This is to allow maximum compatibility with what
        urllib3 does, so that HTTPie doesn't fall over when it uses us.
        """
        response = Response()

        response.status_code = resp.status
        response.headers = CaseInsensitiveDict((
            map(to_native_string, h)
            for h in resp.headers.iter_raw()
        ))
        response.raw = resp
        response.reason = resp.reason
        response.encoding = get_encoding_from_headers(response.headers)

        extract_cookies_to_jar(response.cookies, request, response)
        response.url = request.url

        response.request = request
        response.connection = self

        # First horrible patch: Requests expects its raw responses to have a
        # release_conn method, which I don't. We should monkeypatch a no-op on.
        resp.release_conn = lambda: None

        # Next, add the things HTTPie needs. It needs the following things:
        #
        # - The `raw` object has a property called `_original_response` that is
        #   a `httplib` response object.
        # - `raw._original_response` has three simple properties: `version`,
        #   `status`, `reason`.
        # - `raw._original_response.version` has one of three values: `9`,
        #   `10`, `11`.
        # - `raw._original_response.msg` exists.
        # - `raw._original_response.msg._headers` exists and is an iterable of
        #   two-tuples.
        #
        # We fake this out. Most of this exists on our response object already,
        # and the rest can be faked.
        #
        # All of this exists for httpie, which I don't have any tests for,
        # so I'm not going to bother adding test coverage for it.
        class FakeOriginalResponse(object):  # pragma: no cover
            def __init__(self, headers):
                self._headers = headers

            def get_all(self, name, default=None):
                values = []

                for n, v in self._headers:
                    if n == name.lower():
                        values.append(v)

                if not values:
                    return default

                return values

            def getheaders(self, name):
                return self.get_all(name, [])

        response.raw._original_response = orig = FakeOriginalResponse(None)
        orig.version = 20
        orig.status = resp.status
        orig.reason = resp.reason
        orig.msg = FakeOriginalResponse(resp.headers.iter_raw())

        return response

    def close(self):
        for connection in self.connections.values():
            connection.close()
        self.connections.clear()
  0707010001f456000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/foreign/hpack  0707010001f45b000081a40000000000000000000000016a100daf000009d9000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/hpack/huffman.py   # -*- coding: utf-8 -*-
"""
hpack/huffman_decoder
~~~~~~~~~~~~~~~~~~~~~

An implementation of a bitwise prefix tree specially built for decoding
Huffman-coded content where we already know the Huffman table.
"""
from .compat import to_byte, decode_hex


class HuffmanEncoder(object):
    """
    Encodes a string according to the Huffman encoding table defined in the
    HPACK specification.
    """
    def __init__(self, huffman_code_list, huffman_code_list_lengths):
        self.huffman_code_list = huffman_code_list
        self.huffman_code_list_lengths = huffman_code_list_lengths

    def encode(self, bytes_to_encode):
        """
        Given a string of bytes, encodes them according to the HPACK Huffman
        specification.
        """
        # If handed the empty string, just immediately return.
        if not bytes_to_encode:
            return b''

        final_num = 0
        final_int_len = 0

        # Turn each byte into its huffman code. These codes aren't necessarily
        # octet aligned, so keep track of how far through an octet we are. To
        # handle this cleanly, just use a single giant integer.
        for char in bytes_to_encode:
            byte = to_byte(char)
            bin_int_len = self.huffman_code_list_lengths[byte]
            bin_int = self.huffman_code_list[byte] & (
                2 ** (bin_int_len + 1) - 1
            )
            final_num <<= bin_int_len
            final_num |= bin_int
            final_int_len += bin_int_len

        # Pad out to an octet with ones.
        bits_to_be_padded = (8 - (final_int_len % 8)) % 8
        final_num <<= bits_to_be_padded
        final_num |= (1 << bits_to_be_padded) - 1

        # Convert the number to hex and strip off the leading '0x' and the
        # trailing 'L', if present.
        final_num = hex(final_num)[2:].rstrip('L')

        # If this is odd, prepend a zero.
        final_num = '0' + final_num if len(final_num) % 2 != 0 else final_num

        # This number should have twice as many digits as bytes. If not, we're
        # missing some leading zeroes. Work out how many bytes we want and how
        # many digits we have, then add the missing zero digits to the front.
        total_bytes = (final_int_len + bits_to_be_padded) // 8
        expected_digits = total_bytes * 2

        if len(final_num) != expected_digits:
            missing_digits = expected_digits - len(final_num)
            final_num = ('0' * missing_digits) + final_num

        return decode_hex(final_num)
   0707010001f457000081a40000000000000000000000016a100daf000001e3000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/hpack/__init__.py  # -*- coding: utf-8 -*-
"""
hpack
~~~~~

HTTP/2 header encoding for Python.
"""
from .hpack import Encoder, Decoder
from .struct import HeaderTuple, NeverIndexedHeaderTuple
from .exceptions import (
    HPACKError, HPACKDecodingError, InvalidTableIndex, OversizedHeaderListError
)

__all__ = [
    'Encoder', 'Decoder', 'HPACKError', 'HPACKDecodingError',
    'InvalidTableIndex', 'HeaderTuple', 'NeverIndexedHeaderTuple',
    'OversizedHeaderListError'
]

__version__ = '3.1.0dev0'
 0707010001f45e000081a40000000000000000000000016a100daf0000041c000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/hpack/struct.py    # -*- coding: utf-8 -*-
"""
hpack/struct
~~~~~~~~~~~~

Contains structures for representing header fields with associated metadata.
"""


class HeaderTuple(tuple):
    """
    A data structure that stores a single header field.

    HTTP headers can be thought of as tuples of ``(field name, field value)``.
    A single header block is a sequence of such tuples.

    In HTTP/2, however, certain bits of additional information are required for
    compressing these headers: in particular, whether the header field can be
    safely added to the HPACK compression context.

    This class stores a header that can be added to the compression context. In
    all other ways it behaves exactly like a tuple.
    """
    __slots__ = ()

    indexable = True

    def __new__(_cls, *args):
        return tuple.__new__(_cls, args)


class NeverIndexedHeaderTuple(HeaderTuple):
    """
    A data structure that stores a single header field that cannot be added to
    a HTTP/2 header compression context.
    """
    __slots__ = ()

    indexable = False
0707010001f45f000081a40000000000000000000000016a100daf000025ab000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/hpack/table.py # -*- coding: utf-8 -*-
# flake8: noqa
from collections import deque
import logging

from .exceptions import InvalidTableIndex

log = logging.getLogger(__name__)


def table_entry_size(name, value):
    """
    Calculates the size of a single entry

    This size is mostly irrelevant to us and defined
    specifically to accommodate memory management for
    lower level implementations. The 32 extra bytes are
    considered the "maximum" overhead that would be
    required to represent each entry in the table.

    See RFC7541 Section 4.1
    """
    return 32 + len(name) + len(value)


class HeaderTable(object):
    """
    Implements the combined static and dynamic header table

    The name and value arguments for all the functions
    should ONLY be byte strings (b'') however this is not
    strictly enforced in the interface.

    See RFC7541 Section 2.3
    """
    #: Default maximum size of the dynamic table. See
    #:  RFC7540 Section 6.5.2.
    DEFAULT_SIZE = 4096

    #: Constant list of static headers. See RFC7541 Section
    #:  2.3.1 and Appendix A
    STATIC_TABLE = (
        (b':authority'                  , b''             ),  # noqa
        (b':method'                     , b'GET'          ),  # noqa
        (b':method'                     , b'POST'         ),  # noqa
        (b':path'                       , b'/'            ),  # noqa
        (b':path'                       , b'/index.html'  ),  # noqa
        (b':scheme'                     , b'http'         ),  # noqa
        (b':scheme'                     , b'https'        ),  # noqa
        (b':status'                     , b'200'          ),  # noqa
        (b':status'                     , b'204'          ),  # noqa
        (b':status'                     , b'206'          ),  # noqa
        (b':status'                     , b'304'          ),  # noqa
        (b':status'                     , b'400'          ),  # noqa
        (b':status'                     , b'404'          ),  # noqa
        (b':status'                     , b'500'          ),  # noqa
        (b'accept-charset'              , b''             ),  # noqa
        (b'accept-encoding'             , b'gzip, deflate'),  # noqa
        (b'accept-language'             , b''             ),  # noqa
        (b'accept-ranges'               , b''             ),  # noqa
        (b'accept'                      , b''             ),  # noqa
        (b'access-control-allow-origin' , b''             ),  # noqa
        (b'age'                         , b''             ),  # noqa
        (b'allow'                       , b''             ),  # noqa
        (b'authorization'               , b''             ),  # noqa
        (b'cache-control'               , b''             ),  # noqa
        (b'content-disposition'         , b''             ),  # noqa
        (b'content-encoding'            , b''             ),  # noqa
        (b'content-language'            , b''             ),  # noqa
        (b'content-length'              , b''             ),  # noqa
        (b'content-location'            , b''             ),  # noqa
        (b'content-range'               , b''             ),  # noqa
        (b'content-type'                , b''             ),  # noqa
        (b'cookie'                      , b''             ),  # noqa
        (b'date'                        , b''             ),  # noqa
        (b'etag'                        , b''             ),  # noqa
        (b'expect'                      , b''             ),  # noqa
        (b'expires'                     , b''             ),  # noqa
        (b'from'                        , b''             ),  # noqa
        (b'host'                        , b''             ),  # noqa
        (b'if-match'                    , b''             ),  # noqa
        (b'if-modified-since'           , b''             ),  # noqa
        (b'if-none-match'               , b''             ),  # noqa
        (b'if-range'                    , b''             ),  # noqa
        (b'if-unmodified-since'         , b''             ),  # noqa
        (b'last-modified'               , b''             ),  # noqa
        (b'link'                        , b''             ),  # noqa
        (b'location'                    , b''             ),  # noqa
        (b'max-forwards'                , b''             ),  # noqa
        (b'proxy-authenticate'          , b''             ),  # noqa
        (b'proxy-authorization'         , b''             ),  # noqa
        (b'range'                       , b''             ),  # noqa
        (b'referer'                     , b''             ),  # noqa
        (b'refresh'                     , b''             ),  # noqa
        (b'retry-after'                 , b''             ),  # noqa
        (b'server'                      , b''             ),  # noqa
        (b'set-cookie'                  , b''             ),  # noqa
        (b'strict-transport-security'   , b''             ),  # noqa
        (b'transfer-encoding'           , b''             ),  # noqa
        (b'user-agent'                  , b''             ),  # noqa
        (b'vary'                        , b''             ),  # noqa
        (b'via'                         , b''             ),  # noqa
        (b'www-authenticate'            , b''             ),  # noqa
    )  # noqa

    STATIC_TABLE_LENGTH = len(STATIC_TABLE)

    def __init__(self):
        self._maxsize = HeaderTable.DEFAULT_SIZE
        self._current_size = 0
        self.resized = False
        self.dynamic_entries = deque()

    def get_by_index(self, index):
        """
        Returns the entry specified by index

        Note that the table is 1-based ie an index of 0 is
        invalid.  This is due to the fact that a zero value
        index signals that a completely unindexed header
        follows.

        The entry will either be from the static table or
        the dynamic table depending on the value of index.
        """
        original_index = index
        index -= 1
        if 0 <= index:
            if index < HeaderTable.STATIC_TABLE_LENGTH:
                return HeaderTable.STATIC_TABLE[index]

            index -= HeaderTable.STATIC_TABLE_LENGTH
            if index < len(self.dynamic_entries):
                return self.dynamic_entries[index]

        raise InvalidTableIndex("Invalid table index %d" % original_index)

    def __repr__(self):
        return "HeaderTable(%d, %s, %r)" % (
            self._maxsize,
            self.resized,
            self.dynamic_entries
        )

    def add(self, name, value):
        """
        Adds a new entry to the table

        We reduce the table size if the entry will make the
        table size greater than maxsize.
        """
        # We just clear the table if the entry is too big
        size = table_entry_size(name, value)
        if size > self._maxsize:
            self.dynamic_entries.clear()
            self._current_size = 0
        else:
            # Add new entry
            self.dynamic_entries.appendleft((name, value))
            self._current_size += size
            self._shrink()

    def search(self, name, value):
        """
        Searches the table for the entry specified by name
        and value

        Returns one of the following:
            - ``None``, no match at all
            - ``(index, name, None)`` for partial matches on name only.
            - ``(index, name, value)`` for perfect matches.
        """
        partial = None

        header_name_search_result = HeaderTable.STATIC_TABLE_MAPPING.get(name)
        if header_name_search_result:
            index = header_name_search_result[1].get(value)
            if index is not None:
                return index, name, value
            else:
                partial = (header_name_search_result[0], name, None)

        offset = HeaderTable.STATIC_TABLE_LENGTH + 1
        for (i, (n, v)) in enumerate(self.dynamic_entries):
            if n == name:
                if v == value:
                    return i + offset, n, v
                elif partial is None:
                    partial = (i + offset, n, None)
        return partial

    @property
    def maxsize(self):
        return self._maxsize

    @maxsize.setter
    def maxsize(self, newmax):
        newmax = int(newmax)
        log.debug("Resizing header table to %d from %d", newmax, self._maxsize)
        oldmax = self._maxsize
        self._maxsize = newmax
        self.resized = (newmax != oldmax)
        if newmax <= 0:
            self.dynamic_entries.clear()
            self._current_size = 0
        elif oldmax > newmax:
            self._shrink()

    def _shrink(self):
        """
        Shrinks the dynamic table to be at or below maxsize
        """
        cursize = self._current_size
        while cursize > self._maxsize:
            name, value = self.dynamic_entries.pop()
            cursize -= table_entry_size(name, value)
            log.debug("Evicting %s: %s from the header table", name, value)
        self._current_size = cursize


def _build_static_table_mapping():
    """
    Build static table mapping from header name to tuple with next structure:
    (<minimal index of header>, <mapping from header value to it index>).

    static_table_mapping used for hash searching.
    """
    static_table_mapping = {}
    for index, (name, value) in enumerate(HeaderTable.STATIC_TABLE, 1):
        header_name_search_result = static_table_mapping.setdefault(name, (index, {}))
        header_name_search_result[1][value] = index
    return static_table_mapping


HeaderTable.STATIC_TABLE_MAPPING = _build_static_table_mapping()
 0707010001f459000081a40000000000000000000000016a100daf000003ce000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/foreign/hpack/exceptions.py    # -*- coding: utf-8 -*-
"""
hyper/http20/exceptions
~~~~~~~~~~~~~~~~~~~~~~~

This defines exceptions used in the HTTP/2 portion of hyper.
"""


class HPACKError(Exception):
    """
    The base class for all ``hpack`` exceptions.
    """
    pass


class HPACKDecodingError(HPACKError):
    """
    An error has been encountered while performing HPACK decoding.
    """
    pass


class InvalidTableIndex(HPACKDecodingError):
    """
    An invalid table index was received.
    """
    pass


class OversizedHeaderListError(HPACKDecodingError):
    """
    A header list that was larger than we allow has been received. This may be
    a DoS attack.

    .. versionadded:: 2.3.0
    """
    pass


class InvalidTableSizeError(HPACKDecodingError):
    """
    An attempt was made to change the decoder table size to a value larger than
    allowed, or the list was shrunk and the remote peer didn't shrink their
    table size.

    .. versionadded:: 3.0.0
    """
    pass
  0707010001f458000081a40000000000000000000000016a100daf000002a7000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/hpack/compat.py    # -*- coding: utf-8 -*-
"""
hpack/compat
~~~~~~~~~~~~

Normalizes the Python 2/3 API for internal use.
"""
import sys


_ver = sys.version_info
is_py2 = _ver[0] == 2
is_py3 = _ver[0] == 3

if is_py2:
    def to_byte(char):
        return ord(char)

    def decode_hex(b):
        return b.decode('hex')

    def to_bytes(b):
        if isinstance(b, memoryview):
            return b.tobytes()
        else:
            return bytes(b)

    unicode = unicode  # noqa
    bytes = str

elif is_py3:
    def to_byte(char):
        return char

    def decode_hex(b):
        return bytes.fromhex(b)

    def to_bytes(b):
        return bytes(b)

    unicode = str
    bytes = bytes
 0707010001f45d000081a40000000000000000000000016a100daf00029284000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/foreign/hpack/huffman_table.py # -*- coding: utf-8 -*-
"""
hpack/huffman_table
~~~~~~~~~~~~~~~~~~~

This implementation of a Huffman decoding table for HTTP/2 is essentially a
Python port of the work originally done for nghttp2's Huffman decoding. For
this reason, while this file is made available under the MIT license as is the
rest of this module, this file is undoubtedly a derivative work of the nghttp2
file ``nghttp2_hd_huffman_data.c``, obtained from
https://github.com/tatsuhiro-t/nghttp2/ at commit
d2b55ad1a245e1d1964579fa3fac36ebf3939e72. That work is made available under
the Apache 2.0 license under the following terms:

    Copyright (c) 2013 Tatsuhiro Tsujikawa

    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.

The essence of this approach is that it builds a finite state machine out of
4-bit nibbles of Huffman coded data. The input function passes 4 bits worth of
data to the state machine each time, which uses those 4 bits of data along with
the current accumulated state data to process the data given.

For the sake of efficiency, the in-memory representation of the states,
transitions, and result values of the state machine are represented as a long
list containing three-tuples. This list is enormously long, and viewing it as
an in-memory representation is not very clear, but it is laid out here in a way
that is intended to be *somewhat* more clear.

Essentially, the list is structured as 256 collections of 16 entries (one for
each nibble) of three-tuples. Each collection is called a "node", and the
zeroth collection is called the "root node". The state machine tracks one
value: the "state" byte.

For each nibble passed to the state machine, it first multiplies the "state"
byte by 16 and adds the numerical value of the nibble. This number is the index
into the large flat list.

The three-tuple that is found by looking up that index consists of three
values:

- a new state value, used for subsequent decoding
- a collection of flags, used to determine whether data is emitted or whether
  the state machine is complete.
- the byte value to emit, assuming that emitting a byte is required.

The flags are consulted, if necessary a byte is emitted, and then the next
nibble is used. This continues until the state machine believes it has
completely Huffman-decoded the data.

This approach has relatively little indirection, and therefore performs
relatively well, particularly on implementations like PyPy where the cost of
loops at the Python-level is not too expensive. The total number of loop
iterations is 4x the number of bytes passed to the decoder.
"""
from .exceptions import HPACKDecodingError


# This defines the state machine "class" at the top of the file. The reason we
# do this is to keep the terrifing monster state table at the *bottom* of the
# file so you don't have to actually *look* at the damn thing.
def decode_huffman(huffman_string):
    """
    Given a bytestring of Huffman-encoded data for HPACK, returns a bytestring
    of the decompressed data.
    """
    if not huffman_string:
        return b''

    state = 0
    flags = 0
    decoded_bytes = bytearray()

    # Perversely, bytearrays are a lot more convenient across Python 2 and
    # Python 3 because they behave *the same way* on both platforms. Given that
    # we really do want numerical bytes when we iterate here, let's use a
    # bytearray.
    huffman_string = bytearray(huffman_string)

    # This loop is unrolled somewhat. Because we use a nibble, not a byte, we
    # need to handle each nibble twice. We unroll that: it makes the loop body
    # a bit longer, but that's ok.
    for input_byte in huffman_string:
        index = (state * 16) + (input_byte >> 4)
        state, flags, output_byte = HUFFMAN_TABLE[index]

        if flags & HUFFMAN_FAIL:
            raise HPACKDecodingError("Invalid Huffman String")

        if flags & HUFFMAN_EMIT_SYMBOL:
            decoded_bytes.append(output_byte)

        index = (state * 16) + (input_byte & 0x0F)
        state, flags, output_byte = HUFFMAN_TABLE[index]

        if flags & HUFFMAN_FAIL:
            raise HPACKDecodingError("Invalid Huffman String")

        if flags & HUFFMAN_EMIT_SYMBOL:
            decoded_bytes.append(output_byte)

    if not (flags & HUFFMAN_COMPLETE):
        raise HPACKDecodingError("Incomplete Huffman string")

    return bytes(decoded_bytes)


# Some decoder flags to control state transitions.
HUFFMAN_COMPLETE = 1
HUFFMAN_EMIT_SYMBOL = (1 << 1)
HUFFMAN_FAIL = (1 << 2)

# This is the monster table. Avert your eyes, children.
HUFFMAN_TABLE = [
    # Node 0 (Root Node, never emits symbols.)
    (4, 0, 0),
    (5, 0, 0),
    (7, 0, 0),
    (8, 0, 0),
    (11, 0, 0),
    (12, 0, 0),
    (16, 0, 0),
    (19, 0, 0),
    (25, 0, 0),
    (28, 0, 0),
    (32, 0, 0),
    (35, 0, 0),
    (42, 0, 0),
    (49, 0, 0),
    (57, 0, 0),
    (64, HUFFMAN_COMPLETE, 0),

    # Node 1
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116),
    (13, 0, 0),
    (14, 0, 0),
    (17, 0, 0),
    (18, 0, 0),
    (20, 0, 0),
    (21, 0, 0),

    # Node 2
    (1, HUFFMAN_EMIT_SYMBOL, 48),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48),
    (1, HUFFMAN_EMIT_SYMBOL, 49),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49),
    (1, HUFFMAN_EMIT_SYMBOL, 50),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50),
    (1, HUFFMAN_EMIT_SYMBOL, 97),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97),
    (1, HUFFMAN_EMIT_SYMBOL, 99),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99),
    (1, HUFFMAN_EMIT_SYMBOL, 101),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101),
    (1, HUFFMAN_EMIT_SYMBOL, 105),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105),
    (1, HUFFMAN_EMIT_SYMBOL, 111),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111),

    # Node 3
    (2, HUFFMAN_EMIT_SYMBOL, 48),
    (9, HUFFMAN_EMIT_SYMBOL, 48),
    (23, HUFFMAN_EMIT_SYMBOL, 48),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48),
    (2, HUFFMAN_EMIT_SYMBOL, 49),
    (9, HUFFMAN_EMIT_SYMBOL, 49),
    (23, HUFFMAN_EMIT_SYMBOL, 49),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49),
    (2, HUFFMAN_EMIT_SYMBOL, 50),
    (9, HUFFMAN_EMIT_SYMBOL, 50),
    (23, HUFFMAN_EMIT_SYMBOL, 50),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50),
    (2, HUFFMAN_EMIT_SYMBOL, 97),
    (9, HUFFMAN_EMIT_SYMBOL, 97),
    (23, HUFFMAN_EMIT_SYMBOL, 97),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97),

    # Node 4
    (3, HUFFMAN_EMIT_SYMBOL, 48),
    (6, HUFFMAN_EMIT_SYMBOL, 48),
    (10, HUFFMAN_EMIT_SYMBOL, 48),
    (15, HUFFMAN_EMIT_SYMBOL, 48),
    (24, HUFFMAN_EMIT_SYMBOL, 48),
    (31, HUFFMAN_EMIT_SYMBOL, 48),
    (41, HUFFMAN_EMIT_SYMBOL, 48),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 48),
    (3, HUFFMAN_EMIT_SYMBOL, 49),
    (6, HUFFMAN_EMIT_SYMBOL, 49),
    (10, HUFFMAN_EMIT_SYMBOL, 49),
    (15, HUFFMAN_EMIT_SYMBOL, 49),
    (24, HUFFMAN_EMIT_SYMBOL, 49),
    (31, HUFFMAN_EMIT_SYMBOL, 49),
    (41, HUFFMAN_EMIT_SYMBOL, 49),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 49),

    # Node 5
    (3, HUFFMAN_EMIT_SYMBOL, 50),
    (6, HUFFMAN_EMIT_SYMBOL, 50),
    (10, HUFFMAN_EMIT_SYMBOL, 50),
    (15, HUFFMAN_EMIT_SYMBOL, 50),
    (24, HUFFMAN_EMIT_SYMBOL, 50),
    (31, HUFFMAN_EMIT_SYMBOL, 50),
    (41, HUFFMAN_EMIT_SYMBOL, 50),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 50),
    (3, HUFFMAN_EMIT_SYMBOL, 97),
    (6, HUFFMAN_EMIT_SYMBOL, 97),
    (10, HUFFMAN_EMIT_SYMBOL, 97),
    (15, HUFFMAN_EMIT_SYMBOL, 97),
    (24, HUFFMAN_EMIT_SYMBOL, 97),
    (31, HUFFMAN_EMIT_SYMBOL, 97),
    (41, HUFFMAN_EMIT_SYMBOL, 97),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 97),

    # Node 6
    (2, HUFFMAN_EMIT_SYMBOL, 99),
    (9, HUFFMAN_EMIT_SYMBOL, 99),
    (23, HUFFMAN_EMIT_SYMBOL, 99),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99),
    (2, HUFFMAN_EMIT_SYMBOL, 101),
    (9, HUFFMAN_EMIT_SYMBOL, 101),
    (23, HUFFMAN_EMIT_SYMBOL, 101),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101),
    (2, HUFFMAN_EMIT_SYMBOL, 105),
    (9, HUFFMAN_EMIT_SYMBOL, 105),
    (23, HUFFMAN_EMIT_SYMBOL, 105),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105),
    (2, HUFFMAN_EMIT_SYMBOL, 111),
    (9, HUFFMAN_EMIT_SYMBOL, 111),
    (23, HUFFMAN_EMIT_SYMBOL, 111),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111),

    # Node 7
    (3, HUFFMAN_EMIT_SYMBOL, 99),
    (6, HUFFMAN_EMIT_SYMBOL, 99),
    (10, HUFFMAN_EMIT_SYMBOL, 99),
    (15, HUFFMAN_EMIT_SYMBOL, 99),
    (24, HUFFMAN_EMIT_SYMBOL, 99),
    (31, HUFFMAN_EMIT_SYMBOL, 99),
    (41, HUFFMAN_EMIT_SYMBOL, 99),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 99),
    (3, HUFFMAN_EMIT_SYMBOL, 101),
    (6, HUFFMAN_EMIT_SYMBOL, 101),
    (10, HUFFMAN_EMIT_SYMBOL, 101),
    (15, HUFFMAN_EMIT_SYMBOL, 101),
    (24, HUFFMAN_EMIT_SYMBOL, 101),
    (31, HUFFMAN_EMIT_SYMBOL, 101),
    (41, HUFFMAN_EMIT_SYMBOL, 101),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 101),

    # Node 8
    (3, HUFFMAN_EMIT_SYMBOL, 105),
    (6, HUFFMAN_EMIT_SYMBOL, 105),
    (10, HUFFMAN_EMIT_SYMBOL, 105),
    (15, HUFFMAN_EMIT_SYMBOL, 105),
    (24, HUFFMAN_EMIT_SYMBOL, 105),
    (31, HUFFMAN_EMIT_SYMBOL, 105),
    (41, HUFFMAN_EMIT_SYMBOL, 105),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 105),
    (3, HUFFMAN_EMIT_SYMBOL, 111),
    (6, HUFFMAN_EMIT_SYMBOL, 111),
    (10, HUFFMAN_EMIT_SYMBOL, 111),
    (15, HUFFMAN_EMIT_SYMBOL, 111),
    (24, HUFFMAN_EMIT_SYMBOL, 111),
    (31, HUFFMAN_EMIT_SYMBOL, 111),
    (41, HUFFMAN_EMIT_SYMBOL, 111),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 111),

    # Node 9
    (1, HUFFMAN_EMIT_SYMBOL, 115),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115),
    (1, HUFFMAN_EMIT_SYMBOL, 116),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57),

    # Node 10
    (2, HUFFMAN_EMIT_SYMBOL, 115),
    (9, HUFFMAN_EMIT_SYMBOL, 115),
    (23, HUFFMAN_EMIT_SYMBOL, 115),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115),
    (2, HUFFMAN_EMIT_SYMBOL, 116),
    (9, HUFFMAN_EMIT_SYMBOL, 116),
    (23, HUFFMAN_EMIT_SYMBOL, 116),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116),
    (1, HUFFMAN_EMIT_SYMBOL, 32),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32),
    (1, HUFFMAN_EMIT_SYMBOL, 37),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37),
    (1, HUFFMAN_EMIT_SYMBOL, 45),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45),
    (1, HUFFMAN_EMIT_SYMBOL, 46),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46),

    # Node 11
    (3, HUFFMAN_EMIT_SYMBOL, 115),
    (6, HUFFMAN_EMIT_SYMBOL, 115),
    (10, HUFFMAN_EMIT_SYMBOL, 115),
    (15, HUFFMAN_EMIT_SYMBOL, 115),
    (24, HUFFMAN_EMIT_SYMBOL, 115),
    (31, HUFFMAN_EMIT_SYMBOL, 115),
    (41, HUFFMAN_EMIT_SYMBOL, 115),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 115),
    (3, HUFFMAN_EMIT_SYMBOL, 116),
    (6, HUFFMAN_EMIT_SYMBOL, 116),
    (10, HUFFMAN_EMIT_SYMBOL, 116),
    (15, HUFFMAN_EMIT_SYMBOL, 116),
    (24, HUFFMAN_EMIT_SYMBOL, 116),
    (31, HUFFMAN_EMIT_SYMBOL, 116),
    (41, HUFFMAN_EMIT_SYMBOL, 116),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 116),

    # Node 12
    (2, HUFFMAN_EMIT_SYMBOL, 32),
    (9, HUFFMAN_EMIT_SYMBOL, 32),
    (23, HUFFMAN_EMIT_SYMBOL, 32),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32),
    (2, HUFFMAN_EMIT_SYMBOL, 37),
    (9, HUFFMAN_EMIT_SYMBOL, 37),
    (23, HUFFMAN_EMIT_SYMBOL, 37),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37),
    (2, HUFFMAN_EMIT_SYMBOL, 45),
    (9, HUFFMAN_EMIT_SYMBOL, 45),
    (23, HUFFMAN_EMIT_SYMBOL, 45),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45),
    (2, HUFFMAN_EMIT_SYMBOL, 46),
    (9, HUFFMAN_EMIT_SYMBOL, 46),
    (23, HUFFMAN_EMIT_SYMBOL, 46),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46),

    # Node 13
    (3, HUFFMAN_EMIT_SYMBOL, 32),
    (6, HUFFMAN_EMIT_SYMBOL, 32),
    (10, HUFFMAN_EMIT_SYMBOL, 32),
    (15, HUFFMAN_EMIT_SYMBOL, 32),
    (24, HUFFMAN_EMIT_SYMBOL, 32),
    (31, HUFFMAN_EMIT_SYMBOL, 32),
    (41, HUFFMAN_EMIT_SYMBOL, 32),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 32),
    (3, HUFFMAN_EMIT_SYMBOL, 37),
    (6, HUFFMAN_EMIT_SYMBOL, 37),
    (10, HUFFMAN_EMIT_SYMBOL, 37),
    (15, HUFFMAN_EMIT_SYMBOL, 37),
    (24, HUFFMAN_EMIT_SYMBOL, 37),
    (31, HUFFMAN_EMIT_SYMBOL, 37),
    (41, HUFFMAN_EMIT_SYMBOL, 37),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 37),

    # Node 14
    (3, HUFFMAN_EMIT_SYMBOL, 45),
    (6, HUFFMAN_EMIT_SYMBOL, 45),
    (10, HUFFMAN_EMIT_SYMBOL, 45),
    (15, HUFFMAN_EMIT_SYMBOL, 45),
    (24, HUFFMAN_EMIT_SYMBOL, 45),
    (31, HUFFMAN_EMIT_SYMBOL, 45),
    (41, HUFFMAN_EMIT_SYMBOL, 45),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 45),
    (3, HUFFMAN_EMIT_SYMBOL, 46),
    (6, HUFFMAN_EMIT_SYMBOL, 46),
    (10, HUFFMAN_EMIT_SYMBOL, 46),
    (15, HUFFMAN_EMIT_SYMBOL, 46),
    (24, HUFFMAN_EMIT_SYMBOL, 46),
    (31, HUFFMAN_EMIT_SYMBOL, 46),
    (41, HUFFMAN_EMIT_SYMBOL, 46),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 46),

    # Node 15
    (1, HUFFMAN_EMIT_SYMBOL, 47),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47),
    (1, HUFFMAN_EMIT_SYMBOL, 51),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51),
    (1, HUFFMAN_EMIT_SYMBOL, 52),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52),
    (1, HUFFMAN_EMIT_SYMBOL, 53),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53),
    (1, HUFFMAN_EMIT_SYMBOL, 54),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54),
    (1, HUFFMAN_EMIT_SYMBOL, 55),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55),
    (1, HUFFMAN_EMIT_SYMBOL, 56),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56),
    (1, HUFFMAN_EMIT_SYMBOL, 57),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57),

    # Node 16
    (2, HUFFMAN_EMIT_SYMBOL, 47),
    (9, HUFFMAN_EMIT_SYMBOL, 47),
    (23, HUFFMAN_EMIT_SYMBOL, 47),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47),
    (2, HUFFMAN_EMIT_SYMBOL, 51),
    (9, HUFFMAN_EMIT_SYMBOL, 51),
    (23, HUFFMAN_EMIT_SYMBOL, 51),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51),
    (2, HUFFMAN_EMIT_SYMBOL, 52),
    (9, HUFFMAN_EMIT_SYMBOL, 52),
    (23, HUFFMAN_EMIT_SYMBOL, 52),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52),
    (2, HUFFMAN_EMIT_SYMBOL, 53),
    (9, HUFFMAN_EMIT_SYMBOL, 53),
    (23, HUFFMAN_EMIT_SYMBOL, 53),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53),

    # Node 17
    (3, HUFFMAN_EMIT_SYMBOL, 47),
    (6, HUFFMAN_EMIT_SYMBOL, 47),
    (10, HUFFMAN_EMIT_SYMBOL, 47),
    (15, HUFFMAN_EMIT_SYMBOL, 47),
    (24, HUFFMAN_EMIT_SYMBOL, 47),
    (31, HUFFMAN_EMIT_SYMBOL, 47),
    (41, HUFFMAN_EMIT_SYMBOL, 47),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 47),
    (3, HUFFMAN_EMIT_SYMBOL, 51),
    (6, HUFFMAN_EMIT_SYMBOL, 51),
    (10, HUFFMAN_EMIT_SYMBOL, 51),
    (15, HUFFMAN_EMIT_SYMBOL, 51),
    (24, HUFFMAN_EMIT_SYMBOL, 51),
    (31, HUFFMAN_EMIT_SYMBOL, 51),
    (41, HUFFMAN_EMIT_SYMBOL, 51),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 51),

    # Node 18
    (3, HUFFMAN_EMIT_SYMBOL, 52),
    (6, HUFFMAN_EMIT_SYMBOL, 52),
    (10, HUFFMAN_EMIT_SYMBOL, 52),
    (15, HUFFMAN_EMIT_SYMBOL, 52),
    (24, HUFFMAN_EMIT_SYMBOL, 52),
    (31, HUFFMAN_EMIT_SYMBOL, 52),
    (41, HUFFMAN_EMIT_SYMBOL, 52),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 52),
    (3, HUFFMAN_EMIT_SYMBOL, 53),
    (6, HUFFMAN_EMIT_SYMBOL, 53),
    (10, HUFFMAN_EMIT_SYMBOL, 53),
    (15, HUFFMAN_EMIT_SYMBOL, 53),
    (24, HUFFMAN_EMIT_SYMBOL, 53),
    (31, HUFFMAN_EMIT_SYMBOL, 53),
    (41, HUFFMAN_EMIT_SYMBOL, 53),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 53),

    # Node 19
    (2, HUFFMAN_EMIT_SYMBOL, 54),
    (9, HUFFMAN_EMIT_SYMBOL, 54),
    (23, HUFFMAN_EMIT_SYMBOL, 54),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54),
    (2, HUFFMAN_EMIT_SYMBOL, 55),
    (9, HUFFMAN_EMIT_SYMBOL, 55),
    (23, HUFFMAN_EMIT_SYMBOL, 55),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55),
    (2, HUFFMAN_EMIT_SYMBOL, 56),
    (9, HUFFMAN_EMIT_SYMBOL, 56),
    (23, HUFFMAN_EMIT_SYMBOL, 56),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56),
    (2, HUFFMAN_EMIT_SYMBOL, 57),
    (9, HUFFMAN_EMIT_SYMBOL, 57),
    (23, HUFFMAN_EMIT_SYMBOL, 57),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57),

    # Node 20
    (3, HUFFMAN_EMIT_SYMBOL, 54),
    (6, HUFFMAN_EMIT_SYMBOL, 54),
    (10, HUFFMAN_EMIT_SYMBOL, 54),
    (15, HUFFMAN_EMIT_SYMBOL, 54),
    (24, HUFFMAN_EMIT_SYMBOL, 54),
    (31, HUFFMAN_EMIT_SYMBOL, 54),
    (41, HUFFMAN_EMIT_SYMBOL, 54),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 54),
    (3, HUFFMAN_EMIT_SYMBOL, 55),
    (6, HUFFMAN_EMIT_SYMBOL, 55),
    (10, HUFFMAN_EMIT_SYMBOL, 55),
    (15, HUFFMAN_EMIT_SYMBOL, 55),
    (24, HUFFMAN_EMIT_SYMBOL, 55),
    (31, HUFFMAN_EMIT_SYMBOL, 55),
    (41, HUFFMAN_EMIT_SYMBOL, 55),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 55),

    # Node 21
    (3, HUFFMAN_EMIT_SYMBOL, 56),
    (6, HUFFMAN_EMIT_SYMBOL, 56),
    (10, HUFFMAN_EMIT_SYMBOL, 56),
    (15, HUFFMAN_EMIT_SYMBOL, 56),
    (24, HUFFMAN_EMIT_SYMBOL, 56),
    (31, HUFFMAN_EMIT_SYMBOL, 56),
    (41, HUFFMAN_EMIT_SYMBOL, 56),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 56),
    (3, HUFFMAN_EMIT_SYMBOL, 57),
    (6, HUFFMAN_EMIT_SYMBOL, 57),
    (10, HUFFMAN_EMIT_SYMBOL, 57),
    (15, HUFFMAN_EMIT_SYMBOL, 57),
    (24, HUFFMAN_EMIT_SYMBOL, 57),
    (31, HUFFMAN_EMIT_SYMBOL, 57),
    (41, HUFFMAN_EMIT_SYMBOL, 57),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 57),

    # Node 22
    (26, 0, 0),
    (27, 0, 0),
    (29, 0, 0),
    (30, 0, 0),
    (33, 0, 0),
    (34, 0, 0),
    (36, 0, 0),
    (37, 0, 0),
    (43, 0, 0),
    (46, 0, 0),
    (50, 0, 0),
    (53, 0, 0),
    (58, 0, 0),
    (61, 0, 0),
    (65, 0, 0),
    (68, HUFFMAN_COMPLETE, 0),

    # Node 23
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117),
    (38, 0, 0),
    (39, 0, 0),

    # Node 24
    (1, HUFFMAN_EMIT_SYMBOL, 61),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61),
    (1, HUFFMAN_EMIT_SYMBOL, 65),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65),
    (1, HUFFMAN_EMIT_SYMBOL, 95),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95),
    (1, HUFFMAN_EMIT_SYMBOL, 98),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98),
    (1, HUFFMAN_EMIT_SYMBOL, 100),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100),
    (1, HUFFMAN_EMIT_SYMBOL, 102),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102),
    (1, HUFFMAN_EMIT_SYMBOL, 103),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103),
    (1, HUFFMAN_EMIT_SYMBOL, 104),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104),

    # Node 25
    (2, HUFFMAN_EMIT_SYMBOL, 61),
    (9, HUFFMAN_EMIT_SYMBOL, 61),
    (23, HUFFMAN_EMIT_SYMBOL, 61),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61),
    (2, HUFFMAN_EMIT_SYMBOL, 65),
    (9, HUFFMAN_EMIT_SYMBOL, 65),
    (23, HUFFMAN_EMIT_SYMBOL, 65),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65),
    (2, HUFFMAN_EMIT_SYMBOL, 95),
    (9, HUFFMAN_EMIT_SYMBOL, 95),
    (23, HUFFMAN_EMIT_SYMBOL, 95),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95),
    (2, HUFFMAN_EMIT_SYMBOL, 98),
    (9, HUFFMAN_EMIT_SYMBOL, 98),
    (23, HUFFMAN_EMIT_SYMBOL, 98),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98),

    # Node 26
    (3, HUFFMAN_EMIT_SYMBOL, 61),
    (6, HUFFMAN_EMIT_SYMBOL, 61),
    (10, HUFFMAN_EMIT_SYMBOL, 61),
    (15, HUFFMAN_EMIT_SYMBOL, 61),
    (24, HUFFMAN_EMIT_SYMBOL, 61),
    (31, HUFFMAN_EMIT_SYMBOL, 61),
    (41, HUFFMAN_EMIT_SYMBOL, 61),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 61),
    (3, HUFFMAN_EMIT_SYMBOL, 65),
    (6, HUFFMAN_EMIT_SYMBOL, 65),
    (10, HUFFMAN_EMIT_SYMBOL, 65),
    (15, HUFFMAN_EMIT_SYMBOL, 65),
    (24, HUFFMAN_EMIT_SYMBOL, 65),
    (31, HUFFMAN_EMIT_SYMBOL, 65),
    (41, HUFFMAN_EMIT_SYMBOL, 65),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 65),

    # Node 27
    (3, HUFFMAN_EMIT_SYMBOL, 95),
    (6, HUFFMAN_EMIT_SYMBOL, 95),
    (10, HUFFMAN_EMIT_SYMBOL, 95),
    (15, HUFFMAN_EMIT_SYMBOL, 95),
    (24, HUFFMAN_EMIT_SYMBOL, 95),
    (31, HUFFMAN_EMIT_SYMBOL, 95),
    (41, HUFFMAN_EMIT_SYMBOL, 95),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 95),
    (3, HUFFMAN_EMIT_SYMBOL, 98),
    (6, HUFFMAN_EMIT_SYMBOL, 98),
    (10, HUFFMAN_EMIT_SYMBOL, 98),
    (15, HUFFMAN_EMIT_SYMBOL, 98),
    (24, HUFFMAN_EMIT_SYMBOL, 98),
    (31, HUFFMAN_EMIT_SYMBOL, 98),
    (41, HUFFMAN_EMIT_SYMBOL, 98),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 98),

    # Node 28
    (2, HUFFMAN_EMIT_SYMBOL, 100),
    (9, HUFFMAN_EMIT_SYMBOL, 100),
    (23, HUFFMAN_EMIT_SYMBOL, 100),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100),
    (2, HUFFMAN_EMIT_SYMBOL, 102),
    (9, HUFFMAN_EMIT_SYMBOL, 102),
    (23, HUFFMAN_EMIT_SYMBOL, 102),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102),
    (2, HUFFMAN_EMIT_SYMBOL, 103),
    (9, HUFFMAN_EMIT_SYMBOL, 103),
    (23, HUFFMAN_EMIT_SYMBOL, 103),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103),
    (2, HUFFMAN_EMIT_SYMBOL, 104),
    (9, HUFFMAN_EMIT_SYMBOL, 104),
    (23, HUFFMAN_EMIT_SYMBOL, 104),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104),

    # Node 29
    (3, HUFFMAN_EMIT_SYMBOL, 100),
    (6, HUFFMAN_EMIT_SYMBOL, 100),
    (10, HUFFMAN_EMIT_SYMBOL, 100),
    (15, HUFFMAN_EMIT_SYMBOL, 100),
    (24, HUFFMAN_EMIT_SYMBOL, 100),
    (31, HUFFMAN_EMIT_SYMBOL, 100),
    (41, HUFFMAN_EMIT_SYMBOL, 100),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 100),
    (3, HUFFMAN_EMIT_SYMBOL, 102),
    (6, HUFFMAN_EMIT_SYMBOL, 102),
    (10, HUFFMAN_EMIT_SYMBOL, 102),
    (15, HUFFMAN_EMIT_SYMBOL, 102),
    (24, HUFFMAN_EMIT_SYMBOL, 102),
    (31, HUFFMAN_EMIT_SYMBOL, 102),
    (41, HUFFMAN_EMIT_SYMBOL, 102),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 102),

    # Node 30
    (3, HUFFMAN_EMIT_SYMBOL, 103),
    (6, HUFFMAN_EMIT_SYMBOL, 103),
    (10, HUFFMAN_EMIT_SYMBOL, 103),
    (15, HUFFMAN_EMIT_SYMBOL, 103),
    (24, HUFFMAN_EMIT_SYMBOL, 103),
    (31, HUFFMAN_EMIT_SYMBOL, 103),
    (41, HUFFMAN_EMIT_SYMBOL, 103),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 103),
    (3, HUFFMAN_EMIT_SYMBOL, 104),
    (6, HUFFMAN_EMIT_SYMBOL, 104),
    (10, HUFFMAN_EMIT_SYMBOL, 104),
    (15, HUFFMAN_EMIT_SYMBOL, 104),
    (24, HUFFMAN_EMIT_SYMBOL, 104),
    (31, HUFFMAN_EMIT_SYMBOL, 104),
    (41, HUFFMAN_EMIT_SYMBOL, 104),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 104),

    # Node 31
    (1, HUFFMAN_EMIT_SYMBOL, 108),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108),
    (1, HUFFMAN_EMIT_SYMBOL, 109),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109),
    (1, HUFFMAN_EMIT_SYMBOL, 110),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110),
    (1, HUFFMAN_EMIT_SYMBOL, 112),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112),
    (1, HUFFMAN_EMIT_SYMBOL, 114),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114),
    (1, HUFFMAN_EMIT_SYMBOL, 117),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68),

    # Node 32
    (2, HUFFMAN_EMIT_SYMBOL, 108),
    (9, HUFFMAN_EMIT_SYMBOL, 108),
    (23, HUFFMAN_EMIT_SYMBOL, 108),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108),
    (2, HUFFMAN_EMIT_SYMBOL, 109),
    (9, HUFFMAN_EMIT_SYMBOL, 109),
    (23, HUFFMAN_EMIT_SYMBOL, 109),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109),
    (2, HUFFMAN_EMIT_SYMBOL, 110),
    (9, HUFFMAN_EMIT_SYMBOL, 110),
    (23, HUFFMAN_EMIT_SYMBOL, 110),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110),
    (2, HUFFMAN_EMIT_SYMBOL, 112),
    (9, HUFFMAN_EMIT_SYMBOL, 112),
    (23, HUFFMAN_EMIT_SYMBOL, 112),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112),

    # Node 33
    (3, HUFFMAN_EMIT_SYMBOL, 108),
    (6, HUFFMAN_EMIT_SYMBOL, 108),
    (10, HUFFMAN_EMIT_SYMBOL, 108),
    (15, HUFFMAN_EMIT_SYMBOL, 108),
    (24, HUFFMAN_EMIT_SYMBOL, 108),
    (31, HUFFMAN_EMIT_SYMBOL, 108),
    (41, HUFFMAN_EMIT_SYMBOL, 108),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 108),
    (3, HUFFMAN_EMIT_SYMBOL, 109),
    (6, HUFFMAN_EMIT_SYMBOL, 109),
    (10, HUFFMAN_EMIT_SYMBOL, 109),
    (15, HUFFMAN_EMIT_SYMBOL, 109),
    (24, HUFFMAN_EMIT_SYMBOL, 109),
    (31, HUFFMAN_EMIT_SYMBOL, 109),
    (41, HUFFMAN_EMIT_SYMBOL, 109),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 109),

    # Node 34
    (3, HUFFMAN_EMIT_SYMBOL, 110),
    (6, HUFFMAN_EMIT_SYMBOL, 110),
    (10, HUFFMAN_EMIT_SYMBOL, 110),
    (15, HUFFMAN_EMIT_SYMBOL, 110),
    (24, HUFFMAN_EMIT_SYMBOL, 110),
    (31, HUFFMAN_EMIT_SYMBOL, 110),
    (41, HUFFMAN_EMIT_SYMBOL, 110),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 110),
    (3, HUFFMAN_EMIT_SYMBOL, 112),
    (6, HUFFMAN_EMIT_SYMBOL, 112),
    (10, HUFFMAN_EMIT_SYMBOL, 112),
    (15, HUFFMAN_EMIT_SYMBOL, 112),
    (24, HUFFMAN_EMIT_SYMBOL, 112),
    (31, HUFFMAN_EMIT_SYMBOL, 112),
    (41, HUFFMAN_EMIT_SYMBOL, 112),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 112),

    # Node 35
    (2, HUFFMAN_EMIT_SYMBOL, 114),
    (9, HUFFMAN_EMIT_SYMBOL, 114),
    (23, HUFFMAN_EMIT_SYMBOL, 114),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114),
    (2, HUFFMAN_EMIT_SYMBOL, 117),
    (9, HUFFMAN_EMIT_SYMBOL, 117),
    (23, HUFFMAN_EMIT_SYMBOL, 117),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117),
    (1, HUFFMAN_EMIT_SYMBOL, 58),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58),
    (1, HUFFMAN_EMIT_SYMBOL, 66),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66),
    (1, HUFFMAN_EMIT_SYMBOL, 67),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67),
    (1, HUFFMAN_EMIT_SYMBOL, 68),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68),

    # Node 36
    (3, HUFFMAN_EMIT_SYMBOL, 114),
    (6, HUFFMAN_EMIT_SYMBOL, 114),
    (10, HUFFMAN_EMIT_SYMBOL, 114),
    (15, HUFFMAN_EMIT_SYMBOL, 114),
    (24, HUFFMAN_EMIT_SYMBOL, 114),
    (31, HUFFMAN_EMIT_SYMBOL, 114),
    (41, HUFFMAN_EMIT_SYMBOL, 114),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 114),
    (3, HUFFMAN_EMIT_SYMBOL, 117),
    (6, HUFFMAN_EMIT_SYMBOL, 117),
    (10, HUFFMAN_EMIT_SYMBOL, 117),
    (15, HUFFMAN_EMIT_SYMBOL, 117),
    (24, HUFFMAN_EMIT_SYMBOL, 117),
    (31, HUFFMAN_EMIT_SYMBOL, 117),
    (41, HUFFMAN_EMIT_SYMBOL, 117),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 117),

    # Node 37
    (2, HUFFMAN_EMIT_SYMBOL, 58),
    (9, HUFFMAN_EMIT_SYMBOL, 58),
    (23, HUFFMAN_EMIT_SYMBOL, 58),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58),
    (2, HUFFMAN_EMIT_SYMBOL, 66),
    (9, HUFFMAN_EMIT_SYMBOL, 66),
    (23, HUFFMAN_EMIT_SYMBOL, 66),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66),
    (2, HUFFMAN_EMIT_SYMBOL, 67),
    (9, HUFFMAN_EMIT_SYMBOL, 67),
    (23, HUFFMAN_EMIT_SYMBOL, 67),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67),
    (2, HUFFMAN_EMIT_SYMBOL, 68),
    (9, HUFFMAN_EMIT_SYMBOL, 68),
    (23, HUFFMAN_EMIT_SYMBOL, 68),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68),

    # Node 38
    (3, HUFFMAN_EMIT_SYMBOL, 58),
    (6, HUFFMAN_EMIT_SYMBOL, 58),
    (10, HUFFMAN_EMIT_SYMBOL, 58),
    (15, HUFFMAN_EMIT_SYMBOL, 58),
    (24, HUFFMAN_EMIT_SYMBOL, 58),
    (31, HUFFMAN_EMIT_SYMBOL, 58),
    (41, HUFFMAN_EMIT_SYMBOL, 58),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 58),
    (3, HUFFMAN_EMIT_SYMBOL, 66),
    (6, HUFFMAN_EMIT_SYMBOL, 66),
    (10, HUFFMAN_EMIT_SYMBOL, 66),
    (15, HUFFMAN_EMIT_SYMBOL, 66),
    (24, HUFFMAN_EMIT_SYMBOL, 66),
    (31, HUFFMAN_EMIT_SYMBOL, 66),
    (41, HUFFMAN_EMIT_SYMBOL, 66),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 66),

    # Node 39
    (3, HUFFMAN_EMIT_SYMBOL, 67),
    (6, HUFFMAN_EMIT_SYMBOL, 67),
    (10, HUFFMAN_EMIT_SYMBOL, 67),
    (15, HUFFMAN_EMIT_SYMBOL, 67),
    (24, HUFFMAN_EMIT_SYMBOL, 67),
    (31, HUFFMAN_EMIT_SYMBOL, 67),
    (41, HUFFMAN_EMIT_SYMBOL, 67),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 67),
    (3, HUFFMAN_EMIT_SYMBOL, 68),
    (6, HUFFMAN_EMIT_SYMBOL, 68),
    (10, HUFFMAN_EMIT_SYMBOL, 68),
    (15, HUFFMAN_EMIT_SYMBOL, 68),
    (24, HUFFMAN_EMIT_SYMBOL, 68),
    (31, HUFFMAN_EMIT_SYMBOL, 68),
    (41, HUFFMAN_EMIT_SYMBOL, 68),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 68),

    # Node 40
    (44, 0, 0),
    (45, 0, 0),
    (47, 0, 0),
    (48, 0, 0),
    (51, 0, 0),
    (52, 0, 0),
    (54, 0, 0),
    (55, 0, 0),
    (59, 0, 0),
    (60, 0, 0),
    (62, 0, 0),
    (63, 0, 0),
    (66, 0, 0),
    (67, 0, 0),
    (69, 0, 0),
    (72, HUFFMAN_COMPLETE, 0),

    # Node 41
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84),

    # Node 42
    (1, HUFFMAN_EMIT_SYMBOL, 69),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69),
    (1, HUFFMAN_EMIT_SYMBOL, 70),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70),
    (1, HUFFMAN_EMIT_SYMBOL, 71),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71),
    (1, HUFFMAN_EMIT_SYMBOL, 72),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72),
    (1, HUFFMAN_EMIT_SYMBOL, 73),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73),
    (1, HUFFMAN_EMIT_SYMBOL, 74),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74),
    (1, HUFFMAN_EMIT_SYMBOL, 75),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75),
    (1, HUFFMAN_EMIT_SYMBOL, 76),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76),

    # Node 43
    (2, HUFFMAN_EMIT_SYMBOL, 69),
    (9, HUFFMAN_EMIT_SYMBOL, 69),
    (23, HUFFMAN_EMIT_SYMBOL, 69),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69),
    (2, HUFFMAN_EMIT_SYMBOL, 70),
    (9, HUFFMAN_EMIT_SYMBOL, 70),
    (23, HUFFMAN_EMIT_SYMBOL, 70),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70),
    (2, HUFFMAN_EMIT_SYMBOL, 71),
    (9, HUFFMAN_EMIT_SYMBOL, 71),
    (23, HUFFMAN_EMIT_SYMBOL, 71),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71),
    (2, HUFFMAN_EMIT_SYMBOL, 72),
    (9, HUFFMAN_EMIT_SYMBOL, 72),
    (23, HUFFMAN_EMIT_SYMBOL, 72),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72),

    # Node 44
    (3, HUFFMAN_EMIT_SYMBOL, 69),
    (6, HUFFMAN_EMIT_SYMBOL, 69),
    (10, HUFFMAN_EMIT_SYMBOL, 69),
    (15, HUFFMAN_EMIT_SYMBOL, 69),
    (24, HUFFMAN_EMIT_SYMBOL, 69),
    (31, HUFFMAN_EMIT_SYMBOL, 69),
    (41, HUFFMAN_EMIT_SYMBOL, 69),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 69),
    (3, HUFFMAN_EMIT_SYMBOL, 70),
    (6, HUFFMAN_EMIT_SYMBOL, 70),
    (10, HUFFMAN_EMIT_SYMBOL, 70),
    (15, HUFFMAN_EMIT_SYMBOL, 70),
    (24, HUFFMAN_EMIT_SYMBOL, 70),
    (31, HUFFMAN_EMIT_SYMBOL, 70),
    (41, HUFFMAN_EMIT_SYMBOL, 70),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 70),

    # Node 45
    (3, HUFFMAN_EMIT_SYMBOL, 71),
    (6, HUFFMAN_EMIT_SYMBOL, 71),
    (10, HUFFMAN_EMIT_SYMBOL, 71),
    (15, HUFFMAN_EMIT_SYMBOL, 71),
    (24, HUFFMAN_EMIT_SYMBOL, 71),
    (31, HUFFMAN_EMIT_SYMBOL, 71),
    (41, HUFFMAN_EMIT_SYMBOL, 71),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 71),
    (3, HUFFMAN_EMIT_SYMBOL, 72),
    (6, HUFFMAN_EMIT_SYMBOL, 72),
    (10, HUFFMAN_EMIT_SYMBOL, 72),
    (15, HUFFMAN_EMIT_SYMBOL, 72),
    (24, HUFFMAN_EMIT_SYMBOL, 72),
    (31, HUFFMAN_EMIT_SYMBOL, 72),
    (41, HUFFMAN_EMIT_SYMBOL, 72),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 72),

    # Node 46
    (2, HUFFMAN_EMIT_SYMBOL, 73),
    (9, HUFFMAN_EMIT_SYMBOL, 73),
    (23, HUFFMAN_EMIT_SYMBOL, 73),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73),
    (2, HUFFMAN_EMIT_SYMBOL, 74),
    (9, HUFFMAN_EMIT_SYMBOL, 74),
    (23, HUFFMAN_EMIT_SYMBOL, 74),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74),
    (2, HUFFMAN_EMIT_SYMBOL, 75),
    (9, HUFFMAN_EMIT_SYMBOL, 75),
    (23, HUFFMAN_EMIT_SYMBOL, 75),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75),
    (2, HUFFMAN_EMIT_SYMBOL, 76),
    (9, HUFFMAN_EMIT_SYMBOL, 76),
    (23, HUFFMAN_EMIT_SYMBOL, 76),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76),

    # Node 47
    (3, HUFFMAN_EMIT_SYMBOL, 73),
    (6, HUFFMAN_EMIT_SYMBOL, 73),
    (10, HUFFMAN_EMIT_SYMBOL, 73),
    (15, HUFFMAN_EMIT_SYMBOL, 73),
    (24, HUFFMAN_EMIT_SYMBOL, 73),
    (31, HUFFMAN_EMIT_SYMBOL, 73),
    (41, HUFFMAN_EMIT_SYMBOL, 73),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 73),
    (3, HUFFMAN_EMIT_SYMBOL, 74),
    (6, HUFFMAN_EMIT_SYMBOL, 74),
    (10, HUFFMAN_EMIT_SYMBOL, 74),
    (15, HUFFMAN_EMIT_SYMBOL, 74),
    (24, HUFFMAN_EMIT_SYMBOL, 74),
    (31, HUFFMAN_EMIT_SYMBOL, 74),
    (41, HUFFMAN_EMIT_SYMBOL, 74),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 74),

    # Node 48
    (3, HUFFMAN_EMIT_SYMBOL, 75),
    (6, HUFFMAN_EMIT_SYMBOL, 75),
    (10, HUFFMAN_EMIT_SYMBOL, 75),
    (15, HUFFMAN_EMIT_SYMBOL, 75),
    (24, HUFFMAN_EMIT_SYMBOL, 75),
    (31, HUFFMAN_EMIT_SYMBOL, 75),
    (41, HUFFMAN_EMIT_SYMBOL, 75),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 75),
    (3, HUFFMAN_EMIT_SYMBOL, 76),
    (6, HUFFMAN_EMIT_SYMBOL, 76),
    (10, HUFFMAN_EMIT_SYMBOL, 76),
    (15, HUFFMAN_EMIT_SYMBOL, 76),
    (24, HUFFMAN_EMIT_SYMBOL, 76),
    (31, HUFFMAN_EMIT_SYMBOL, 76),
    (41, HUFFMAN_EMIT_SYMBOL, 76),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 76),

    # Node 49
    (1, HUFFMAN_EMIT_SYMBOL, 77),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77),
    (1, HUFFMAN_EMIT_SYMBOL, 78),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78),
    (1, HUFFMAN_EMIT_SYMBOL, 79),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79),
    (1, HUFFMAN_EMIT_SYMBOL, 80),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80),
    (1, HUFFMAN_EMIT_SYMBOL, 81),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81),
    (1, HUFFMAN_EMIT_SYMBOL, 82),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82),
    (1, HUFFMAN_EMIT_SYMBOL, 83),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83),
    (1, HUFFMAN_EMIT_SYMBOL, 84),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84),

    # Node 50
    (2, HUFFMAN_EMIT_SYMBOL, 77),
    (9, HUFFMAN_EMIT_SYMBOL, 77),
    (23, HUFFMAN_EMIT_SYMBOL, 77),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77),
    (2, HUFFMAN_EMIT_SYMBOL, 78),
    (9, HUFFMAN_EMIT_SYMBOL, 78),
    (23, HUFFMAN_EMIT_SYMBOL, 78),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78),
    (2, HUFFMAN_EMIT_SYMBOL, 79),
    (9, HUFFMAN_EMIT_SYMBOL, 79),
    (23, HUFFMAN_EMIT_SYMBOL, 79),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79),
    (2, HUFFMAN_EMIT_SYMBOL, 80),
    (9, HUFFMAN_EMIT_SYMBOL, 80),
    (23, HUFFMAN_EMIT_SYMBOL, 80),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80),

    # Node 51
    (3, HUFFMAN_EMIT_SYMBOL, 77),
    (6, HUFFMAN_EMIT_SYMBOL, 77),
    (10, HUFFMAN_EMIT_SYMBOL, 77),
    (15, HUFFMAN_EMIT_SYMBOL, 77),
    (24, HUFFMAN_EMIT_SYMBOL, 77),
    (31, HUFFMAN_EMIT_SYMBOL, 77),
    (41, HUFFMAN_EMIT_SYMBOL, 77),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 77),
    (3, HUFFMAN_EMIT_SYMBOL, 78),
    (6, HUFFMAN_EMIT_SYMBOL, 78),
    (10, HUFFMAN_EMIT_SYMBOL, 78),
    (15, HUFFMAN_EMIT_SYMBOL, 78),
    (24, HUFFMAN_EMIT_SYMBOL, 78),
    (31, HUFFMAN_EMIT_SYMBOL, 78),
    (41, HUFFMAN_EMIT_SYMBOL, 78),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 78),

    # Node 52
    (3, HUFFMAN_EMIT_SYMBOL, 79),
    (6, HUFFMAN_EMIT_SYMBOL, 79),
    (10, HUFFMAN_EMIT_SYMBOL, 79),
    (15, HUFFMAN_EMIT_SYMBOL, 79),
    (24, HUFFMAN_EMIT_SYMBOL, 79),
    (31, HUFFMAN_EMIT_SYMBOL, 79),
    (41, HUFFMAN_EMIT_SYMBOL, 79),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 79),
    (3, HUFFMAN_EMIT_SYMBOL, 80),
    (6, HUFFMAN_EMIT_SYMBOL, 80),
    (10, HUFFMAN_EMIT_SYMBOL, 80),
    (15, HUFFMAN_EMIT_SYMBOL, 80),
    (24, HUFFMAN_EMIT_SYMBOL, 80),
    (31, HUFFMAN_EMIT_SYMBOL, 80),
    (41, HUFFMAN_EMIT_SYMBOL, 80),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 80),

    # Node 53
    (2, HUFFMAN_EMIT_SYMBOL, 81),
    (9, HUFFMAN_EMIT_SYMBOL, 81),
    (23, HUFFMAN_EMIT_SYMBOL, 81),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81),
    (2, HUFFMAN_EMIT_SYMBOL, 82),
    (9, HUFFMAN_EMIT_SYMBOL, 82),
    (23, HUFFMAN_EMIT_SYMBOL, 82),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82),
    (2, HUFFMAN_EMIT_SYMBOL, 83),
    (9, HUFFMAN_EMIT_SYMBOL, 83),
    (23, HUFFMAN_EMIT_SYMBOL, 83),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83),
    (2, HUFFMAN_EMIT_SYMBOL, 84),
    (9, HUFFMAN_EMIT_SYMBOL, 84),
    (23, HUFFMAN_EMIT_SYMBOL, 84),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84),

    # Node 54
    (3, HUFFMAN_EMIT_SYMBOL, 81),
    (6, HUFFMAN_EMIT_SYMBOL, 81),
    (10, HUFFMAN_EMIT_SYMBOL, 81),
    (15, HUFFMAN_EMIT_SYMBOL, 81),
    (24, HUFFMAN_EMIT_SYMBOL, 81),
    (31, HUFFMAN_EMIT_SYMBOL, 81),
    (41, HUFFMAN_EMIT_SYMBOL, 81),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 81),
    (3, HUFFMAN_EMIT_SYMBOL, 82),
    (6, HUFFMAN_EMIT_SYMBOL, 82),
    (10, HUFFMAN_EMIT_SYMBOL, 82),
    (15, HUFFMAN_EMIT_SYMBOL, 82),
    (24, HUFFMAN_EMIT_SYMBOL, 82),
    (31, HUFFMAN_EMIT_SYMBOL, 82),
    (41, HUFFMAN_EMIT_SYMBOL, 82),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 82),

    # Node 55
    (3, HUFFMAN_EMIT_SYMBOL, 83),
    (6, HUFFMAN_EMIT_SYMBOL, 83),
    (10, HUFFMAN_EMIT_SYMBOL, 83),
    (15, HUFFMAN_EMIT_SYMBOL, 83),
    (24, HUFFMAN_EMIT_SYMBOL, 83),
    (31, HUFFMAN_EMIT_SYMBOL, 83),
    (41, HUFFMAN_EMIT_SYMBOL, 83),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 83),
    (3, HUFFMAN_EMIT_SYMBOL, 84),
    (6, HUFFMAN_EMIT_SYMBOL, 84),
    (10, HUFFMAN_EMIT_SYMBOL, 84),
    (15, HUFFMAN_EMIT_SYMBOL, 84),
    (24, HUFFMAN_EMIT_SYMBOL, 84),
    (31, HUFFMAN_EMIT_SYMBOL, 84),
    (41, HUFFMAN_EMIT_SYMBOL, 84),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 84),

    # Node 56
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122),
    (70, 0, 0),
    (71, 0, 0),
    (73, 0, 0),
    (74, HUFFMAN_COMPLETE, 0),

    # Node 57
    (1, HUFFMAN_EMIT_SYMBOL, 85),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85),
    (1, HUFFMAN_EMIT_SYMBOL, 86),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86),
    (1, HUFFMAN_EMIT_SYMBOL, 87),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87),
    (1, HUFFMAN_EMIT_SYMBOL, 89),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89),
    (1, HUFFMAN_EMIT_SYMBOL, 106),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106),
    (1, HUFFMAN_EMIT_SYMBOL, 107),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107),
    (1, HUFFMAN_EMIT_SYMBOL, 113),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113),
    (1, HUFFMAN_EMIT_SYMBOL, 118),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118),

    # Node 58
    (2, HUFFMAN_EMIT_SYMBOL, 85),
    (9, HUFFMAN_EMIT_SYMBOL, 85),
    (23, HUFFMAN_EMIT_SYMBOL, 85),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85),
    (2, HUFFMAN_EMIT_SYMBOL, 86),
    (9, HUFFMAN_EMIT_SYMBOL, 86),
    (23, HUFFMAN_EMIT_SYMBOL, 86),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86),
    (2, HUFFMAN_EMIT_SYMBOL, 87),
    (9, HUFFMAN_EMIT_SYMBOL, 87),
    (23, HUFFMAN_EMIT_SYMBOL, 87),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87),
    (2, HUFFMAN_EMIT_SYMBOL, 89),
    (9, HUFFMAN_EMIT_SYMBOL, 89),
    (23, HUFFMAN_EMIT_SYMBOL, 89),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89),

    # Node 59
    (3, HUFFMAN_EMIT_SYMBOL, 85),
    (6, HUFFMAN_EMIT_SYMBOL, 85),
    (10, HUFFMAN_EMIT_SYMBOL, 85),
    (15, HUFFMAN_EMIT_SYMBOL, 85),
    (24, HUFFMAN_EMIT_SYMBOL, 85),
    (31, HUFFMAN_EMIT_SYMBOL, 85),
    (41, HUFFMAN_EMIT_SYMBOL, 85),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 85),
    (3, HUFFMAN_EMIT_SYMBOL, 86),
    (6, HUFFMAN_EMIT_SYMBOL, 86),
    (10, HUFFMAN_EMIT_SYMBOL, 86),
    (15, HUFFMAN_EMIT_SYMBOL, 86),
    (24, HUFFMAN_EMIT_SYMBOL, 86),
    (31, HUFFMAN_EMIT_SYMBOL, 86),
    (41, HUFFMAN_EMIT_SYMBOL, 86),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 86),

    # Node 60
    (3, HUFFMAN_EMIT_SYMBOL, 87),
    (6, HUFFMAN_EMIT_SYMBOL, 87),
    (10, HUFFMAN_EMIT_SYMBOL, 87),
    (15, HUFFMAN_EMIT_SYMBOL, 87),
    (24, HUFFMAN_EMIT_SYMBOL, 87),
    (31, HUFFMAN_EMIT_SYMBOL, 87),
    (41, HUFFMAN_EMIT_SYMBOL, 87),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 87),
    (3, HUFFMAN_EMIT_SYMBOL, 89),
    (6, HUFFMAN_EMIT_SYMBOL, 89),
    (10, HUFFMAN_EMIT_SYMBOL, 89),
    (15, HUFFMAN_EMIT_SYMBOL, 89),
    (24, HUFFMAN_EMIT_SYMBOL, 89),
    (31, HUFFMAN_EMIT_SYMBOL, 89),
    (41, HUFFMAN_EMIT_SYMBOL, 89),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 89),

    # Node 61
    (2, HUFFMAN_EMIT_SYMBOL, 106),
    (9, HUFFMAN_EMIT_SYMBOL, 106),
    (23, HUFFMAN_EMIT_SYMBOL, 106),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106),
    (2, HUFFMAN_EMIT_SYMBOL, 107),
    (9, HUFFMAN_EMIT_SYMBOL, 107),
    (23, HUFFMAN_EMIT_SYMBOL, 107),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107),
    (2, HUFFMAN_EMIT_SYMBOL, 113),
    (9, HUFFMAN_EMIT_SYMBOL, 113),
    (23, HUFFMAN_EMIT_SYMBOL, 113),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113),
    (2, HUFFMAN_EMIT_SYMBOL, 118),
    (9, HUFFMAN_EMIT_SYMBOL, 118),
    (23, HUFFMAN_EMIT_SYMBOL, 118),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118),

    # Node 62
    (3, HUFFMAN_EMIT_SYMBOL, 106),
    (6, HUFFMAN_EMIT_SYMBOL, 106),
    (10, HUFFMAN_EMIT_SYMBOL, 106),
    (15, HUFFMAN_EMIT_SYMBOL, 106),
    (24, HUFFMAN_EMIT_SYMBOL, 106),
    (31, HUFFMAN_EMIT_SYMBOL, 106),
    (41, HUFFMAN_EMIT_SYMBOL, 106),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 106),
    (3, HUFFMAN_EMIT_SYMBOL, 107),
    (6, HUFFMAN_EMIT_SYMBOL, 107),
    (10, HUFFMAN_EMIT_SYMBOL, 107),
    (15, HUFFMAN_EMIT_SYMBOL, 107),
    (24, HUFFMAN_EMIT_SYMBOL, 107),
    (31, HUFFMAN_EMIT_SYMBOL, 107),
    (41, HUFFMAN_EMIT_SYMBOL, 107),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 107),

    # Node 63
    (3, HUFFMAN_EMIT_SYMBOL, 113),
    (6, HUFFMAN_EMIT_SYMBOL, 113),
    (10, HUFFMAN_EMIT_SYMBOL, 113),
    (15, HUFFMAN_EMIT_SYMBOL, 113),
    (24, HUFFMAN_EMIT_SYMBOL, 113),
    (31, HUFFMAN_EMIT_SYMBOL, 113),
    (41, HUFFMAN_EMIT_SYMBOL, 113),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 113),
    (3, HUFFMAN_EMIT_SYMBOL, 118),
    (6, HUFFMAN_EMIT_SYMBOL, 118),
    (10, HUFFMAN_EMIT_SYMBOL, 118),
    (15, HUFFMAN_EMIT_SYMBOL, 118),
    (24, HUFFMAN_EMIT_SYMBOL, 118),
    (31, HUFFMAN_EMIT_SYMBOL, 118),
    (41, HUFFMAN_EMIT_SYMBOL, 118),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 118),

    # Node 64
    (1, HUFFMAN_EMIT_SYMBOL, 119),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119),
    (1, HUFFMAN_EMIT_SYMBOL, 120),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120),
    (1, HUFFMAN_EMIT_SYMBOL, 121),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121),
    (1, HUFFMAN_EMIT_SYMBOL, 122),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90),
    (75, 0, 0),
    (78, 0, 0),

    # Node 65
    (2, HUFFMAN_EMIT_SYMBOL, 119),
    (9, HUFFMAN_EMIT_SYMBOL, 119),
    (23, HUFFMAN_EMIT_SYMBOL, 119),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119),
    (2, HUFFMAN_EMIT_SYMBOL, 120),
    (9, HUFFMAN_EMIT_SYMBOL, 120),
    (23, HUFFMAN_EMIT_SYMBOL, 120),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120),
    (2, HUFFMAN_EMIT_SYMBOL, 121),
    (9, HUFFMAN_EMIT_SYMBOL, 121),
    (23, HUFFMAN_EMIT_SYMBOL, 121),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121),
    (2, HUFFMAN_EMIT_SYMBOL, 122),
    (9, HUFFMAN_EMIT_SYMBOL, 122),
    (23, HUFFMAN_EMIT_SYMBOL, 122),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122),

    # Node 66
    (3, HUFFMAN_EMIT_SYMBOL, 119),
    (6, HUFFMAN_EMIT_SYMBOL, 119),
    (10, HUFFMAN_EMIT_SYMBOL, 119),
    (15, HUFFMAN_EMIT_SYMBOL, 119),
    (24, HUFFMAN_EMIT_SYMBOL, 119),
    (31, HUFFMAN_EMIT_SYMBOL, 119),
    (41, HUFFMAN_EMIT_SYMBOL, 119),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 119),
    (3, HUFFMAN_EMIT_SYMBOL, 120),
    (6, HUFFMAN_EMIT_SYMBOL, 120),
    (10, HUFFMAN_EMIT_SYMBOL, 120),
    (15, HUFFMAN_EMIT_SYMBOL, 120),
    (24, HUFFMAN_EMIT_SYMBOL, 120),
    (31, HUFFMAN_EMIT_SYMBOL, 120),
    (41, HUFFMAN_EMIT_SYMBOL, 120),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 120),

    # Node 67
    (3, HUFFMAN_EMIT_SYMBOL, 121),
    (6, HUFFMAN_EMIT_SYMBOL, 121),
    (10, HUFFMAN_EMIT_SYMBOL, 121),
    (15, HUFFMAN_EMIT_SYMBOL, 121),
    (24, HUFFMAN_EMIT_SYMBOL, 121),
    (31, HUFFMAN_EMIT_SYMBOL, 121),
    (41, HUFFMAN_EMIT_SYMBOL, 121),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 121),
    (3, HUFFMAN_EMIT_SYMBOL, 122),
    (6, HUFFMAN_EMIT_SYMBOL, 122),
    (10, HUFFMAN_EMIT_SYMBOL, 122),
    (15, HUFFMAN_EMIT_SYMBOL, 122),
    (24, HUFFMAN_EMIT_SYMBOL, 122),
    (31, HUFFMAN_EMIT_SYMBOL, 122),
    (41, HUFFMAN_EMIT_SYMBOL, 122),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 122),

    # Node 68
    (1, HUFFMAN_EMIT_SYMBOL, 38),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38),
    (1, HUFFMAN_EMIT_SYMBOL, 42),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42),
    (1, HUFFMAN_EMIT_SYMBOL, 44),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44),
    (1, HUFFMAN_EMIT_SYMBOL, 59),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59),
    (1, HUFFMAN_EMIT_SYMBOL, 88),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88),
    (1, HUFFMAN_EMIT_SYMBOL, 90),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90),
    (76, 0, 0),
    (77, 0, 0),
    (79, 0, 0),
    (81, 0, 0),

    # Node 69
    (2, HUFFMAN_EMIT_SYMBOL, 38),
    (9, HUFFMAN_EMIT_SYMBOL, 38),
    (23, HUFFMAN_EMIT_SYMBOL, 38),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38),
    (2, HUFFMAN_EMIT_SYMBOL, 42),
    (9, HUFFMAN_EMIT_SYMBOL, 42),
    (23, HUFFMAN_EMIT_SYMBOL, 42),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42),
    (2, HUFFMAN_EMIT_SYMBOL, 44),
    (9, HUFFMAN_EMIT_SYMBOL, 44),
    (23, HUFFMAN_EMIT_SYMBOL, 44),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44),
    (2, HUFFMAN_EMIT_SYMBOL, 59),
    (9, HUFFMAN_EMIT_SYMBOL, 59),
    (23, HUFFMAN_EMIT_SYMBOL, 59),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59),

    # Node 70
    (3, HUFFMAN_EMIT_SYMBOL, 38),
    (6, HUFFMAN_EMIT_SYMBOL, 38),
    (10, HUFFMAN_EMIT_SYMBOL, 38),
    (15, HUFFMAN_EMIT_SYMBOL, 38),
    (24, HUFFMAN_EMIT_SYMBOL, 38),
    (31, HUFFMAN_EMIT_SYMBOL, 38),
    (41, HUFFMAN_EMIT_SYMBOL, 38),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 38),
    (3, HUFFMAN_EMIT_SYMBOL, 42),
    (6, HUFFMAN_EMIT_SYMBOL, 42),
    (10, HUFFMAN_EMIT_SYMBOL, 42),
    (15, HUFFMAN_EMIT_SYMBOL, 42),
    (24, HUFFMAN_EMIT_SYMBOL, 42),
    (31, HUFFMAN_EMIT_SYMBOL, 42),
    (41, HUFFMAN_EMIT_SYMBOL, 42),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 42),

    # Node 71
    (3, HUFFMAN_EMIT_SYMBOL, 44),
    (6, HUFFMAN_EMIT_SYMBOL, 44),
    (10, HUFFMAN_EMIT_SYMBOL, 44),
    (15, HUFFMAN_EMIT_SYMBOL, 44),
    (24, HUFFMAN_EMIT_SYMBOL, 44),
    (31, HUFFMAN_EMIT_SYMBOL, 44),
    (41, HUFFMAN_EMIT_SYMBOL, 44),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 44),
    (3, HUFFMAN_EMIT_SYMBOL, 59),
    (6, HUFFMAN_EMIT_SYMBOL, 59),
    (10, HUFFMAN_EMIT_SYMBOL, 59),
    (15, HUFFMAN_EMIT_SYMBOL, 59),
    (24, HUFFMAN_EMIT_SYMBOL, 59),
    (31, HUFFMAN_EMIT_SYMBOL, 59),
    (41, HUFFMAN_EMIT_SYMBOL, 59),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 59),

    # Node 72
    (2, HUFFMAN_EMIT_SYMBOL, 88),
    (9, HUFFMAN_EMIT_SYMBOL, 88),
    (23, HUFFMAN_EMIT_SYMBOL, 88),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88),
    (2, HUFFMAN_EMIT_SYMBOL, 90),
    (9, HUFFMAN_EMIT_SYMBOL, 90),
    (23, HUFFMAN_EMIT_SYMBOL, 90),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63),
    (80, 0, 0),
    (82, 0, 0),
    (84, 0, 0),

    # Node 73
    (3, HUFFMAN_EMIT_SYMBOL, 88),
    (6, HUFFMAN_EMIT_SYMBOL, 88),
    (10, HUFFMAN_EMIT_SYMBOL, 88),
    (15, HUFFMAN_EMIT_SYMBOL, 88),
    (24, HUFFMAN_EMIT_SYMBOL, 88),
    (31, HUFFMAN_EMIT_SYMBOL, 88),
    (41, HUFFMAN_EMIT_SYMBOL, 88),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 88),
    (3, HUFFMAN_EMIT_SYMBOL, 90),
    (6, HUFFMAN_EMIT_SYMBOL, 90),
    (10, HUFFMAN_EMIT_SYMBOL, 90),
    (15, HUFFMAN_EMIT_SYMBOL, 90),
    (24, HUFFMAN_EMIT_SYMBOL, 90),
    (31, HUFFMAN_EMIT_SYMBOL, 90),
    (41, HUFFMAN_EMIT_SYMBOL, 90),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 90),

    # Node 74
    (1, HUFFMAN_EMIT_SYMBOL, 33),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33),
    (1, HUFFMAN_EMIT_SYMBOL, 34),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34),
    (1, HUFFMAN_EMIT_SYMBOL, 40),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40),
    (1, HUFFMAN_EMIT_SYMBOL, 41),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41),
    (1, HUFFMAN_EMIT_SYMBOL, 63),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124),
    (83, 0, 0),
    (85, 0, 0),
    (88, 0, 0),

    # Node 75
    (2, HUFFMAN_EMIT_SYMBOL, 33),
    (9, HUFFMAN_EMIT_SYMBOL, 33),
    (23, HUFFMAN_EMIT_SYMBOL, 33),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33),
    (2, HUFFMAN_EMIT_SYMBOL, 34),
    (9, HUFFMAN_EMIT_SYMBOL, 34),
    (23, HUFFMAN_EMIT_SYMBOL, 34),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34),
    (2, HUFFMAN_EMIT_SYMBOL, 40),
    (9, HUFFMAN_EMIT_SYMBOL, 40),
    (23, HUFFMAN_EMIT_SYMBOL, 40),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40),
    (2, HUFFMAN_EMIT_SYMBOL, 41),
    (9, HUFFMAN_EMIT_SYMBOL, 41),
    (23, HUFFMAN_EMIT_SYMBOL, 41),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41),

    # Node 76
    (3, HUFFMAN_EMIT_SYMBOL, 33),
    (6, HUFFMAN_EMIT_SYMBOL, 33),
    (10, HUFFMAN_EMIT_SYMBOL, 33),
    (15, HUFFMAN_EMIT_SYMBOL, 33),
    (24, HUFFMAN_EMIT_SYMBOL, 33),
    (31, HUFFMAN_EMIT_SYMBOL, 33),
    (41, HUFFMAN_EMIT_SYMBOL, 33),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 33),
    (3, HUFFMAN_EMIT_SYMBOL, 34),
    (6, HUFFMAN_EMIT_SYMBOL, 34),
    (10, HUFFMAN_EMIT_SYMBOL, 34),
    (15, HUFFMAN_EMIT_SYMBOL, 34),
    (24, HUFFMAN_EMIT_SYMBOL, 34),
    (31, HUFFMAN_EMIT_SYMBOL, 34),
    (41, HUFFMAN_EMIT_SYMBOL, 34),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 34),

    # Node 77
    (3, HUFFMAN_EMIT_SYMBOL, 40),
    (6, HUFFMAN_EMIT_SYMBOL, 40),
    (10, HUFFMAN_EMIT_SYMBOL, 40),
    (15, HUFFMAN_EMIT_SYMBOL, 40),
    (24, HUFFMAN_EMIT_SYMBOL, 40),
    (31, HUFFMAN_EMIT_SYMBOL, 40),
    (41, HUFFMAN_EMIT_SYMBOL, 40),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 40),
    (3, HUFFMAN_EMIT_SYMBOL, 41),
    (6, HUFFMAN_EMIT_SYMBOL, 41),
    (10, HUFFMAN_EMIT_SYMBOL, 41),
    (15, HUFFMAN_EMIT_SYMBOL, 41),
    (24, HUFFMAN_EMIT_SYMBOL, 41),
    (31, HUFFMAN_EMIT_SYMBOL, 41),
    (41, HUFFMAN_EMIT_SYMBOL, 41),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 41),

    # Node 78
    (2, HUFFMAN_EMIT_SYMBOL, 63),
    (9, HUFFMAN_EMIT_SYMBOL, 63),
    (23, HUFFMAN_EMIT_SYMBOL, 63),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63),
    (1, HUFFMAN_EMIT_SYMBOL, 39),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39),
    (1, HUFFMAN_EMIT_SYMBOL, 43),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43),
    (1, HUFFMAN_EMIT_SYMBOL, 124),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62),
    (86, 0, 0),
    (87, 0, 0),
    (89, 0, 0),
    (90, 0, 0),

    # Node 79
    (3, HUFFMAN_EMIT_SYMBOL, 63),
    (6, HUFFMAN_EMIT_SYMBOL, 63),
    (10, HUFFMAN_EMIT_SYMBOL, 63),
    (15, HUFFMAN_EMIT_SYMBOL, 63),
    (24, HUFFMAN_EMIT_SYMBOL, 63),
    (31, HUFFMAN_EMIT_SYMBOL, 63),
    (41, HUFFMAN_EMIT_SYMBOL, 63),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 63),
    (2, HUFFMAN_EMIT_SYMBOL, 39),
    (9, HUFFMAN_EMIT_SYMBOL, 39),
    (23, HUFFMAN_EMIT_SYMBOL, 39),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39),
    (2, HUFFMAN_EMIT_SYMBOL, 43),
    (9, HUFFMAN_EMIT_SYMBOL, 43),
    (23, HUFFMAN_EMIT_SYMBOL, 43),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43),

    # Node 80
    (3, HUFFMAN_EMIT_SYMBOL, 39),
    (6, HUFFMAN_EMIT_SYMBOL, 39),
    (10, HUFFMAN_EMIT_SYMBOL, 39),
    (15, HUFFMAN_EMIT_SYMBOL, 39),
    (24, HUFFMAN_EMIT_SYMBOL, 39),
    (31, HUFFMAN_EMIT_SYMBOL, 39),
    (41, HUFFMAN_EMIT_SYMBOL, 39),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 39),
    (3, HUFFMAN_EMIT_SYMBOL, 43),
    (6, HUFFMAN_EMIT_SYMBOL, 43),
    (10, HUFFMAN_EMIT_SYMBOL, 43),
    (15, HUFFMAN_EMIT_SYMBOL, 43),
    (24, HUFFMAN_EMIT_SYMBOL, 43),
    (31, HUFFMAN_EMIT_SYMBOL, 43),
    (41, HUFFMAN_EMIT_SYMBOL, 43),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 43),

    # Node 81
    (2, HUFFMAN_EMIT_SYMBOL, 124),
    (9, HUFFMAN_EMIT_SYMBOL, 124),
    (23, HUFFMAN_EMIT_SYMBOL, 124),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124),
    (1, HUFFMAN_EMIT_SYMBOL, 35),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35),
    (1, HUFFMAN_EMIT_SYMBOL, 62),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126),
    (91, 0, 0),
    (92, 0, 0),

    # Node 82
    (3, HUFFMAN_EMIT_SYMBOL, 124),
    (6, HUFFMAN_EMIT_SYMBOL, 124),
    (10, HUFFMAN_EMIT_SYMBOL, 124),
    (15, HUFFMAN_EMIT_SYMBOL, 124),
    (24, HUFFMAN_EMIT_SYMBOL, 124),
    (31, HUFFMAN_EMIT_SYMBOL, 124),
    (41, HUFFMAN_EMIT_SYMBOL, 124),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 124),
    (2, HUFFMAN_EMIT_SYMBOL, 35),
    (9, HUFFMAN_EMIT_SYMBOL, 35),
    (23, HUFFMAN_EMIT_SYMBOL, 35),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35),
    (2, HUFFMAN_EMIT_SYMBOL, 62),
    (9, HUFFMAN_EMIT_SYMBOL, 62),
    (23, HUFFMAN_EMIT_SYMBOL, 62),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62),

    # Node 83
    (3, HUFFMAN_EMIT_SYMBOL, 35),
    (6, HUFFMAN_EMIT_SYMBOL, 35),
    (10, HUFFMAN_EMIT_SYMBOL, 35),
    (15, HUFFMAN_EMIT_SYMBOL, 35),
    (24, HUFFMAN_EMIT_SYMBOL, 35),
    (31, HUFFMAN_EMIT_SYMBOL, 35),
    (41, HUFFMAN_EMIT_SYMBOL, 35),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 35),
    (3, HUFFMAN_EMIT_SYMBOL, 62),
    (6, HUFFMAN_EMIT_SYMBOL, 62),
    (10, HUFFMAN_EMIT_SYMBOL, 62),
    (15, HUFFMAN_EMIT_SYMBOL, 62),
    (24, HUFFMAN_EMIT_SYMBOL, 62),
    (31, HUFFMAN_EMIT_SYMBOL, 62),
    (41, HUFFMAN_EMIT_SYMBOL, 62),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 62),

    # Node 84
    (1, HUFFMAN_EMIT_SYMBOL, 0),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0),
    (1, HUFFMAN_EMIT_SYMBOL, 36),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36),
    (1, HUFFMAN_EMIT_SYMBOL, 64),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64),
    (1, HUFFMAN_EMIT_SYMBOL, 91),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91),
    (1, HUFFMAN_EMIT_SYMBOL, 93),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93),
    (1, HUFFMAN_EMIT_SYMBOL, 126),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125),
    (93, 0, 0),
    (94, 0, 0),

    # Node 85
    (2, HUFFMAN_EMIT_SYMBOL, 0),
    (9, HUFFMAN_EMIT_SYMBOL, 0),
    (23, HUFFMAN_EMIT_SYMBOL, 0),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0),
    (2, HUFFMAN_EMIT_SYMBOL, 36),
    (9, HUFFMAN_EMIT_SYMBOL, 36),
    (23, HUFFMAN_EMIT_SYMBOL, 36),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36),
    (2, HUFFMAN_EMIT_SYMBOL, 64),
    (9, HUFFMAN_EMIT_SYMBOL, 64),
    (23, HUFFMAN_EMIT_SYMBOL, 64),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64),
    (2, HUFFMAN_EMIT_SYMBOL, 91),
    (9, HUFFMAN_EMIT_SYMBOL, 91),
    (23, HUFFMAN_EMIT_SYMBOL, 91),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91),

    # Node 86
    (3, HUFFMAN_EMIT_SYMBOL, 0),
    (6, HUFFMAN_EMIT_SYMBOL, 0),
    (10, HUFFMAN_EMIT_SYMBOL, 0),
    (15, HUFFMAN_EMIT_SYMBOL, 0),
    (24, HUFFMAN_EMIT_SYMBOL, 0),
    (31, HUFFMAN_EMIT_SYMBOL, 0),
    (41, HUFFMAN_EMIT_SYMBOL, 0),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 0),
    (3, HUFFMAN_EMIT_SYMBOL, 36),
    (6, HUFFMAN_EMIT_SYMBOL, 36),
    (10, HUFFMAN_EMIT_SYMBOL, 36),
    (15, HUFFMAN_EMIT_SYMBOL, 36),
    (24, HUFFMAN_EMIT_SYMBOL, 36),
    (31, HUFFMAN_EMIT_SYMBOL, 36),
    (41, HUFFMAN_EMIT_SYMBOL, 36),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 36),

    # Node 87
    (3, HUFFMAN_EMIT_SYMBOL, 64),
    (6, HUFFMAN_EMIT_SYMBOL, 64),
    (10, HUFFMAN_EMIT_SYMBOL, 64),
    (15, HUFFMAN_EMIT_SYMBOL, 64),
    (24, HUFFMAN_EMIT_SYMBOL, 64),
    (31, HUFFMAN_EMIT_SYMBOL, 64),
    (41, HUFFMAN_EMIT_SYMBOL, 64),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 64),
    (3, HUFFMAN_EMIT_SYMBOL, 91),
    (6, HUFFMAN_EMIT_SYMBOL, 91),
    (10, HUFFMAN_EMIT_SYMBOL, 91),
    (15, HUFFMAN_EMIT_SYMBOL, 91),
    (24, HUFFMAN_EMIT_SYMBOL, 91),
    (31, HUFFMAN_EMIT_SYMBOL, 91),
    (41, HUFFMAN_EMIT_SYMBOL, 91),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 91),

    # Node 88
    (2, HUFFMAN_EMIT_SYMBOL, 93),
    (9, HUFFMAN_EMIT_SYMBOL, 93),
    (23, HUFFMAN_EMIT_SYMBOL, 93),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93),
    (2, HUFFMAN_EMIT_SYMBOL, 126),
    (9, HUFFMAN_EMIT_SYMBOL, 126),
    (23, HUFFMAN_EMIT_SYMBOL, 126),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126),
    (1, HUFFMAN_EMIT_SYMBOL, 94),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94),
    (1, HUFFMAN_EMIT_SYMBOL, 125),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123),
    (95, 0, 0),

    # Node 89
    (3, HUFFMAN_EMIT_SYMBOL, 93),
    (6, HUFFMAN_EMIT_SYMBOL, 93),
    (10, HUFFMAN_EMIT_SYMBOL, 93),
    (15, HUFFMAN_EMIT_SYMBOL, 93),
    (24, HUFFMAN_EMIT_SYMBOL, 93),
    (31, HUFFMAN_EMIT_SYMBOL, 93),
    (41, HUFFMAN_EMIT_SYMBOL, 93),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 93),
    (3, HUFFMAN_EMIT_SYMBOL, 126),
    (6, HUFFMAN_EMIT_SYMBOL, 126),
    (10, HUFFMAN_EMIT_SYMBOL, 126),
    (15, HUFFMAN_EMIT_SYMBOL, 126),
    (24, HUFFMAN_EMIT_SYMBOL, 126),
    (31, HUFFMAN_EMIT_SYMBOL, 126),
    (41, HUFFMAN_EMIT_SYMBOL, 126),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 126),

    # Node 90
    (2, HUFFMAN_EMIT_SYMBOL, 94),
    (9, HUFFMAN_EMIT_SYMBOL, 94),
    (23, HUFFMAN_EMIT_SYMBOL, 94),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94),
    (2, HUFFMAN_EMIT_SYMBOL, 125),
    (9, HUFFMAN_EMIT_SYMBOL, 125),
    (23, HUFFMAN_EMIT_SYMBOL, 125),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125),
    (1, HUFFMAN_EMIT_SYMBOL, 60),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60),
    (1, HUFFMAN_EMIT_SYMBOL, 96),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96),
    (1, HUFFMAN_EMIT_SYMBOL, 123),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123),
    (96, 0, 0),
    (110, 0, 0),

    # Node 91
    (3, HUFFMAN_EMIT_SYMBOL, 94),
    (6, HUFFMAN_EMIT_SYMBOL, 94),
    (10, HUFFMAN_EMIT_SYMBOL, 94),
    (15, HUFFMAN_EMIT_SYMBOL, 94),
    (24, HUFFMAN_EMIT_SYMBOL, 94),
    (31, HUFFMAN_EMIT_SYMBOL, 94),
    (41, HUFFMAN_EMIT_SYMBOL, 94),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 94),
    (3, HUFFMAN_EMIT_SYMBOL, 125),
    (6, HUFFMAN_EMIT_SYMBOL, 125),
    (10, HUFFMAN_EMIT_SYMBOL, 125),
    (15, HUFFMAN_EMIT_SYMBOL, 125),
    (24, HUFFMAN_EMIT_SYMBOL, 125),
    (31, HUFFMAN_EMIT_SYMBOL, 125),
    (41, HUFFMAN_EMIT_SYMBOL, 125),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 125),

    # Node 92
    (2, HUFFMAN_EMIT_SYMBOL, 60),
    (9, HUFFMAN_EMIT_SYMBOL, 60),
    (23, HUFFMAN_EMIT_SYMBOL, 60),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60),
    (2, HUFFMAN_EMIT_SYMBOL, 96),
    (9, HUFFMAN_EMIT_SYMBOL, 96),
    (23, HUFFMAN_EMIT_SYMBOL, 96),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96),
    (2, HUFFMAN_EMIT_SYMBOL, 123),
    (9, HUFFMAN_EMIT_SYMBOL, 123),
    (23, HUFFMAN_EMIT_SYMBOL, 123),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123),
    (97, 0, 0),
    (101, 0, 0),
    (111, 0, 0),
    (133, 0, 0),

    # Node 93
    (3, HUFFMAN_EMIT_SYMBOL, 60),
    (6, HUFFMAN_EMIT_SYMBOL, 60),
    (10, HUFFMAN_EMIT_SYMBOL, 60),
    (15, HUFFMAN_EMIT_SYMBOL, 60),
    (24, HUFFMAN_EMIT_SYMBOL, 60),
    (31, HUFFMAN_EMIT_SYMBOL, 60),
    (41, HUFFMAN_EMIT_SYMBOL, 60),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 60),
    (3, HUFFMAN_EMIT_SYMBOL, 96),
    (6, HUFFMAN_EMIT_SYMBOL, 96),
    (10, HUFFMAN_EMIT_SYMBOL, 96),
    (15, HUFFMAN_EMIT_SYMBOL, 96),
    (24, HUFFMAN_EMIT_SYMBOL, 96),
    (31, HUFFMAN_EMIT_SYMBOL, 96),
    (41, HUFFMAN_EMIT_SYMBOL, 96),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 96),

    # Node 94
    (3, HUFFMAN_EMIT_SYMBOL, 123),
    (6, HUFFMAN_EMIT_SYMBOL, 123),
    (10, HUFFMAN_EMIT_SYMBOL, 123),
    (15, HUFFMAN_EMIT_SYMBOL, 123),
    (24, HUFFMAN_EMIT_SYMBOL, 123),
    (31, HUFFMAN_EMIT_SYMBOL, 123),
    (41, HUFFMAN_EMIT_SYMBOL, 123),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 123),
    (98, 0, 0),
    (99, 0, 0),
    (102, 0, 0),
    (105, 0, 0),
    (112, 0, 0),
    (119, 0, 0),
    (134, 0, 0),
    (153, 0, 0),

    # Node 95
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208),
    (100, 0, 0),
    (103, 0, 0),
    (104, 0, 0),
    (106, 0, 0),
    (107, 0, 0),
    (113, 0, 0),
    (116, 0, 0),
    (120, 0, 0),
    (126, 0, 0),
    (135, 0, 0),
    (142, 0, 0),
    (154, 0, 0),
    (169, 0, 0),

    # Node 96
    (1, HUFFMAN_EMIT_SYMBOL, 92),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92),
    (1, HUFFMAN_EMIT_SYMBOL, 195),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195),
    (1, HUFFMAN_EMIT_SYMBOL, 208),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226),
    (108, 0, 0),
    (109, 0, 0),

    # Node 97
    (2, HUFFMAN_EMIT_SYMBOL, 92),
    (9, HUFFMAN_EMIT_SYMBOL, 92),
    (23, HUFFMAN_EMIT_SYMBOL, 92),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92),
    (2, HUFFMAN_EMIT_SYMBOL, 195),
    (9, HUFFMAN_EMIT_SYMBOL, 195),
    (23, HUFFMAN_EMIT_SYMBOL, 195),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195),
    (2, HUFFMAN_EMIT_SYMBOL, 208),
    (9, HUFFMAN_EMIT_SYMBOL, 208),
    (23, HUFFMAN_EMIT_SYMBOL, 208),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208),
    (1, HUFFMAN_EMIT_SYMBOL, 128),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128),
    (1, HUFFMAN_EMIT_SYMBOL, 130),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130),

    # Node 98
    (3, HUFFMAN_EMIT_SYMBOL, 92),
    (6, HUFFMAN_EMIT_SYMBOL, 92),
    (10, HUFFMAN_EMIT_SYMBOL, 92),
    (15, HUFFMAN_EMIT_SYMBOL, 92),
    (24, HUFFMAN_EMIT_SYMBOL, 92),
    (31, HUFFMAN_EMIT_SYMBOL, 92),
    (41, HUFFMAN_EMIT_SYMBOL, 92),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 92),
    (3, HUFFMAN_EMIT_SYMBOL, 195),
    (6, HUFFMAN_EMIT_SYMBOL, 195),
    (10, HUFFMAN_EMIT_SYMBOL, 195),
    (15, HUFFMAN_EMIT_SYMBOL, 195),
    (24, HUFFMAN_EMIT_SYMBOL, 195),
    (31, HUFFMAN_EMIT_SYMBOL, 195),
    (41, HUFFMAN_EMIT_SYMBOL, 195),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 195),

    # Node 99
    (3, HUFFMAN_EMIT_SYMBOL, 208),
    (6, HUFFMAN_EMIT_SYMBOL, 208),
    (10, HUFFMAN_EMIT_SYMBOL, 208),
    (15, HUFFMAN_EMIT_SYMBOL, 208),
    (24, HUFFMAN_EMIT_SYMBOL, 208),
    (31, HUFFMAN_EMIT_SYMBOL, 208),
    (41, HUFFMAN_EMIT_SYMBOL, 208),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 208),
    (2, HUFFMAN_EMIT_SYMBOL, 128),
    (9, HUFFMAN_EMIT_SYMBOL, 128),
    (23, HUFFMAN_EMIT_SYMBOL, 128),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128),
    (2, HUFFMAN_EMIT_SYMBOL, 130),
    (9, HUFFMAN_EMIT_SYMBOL, 130),
    (23, HUFFMAN_EMIT_SYMBOL, 130),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130),

    # Node 100
    (3, HUFFMAN_EMIT_SYMBOL, 128),
    (6, HUFFMAN_EMIT_SYMBOL, 128),
    (10, HUFFMAN_EMIT_SYMBOL, 128),
    (15, HUFFMAN_EMIT_SYMBOL, 128),
    (24, HUFFMAN_EMIT_SYMBOL, 128),
    (31, HUFFMAN_EMIT_SYMBOL, 128),
    (41, HUFFMAN_EMIT_SYMBOL, 128),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 128),
    (3, HUFFMAN_EMIT_SYMBOL, 130),
    (6, HUFFMAN_EMIT_SYMBOL, 130),
    (10, HUFFMAN_EMIT_SYMBOL, 130),
    (15, HUFFMAN_EMIT_SYMBOL, 130),
    (24, HUFFMAN_EMIT_SYMBOL, 130),
    (31, HUFFMAN_EMIT_SYMBOL, 130),
    (41, HUFFMAN_EMIT_SYMBOL, 130),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 130),

    # Node 101
    (1, HUFFMAN_EMIT_SYMBOL, 131),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131),
    (1, HUFFMAN_EMIT_SYMBOL, 162),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162),
    (1, HUFFMAN_EMIT_SYMBOL, 184),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184),
    (1, HUFFMAN_EMIT_SYMBOL, 194),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194),
    (1, HUFFMAN_EMIT_SYMBOL, 224),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224),
    (1, HUFFMAN_EMIT_SYMBOL, 226),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172),

    # Node 102
    (2, HUFFMAN_EMIT_SYMBOL, 131),
    (9, HUFFMAN_EMIT_SYMBOL, 131),
    (23, HUFFMAN_EMIT_SYMBOL, 131),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131),
    (2, HUFFMAN_EMIT_SYMBOL, 162),
    (9, HUFFMAN_EMIT_SYMBOL, 162),
    (23, HUFFMAN_EMIT_SYMBOL, 162),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162),
    (2, HUFFMAN_EMIT_SYMBOL, 184),
    (9, HUFFMAN_EMIT_SYMBOL, 184),
    (23, HUFFMAN_EMIT_SYMBOL, 184),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184),
    (2, HUFFMAN_EMIT_SYMBOL, 194),
    (9, HUFFMAN_EMIT_SYMBOL, 194),
    (23, HUFFMAN_EMIT_SYMBOL, 194),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194),

    # Node 103
    (3, HUFFMAN_EMIT_SYMBOL, 131),
    (6, HUFFMAN_EMIT_SYMBOL, 131),
    (10, HUFFMAN_EMIT_SYMBOL, 131),
    (15, HUFFMAN_EMIT_SYMBOL, 131),
    (24, HUFFMAN_EMIT_SYMBOL, 131),
    (31, HUFFMAN_EMIT_SYMBOL, 131),
    (41, HUFFMAN_EMIT_SYMBOL, 131),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 131),
    (3, HUFFMAN_EMIT_SYMBOL, 162),
    (6, HUFFMAN_EMIT_SYMBOL, 162),
    (10, HUFFMAN_EMIT_SYMBOL, 162),
    (15, HUFFMAN_EMIT_SYMBOL, 162),
    (24, HUFFMAN_EMIT_SYMBOL, 162),
    (31, HUFFMAN_EMIT_SYMBOL, 162),
    (41, HUFFMAN_EMIT_SYMBOL, 162),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 162),

    # Node 104
    (3, HUFFMAN_EMIT_SYMBOL, 184),
    (6, HUFFMAN_EMIT_SYMBOL, 184),
    (10, HUFFMAN_EMIT_SYMBOL, 184),
    (15, HUFFMAN_EMIT_SYMBOL, 184),
    (24, HUFFMAN_EMIT_SYMBOL, 184),
    (31, HUFFMAN_EMIT_SYMBOL, 184),
    (41, HUFFMAN_EMIT_SYMBOL, 184),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 184),
    (3, HUFFMAN_EMIT_SYMBOL, 194),
    (6, HUFFMAN_EMIT_SYMBOL, 194),
    (10, HUFFMAN_EMIT_SYMBOL, 194),
    (15, HUFFMAN_EMIT_SYMBOL, 194),
    (24, HUFFMAN_EMIT_SYMBOL, 194),
    (31, HUFFMAN_EMIT_SYMBOL, 194),
    (41, HUFFMAN_EMIT_SYMBOL, 194),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 194),

    # Node 105
    (2, HUFFMAN_EMIT_SYMBOL, 224),
    (9, HUFFMAN_EMIT_SYMBOL, 224),
    (23, HUFFMAN_EMIT_SYMBOL, 224),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224),
    (2, HUFFMAN_EMIT_SYMBOL, 226),
    (9, HUFFMAN_EMIT_SYMBOL, 226),
    (23, HUFFMAN_EMIT_SYMBOL, 226),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226),
    (1, HUFFMAN_EMIT_SYMBOL, 153),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153),
    (1, HUFFMAN_EMIT_SYMBOL, 161),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161),
    (1, HUFFMAN_EMIT_SYMBOL, 167),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167),
    (1, HUFFMAN_EMIT_SYMBOL, 172),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172),

    # Node 106
    (3, HUFFMAN_EMIT_SYMBOL, 224),
    (6, HUFFMAN_EMIT_SYMBOL, 224),
    (10, HUFFMAN_EMIT_SYMBOL, 224),
    (15, HUFFMAN_EMIT_SYMBOL, 224),
    (24, HUFFMAN_EMIT_SYMBOL, 224),
    (31, HUFFMAN_EMIT_SYMBOL, 224),
    (41, HUFFMAN_EMIT_SYMBOL, 224),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 224),
    (3, HUFFMAN_EMIT_SYMBOL, 226),
    (6, HUFFMAN_EMIT_SYMBOL, 226),
    (10, HUFFMAN_EMIT_SYMBOL, 226),
    (15, HUFFMAN_EMIT_SYMBOL, 226),
    (24, HUFFMAN_EMIT_SYMBOL, 226),
    (31, HUFFMAN_EMIT_SYMBOL, 226),
    (41, HUFFMAN_EMIT_SYMBOL, 226),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 226),

    # Node 107
    (2, HUFFMAN_EMIT_SYMBOL, 153),
    (9, HUFFMAN_EMIT_SYMBOL, 153),
    (23, HUFFMAN_EMIT_SYMBOL, 153),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153),
    (2, HUFFMAN_EMIT_SYMBOL, 161),
    (9, HUFFMAN_EMIT_SYMBOL, 161),
    (23, HUFFMAN_EMIT_SYMBOL, 161),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161),
    (2, HUFFMAN_EMIT_SYMBOL, 167),
    (9, HUFFMAN_EMIT_SYMBOL, 167),
    (23, HUFFMAN_EMIT_SYMBOL, 167),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167),
    (2, HUFFMAN_EMIT_SYMBOL, 172),
    (9, HUFFMAN_EMIT_SYMBOL, 172),
    (23, HUFFMAN_EMIT_SYMBOL, 172),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172),

    # Node 108
    (3, HUFFMAN_EMIT_SYMBOL, 153),
    (6, HUFFMAN_EMIT_SYMBOL, 153),
    (10, HUFFMAN_EMIT_SYMBOL, 153),
    (15, HUFFMAN_EMIT_SYMBOL, 153),
    (24, HUFFMAN_EMIT_SYMBOL, 153),
    (31, HUFFMAN_EMIT_SYMBOL, 153),
    (41, HUFFMAN_EMIT_SYMBOL, 153),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 153),
    (3, HUFFMAN_EMIT_SYMBOL, 161),
    (6, HUFFMAN_EMIT_SYMBOL, 161),
    (10, HUFFMAN_EMIT_SYMBOL, 161),
    (15, HUFFMAN_EMIT_SYMBOL, 161),
    (24, HUFFMAN_EMIT_SYMBOL, 161),
    (31, HUFFMAN_EMIT_SYMBOL, 161),
    (41, HUFFMAN_EMIT_SYMBOL, 161),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 161),

    # Node 109
    (3, HUFFMAN_EMIT_SYMBOL, 167),
    (6, HUFFMAN_EMIT_SYMBOL, 167),
    (10, HUFFMAN_EMIT_SYMBOL, 167),
    (15, HUFFMAN_EMIT_SYMBOL, 167),
    (24, HUFFMAN_EMIT_SYMBOL, 167),
    (31, HUFFMAN_EMIT_SYMBOL, 167),
    (41, HUFFMAN_EMIT_SYMBOL, 167),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 167),
    (3, HUFFMAN_EMIT_SYMBOL, 172),
    (6, HUFFMAN_EMIT_SYMBOL, 172),
    (10, HUFFMAN_EMIT_SYMBOL, 172),
    (15, HUFFMAN_EMIT_SYMBOL, 172),
    (24, HUFFMAN_EMIT_SYMBOL, 172),
    (31, HUFFMAN_EMIT_SYMBOL, 172),
    (41, HUFFMAN_EMIT_SYMBOL, 172),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 172),

    # Node 110
    (114, 0, 0),
    (115, 0, 0),
    (117, 0, 0),
    (118, 0, 0),
    (121, 0, 0),
    (123, 0, 0),
    (127, 0, 0),
    (130, 0, 0),
    (136, 0, 0),
    (139, 0, 0),
    (143, 0, 0),
    (146, 0, 0),
    (155, 0, 0),
    (162, 0, 0),
    (170, 0, 0),
    (180, 0, 0),

    # Node 111
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230),
    (122, 0, 0),
    (124, 0, 0),
    (125, 0, 0),
    (128, 0, 0),
    (129, 0, 0),
    (131, 0, 0),
    (132, 0, 0),

    # Node 112
    (1, HUFFMAN_EMIT_SYMBOL, 176),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176),
    (1, HUFFMAN_EMIT_SYMBOL, 177),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177),
    (1, HUFFMAN_EMIT_SYMBOL, 179),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179),
    (1, HUFFMAN_EMIT_SYMBOL, 209),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209),
    (1, HUFFMAN_EMIT_SYMBOL, 216),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216),
    (1, HUFFMAN_EMIT_SYMBOL, 217),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217),
    (1, HUFFMAN_EMIT_SYMBOL, 227),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227),
    (1, HUFFMAN_EMIT_SYMBOL, 229),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229),

    # Node 113
    (2, HUFFMAN_EMIT_SYMBOL, 176),
    (9, HUFFMAN_EMIT_SYMBOL, 176),
    (23, HUFFMAN_EMIT_SYMBOL, 176),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176),
    (2, HUFFMAN_EMIT_SYMBOL, 177),
    (9, HUFFMAN_EMIT_SYMBOL, 177),
    (23, HUFFMAN_EMIT_SYMBOL, 177),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177),
    (2, HUFFMAN_EMIT_SYMBOL, 179),
    (9, HUFFMAN_EMIT_SYMBOL, 179),
    (23, HUFFMAN_EMIT_SYMBOL, 179),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179),
    (2, HUFFMAN_EMIT_SYMBOL, 209),
    (9, HUFFMAN_EMIT_SYMBOL, 209),
    (23, HUFFMAN_EMIT_SYMBOL, 209),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209),

    # Node 114
    (3, HUFFMAN_EMIT_SYMBOL, 176),
    (6, HUFFMAN_EMIT_SYMBOL, 176),
    (10, HUFFMAN_EMIT_SYMBOL, 176),
    (15, HUFFMAN_EMIT_SYMBOL, 176),
    (24, HUFFMAN_EMIT_SYMBOL, 176),
    (31, HUFFMAN_EMIT_SYMBOL, 176),
    (41, HUFFMAN_EMIT_SYMBOL, 176),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 176),
    (3, HUFFMAN_EMIT_SYMBOL, 177),
    (6, HUFFMAN_EMIT_SYMBOL, 177),
    (10, HUFFMAN_EMIT_SYMBOL, 177),
    (15, HUFFMAN_EMIT_SYMBOL, 177),
    (24, HUFFMAN_EMIT_SYMBOL, 177),
    (31, HUFFMAN_EMIT_SYMBOL, 177),
    (41, HUFFMAN_EMIT_SYMBOL, 177),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 177),

    # Node 115
    (3, HUFFMAN_EMIT_SYMBOL, 179),
    (6, HUFFMAN_EMIT_SYMBOL, 179),
    (10, HUFFMAN_EMIT_SYMBOL, 179),
    (15, HUFFMAN_EMIT_SYMBOL, 179),
    (24, HUFFMAN_EMIT_SYMBOL, 179),
    (31, HUFFMAN_EMIT_SYMBOL, 179),
    (41, HUFFMAN_EMIT_SYMBOL, 179),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 179),
    (3, HUFFMAN_EMIT_SYMBOL, 209),
    (6, HUFFMAN_EMIT_SYMBOL, 209),
    (10, HUFFMAN_EMIT_SYMBOL, 209),
    (15, HUFFMAN_EMIT_SYMBOL, 209),
    (24, HUFFMAN_EMIT_SYMBOL, 209),
    (31, HUFFMAN_EMIT_SYMBOL, 209),
    (41, HUFFMAN_EMIT_SYMBOL, 209),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 209),

    # Node 116
    (2, HUFFMAN_EMIT_SYMBOL, 216),
    (9, HUFFMAN_EMIT_SYMBOL, 216),
    (23, HUFFMAN_EMIT_SYMBOL, 216),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216),
    (2, HUFFMAN_EMIT_SYMBOL, 217),
    (9, HUFFMAN_EMIT_SYMBOL, 217),
    (23, HUFFMAN_EMIT_SYMBOL, 217),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217),
    (2, HUFFMAN_EMIT_SYMBOL, 227),
    (9, HUFFMAN_EMIT_SYMBOL, 227),
    (23, HUFFMAN_EMIT_SYMBOL, 227),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227),
    (2, HUFFMAN_EMIT_SYMBOL, 229),
    (9, HUFFMAN_EMIT_SYMBOL, 229),
    (23, HUFFMAN_EMIT_SYMBOL, 229),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229),

    # Node 117
    (3, HUFFMAN_EMIT_SYMBOL, 216),
    (6, HUFFMAN_EMIT_SYMBOL, 216),
    (10, HUFFMAN_EMIT_SYMBOL, 216),
    (15, HUFFMAN_EMIT_SYMBOL, 216),
    (24, HUFFMAN_EMIT_SYMBOL, 216),
    (31, HUFFMAN_EMIT_SYMBOL, 216),
    (41, HUFFMAN_EMIT_SYMBOL, 216),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 216),
    (3, HUFFMAN_EMIT_SYMBOL, 217),
    (6, HUFFMAN_EMIT_SYMBOL, 217),
    (10, HUFFMAN_EMIT_SYMBOL, 217),
    (15, HUFFMAN_EMIT_SYMBOL, 217),
    (24, HUFFMAN_EMIT_SYMBOL, 217),
    (31, HUFFMAN_EMIT_SYMBOL, 217),
    (41, HUFFMAN_EMIT_SYMBOL, 217),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 217),

    # Node 118
    (3, HUFFMAN_EMIT_SYMBOL, 227),
    (6, HUFFMAN_EMIT_SYMBOL, 227),
    (10, HUFFMAN_EMIT_SYMBOL, 227),
    (15, HUFFMAN_EMIT_SYMBOL, 227),
    (24, HUFFMAN_EMIT_SYMBOL, 227),
    (31, HUFFMAN_EMIT_SYMBOL, 227),
    (41, HUFFMAN_EMIT_SYMBOL, 227),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 227),
    (3, HUFFMAN_EMIT_SYMBOL, 229),
    (6, HUFFMAN_EMIT_SYMBOL, 229),
    (10, HUFFMAN_EMIT_SYMBOL, 229),
    (15, HUFFMAN_EMIT_SYMBOL, 229),
    (24, HUFFMAN_EMIT_SYMBOL, 229),
    (31, HUFFMAN_EMIT_SYMBOL, 229),
    (41, HUFFMAN_EMIT_SYMBOL, 229),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 229),

    # Node 119
    (1, HUFFMAN_EMIT_SYMBOL, 230),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173),

    # Node 120
    (2, HUFFMAN_EMIT_SYMBOL, 230),
    (9, HUFFMAN_EMIT_SYMBOL, 230),
    (23, HUFFMAN_EMIT_SYMBOL, 230),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230),
    (1, HUFFMAN_EMIT_SYMBOL, 129),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129),
    (1, HUFFMAN_EMIT_SYMBOL, 132),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132),
    (1, HUFFMAN_EMIT_SYMBOL, 133),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133),
    (1, HUFFMAN_EMIT_SYMBOL, 134),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134),
    (1, HUFFMAN_EMIT_SYMBOL, 136),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136),
    (1, HUFFMAN_EMIT_SYMBOL, 146),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146),

    # Node 121
    (3, HUFFMAN_EMIT_SYMBOL, 230),
    (6, HUFFMAN_EMIT_SYMBOL, 230),
    (10, HUFFMAN_EMIT_SYMBOL, 230),
    (15, HUFFMAN_EMIT_SYMBOL, 230),
    (24, HUFFMAN_EMIT_SYMBOL, 230),
    (31, HUFFMAN_EMIT_SYMBOL, 230),
    (41, HUFFMAN_EMIT_SYMBOL, 230),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 230),
    (2, HUFFMAN_EMIT_SYMBOL, 129),
    (9, HUFFMAN_EMIT_SYMBOL, 129),
    (23, HUFFMAN_EMIT_SYMBOL, 129),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129),
    (2, HUFFMAN_EMIT_SYMBOL, 132),
    (9, HUFFMAN_EMIT_SYMBOL, 132),
    (23, HUFFMAN_EMIT_SYMBOL, 132),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132),

    # Node 122
    (3, HUFFMAN_EMIT_SYMBOL, 129),
    (6, HUFFMAN_EMIT_SYMBOL, 129),
    (10, HUFFMAN_EMIT_SYMBOL, 129),
    (15, HUFFMAN_EMIT_SYMBOL, 129),
    (24, HUFFMAN_EMIT_SYMBOL, 129),
    (31, HUFFMAN_EMIT_SYMBOL, 129),
    (41, HUFFMAN_EMIT_SYMBOL, 129),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 129),
    (3, HUFFMAN_EMIT_SYMBOL, 132),
    (6, HUFFMAN_EMIT_SYMBOL, 132),
    (10, HUFFMAN_EMIT_SYMBOL, 132),
    (15, HUFFMAN_EMIT_SYMBOL, 132),
    (24, HUFFMAN_EMIT_SYMBOL, 132),
    (31, HUFFMAN_EMIT_SYMBOL, 132),
    (41, HUFFMAN_EMIT_SYMBOL, 132),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 132),

    # Node 123
    (2, HUFFMAN_EMIT_SYMBOL, 133),
    (9, HUFFMAN_EMIT_SYMBOL, 133),
    (23, HUFFMAN_EMIT_SYMBOL, 133),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133),
    (2, HUFFMAN_EMIT_SYMBOL, 134),
    (9, HUFFMAN_EMIT_SYMBOL, 134),
    (23, HUFFMAN_EMIT_SYMBOL, 134),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134),
    (2, HUFFMAN_EMIT_SYMBOL, 136),
    (9, HUFFMAN_EMIT_SYMBOL, 136),
    (23, HUFFMAN_EMIT_SYMBOL, 136),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136),
    (2, HUFFMAN_EMIT_SYMBOL, 146),
    (9, HUFFMAN_EMIT_SYMBOL, 146),
    (23, HUFFMAN_EMIT_SYMBOL, 146),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146),

    # Node 124
    (3, HUFFMAN_EMIT_SYMBOL, 133),
    (6, HUFFMAN_EMIT_SYMBOL, 133),
    (10, HUFFMAN_EMIT_SYMBOL, 133),
    (15, HUFFMAN_EMIT_SYMBOL, 133),
    (24, HUFFMAN_EMIT_SYMBOL, 133),
    (31, HUFFMAN_EMIT_SYMBOL, 133),
    (41, HUFFMAN_EMIT_SYMBOL, 133),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 133),
    (3, HUFFMAN_EMIT_SYMBOL, 134),
    (6, HUFFMAN_EMIT_SYMBOL, 134),
    (10, HUFFMAN_EMIT_SYMBOL, 134),
    (15, HUFFMAN_EMIT_SYMBOL, 134),
    (24, HUFFMAN_EMIT_SYMBOL, 134),
    (31, HUFFMAN_EMIT_SYMBOL, 134),
    (41, HUFFMAN_EMIT_SYMBOL, 134),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 134),

    # Node 125
    (3, HUFFMAN_EMIT_SYMBOL, 136),
    (6, HUFFMAN_EMIT_SYMBOL, 136),
    (10, HUFFMAN_EMIT_SYMBOL, 136),
    (15, HUFFMAN_EMIT_SYMBOL, 136),
    (24, HUFFMAN_EMIT_SYMBOL, 136),
    (31, HUFFMAN_EMIT_SYMBOL, 136),
    (41, HUFFMAN_EMIT_SYMBOL, 136),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 136),
    (3, HUFFMAN_EMIT_SYMBOL, 146),
    (6, HUFFMAN_EMIT_SYMBOL, 146),
    (10, HUFFMAN_EMIT_SYMBOL, 146),
    (15, HUFFMAN_EMIT_SYMBOL, 146),
    (24, HUFFMAN_EMIT_SYMBOL, 146),
    (31, HUFFMAN_EMIT_SYMBOL, 146),
    (41, HUFFMAN_EMIT_SYMBOL, 146),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 146),

    # Node 126
    (1, HUFFMAN_EMIT_SYMBOL, 154),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154),
    (1, HUFFMAN_EMIT_SYMBOL, 156),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156),
    (1, HUFFMAN_EMIT_SYMBOL, 160),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160),
    (1, HUFFMAN_EMIT_SYMBOL, 163),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163),
    (1, HUFFMAN_EMIT_SYMBOL, 164),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164),
    (1, HUFFMAN_EMIT_SYMBOL, 169),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169),
    (1, HUFFMAN_EMIT_SYMBOL, 170),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170),
    (1, HUFFMAN_EMIT_SYMBOL, 173),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173),

    # Node 127
    (2, HUFFMAN_EMIT_SYMBOL, 154),
    (9, HUFFMAN_EMIT_SYMBOL, 154),
    (23, HUFFMAN_EMIT_SYMBOL, 154),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154),
    (2, HUFFMAN_EMIT_SYMBOL, 156),
    (9, HUFFMAN_EMIT_SYMBOL, 156),
    (23, HUFFMAN_EMIT_SYMBOL, 156),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156),
    (2, HUFFMAN_EMIT_SYMBOL, 160),
    (9, HUFFMAN_EMIT_SYMBOL, 160),
    (23, HUFFMAN_EMIT_SYMBOL, 160),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160),
    (2, HUFFMAN_EMIT_SYMBOL, 163),
    (9, HUFFMAN_EMIT_SYMBOL, 163),
    (23, HUFFMAN_EMIT_SYMBOL, 163),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163),

    # Node 128
    (3, HUFFMAN_EMIT_SYMBOL, 154),
    (6, HUFFMAN_EMIT_SYMBOL, 154),
    (10, HUFFMAN_EMIT_SYMBOL, 154),
    (15, HUFFMAN_EMIT_SYMBOL, 154),
    (24, HUFFMAN_EMIT_SYMBOL, 154),
    (31, HUFFMAN_EMIT_SYMBOL, 154),
    (41, HUFFMAN_EMIT_SYMBOL, 154),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 154),
    (3, HUFFMAN_EMIT_SYMBOL, 156),
    (6, HUFFMAN_EMIT_SYMBOL, 156),
    (10, HUFFMAN_EMIT_SYMBOL, 156),
    (15, HUFFMAN_EMIT_SYMBOL, 156),
    (24, HUFFMAN_EMIT_SYMBOL, 156),
    (31, HUFFMAN_EMIT_SYMBOL, 156),
    (41, HUFFMAN_EMIT_SYMBOL, 156),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 156),

    # Node 129
    (3, HUFFMAN_EMIT_SYMBOL, 160),
    (6, HUFFMAN_EMIT_SYMBOL, 160),
    (10, HUFFMAN_EMIT_SYMBOL, 160),
    (15, HUFFMAN_EMIT_SYMBOL, 160),
    (24, HUFFMAN_EMIT_SYMBOL, 160),
    (31, HUFFMAN_EMIT_SYMBOL, 160),
    (41, HUFFMAN_EMIT_SYMBOL, 160),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 160),
    (3, HUFFMAN_EMIT_SYMBOL, 163),
    (6, HUFFMAN_EMIT_SYMBOL, 163),
    (10, HUFFMAN_EMIT_SYMBOL, 163),
    (15, HUFFMAN_EMIT_SYMBOL, 163),
    (24, HUFFMAN_EMIT_SYMBOL, 163),
    (31, HUFFMAN_EMIT_SYMBOL, 163),
    (41, HUFFMAN_EMIT_SYMBOL, 163),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 163),

    # Node 130
    (2, HUFFMAN_EMIT_SYMBOL, 164),
    (9, HUFFMAN_EMIT_SYMBOL, 164),
    (23, HUFFMAN_EMIT_SYMBOL, 164),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164),
    (2, HUFFMAN_EMIT_SYMBOL, 169),
    (9, HUFFMAN_EMIT_SYMBOL, 169),
    (23, HUFFMAN_EMIT_SYMBOL, 169),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169),
    (2, HUFFMAN_EMIT_SYMBOL, 170),
    (9, HUFFMAN_EMIT_SYMBOL, 170),
    (23, HUFFMAN_EMIT_SYMBOL, 170),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170),
    (2, HUFFMAN_EMIT_SYMBOL, 173),
    (9, HUFFMAN_EMIT_SYMBOL, 173),
    (23, HUFFMAN_EMIT_SYMBOL, 173),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173),

    # Node 131
    (3, HUFFMAN_EMIT_SYMBOL, 164),
    (6, HUFFMAN_EMIT_SYMBOL, 164),
    (10, HUFFMAN_EMIT_SYMBOL, 164),
    (15, HUFFMAN_EMIT_SYMBOL, 164),
    (24, HUFFMAN_EMIT_SYMBOL, 164),
    (31, HUFFMAN_EMIT_SYMBOL, 164),
    (41, HUFFMAN_EMIT_SYMBOL, 164),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 164),
    (3, HUFFMAN_EMIT_SYMBOL, 169),
    (6, HUFFMAN_EMIT_SYMBOL, 169),
    (10, HUFFMAN_EMIT_SYMBOL, 169),
    (15, HUFFMAN_EMIT_SYMBOL, 169),
    (24, HUFFMAN_EMIT_SYMBOL, 169),
    (31, HUFFMAN_EMIT_SYMBOL, 169),
    (41, HUFFMAN_EMIT_SYMBOL, 169),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 169),

    # Node 132
    (3, HUFFMAN_EMIT_SYMBOL, 170),
    (6, HUFFMAN_EMIT_SYMBOL, 170),
    (10, HUFFMAN_EMIT_SYMBOL, 170),
    (15, HUFFMAN_EMIT_SYMBOL, 170),
    (24, HUFFMAN_EMIT_SYMBOL, 170),
    (31, HUFFMAN_EMIT_SYMBOL, 170),
    (41, HUFFMAN_EMIT_SYMBOL, 170),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 170),
    (3, HUFFMAN_EMIT_SYMBOL, 173),
    (6, HUFFMAN_EMIT_SYMBOL, 173),
    (10, HUFFMAN_EMIT_SYMBOL, 173),
    (15, HUFFMAN_EMIT_SYMBOL, 173),
    (24, HUFFMAN_EMIT_SYMBOL, 173),
    (31, HUFFMAN_EMIT_SYMBOL, 173),
    (41, HUFFMAN_EMIT_SYMBOL, 173),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 173),

    # Node 133
    (137, 0, 0),
    (138, 0, 0),
    (140, 0, 0),
    (141, 0, 0),
    (144, 0, 0),
    (145, 0, 0),
    (147, 0, 0),
    (150, 0, 0),
    (156, 0, 0),
    (159, 0, 0),
    (163, 0, 0),
    (166, 0, 0),
    (171, 0, 0),
    (174, 0, 0),
    (181, 0, 0),
    (190, 0, 0),

    # Node 134
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233),
    (148, 0, 0),
    (149, 0, 0),
    (151, 0, 0),
    (152, 0, 0),

    # Node 135
    (1, HUFFMAN_EMIT_SYMBOL, 178),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178),
    (1, HUFFMAN_EMIT_SYMBOL, 181),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181),
    (1, HUFFMAN_EMIT_SYMBOL, 185),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185),
    (1, HUFFMAN_EMIT_SYMBOL, 186),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186),
    (1, HUFFMAN_EMIT_SYMBOL, 187),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187),
    (1, HUFFMAN_EMIT_SYMBOL, 189),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189),
    (1, HUFFMAN_EMIT_SYMBOL, 190),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190),
    (1, HUFFMAN_EMIT_SYMBOL, 196),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196),

    # Node 136
    (2, HUFFMAN_EMIT_SYMBOL, 178),
    (9, HUFFMAN_EMIT_SYMBOL, 178),
    (23, HUFFMAN_EMIT_SYMBOL, 178),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178),
    (2, HUFFMAN_EMIT_SYMBOL, 181),
    (9, HUFFMAN_EMIT_SYMBOL, 181),
    (23, HUFFMAN_EMIT_SYMBOL, 181),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181),
    (2, HUFFMAN_EMIT_SYMBOL, 185),
    (9, HUFFMAN_EMIT_SYMBOL, 185),
    (23, HUFFMAN_EMIT_SYMBOL, 185),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185),
    (2, HUFFMAN_EMIT_SYMBOL, 186),
    (9, HUFFMAN_EMIT_SYMBOL, 186),
    (23, HUFFMAN_EMIT_SYMBOL, 186),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186),

    # Node 137
    (3, HUFFMAN_EMIT_SYMBOL, 178),
    (6, HUFFMAN_EMIT_SYMBOL, 178),
    (10, HUFFMAN_EMIT_SYMBOL, 178),
    (15, HUFFMAN_EMIT_SYMBOL, 178),
    (24, HUFFMAN_EMIT_SYMBOL, 178),
    (31, HUFFMAN_EMIT_SYMBOL, 178),
    (41, HUFFMAN_EMIT_SYMBOL, 178),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 178),
    (3, HUFFMAN_EMIT_SYMBOL, 181),
    (6, HUFFMAN_EMIT_SYMBOL, 181),
    (10, HUFFMAN_EMIT_SYMBOL, 181),
    (15, HUFFMAN_EMIT_SYMBOL, 181),
    (24, HUFFMAN_EMIT_SYMBOL, 181),
    (31, HUFFMAN_EMIT_SYMBOL, 181),
    (41, HUFFMAN_EMIT_SYMBOL, 181),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 181),

    # Node 138
    (3, HUFFMAN_EMIT_SYMBOL, 185),
    (6, HUFFMAN_EMIT_SYMBOL, 185),
    (10, HUFFMAN_EMIT_SYMBOL, 185),
    (15, HUFFMAN_EMIT_SYMBOL, 185),
    (24, HUFFMAN_EMIT_SYMBOL, 185),
    (31, HUFFMAN_EMIT_SYMBOL, 185),
    (41, HUFFMAN_EMIT_SYMBOL, 185),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 185),
    (3, HUFFMAN_EMIT_SYMBOL, 186),
    (6, HUFFMAN_EMIT_SYMBOL, 186),
    (10, HUFFMAN_EMIT_SYMBOL, 186),
    (15, HUFFMAN_EMIT_SYMBOL, 186),
    (24, HUFFMAN_EMIT_SYMBOL, 186),
    (31, HUFFMAN_EMIT_SYMBOL, 186),
    (41, HUFFMAN_EMIT_SYMBOL, 186),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 186),

    # Node 139
    (2, HUFFMAN_EMIT_SYMBOL, 187),
    (9, HUFFMAN_EMIT_SYMBOL, 187),
    (23, HUFFMAN_EMIT_SYMBOL, 187),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187),
    (2, HUFFMAN_EMIT_SYMBOL, 189),
    (9, HUFFMAN_EMIT_SYMBOL, 189),
    (23, HUFFMAN_EMIT_SYMBOL, 189),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189),
    (2, HUFFMAN_EMIT_SYMBOL, 190),
    (9, HUFFMAN_EMIT_SYMBOL, 190),
    (23, HUFFMAN_EMIT_SYMBOL, 190),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190),
    (2, HUFFMAN_EMIT_SYMBOL, 196),
    (9, HUFFMAN_EMIT_SYMBOL, 196),
    (23, HUFFMAN_EMIT_SYMBOL, 196),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196),

    # Node 140
    (3, HUFFMAN_EMIT_SYMBOL, 187),
    (6, HUFFMAN_EMIT_SYMBOL, 187),
    (10, HUFFMAN_EMIT_SYMBOL, 187),
    (15, HUFFMAN_EMIT_SYMBOL, 187),
    (24, HUFFMAN_EMIT_SYMBOL, 187),
    (31, HUFFMAN_EMIT_SYMBOL, 187),
    (41, HUFFMAN_EMIT_SYMBOL, 187),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 187),
    (3, HUFFMAN_EMIT_SYMBOL, 189),
    (6, HUFFMAN_EMIT_SYMBOL, 189),
    (10, HUFFMAN_EMIT_SYMBOL, 189),
    (15, HUFFMAN_EMIT_SYMBOL, 189),
    (24, HUFFMAN_EMIT_SYMBOL, 189),
    (31, HUFFMAN_EMIT_SYMBOL, 189),
    (41, HUFFMAN_EMIT_SYMBOL, 189),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 189),

    # Node 141
    (3, HUFFMAN_EMIT_SYMBOL, 190),
    (6, HUFFMAN_EMIT_SYMBOL, 190),
    (10, HUFFMAN_EMIT_SYMBOL, 190),
    (15, HUFFMAN_EMIT_SYMBOL, 190),
    (24, HUFFMAN_EMIT_SYMBOL, 190),
    (31, HUFFMAN_EMIT_SYMBOL, 190),
    (41, HUFFMAN_EMIT_SYMBOL, 190),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 190),
    (3, HUFFMAN_EMIT_SYMBOL, 196),
    (6, HUFFMAN_EMIT_SYMBOL, 196),
    (10, HUFFMAN_EMIT_SYMBOL, 196),
    (15, HUFFMAN_EMIT_SYMBOL, 196),
    (24, HUFFMAN_EMIT_SYMBOL, 196),
    (31, HUFFMAN_EMIT_SYMBOL, 196),
    (41, HUFFMAN_EMIT_SYMBOL, 196),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 196),

    # Node 142
    (1, HUFFMAN_EMIT_SYMBOL, 198),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198),
    (1, HUFFMAN_EMIT_SYMBOL, 228),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228),
    (1, HUFFMAN_EMIT_SYMBOL, 232),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232),
    (1, HUFFMAN_EMIT_SYMBOL, 233),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143),

    # Node 143
    (2, HUFFMAN_EMIT_SYMBOL, 198),
    (9, HUFFMAN_EMIT_SYMBOL, 198),
    (23, HUFFMAN_EMIT_SYMBOL, 198),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198),
    (2, HUFFMAN_EMIT_SYMBOL, 228),
    (9, HUFFMAN_EMIT_SYMBOL, 228),
    (23, HUFFMAN_EMIT_SYMBOL, 228),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228),
    (2, HUFFMAN_EMIT_SYMBOL, 232),
    (9, HUFFMAN_EMIT_SYMBOL, 232),
    (23, HUFFMAN_EMIT_SYMBOL, 232),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232),
    (2, HUFFMAN_EMIT_SYMBOL, 233),
    (9, HUFFMAN_EMIT_SYMBOL, 233),
    (23, HUFFMAN_EMIT_SYMBOL, 233),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233),

    # Node 144
    (3, HUFFMAN_EMIT_SYMBOL, 198),
    (6, HUFFMAN_EMIT_SYMBOL, 198),
    (10, HUFFMAN_EMIT_SYMBOL, 198),
    (15, HUFFMAN_EMIT_SYMBOL, 198),
    (24, HUFFMAN_EMIT_SYMBOL, 198),
    (31, HUFFMAN_EMIT_SYMBOL, 198),
    (41, HUFFMAN_EMIT_SYMBOL, 198),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 198),
    (3, HUFFMAN_EMIT_SYMBOL, 228),
    (6, HUFFMAN_EMIT_SYMBOL, 228),
    (10, HUFFMAN_EMIT_SYMBOL, 228),
    (15, HUFFMAN_EMIT_SYMBOL, 228),
    (24, HUFFMAN_EMIT_SYMBOL, 228),
    (31, HUFFMAN_EMIT_SYMBOL, 228),
    (41, HUFFMAN_EMIT_SYMBOL, 228),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 228),

    # Node 145
    (3, HUFFMAN_EMIT_SYMBOL, 232),
    (6, HUFFMAN_EMIT_SYMBOL, 232),
    (10, HUFFMAN_EMIT_SYMBOL, 232),
    (15, HUFFMAN_EMIT_SYMBOL, 232),
    (24, HUFFMAN_EMIT_SYMBOL, 232),
    (31, HUFFMAN_EMIT_SYMBOL, 232),
    (41, HUFFMAN_EMIT_SYMBOL, 232),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 232),
    (3, HUFFMAN_EMIT_SYMBOL, 233),
    (6, HUFFMAN_EMIT_SYMBOL, 233),
    (10, HUFFMAN_EMIT_SYMBOL, 233),
    (15, HUFFMAN_EMIT_SYMBOL, 233),
    (24, HUFFMAN_EMIT_SYMBOL, 233),
    (31, HUFFMAN_EMIT_SYMBOL, 233),
    (41, HUFFMAN_EMIT_SYMBOL, 233),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 233),

    # Node 146
    (1, HUFFMAN_EMIT_SYMBOL, 1),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1),
    (1, HUFFMAN_EMIT_SYMBOL, 135),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135),
    (1, HUFFMAN_EMIT_SYMBOL, 137),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137),
    (1, HUFFMAN_EMIT_SYMBOL, 138),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138),
    (1, HUFFMAN_EMIT_SYMBOL, 139),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139),
    (1, HUFFMAN_EMIT_SYMBOL, 140),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140),
    (1, HUFFMAN_EMIT_SYMBOL, 141),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141),
    (1, HUFFMAN_EMIT_SYMBOL, 143),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143),

    # Node 147
    (2, HUFFMAN_EMIT_SYMBOL, 1),
    (9, HUFFMAN_EMIT_SYMBOL, 1),
    (23, HUFFMAN_EMIT_SYMBOL, 1),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1),
    (2, HUFFMAN_EMIT_SYMBOL, 135),
    (9, HUFFMAN_EMIT_SYMBOL, 135),
    (23, HUFFMAN_EMIT_SYMBOL, 135),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135),
    (2, HUFFMAN_EMIT_SYMBOL, 137),
    (9, HUFFMAN_EMIT_SYMBOL, 137),
    (23, HUFFMAN_EMIT_SYMBOL, 137),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137),
    (2, HUFFMAN_EMIT_SYMBOL, 138),
    (9, HUFFMAN_EMIT_SYMBOL, 138),
    (23, HUFFMAN_EMIT_SYMBOL, 138),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138),

    # Node 148
    (3, HUFFMAN_EMIT_SYMBOL, 1),
    (6, HUFFMAN_EMIT_SYMBOL, 1),
    (10, HUFFMAN_EMIT_SYMBOL, 1),
    (15, HUFFMAN_EMIT_SYMBOL, 1),
    (24, HUFFMAN_EMIT_SYMBOL, 1),
    (31, HUFFMAN_EMIT_SYMBOL, 1),
    (41, HUFFMAN_EMIT_SYMBOL, 1),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 1),
    (3, HUFFMAN_EMIT_SYMBOL, 135),
    (6, HUFFMAN_EMIT_SYMBOL, 135),
    (10, HUFFMAN_EMIT_SYMBOL, 135),
    (15, HUFFMAN_EMIT_SYMBOL, 135),
    (24, HUFFMAN_EMIT_SYMBOL, 135),
    (31, HUFFMAN_EMIT_SYMBOL, 135),
    (41, HUFFMAN_EMIT_SYMBOL, 135),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 135),

    # Node 149
    (3, HUFFMAN_EMIT_SYMBOL, 137),
    (6, HUFFMAN_EMIT_SYMBOL, 137),
    (10, HUFFMAN_EMIT_SYMBOL, 137),
    (15, HUFFMAN_EMIT_SYMBOL, 137),
    (24, HUFFMAN_EMIT_SYMBOL, 137),
    (31, HUFFMAN_EMIT_SYMBOL, 137),
    (41, HUFFMAN_EMIT_SYMBOL, 137),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 137),
    (3, HUFFMAN_EMIT_SYMBOL, 138),
    (6, HUFFMAN_EMIT_SYMBOL, 138),
    (10, HUFFMAN_EMIT_SYMBOL, 138),
    (15, HUFFMAN_EMIT_SYMBOL, 138),
    (24, HUFFMAN_EMIT_SYMBOL, 138),
    (31, HUFFMAN_EMIT_SYMBOL, 138),
    (41, HUFFMAN_EMIT_SYMBOL, 138),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 138),

    # Node 150
    (2, HUFFMAN_EMIT_SYMBOL, 139),
    (9, HUFFMAN_EMIT_SYMBOL, 139),
    (23, HUFFMAN_EMIT_SYMBOL, 139),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139),
    (2, HUFFMAN_EMIT_SYMBOL, 140),
    (9, HUFFMAN_EMIT_SYMBOL, 140),
    (23, HUFFMAN_EMIT_SYMBOL, 140),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140),
    (2, HUFFMAN_EMIT_SYMBOL, 141),
    (9, HUFFMAN_EMIT_SYMBOL, 141),
    (23, HUFFMAN_EMIT_SYMBOL, 141),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141),
    (2, HUFFMAN_EMIT_SYMBOL, 143),
    (9, HUFFMAN_EMIT_SYMBOL, 143),
    (23, HUFFMAN_EMIT_SYMBOL, 143),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143),

    # Node 151
    (3, HUFFMAN_EMIT_SYMBOL, 139),
    (6, HUFFMAN_EMIT_SYMBOL, 139),
    (10, HUFFMAN_EMIT_SYMBOL, 139),
    (15, HUFFMAN_EMIT_SYMBOL, 139),
    (24, HUFFMAN_EMIT_SYMBOL, 139),
    (31, HUFFMAN_EMIT_SYMBOL, 139),
    (41, HUFFMAN_EMIT_SYMBOL, 139),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 139),
    (3, HUFFMAN_EMIT_SYMBOL, 140),
    (6, HUFFMAN_EMIT_SYMBOL, 140),
    (10, HUFFMAN_EMIT_SYMBOL, 140),
    (15, HUFFMAN_EMIT_SYMBOL, 140),
    (24, HUFFMAN_EMIT_SYMBOL, 140),
    (31, HUFFMAN_EMIT_SYMBOL, 140),
    (41, HUFFMAN_EMIT_SYMBOL, 140),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 140),

    # Node 152
    (3, HUFFMAN_EMIT_SYMBOL, 141),
    (6, HUFFMAN_EMIT_SYMBOL, 141),
    (10, HUFFMAN_EMIT_SYMBOL, 141),
    (15, HUFFMAN_EMIT_SYMBOL, 141),
    (24, HUFFMAN_EMIT_SYMBOL, 141),
    (31, HUFFMAN_EMIT_SYMBOL, 141),
    (41, HUFFMAN_EMIT_SYMBOL, 141),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 141),
    (3, HUFFMAN_EMIT_SYMBOL, 143),
    (6, HUFFMAN_EMIT_SYMBOL, 143),
    (10, HUFFMAN_EMIT_SYMBOL, 143),
    (15, HUFFMAN_EMIT_SYMBOL, 143),
    (24, HUFFMAN_EMIT_SYMBOL, 143),
    (31, HUFFMAN_EMIT_SYMBOL, 143),
    (41, HUFFMAN_EMIT_SYMBOL, 143),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 143),

    # Node 153
    (157, 0, 0),
    (158, 0, 0),
    (160, 0, 0),
    (161, 0, 0),
    (164, 0, 0),
    (165, 0, 0),
    (167, 0, 0),
    (168, 0, 0),
    (172, 0, 0),
    (173, 0, 0),
    (175, 0, 0),
    (177, 0, 0),
    (182, 0, 0),
    (185, 0, 0),
    (191, 0, 0),
    (207, 0, 0),

    # Node 154
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183),

    # Node 155
    (1, HUFFMAN_EMIT_SYMBOL, 147),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147),
    (1, HUFFMAN_EMIT_SYMBOL, 149),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149),
    (1, HUFFMAN_EMIT_SYMBOL, 150),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150),
    (1, HUFFMAN_EMIT_SYMBOL, 151),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151),
    (1, HUFFMAN_EMIT_SYMBOL, 152),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152),
    (1, HUFFMAN_EMIT_SYMBOL, 155),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155),
    (1, HUFFMAN_EMIT_SYMBOL, 157),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157),
    (1, HUFFMAN_EMIT_SYMBOL, 158),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158),

    # Node 156
    (2, HUFFMAN_EMIT_SYMBOL, 147),
    (9, HUFFMAN_EMIT_SYMBOL, 147),
    (23, HUFFMAN_EMIT_SYMBOL, 147),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147),
    (2, HUFFMAN_EMIT_SYMBOL, 149),
    (9, HUFFMAN_EMIT_SYMBOL, 149),
    (23, HUFFMAN_EMIT_SYMBOL, 149),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149),
    (2, HUFFMAN_EMIT_SYMBOL, 150),
    (9, HUFFMAN_EMIT_SYMBOL, 150),
    (23, HUFFMAN_EMIT_SYMBOL, 150),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150),
    (2, HUFFMAN_EMIT_SYMBOL, 151),
    (9, HUFFMAN_EMIT_SYMBOL, 151),
    (23, HUFFMAN_EMIT_SYMBOL, 151),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151),

    # Node 157
    (3, HUFFMAN_EMIT_SYMBOL, 147),
    (6, HUFFMAN_EMIT_SYMBOL, 147),
    (10, HUFFMAN_EMIT_SYMBOL, 147),
    (15, HUFFMAN_EMIT_SYMBOL, 147),
    (24, HUFFMAN_EMIT_SYMBOL, 147),
    (31, HUFFMAN_EMIT_SYMBOL, 147),
    (41, HUFFMAN_EMIT_SYMBOL, 147),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 147),
    (3, HUFFMAN_EMIT_SYMBOL, 149),
    (6, HUFFMAN_EMIT_SYMBOL, 149),
    (10, HUFFMAN_EMIT_SYMBOL, 149),
    (15, HUFFMAN_EMIT_SYMBOL, 149),
    (24, HUFFMAN_EMIT_SYMBOL, 149),
    (31, HUFFMAN_EMIT_SYMBOL, 149),
    (41, HUFFMAN_EMIT_SYMBOL, 149),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 149),

    # Node 158
    (3, HUFFMAN_EMIT_SYMBOL, 150),
    (6, HUFFMAN_EMIT_SYMBOL, 150),
    (10, HUFFMAN_EMIT_SYMBOL, 150),
    (15, HUFFMAN_EMIT_SYMBOL, 150),
    (24, HUFFMAN_EMIT_SYMBOL, 150),
    (31, HUFFMAN_EMIT_SYMBOL, 150),
    (41, HUFFMAN_EMIT_SYMBOL, 150),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 150),
    (3, HUFFMAN_EMIT_SYMBOL, 151),
    (6, HUFFMAN_EMIT_SYMBOL, 151),
    (10, HUFFMAN_EMIT_SYMBOL, 151),
    (15, HUFFMAN_EMIT_SYMBOL, 151),
    (24, HUFFMAN_EMIT_SYMBOL, 151),
    (31, HUFFMAN_EMIT_SYMBOL, 151),
    (41, HUFFMAN_EMIT_SYMBOL, 151),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 151),

    # Node 159
    (2, HUFFMAN_EMIT_SYMBOL, 152),
    (9, HUFFMAN_EMIT_SYMBOL, 152),
    (23, HUFFMAN_EMIT_SYMBOL, 152),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152),
    (2, HUFFMAN_EMIT_SYMBOL, 155),
    (9, HUFFMAN_EMIT_SYMBOL, 155),
    (23, HUFFMAN_EMIT_SYMBOL, 155),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155),
    (2, HUFFMAN_EMIT_SYMBOL, 157),
    (9, HUFFMAN_EMIT_SYMBOL, 157),
    (23, HUFFMAN_EMIT_SYMBOL, 157),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157),
    (2, HUFFMAN_EMIT_SYMBOL, 158),
    (9, HUFFMAN_EMIT_SYMBOL, 158),
    (23, HUFFMAN_EMIT_SYMBOL, 158),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158),

    # Node 160
    (3, HUFFMAN_EMIT_SYMBOL, 152),
    (6, HUFFMAN_EMIT_SYMBOL, 152),
    (10, HUFFMAN_EMIT_SYMBOL, 152),
    (15, HUFFMAN_EMIT_SYMBOL, 152),
    (24, HUFFMAN_EMIT_SYMBOL, 152),
    (31, HUFFMAN_EMIT_SYMBOL, 152),
    (41, HUFFMAN_EMIT_SYMBOL, 152),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 152),
    (3, HUFFMAN_EMIT_SYMBOL, 155),
    (6, HUFFMAN_EMIT_SYMBOL, 155),
    (10, HUFFMAN_EMIT_SYMBOL, 155),
    (15, HUFFMAN_EMIT_SYMBOL, 155),
    (24, HUFFMAN_EMIT_SYMBOL, 155),
    (31, HUFFMAN_EMIT_SYMBOL, 155),
    (41, HUFFMAN_EMIT_SYMBOL, 155),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 155),

    # Node 161
    (3, HUFFMAN_EMIT_SYMBOL, 157),
    (6, HUFFMAN_EMIT_SYMBOL, 157),
    (10, HUFFMAN_EMIT_SYMBOL, 157),
    (15, HUFFMAN_EMIT_SYMBOL, 157),
    (24, HUFFMAN_EMIT_SYMBOL, 157),
    (31, HUFFMAN_EMIT_SYMBOL, 157),
    (41, HUFFMAN_EMIT_SYMBOL, 157),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 157),
    (3, HUFFMAN_EMIT_SYMBOL, 158),
    (6, HUFFMAN_EMIT_SYMBOL, 158),
    (10, HUFFMAN_EMIT_SYMBOL, 158),
    (15, HUFFMAN_EMIT_SYMBOL, 158),
    (24, HUFFMAN_EMIT_SYMBOL, 158),
    (31, HUFFMAN_EMIT_SYMBOL, 158),
    (41, HUFFMAN_EMIT_SYMBOL, 158),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 158),

    # Node 162
    (1, HUFFMAN_EMIT_SYMBOL, 165),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165),
    (1, HUFFMAN_EMIT_SYMBOL, 166),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166),
    (1, HUFFMAN_EMIT_SYMBOL, 168),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168),
    (1, HUFFMAN_EMIT_SYMBOL, 174),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174),
    (1, HUFFMAN_EMIT_SYMBOL, 175),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175),
    (1, HUFFMAN_EMIT_SYMBOL, 180),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180),
    (1, HUFFMAN_EMIT_SYMBOL, 182),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182),
    (1, HUFFMAN_EMIT_SYMBOL, 183),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183),

    # Node 163
    (2, HUFFMAN_EMIT_SYMBOL, 165),
    (9, HUFFMAN_EMIT_SYMBOL, 165),
    (23, HUFFMAN_EMIT_SYMBOL, 165),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165),
    (2, HUFFMAN_EMIT_SYMBOL, 166),
    (9, HUFFMAN_EMIT_SYMBOL, 166),
    (23, HUFFMAN_EMIT_SYMBOL, 166),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166),
    (2, HUFFMAN_EMIT_SYMBOL, 168),
    (9, HUFFMAN_EMIT_SYMBOL, 168),
    (23, HUFFMAN_EMIT_SYMBOL, 168),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168),
    (2, HUFFMAN_EMIT_SYMBOL, 174),
    (9, HUFFMAN_EMIT_SYMBOL, 174),
    (23, HUFFMAN_EMIT_SYMBOL, 174),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174),

    # Node 164
    (3, HUFFMAN_EMIT_SYMBOL, 165),
    (6, HUFFMAN_EMIT_SYMBOL, 165),
    (10, HUFFMAN_EMIT_SYMBOL, 165),
    (15, HUFFMAN_EMIT_SYMBOL, 165),
    (24, HUFFMAN_EMIT_SYMBOL, 165),
    (31, HUFFMAN_EMIT_SYMBOL, 165),
    (41, HUFFMAN_EMIT_SYMBOL, 165),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 165),
    (3, HUFFMAN_EMIT_SYMBOL, 166),
    (6, HUFFMAN_EMIT_SYMBOL, 166),
    (10, HUFFMAN_EMIT_SYMBOL, 166),
    (15, HUFFMAN_EMIT_SYMBOL, 166),
    (24, HUFFMAN_EMIT_SYMBOL, 166),
    (31, HUFFMAN_EMIT_SYMBOL, 166),
    (41, HUFFMAN_EMIT_SYMBOL, 166),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 166),

    # Node 165
    (3, HUFFMAN_EMIT_SYMBOL, 168),
    (6, HUFFMAN_EMIT_SYMBOL, 168),
    (10, HUFFMAN_EMIT_SYMBOL, 168),
    (15, HUFFMAN_EMIT_SYMBOL, 168),
    (24, HUFFMAN_EMIT_SYMBOL, 168),
    (31, HUFFMAN_EMIT_SYMBOL, 168),
    (41, HUFFMAN_EMIT_SYMBOL, 168),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 168),
    (3, HUFFMAN_EMIT_SYMBOL, 174),
    (6, HUFFMAN_EMIT_SYMBOL, 174),
    (10, HUFFMAN_EMIT_SYMBOL, 174),
    (15, HUFFMAN_EMIT_SYMBOL, 174),
    (24, HUFFMAN_EMIT_SYMBOL, 174),
    (31, HUFFMAN_EMIT_SYMBOL, 174),
    (41, HUFFMAN_EMIT_SYMBOL, 174),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 174),

    # Node 166
    (2, HUFFMAN_EMIT_SYMBOL, 175),
    (9, HUFFMAN_EMIT_SYMBOL, 175),
    (23, HUFFMAN_EMIT_SYMBOL, 175),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175),
    (2, HUFFMAN_EMIT_SYMBOL, 180),
    (9, HUFFMAN_EMIT_SYMBOL, 180),
    (23, HUFFMAN_EMIT_SYMBOL, 180),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180),
    (2, HUFFMAN_EMIT_SYMBOL, 182),
    (9, HUFFMAN_EMIT_SYMBOL, 182),
    (23, HUFFMAN_EMIT_SYMBOL, 182),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182),
    (2, HUFFMAN_EMIT_SYMBOL, 183),
    (9, HUFFMAN_EMIT_SYMBOL, 183),
    (23, HUFFMAN_EMIT_SYMBOL, 183),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183),

    # Node 167
    (3, HUFFMAN_EMIT_SYMBOL, 175),
    (6, HUFFMAN_EMIT_SYMBOL, 175),
    (10, HUFFMAN_EMIT_SYMBOL, 175),
    (15, HUFFMAN_EMIT_SYMBOL, 175),
    (24, HUFFMAN_EMIT_SYMBOL, 175),
    (31, HUFFMAN_EMIT_SYMBOL, 175),
    (41, HUFFMAN_EMIT_SYMBOL, 175),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 175),
    (3, HUFFMAN_EMIT_SYMBOL, 180),
    (6, HUFFMAN_EMIT_SYMBOL, 180),
    (10, HUFFMAN_EMIT_SYMBOL, 180),
    (15, HUFFMAN_EMIT_SYMBOL, 180),
    (24, HUFFMAN_EMIT_SYMBOL, 180),
    (31, HUFFMAN_EMIT_SYMBOL, 180),
    (41, HUFFMAN_EMIT_SYMBOL, 180),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 180),

    # Node 168
    (3, HUFFMAN_EMIT_SYMBOL, 182),
    (6, HUFFMAN_EMIT_SYMBOL, 182),
    (10, HUFFMAN_EMIT_SYMBOL, 182),
    (15, HUFFMAN_EMIT_SYMBOL, 182),
    (24, HUFFMAN_EMIT_SYMBOL, 182),
    (31, HUFFMAN_EMIT_SYMBOL, 182),
    (41, HUFFMAN_EMIT_SYMBOL, 182),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 182),
    (3, HUFFMAN_EMIT_SYMBOL, 183),
    (6, HUFFMAN_EMIT_SYMBOL, 183),
    (10, HUFFMAN_EMIT_SYMBOL, 183),
    (15, HUFFMAN_EMIT_SYMBOL, 183),
    (24, HUFFMAN_EMIT_SYMBOL, 183),
    (31, HUFFMAN_EMIT_SYMBOL, 183),
    (41, HUFFMAN_EMIT_SYMBOL, 183),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 183),

    # Node 169
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239),
    (176, 0, 0),
    (178, 0, 0),
    (179, 0, 0),
    (183, 0, 0),
    (184, 0, 0),
    (186, 0, 0),
    (187, 0, 0),
    (192, 0, 0),
    (199, 0, 0),
    (208, 0, 0),
    (223, 0, 0),

    # Node 170
    (1, HUFFMAN_EMIT_SYMBOL, 188),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188),
    (1, HUFFMAN_EMIT_SYMBOL, 191),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191),
    (1, HUFFMAN_EMIT_SYMBOL, 197),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197),
    (1, HUFFMAN_EMIT_SYMBOL, 231),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231),
    (1, HUFFMAN_EMIT_SYMBOL, 239),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159),

    # Node 171
    (2, HUFFMAN_EMIT_SYMBOL, 188),
    (9, HUFFMAN_EMIT_SYMBOL, 188),
    (23, HUFFMAN_EMIT_SYMBOL, 188),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188),
    (2, HUFFMAN_EMIT_SYMBOL, 191),
    (9, HUFFMAN_EMIT_SYMBOL, 191),
    (23, HUFFMAN_EMIT_SYMBOL, 191),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191),
    (2, HUFFMAN_EMIT_SYMBOL, 197),
    (9, HUFFMAN_EMIT_SYMBOL, 197),
    (23, HUFFMAN_EMIT_SYMBOL, 197),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197),
    (2, HUFFMAN_EMIT_SYMBOL, 231),
    (9, HUFFMAN_EMIT_SYMBOL, 231),
    (23, HUFFMAN_EMIT_SYMBOL, 231),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231),

    # Node 172
    (3, HUFFMAN_EMIT_SYMBOL, 188),
    (6, HUFFMAN_EMIT_SYMBOL, 188),
    (10, HUFFMAN_EMIT_SYMBOL, 188),
    (15, HUFFMAN_EMIT_SYMBOL, 188),
    (24, HUFFMAN_EMIT_SYMBOL, 188),
    (31, HUFFMAN_EMIT_SYMBOL, 188),
    (41, HUFFMAN_EMIT_SYMBOL, 188),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 188),
    (3, HUFFMAN_EMIT_SYMBOL, 191),
    (6, HUFFMAN_EMIT_SYMBOL, 191),
    (10, HUFFMAN_EMIT_SYMBOL, 191),
    (15, HUFFMAN_EMIT_SYMBOL, 191),
    (24, HUFFMAN_EMIT_SYMBOL, 191),
    (31, HUFFMAN_EMIT_SYMBOL, 191),
    (41, HUFFMAN_EMIT_SYMBOL, 191),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 191),

    # Node 173
    (3, HUFFMAN_EMIT_SYMBOL, 197),
    (6, HUFFMAN_EMIT_SYMBOL, 197),
    (10, HUFFMAN_EMIT_SYMBOL, 197),
    (15, HUFFMAN_EMIT_SYMBOL, 197),
    (24, HUFFMAN_EMIT_SYMBOL, 197),
    (31, HUFFMAN_EMIT_SYMBOL, 197),
    (41, HUFFMAN_EMIT_SYMBOL, 197),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 197),
    (3, HUFFMAN_EMIT_SYMBOL, 231),
    (6, HUFFMAN_EMIT_SYMBOL, 231),
    (10, HUFFMAN_EMIT_SYMBOL, 231),
    (15, HUFFMAN_EMIT_SYMBOL, 231),
    (24, HUFFMAN_EMIT_SYMBOL, 231),
    (31, HUFFMAN_EMIT_SYMBOL, 231),
    (41, HUFFMAN_EMIT_SYMBOL, 231),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 231),

    # Node 174
    (2, HUFFMAN_EMIT_SYMBOL, 239),
    (9, HUFFMAN_EMIT_SYMBOL, 239),
    (23, HUFFMAN_EMIT_SYMBOL, 239),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239),
    (1, HUFFMAN_EMIT_SYMBOL, 9),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9),
    (1, HUFFMAN_EMIT_SYMBOL, 142),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142),
    (1, HUFFMAN_EMIT_SYMBOL, 144),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144),
    (1, HUFFMAN_EMIT_SYMBOL, 145),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145),
    (1, HUFFMAN_EMIT_SYMBOL, 148),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148),
    (1, HUFFMAN_EMIT_SYMBOL, 159),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159),

    # Node 175
    (3, HUFFMAN_EMIT_SYMBOL, 239),
    (6, HUFFMAN_EMIT_SYMBOL, 239),
    (10, HUFFMAN_EMIT_SYMBOL, 239),
    (15, HUFFMAN_EMIT_SYMBOL, 239),
    (24, HUFFMAN_EMIT_SYMBOL, 239),
    (31, HUFFMAN_EMIT_SYMBOL, 239),
    (41, HUFFMAN_EMIT_SYMBOL, 239),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 239),
    (2, HUFFMAN_EMIT_SYMBOL, 9),
    (9, HUFFMAN_EMIT_SYMBOL, 9),
    (23, HUFFMAN_EMIT_SYMBOL, 9),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9),
    (2, HUFFMAN_EMIT_SYMBOL, 142),
    (9, HUFFMAN_EMIT_SYMBOL, 142),
    (23, HUFFMAN_EMIT_SYMBOL, 142),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142),

    # Node 176
    (3, HUFFMAN_EMIT_SYMBOL, 9),
    (6, HUFFMAN_EMIT_SYMBOL, 9),
    (10, HUFFMAN_EMIT_SYMBOL, 9),
    (15, HUFFMAN_EMIT_SYMBOL, 9),
    (24, HUFFMAN_EMIT_SYMBOL, 9),
    (31, HUFFMAN_EMIT_SYMBOL, 9),
    (41, HUFFMAN_EMIT_SYMBOL, 9),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 9),
    (3, HUFFMAN_EMIT_SYMBOL, 142),
    (6, HUFFMAN_EMIT_SYMBOL, 142),
    (10, HUFFMAN_EMIT_SYMBOL, 142),
    (15, HUFFMAN_EMIT_SYMBOL, 142),
    (24, HUFFMAN_EMIT_SYMBOL, 142),
    (31, HUFFMAN_EMIT_SYMBOL, 142),
    (41, HUFFMAN_EMIT_SYMBOL, 142),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 142),

    # Node 177
    (2, HUFFMAN_EMIT_SYMBOL, 144),
    (9, HUFFMAN_EMIT_SYMBOL, 144),
    (23, HUFFMAN_EMIT_SYMBOL, 144),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144),
    (2, HUFFMAN_EMIT_SYMBOL, 145),
    (9, HUFFMAN_EMIT_SYMBOL, 145),
    (23, HUFFMAN_EMIT_SYMBOL, 145),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145),
    (2, HUFFMAN_EMIT_SYMBOL, 148),
    (9, HUFFMAN_EMIT_SYMBOL, 148),
    (23, HUFFMAN_EMIT_SYMBOL, 148),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148),
    (2, HUFFMAN_EMIT_SYMBOL, 159),
    (9, HUFFMAN_EMIT_SYMBOL, 159),
    (23, HUFFMAN_EMIT_SYMBOL, 159),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159),

    # Node 178
    (3, HUFFMAN_EMIT_SYMBOL, 144),
    (6, HUFFMAN_EMIT_SYMBOL, 144),
    (10, HUFFMAN_EMIT_SYMBOL, 144),
    (15, HUFFMAN_EMIT_SYMBOL, 144),
    (24, HUFFMAN_EMIT_SYMBOL, 144),
    (31, HUFFMAN_EMIT_SYMBOL, 144),
    (41, HUFFMAN_EMIT_SYMBOL, 144),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 144),
    (3, HUFFMAN_EMIT_SYMBOL, 145),
    (6, HUFFMAN_EMIT_SYMBOL, 145),
    (10, HUFFMAN_EMIT_SYMBOL, 145),
    (15, HUFFMAN_EMIT_SYMBOL, 145),
    (24, HUFFMAN_EMIT_SYMBOL, 145),
    (31, HUFFMAN_EMIT_SYMBOL, 145),
    (41, HUFFMAN_EMIT_SYMBOL, 145),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 145),

    # Node 179
    (3, HUFFMAN_EMIT_SYMBOL, 148),
    (6, HUFFMAN_EMIT_SYMBOL, 148),
    (10, HUFFMAN_EMIT_SYMBOL, 148),
    (15, HUFFMAN_EMIT_SYMBOL, 148),
    (24, HUFFMAN_EMIT_SYMBOL, 148),
    (31, HUFFMAN_EMIT_SYMBOL, 148),
    (41, HUFFMAN_EMIT_SYMBOL, 148),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 148),
    (3, HUFFMAN_EMIT_SYMBOL, 159),
    (6, HUFFMAN_EMIT_SYMBOL, 159),
    (10, HUFFMAN_EMIT_SYMBOL, 159),
    (15, HUFFMAN_EMIT_SYMBOL, 159),
    (24, HUFFMAN_EMIT_SYMBOL, 159),
    (31, HUFFMAN_EMIT_SYMBOL, 159),
    (41, HUFFMAN_EMIT_SYMBOL, 159),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 159),

    # Node 180
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237),
    (188, 0, 0),
    (189, 0, 0),
    (193, 0, 0),
    (196, 0, 0),
    (200, 0, 0),
    (203, 0, 0),
    (209, 0, 0),
    (216, 0, 0),
    (224, 0, 0),
    (238, 0, 0),

    # Node 181
    (1, HUFFMAN_EMIT_SYMBOL, 171),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171),
    (1, HUFFMAN_EMIT_SYMBOL, 206),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206),
    (1, HUFFMAN_EMIT_SYMBOL, 215),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215),
    (1, HUFFMAN_EMIT_SYMBOL, 225),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225),
    (1, HUFFMAN_EMIT_SYMBOL, 236),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236),
    (1, HUFFMAN_EMIT_SYMBOL, 237),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235),

    # Node 182
    (2, HUFFMAN_EMIT_SYMBOL, 171),
    (9, HUFFMAN_EMIT_SYMBOL, 171),
    (23, HUFFMAN_EMIT_SYMBOL, 171),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171),
    (2, HUFFMAN_EMIT_SYMBOL, 206),
    (9, HUFFMAN_EMIT_SYMBOL, 206),
    (23, HUFFMAN_EMIT_SYMBOL, 206),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206),
    (2, HUFFMAN_EMIT_SYMBOL, 215),
    (9, HUFFMAN_EMIT_SYMBOL, 215),
    (23, HUFFMAN_EMIT_SYMBOL, 215),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215),
    (2, HUFFMAN_EMIT_SYMBOL, 225),
    (9, HUFFMAN_EMIT_SYMBOL, 225),
    (23, HUFFMAN_EMIT_SYMBOL, 225),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225),

    # Node 183
    (3, HUFFMAN_EMIT_SYMBOL, 171),
    (6, HUFFMAN_EMIT_SYMBOL, 171),
    (10, HUFFMAN_EMIT_SYMBOL, 171),
    (15, HUFFMAN_EMIT_SYMBOL, 171),
    (24, HUFFMAN_EMIT_SYMBOL, 171),
    (31, HUFFMAN_EMIT_SYMBOL, 171),
    (41, HUFFMAN_EMIT_SYMBOL, 171),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 171),
    (3, HUFFMAN_EMIT_SYMBOL, 206),
    (6, HUFFMAN_EMIT_SYMBOL, 206),
    (10, HUFFMAN_EMIT_SYMBOL, 206),
    (15, HUFFMAN_EMIT_SYMBOL, 206),
    (24, HUFFMAN_EMIT_SYMBOL, 206),
    (31, HUFFMAN_EMIT_SYMBOL, 206),
    (41, HUFFMAN_EMIT_SYMBOL, 206),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 206),

    # Node 184
    (3, HUFFMAN_EMIT_SYMBOL, 215),
    (6, HUFFMAN_EMIT_SYMBOL, 215),
    (10, HUFFMAN_EMIT_SYMBOL, 215),
    (15, HUFFMAN_EMIT_SYMBOL, 215),
    (24, HUFFMAN_EMIT_SYMBOL, 215),
    (31, HUFFMAN_EMIT_SYMBOL, 215),
    (41, HUFFMAN_EMIT_SYMBOL, 215),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 215),
    (3, HUFFMAN_EMIT_SYMBOL, 225),
    (6, HUFFMAN_EMIT_SYMBOL, 225),
    (10, HUFFMAN_EMIT_SYMBOL, 225),
    (15, HUFFMAN_EMIT_SYMBOL, 225),
    (24, HUFFMAN_EMIT_SYMBOL, 225),
    (31, HUFFMAN_EMIT_SYMBOL, 225),
    (41, HUFFMAN_EMIT_SYMBOL, 225),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 225),

    # Node 185
    (2, HUFFMAN_EMIT_SYMBOL, 236),
    (9, HUFFMAN_EMIT_SYMBOL, 236),
    (23, HUFFMAN_EMIT_SYMBOL, 236),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236),
    (2, HUFFMAN_EMIT_SYMBOL, 237),
    (9, HUFFMAN_EMIT_SYMBOL, 237),
    (23, HUFFMAN_EMIT_SYMBOL, 237),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237),
    (1, HUFFMAN_EMIT_SYMBOL, 199),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199),
    (1, HUFFMAN_EMIT_SYMBOL, 207),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207),
    (1, HUFFMAN_EMIT_SYMBOL, 234),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234),
    (1, HUFFMAN_EMIT_SYMBOL, 235),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235),

    # Node 186
    (3, HUFFMAN_EMIT_SYMBOL, 236),
    (6, HUFFMAN_EMIT_SYMBOL, 236),
    (10, HUFFMAN_EMIT_SYMBOL, 236),
    (15, HUFFMAN_EMIT_SYMBOL, 236),
    (24, HUFFMAN_EMIT_SYMBOL, 236),
    (31, HUFFMAN_EMIT_SYMBOL, 236),
    (41, HUFFMAN_EMIT_SYMBOL, 236),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 236),
    (3, HUFFMAN_EMIT_SYMBOL, 237),
    (6, HUFFMAN_EMIT_SYMBOL, 237),
    (10, HUFFMAN_EMIT_SYMBOL, 237),
    (15, HUFFMAN_EMIT_SYMBOL, 237),
    (24, HUFFMAN_EMIT_SYMBOL, 237),
    (31, HUFFMAN_EMIT_SYMBOL, 237),
    (41, HUFFMAN_EMIT_SYMBOL, 237),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 237),

    # Node 187
    (2, HUFFMAN_EMIT_SYMBOL, 199),
    (9, HUFFMAN_EMIT_SYMBOL, 199),
    (23, HUFFMAN_EMIT_SYMBOL, 199),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199),
    (2, HUFFMAN_EMIT_SYMBOL, 207),
    (9, HUFFMAN_EMIT_SYMBOL, 207),
    (23, HUFFMAN_EMIT_SYMBOL, 207),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207),
    (2, HUFFMAN_EMIT_SYMBOL, 234),
    (9, HUFFMAN_EMIT_SYMBOL, 234),
    (23, HUFFMAN_EMIT_SYMBOL, 234),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234),
    (2, HUFFMAN_EMIT_SYMBOL, 235),
    (9, HUFFMAN_EMIT_SYMBOL, 235),
    (23, HUFFMAN_EMIT_SYMBOL, 235),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235),

    # Node 188
    (3, HUFFMAN_EMIT_SYMBOL, 199),
    (6, HUFFMAN_EMIT_SYMBOL, 199),
    (10, HUFFMAN_EMIT_SYMBOL, 199),
    (15, HUFFMAN_EMIT_SYMBOL, 199),
    (24, HUFFMAN_EMIT_SYMBOL, 199),
    (31, HUFFMAN_EMIT_SYMBOL, 199),
    (41, HUFFMAN_EMIT_SYMBOL, 199),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 199),
    (3, HUFFMAN_EMIT_SYMBOL, 207),
    (6, HUFFMAN_EMIT_SYMBOL, 207),
    (10, HUFFMAN_EMIT_SYMBOL, 207),
    (15, HUFFMAN_EMIT_SYMBOL, 207),
    (24, HUFFMAN_EMIT_SYMBOL, 207),
    (31, HUFFMAN_EMIT_SYMBOL, 207),
    (41, HUFFMAN_EMIT_SYMBOL, 207),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 207),

    # Node 189
    (3, HUFFMAN_EMIT_SYMBOL, 234),
    (6, HUFFMAN_EMIT_SYMBOL, 234),
    (10, HUFFMAN_EMIT_SYMBOL, 234),
    (15, HUFFMAN_EMIT_SYMBOL, 234),
    (24, HUFFMAN_EMIT_SYMBOL, 234),
    (31, HUFFMAN_EMIT_SYMBOL, 234),
    (41, HUFFMAN_EMIT_SYMBOL, 234),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 234),
    (3, HUFFMAN_EMIT_SYMBOL, 235),
    (6, HUFFMAN_EMIT_SYMBOL, 235),
    (10, HUFFMAN_EMIT_SYMBOL, 235),
    (15, HUFFMAN_EMIT_SYMBOL, 235),
    (24, HUFFMAN_EMIT_SYMBOL, 235),
    (31, HUFFMAN_EMIT_SYMBOL, 235),
    (41, HUFFMAN_EMIT_SYMBOL, 235),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 235),

    # Node 190
    (194, 0, 0),
    (195, 0, 0),
    (197, 0, 0),
    (198, 0, 0),
    (201, 0, 0),
    (202, 0, 0),
    (204, 0, 0),
    (205, 0, 0),
    (210, 0, 0),
    (213, 0, 0),
    (217, 0, 0),
    (220, 0, 0),
    (225, 0, 0),
    (231, 0, 0),
    (239, 0, 0),
    (246, 0, 0),

    # Node 191
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255),
    (206, 0, 0),

    # Node 192
    (1, HUFFMAN_EMIT_SYMBOL, 192),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192),
    (1, HUFFMAN_EMIT_SYMBOL, 193),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193),
    (1, HUFFMAN_EMIT_SYMBOL, 200),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200),
    (1, HUFFMAN_EMIT_SYMBOL, 201),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201),
    (1, HUFFMAN_EMIT_SYMBOL, 202),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202),
    (1, HUFFMAN_EMIT_SYMBOL, 205),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205),
    (1, HUFFMAN_EMIT_SYMBOL, 210),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210),
    (1, HUFFMAN_EMIT_SYMBOL, 213),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213),

    # Node 193
    (2, HUFFMAN_EMIT_SYMBOL, 192),
    (9, HUFFMAN_EMIT_SYMBOL, 192),
    (23, HUFFMAN_EMIT_SYMBOL, 192),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192),
    (2, HUFFMAN_EMIT_SYMBOL, 193),
    (9, HUFFMAN_EMIT_SYMBOL, 193),
    (23, HUFFMAN_EMIT_SYMBOL, 193),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193),
    (2, HUFFMAN_EMIT_SYMBOL, 200),
    (9, HUFFMAN_EMIT_SYMBOL, 200),
    (23, HUFFMAN_EMIT_SYMBOL, 200),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200),
    (2, HUFFMAN_EMIT_SYMBOL, 201),
    (9, HUFFMAN_EMIT_SYMBOL, 201),
    (23, HUFFMAN_EMIT_SYMBOL, 201),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201),

    # Node 194
    (3, HUFFMAN_EMIT_SYMBOL, 192),
    (6, HUFFMAN_EMIT_SYMBOL, 192),
    (10, HUFFMAN_EMIT_SYMBOL, 192),
    (15, HUFFMAN_EMIT_SYMBOL, 192),
    (24, HUFFMAN_EMIT_SYMBOL, 192),
    (31, HUFFMAN_EMIT_SYMBOL, 192),
    (41, HUFFMAN_EMIT_SYMBOL, 192),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 192),
    (3, HUFFMAN_EMIT_SYMBOL, 193),
    (6, HUFFMAN_EMIT_SYMBOL, 193),
    (10, HUFFMAN_EMIT_SYMBOL, 193),
    (15, HUFFMAN_EMIT_SYMBOL, 193),
    (24, HUFFMAN_EMIT_SYMBOL, 193),
    (31, HUFFMAN_EMIT_SYMBOL, 193),
    (41, HUFFMAN_EMIT_SYMBOL, 193),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 193),

    # Node 195
    (3, HUFFMAN_EMIT_SYMBOL, 200),
    (6, HUFFMAN_EMIT_SYMBOL, 200),
    (10, HUFFMAN_EMIT_SYMBOL, 200),
    (15, HUFFMAN_EMIT_SYMBOL, 200),
    (24, HUFFMAN_EMIT_SYMBOL, 200),
    (31, HUFFMAN_EMIT_SYMBOL, 200),
    (41, HUFFMAN_EMIT_SYMBOL, 200),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 200),
    (3, HUFFMAN_EMIT_SYMBOL, 201),
    (6, HUFFMAN_EMIT_SYMBOL, 201),
    (10, HUFFMAN_EMIT_SYMBOL, 201),
    (15, HUFFMAN_EMIT_SYMBOL, 201),
    (24, HUFFMAN_EMIT_SYMBOL, 201),
    (31, HUFFMAN_EMIT_SYMBOL, 201),
    (41, HUFFMAN_EMIT_SYMBOL, 201),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 201),

    # Node 196
    (2, HUFFMAN_EMIT_SYMBOL, 202),
    (9, HUFFMAN_EMIT_SYMBOL, 202),
    (23, HUFFMAN_EMIT_SYMBOL, 202),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202),
    (2, HUFFMAN_EMIT_SYMBOL, 205),
    (9, HUFFMAN_EMIT_SYMBOL, 205),
    (23, HUFFMAN_EMIT_SYMBOL, 205),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205),
    (2, HUFFMAN_EMIT_SYMBOL, 210),
    (9, HUFFMAN_EMIT_SYMBOL, 210),
    (23, HUFFMAN_EMIT_SYMBOL, 210),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210),
    (2, HUFFMAN_EMIT_SYMBOL, 213),
    (9, HUFFMAN_EMIT_SYMBOL, 213),
    (23, HUFFMAN_EMIT_SYMBOL, 213),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213),

    # Node 197
    (3, HUFFMAN_EMIT_SYMBOL, 202),
    (6, HUFFMAN_EMIT_SYMBOL, 202),
    (10, HUFFMAN_EMIT_SYMBOL, 202),
    (15, HUFFMAN_EMIT_SYMBOL, 202),
    (24, HUFFMAN_EMIT_SYMBOL, 202),
    (31, HUFFMAN_EMIT_SYMBOL, 202),
    (41, HUFFMAN_EMIT_SYMBOL, 202),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 202),
    (3, HUFFMAN_EMIT_SYMBOL, 205),
    (6, HUFFMAN_EMIT_SYMBOL, 205),
    (10, HUFFMAN_EMIT_SYMBOL, 205),
    (15, HUFFMAN_EMIT_SYMBOL, 205),
    (24, HUFFMAN_EMIT_SYMBOL, 205),
    (31, HUFFMAN_EMIT_SYMBOL, 205),
    (41, HUFFMAN_EMIT_SYMBOL, 205),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 205),

    # Node 198
    (3, HUFFMAN_EMIT_SYMBOL, 210),
    (6, HUFFMAN_EMIT_SYMBOL, 210),
    (10, HUFFMAN_EMIT_SYMBOL, 210),
    (15, HUFFMAN_EMIT_SYMBOL, 210),
    (24, HUFFMAN_EMIT_SYMBOL, 210),
    (31, HUFFMAN_EMIT_SYMBOL, 210),
    (41, HUFFMAN_EMIT_SYMBOL, 210),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 210),
    (3, HUFFMAN_EMIT_SYMBOL, 213),
    (6, HUFFMAN_EMIT_SYMBOL, 213),
    (10, HUFFMAN_EMIT_SYMBOL, 213),
    (15, HUFFMAN_EMIT_SYMBOL, 213),
    (24, HUFFMAN_EMIT_SYMBOL, 213),
    (31, HUFFMAN_EMIT_SYMBOL, 213),
    (41, HUFFMAN_EMIT_SYMBOL, 213),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 213),

    # Node 199
    (1, HUFFMAN_EMIT_SYMBOL, 218),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218),
    (1, HUFFMAN_EMIT_SYMBOL, 219),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219),
    (1, HUFFMAN_EMIT_SYMBOL, 238),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238),
    (1, HUFFMAN_EMIT_SYMBOL, 240),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240),
    (1, HUFFMAN_EMIT_SYMBOL, 242),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242),
    (1, HUFFMAN_EMIT_SYMBOL, 243),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243),
    (1, HUFFMAN_EMIT_SYMBOL, 255),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204),

    # Node 200
    (2, HUFFMAN_EMIT_SYMBOL, 218),
    (9, HUFFMAN_EMIT_SYMBOL, 218),
    (23, HUFFMAN_EMIT_SYMBOL, 218),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218),
    (2, HUFFMAN_EMIT_SYMBOL, 219),
    (9, HUFFMAN_EMIT_SYMBOL, 219),
    (23, HUFFMAN_EMIT_SYMBOL, 219),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219),
    (2, HUFFMAN_EMIT_SYMBOL, 238),
    (9, HUFFMAN_EMIT_SYMBOL, 238),
    (23, HUFFMAN_EMIT_SYMBOL, 238),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238),
    (2, HUFFMAN_EMIT_SYMBOL, 240),
    (9, HUFFMAN_EMIT_SYMBOL, 240),
    (23, HUFFMAN_EMIT_SYMBOL, 240),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240),

    # Node 201
    (3, HUFFMAN_EMIT_SYMBOL, 218),
    (6, HUFFMAN_EMIT_SYMBOL, 218),
    (10, HUFFMAN_EMIT_SYMBOL, 218),
    (15, HUFFMAN_EMIT_SYMBOL, 218),
    (24, HUFFMAN_EMIT_SYMBOL, 218),
    (31, HUFFMAN_EMIT_SYMBOL, 218),
    (41, HUFFMAN_EMIT_SYMBOL, 218),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 218),
    (3, HUFFMAN_EMIT_SYMBOL, 219),
    (6, HUFFMAN_EMIT_SYMBOL, 219),
    (10, HUFFMAN_EMIT_SYMBOL, 219),
    (15, HUFFMAN_EMIT_SYMBOL, 219),
    (24, HUFFMAN_EMIT_SYMBOL, 219),
    (31, HUFFMAN_EMIT_SYMBOL, 219),
    (41, HUFFMAN_EMIT_SYMBOL, 219),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 219),

    # Node 202
    (3, HUFFMAN_EMIT_SYMBOL, 238),
    (6, HUFFMAN_EMIT_SYMBOL, 238),
    (10, HUFFMAN_EMIT_SYMBOL, 238),
    (15, HUFFMAN_EMIT_SYMBOL, 238),
    (24, HUFFMAN_EMIT_SYMBOL, 238),
    (31, HUFFMAN_EMIT_SYMBOL, 238),
    (41, HUFFMAN_EMIT_SYMBOL, 238),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 238),
    (3, HUFFMAN_EMIT_SYMBOL, 240),
    (6, HUFFMAN_EMIT_SYMBOL, 240),
    (10, HUFFMAN_EMIT_SYMBOL, 240),
    (15, HUFFMAN_EMIT_SYMBOL, 240),
    (24, HUFFMAN_EMIT_SYMBOL, 240),
    (31, HUFFMAN_EMIT_SYMBOL, 240),
    (41, HUFFMAN_EMIT_SYMBOL, 240),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 240),

    # Node 203
    (2, HUFFMAN_EMIT_SYMBOL, 242),
    (9, HUFFMAN_EMIT_SYMBOL, 242),
    (23, HUFFMAN_EMIT_SYMBOL, 242),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242),
    (2, HUFFMAN_EMIT_SYMBOL, 243),
    (9, HUFFMAN_EMIT_SYMBOL, 243),
    (23, HUFFMAN_EMIT_SYMBOL, 243),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243),
    (2, HUFFMAN_EMIT_SYMBOL, 255),
    (9, HUFFMAN_EMIT_SYMBOL, 255),
    (23, HUFFMAN_EMIT_SYMBOL, 255),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255),
    (1, HUFFMAN_EMIT_SYMBOL, 203),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203),
    (1, HUFFMAN_EMIT_SYMBOL, 204),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204),

    # Node 204
    (3, HUFFMAN_EMIT_SYMBOL, 242),
    (6, HUFFMAN_EMIT_SYMBOL, 242),
    (10, HUFFMAN_EMIT_SYMBOL, 242),
    (15, HUFFMAN_EMIT_SYMBOL, 242),
    (24, HUFFMAN_EMIT_SYMBOL, 242),
    (31, HUFFMAN_EMIT_SYMBOL, 242),
    (41, HUFFMAN_EMIT_SYMBOL, 242),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 242),
    (3, HUFFMAN_EMIT_SYMBOL, 243),
    (6, HUFFMAN_EMIT_SYMBOL, 243),
    (10, HUFFMAN_EMIT_SYMBOL, 243),
    (15, HUFFMAN_EMIT_SYMBOL, 243),
    (24, HUFFMAN_EMIT_SYMBOL, 243),
    (31, HUFFMAN_EMIT_SYMBOL, 243),
    (41, HUFFMAN_EMIT_SYMBOL, 243),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 243),

    # Node 205
    (3, HUFFMAN_EMIT_SYMBOL, 255),
    (6, HUFFMAN_EMIT_SYMBOL, 255),
    (10, HUFFMAN_EMIT_SYMBOL, 255),
    (15, HUFFMAN_EMIT_SYMBOL, 255),
    (24, HUFFMAN_EMIT_SYMBOL, 255),
    (31, HUFFMAN_EMIT_SYMBOL, 255),
    (41, HUFFMAN_EMIT_SYMBOL, 255),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 255),
    (2, HUFFMAN_EMIT_SYMBOL, 203),
    (9, HUFFMAN_EMIT_SYMBOL, 203),
    (23, HUFFMAN_EMIT_SYMBOL, 203),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203),
    (2, HUFFMAN_EMIT_SYMBOL, 204),
    (9, HUFFMAN_EMIT_SYMBOL, 204),
    (23, HUFFMAN_EMIT_SYMBOL, 204),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204),

    # Node 206
    (3, HUFFMAN_EMIT_SYMBOL, 203),
    (6, HUFFMAN_EMIT_SYMBOL, 203),
    (10, HUFFMAN_EMIT_SYMBOL, 203),
    (15, HUFFMAN_EMIT_SYMBOL, 203),
    (24, HUFFMAN_EMIT_SYMBOL, 203),
    (31, HUFFMAN_EMIT_SYMBOL, 203),
    (41, HUFFMAN_EMIT_SYMBOL, 203),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 203),
    (3, HUFFMAN_EMIT_SYMBOL, 204),
    (6, HUFFMAN_EMIT_SYMBOL, 204),
    (10, HUFFMAN_EMIT_SYMBOL, 204),
    (15, HUFFMAN_EMIT_SYMBOL, 204),
    (24, HUFFMAN_EMIT_SYMBOL, 204),
    (31, HUFFMAN_EMIT_SYMBOL, 204),
    (41, HUFFMAN_EMIT_SYMBOL, 204),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 204),

    # Node 207
    (211, 0, 0),
    (212, 0, 0),
    (214, 0, 0),
    (215, 0, 0),
    (218, 0, 0),
    (219, 0, 0),
    (221, 0, 0),
    (222, 0, 0),
    (226, 0, 0),
    (228, 0, 0),
    (232, 0, 0),
    (235, 0, 0),
    (240, 0, 0),
    (243, 0, 0),
    (247, 0, 0),
    (250, 0, 0),

    # Node 208
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253),

    # Node 209
    (1, HUFFMAN_EMIT_SYMBOL, 211),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211),
    (1, HUFFMAN_EMIT_SYMBOL, 212),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212),
    (1, HUFFMAN_EMIT_SYMBOL, 214),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214),
    (1, HUFFMAN_EMIT_SYMBOL, 221),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221),
    (1, HUFFMAN_EMIT_SYMBOL, 222),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222),
    (1, HUFFMAN_EMIT_SYMBOL, 223),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223),
    (1, HUFFMAN_EMIT_SYMBOL, 241),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241),
    (1, HUFFMAN_EMIT_SYMBOL, 244),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244),

    # Node 210
    (2, HUFFMAN_EMIT_SYMBOL, 211),
    (9, HUFFMAN_EMIT_SYMBOL, 211),
    (23, HUFFMAN_EMIT_SYMBOL, 211),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211),
    (2, HUFFMAN_EMIT_SYMBOL, 212),
    (9, HUFFMAN_EMIT_SYMBOL, 212),
    (23, HUFFMAN_EMIT_SYMBOL, 212),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212),
    (2, HUFFMAN_EMIT_SYMBOL, 214),
    (9, HUFFMAN_EMIT_SYMBOL, 214),
    (23, HUFFMAN_EMIT_SYMBOL, 214),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214),
    (2, HUFFMAN_EMIT_SYMBOL, 221),
    (9, HUFFMAN_EMIT_SYMBOL, 221),
    (23, HUFFMAN_EMIT_SYMBOL, 221),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221),

    # Node 211
    (3, HUFFMAN_EMIT_SYMBOL, 211),
    (6, HUFFMAN_EMIT_SYMBOL, 211),
    (10, HUFFMAN_EMIT_SYMBOL, 211),
    (15, HUFFMAN_EMIT_SYMBOL, 211),
    (24, HUFFMAN_EMIT_SYMBOL, 211),
    (31, HUFFMAN_EMIT_SYMBOL, 211),
    (41, HUFFMAN_EMIT_SYMBOL, 211),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 211),
    (3, HUFFMAN_EMIT_SYMBOL, 212),
    (6, HUFFMAN_EMIT_SYMBOL, 212),
    (10, HUFFMAN_EMIT_SYMBOL, 212),
    (15, HUFFMAN_EMIT_SYMBOL, 212),
    (24, HUFFMAN_EMIT_SYMBOL, 212),
    (31, HUFFMAN_EMIT_SYMBOL, 212),
    (41, HUFFMAN_EMIT_SYMBOL, 212),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 212),

    # Node 212
    (3, HUFFMAN_EMIT_SYMBOL, 214),
    (6, HUFFMAN_EMIT_SYMBOL, 214),
    (10, HUFFMAN_EMIT_SYMBOL, 214),
    (15, HUFFMAN_EMIT_SYMBOL, 214),
    (24, HUFFMAN_EMIT_SYMBOL, 214),
    (31, HUFFMAN_EMIT_SYMBOL, 214),
    (41, HUFFMAN_EMIT_SYMBOL, 214),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 214),
    (3, HUFFMAN_EMIT_SYMBOL, 221),
    (6, HUFFMAN_EMIT_SYMBOL, 221),
    (10, HUFFMAN_EMIT_SYMBOL, 221),
    (15, HUFFMAN_EMIT_SYMBOL, 221),
    (24, HUFFMAN_EMIT_SYMBOL, 221),
    (31, HUFFMAN_EMIT_SYMBOL, 221),
    (41, HUFFMAN_EMIT_SYMBOL, 221),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 221),

    # Node 213
    (2, HUFFMAN_EMIT_SYMBOL, 222),
    (9, HUFFMAN_EMIT_SYMBOL, 222),
    (23, HUFFMAN_EMIT_SYMBOL, 222),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222),
    (2, HUFFMAN_EMIT_SYMBOL, 223),
    (9, HUFFMAN_EMIT_SYMBOL, 223),
    (23, HUFFMAN_EMIT_SYMBOL, 223),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223),
    (2, HUFFMAN_EMIT_SYMBOL, 241),
    (9, HUFFMAN_EMIT_SYMBOL, 241),
    (23, HUFFMAN_EMIT_SYMBOL, 241),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241),
    (2, HUFFMAN_EMIT_SYMBOL, 244),
    (9, HUFFMAN_EMIT_SYMBOL, 244),
    (23, HUFFMAN_EMIT_SYMBOL, 244),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244),

    # Node 214
    (3, HUFFMAN_EMIT_SYMBOL, 222),
    (6, HUFFMAN_EMIT_SYMBOL, 222),
    (10, HUFFMAN_EMIT_SYMBOL, 222),
    (15, HUFFMAN_EMIT_SYMBOL, 222),
    (24, HUFFMAN_EMIT_SYMBOL, 222),
    (31, HUFFMAN_EMIT_SYMBOL, 222),
    (41, HUFFMAN_EMIT_SYMBOL, 222),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 222),
    (3, HUFFMAN_EMIT_SYMBOL, 223),
    (6, HUFFMAN_EMIT_SYMBOL, 223),
    (10, HUFFMAN_EMIT_SYMBOL, 223),
    (15, HUFFMAN_EMIT_SYMBOL, 223),
    (24, HUFFMAN_EMIT_SYMBOL, 223),
    (31, HUFFMAN_EMIT_SYMBOL, 223),
    (41, HUFFMAN_EMIT_SYMBOL, 223),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 223),

    # Node 215
    (3, HUFFMAN_EMIT_SYMBOL, 241),
    (6, HUFFMAN_EMIT_SYMBOL, 241),
    (10, HUFFMAN_EMIT_SYMBOL, 241),
    (15, HUFFMAN_EMIT_SYMBOL, 241),
    (24, HUFFMAN_EMIT_SYMBOL, 241),
    (31, HUFFMAN_EMIT_SYMBOL, 241),
    (41, HUFFMAN_EMIT_SYMBOL, 241),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 241),
    (3, HUFFMAN_EMIT_SYMBOL, 244),
    (6, HUFFMAN_EMIT_SYMBOL, 244),
    (10, HUFFMAN_EMIT_SYMBOL, 244),
    (15, HUFFMAN_EMIT_SYMBOL, 244),
    (24, HUFFMAN_EMIT_SYMBOL, 244),
    (31, HUFFMAN_EMIT_SYMBOL, 244),
    (41, HUFFMAN_EMIT_SYMBOL, 244),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 244),

    # Node 216
    (1, HUFFMAN_EMIT_SYMBOL, 245),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245),
    (1, HUFFMAN_EMIT_SYMBOL, 246),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246),
    (1, HUFFMAN_EMIT_SYMBOL, 247),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247),
    (1, HUFFMAN_EMIT_SYMBOL, 248),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248),
    (1, HUFFMAN_EMIT_SYMBOL, 250),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250),
    (1, HUFFMAN_EMIT_SYMBOL, 251),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251),
    (1, HUFFMAN_EMIT_SYMBOL, 252),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252),
    (1, HUFFMAN_EMIT_SYMBOL, 253),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253),

    # Node 217
    (2, HUFFMAN_EMIT_SYMBOL, 245),
    (9, HUFFMAN_EMIT_SYMBOL, 245),
    (23, HUFFMAN_EMIT_SYMBOL, 245),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245),
    (2, HUFFMAN_EMIT_SYMBOL, 246),
    (9, HUFFMAN_EMIT_SYMBOL, 246),
    (23, HUFFMAN_EMIT_SYMBOL, 246),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246),
    (2, HUFFMAN_EMIT_SYMBOL, 247),
    (9, HUFFMAN_EMIT_SYMBOL, 247),
    (23, HUFFMAN_EMIT_SYMBOL, 247),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247),
    (2, HUFFMAN_EMIT_SYMBOL, 248),
    (9, HUFFMAN_EMIT_SYMBOL, 248),
    (23, HUFFMAN_EMIT_SYMBOL, 248),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248),

    # Node 218
    (3, HUFFMAN_EMIT_SYMBOL, 245),
    (6, HUFFMAN_EMIT_SYMBOL, 245),
    (10, HUFFMAN_EMIT_SYMBOL, 245),
    (15, HUFFMAN_EMIT_SYMBOL, 245),
    (24, HUFFMAN_EMIT_SYMBOL, 245),
    (31, HUFFMAN_EMIT_SYMBOL, 245),
    (41, HUFFMAN_EMIT_SYMBOL, 245),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 245),
    (3, HUFFMAN_EMIT_SYMBOL, 246),
    (6, HUFFMAN_EMIT_SYMBOL, 246),
    (10, HUFFMAN_EMIT_SYMBOL, 246),
    (15, HUFFMAN_EMIT_SYMBOL, 246),
    (24, HUFFMAN_EMIT_SYMBOL, 246),
    (31, HUFFMAN_EMIT_SYMBOL, 246),
    (41, HUFFMAN_EMIT_SYMBOL, 246),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 246),

    # Node 219
    (3, HUFFMAN_EMIT_SYMBOL, 247),
    (6, HUFFMAN_EMIT_SYMBOL, 247),
    (10, HUFFMAN_EMIT_SYMBOL, 247),
    (15, HUFFMAN_EMIT_SYMBOL, 247),
    (24, HUFFMAN_EMIT_SYMBOL, 247),
    (31, HUFFMAN_EMIT_SYMBOL, 247),
    (41, HUFFMAN_EMIT_SYMBOL, 247),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 247),
    (3, HUFFMAN_EMIT_SYMBOL, 248),
    (6, HUFFMAN_EMIT_SYMBOL, 248),
    (10, HUFFMAN_EMIT_SYMBOL, 248),
    (15, HUFFMAN_EMIT_SYMBOL, 248),
    (24, HUFFMAN_EMIT_SYMBOL, 248),
    (31, HUFFMAN_EMIT_SYMBOL, 248),
    (41, HUFFMAN_EMIT_SYMBOL, 248),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 248),

    # Node 220
    (2, HUFFMAN_EMIT_SYMBOL, 250),
    (9, HUFFMAN_EMIT_SYMBOL, 250),
    (23, HUFFMAN_EMIT_SYMBOL, 250),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250),
    (2, HUFFMAN_EMIT_SYMBOL, 251),
    (9, HUFFMAN_EMIT_SYMBOL, 251),
    (23, HUFFMAN_EMIT_SYMBOL, 251),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251),
    (2, HUFFMAN_EMIT_SYMBOL, 252),
    (9, HUFFMAN_EMIT_SYMBOL, 252),
    (23, HUFFMAN_EMIT_SYMBOL, 252),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252),
    (2, HUFFMAN_EMIT_SYMBOL, 253),
    (9, HUFFMAN_EMIT_SYMBOL, 253),
    (23, HUFFMAN_EMIT_SYMBOL, 253),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253),

    # Node 221
    (3, HUFFMAN_EMIT_SYMBOL, 250),
    (6, HUFFMAN_EMIT_SYMBOL, 250),
    (10, HUFFMAN_EMIT_SYMBOL, 250),
    (15, HUFFMAN_EMIT_SYMBOL, 250),
    (24, HUFFMAN_EMIT_SYMBOL, 250),
    (31, HUFFMAN_EMIT_SYMBOL, 250),
    (41, HUFFMAN_EMIT_SYMBOL, 250),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 250),
    (3, HUFFMAN_EMIT_SYMBOL, 251),
    (6, HUFFMAN_EMIT_SYMBOL, 251),
    (10, HUFFMAN_EMIT_SYMBOL, 251),
    (15, HUFFMAN_EMIT_SYMBOL, 251),
    (24, HUFFMAN_EMIT_SYMBOL, 251),
    (31, HUFFMAN_EMIT_SYMBOL, 251),
    (41, HUFFMAN_EMIT_SYMBOL, 251),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 251),

    # Node 222
    (3, HUFFMAN_EMIT_SYMBOL, 252),
    (6, HUFFMAN_EMIT_SYMBOL, 252),
    (10, HUFFMAN_EMIT_SYMBOL, 252),
    (15, HUFFMAN_EMIT_SYMBOL, 252),
    (24, HUFFMAN_EMIT_SYMBOL, 252),
    (31, HUFFMAN_EMIT_SYMBOL, 252),
    (41, HUFFMAN_EMIT_SYMBOL, 252),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 252),
    (3, HUFFMAN_EMIT_SYMBOL, 253),
    (6, HUFFMAN_EMIT_SYMBOL, 253),
    (10, HUFFMAN_EMIT_SYMBOL, 253),
    (15, HUFFMAN_EMIT_SYMBOL, 253),
    (24, HUFFMAN_EMIT_SYMBOL, 253),
    (31, HUFFMAN_EMIT_SYMBOL, 253),
    (41, HUFFMAN_EMIT_SYMBOL, 253),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 253),

    # Node 223
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254),
    (227, 0, 0),
    (229, 0, 0),
    (230, 0, 0),
    (233, 0, 0),
    (234, 0, 0),
    (236, 0, 0),
    (237, 0, 0),
    (241, 0, 0),
    (242, 0, 0),
    (244, 0, 0),
    (245, 0, 0),
    (248, 0, 0),
    (249, 0, 0),
    (251, 0, 0),
    (252, 0, 0),

    # Node 224
    (1, HUFFMAN_EMIT_SYMBOL, 254),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18),

    # Node 225
    (2, HUFFMAN_EMIT_SYMBOL, 254),
    (9, HUFFMAN_EMIT_SYMBOL, 254),
    (23, HUFFMAN_EMIT_SYMBOL, 254),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254),
    (1, HUFFMAN_EMIT_SYMBOL, 2),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2),
    (1, HUFFMAN_EMIT_SYMBOL, 3),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3),
    (1, HUFFMAN_EMIT_SYMBOL, 4),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4),
    (1, HUFFMAN_EMIT_SYMBOL, 5),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5),
    (1, HUFFMAN_EMIT_SYMBOL, 6),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6),
    (1, HUFFMAN_EMIT_SYMBOL, 7),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7),

    # Node 226
    (3, HUFFMAN_EMIT_SYMBOL, 254),
    (6, HUFFMAN_EMIT_SYMBOL, 254),
    (10, HUFFMAN_EMIT_SYMBOL, 254),
    (15, HUFFMAN_EMIT_SYMBOL, 254),
    (24, HUFFMAN_EMIT_SYMBOL, 254),
    (31, HUFFMAN_EMIT_SYMBOL, 254),
    (41, HUFFMAN_EMIT_SYMBOL, 254),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 254),
    (2, HUFFMAN_EMIT_SYMBOL, 2),
    (9, HUFFMAN_EMIT_SYMBOL, 2),
    (23, HUFFMAN_EMIT_SYMBOL, 2),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2),
    (2, HUFFMAN_EMIT_SYMBOL, 3),
    (9, HUFFMAN_EMIT_SYMBOL, 3),
    (23, HUFFMAN_EMIT_SYMBOL, 3),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3),

    # Node 227
    (3, HUFFMAN_EMIT_SYMBOL, 2),
    (6, HUFFMAN_EMIT_SYMBOL, 2),
    (10, HUFFMAN_EMIT_SYMBOL, 2),
    (15, HUFFMAN_EMIT_SYMBOL, 2),
    (24, HUFFMAN_EMIT_SYMBOL, 2),
    (31, HUFFMAN_EMIT_SYMBOL, 2),
    (41, HUFFMAN_EMIT_SYMBOL, 2),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 2),
    (3, HUFFMAN_EMIT_SYMBOL, 3),
    (6, HUFFMAN_EMIT_SYMBOL, 3),
    (10, HUFFMAN_EMIT_SYMBOL, 3),
    (15, HUFFMAN_EMIT_SYMBOL, 3),
    (24, HUFFMAN_EMIT_SYMBOL, 3),
    (31, HUFFMAN_EMIT_SYMBOL, 3),
    (41, HUFFMAN_EMIT_SYMBOL, 3),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 3),

    # Node 228
    (2, HUFFMAN_EMIT_SYMBOL, 4),
    (9, HUFFMAN_EMIT_SYMBOL, 4),
    (23, HUFFMAN_EMIT_SYMBOL, 4),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4),
    (2, HUFFMAN_EMIT_SYMBOL, 5),
    (9, HUFFMAN_EMIT_SYMBOL, 5),
    (23, HUFFMAN_EMIT_SYMBOL, 5),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5),
    (2, HUFFMAN_EMIT_SYMBOL, 6),
    (9, HUFFMAN_EMIT_SYMBOL, 6),
    (23, HUFFMAN_EMIT_SYMBOL, 6),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6),
    (2, HUFFMAN_EMIT_SYMBOL, 7),
    (9, HUFFMAN_EMIT_SYMBOL, 7),
    (23, HUFFMAN_EMIT_SYMBOL, 7),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7),

    # Node 229
    (3, HUFFMAN_EMIT_SYMBOL, 4),
    (6, HUFFMAN_EMIT_SYMBOL, 4),
    (10, HUFFMAN_EMIT_SYMBOL, 4),
    (15, HUFFMAN_EMIT_SYMBOL, 4),
    (24, HUFFMAN_EMIT_SYMBOL, 4),
    (31, HUFFMAN_EMIT_SYMBOL, 4),
    (41, HUFFMAN_EMIT_SYMBOL, 4),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 4),
    (3, HUFFMAN_EMIT_SYMBOL, 5),
    (6, HUFFMAN_EMIT_SYMBOL, 5),
    (10, HUFFMAN_EMIT_SYMBOL, 5),
    (15, HUFFMAN_EMIT_SYMBOL, 5),
    (24, HUFFMAN_EMIT_SYMBOL, 5),
    (31, HUFFMAN_EMIT_SYMBOL, 5),
    (41, HUFFMAN_EMIT_SYMBOL, 5),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 5),

    # Node 230
    (3, HUFFMAN_EMIT_SYMBOL, 6),
    (6, HUFFMAN_EMIT_SYMBOL, 6),
    (10, HUFFMAN_EMIT_SYMBOL, 6),
    (15, HUFFMAN_EMIT_SYMBOL, 6),
    (24, HUFFMAN_EMIT_SYMBOL, 6),
    (31, HUFFMAN_EMIT_SYMBOL, 6),
    (41, HUFFMAN_EMIT_SYMBOL, 6),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 6),
    (3, HUFFMAN_EMIT_SYMBOL, 7),
    (6, HUFFMAN_EMIT_SYMBOL, 7),
    (10, HUFFMAN_EMIT_SYMBOL, 7),
    (15, HUFFMAN_EMIT_SYMBOL, 7),
    (24, HUFFMAN_EMIT_SYMBOL, 7),
    (31, HUFFMAN_EMIT_SYMBOL, 7),
    (41, HUFFMAN_EMIT_SYMBOL, 7),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 7),

    # Node 231
    (1, HUFFMAN_EMIT_SYMBOL, 8),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8),
    (1, HUFFMAN_EMIT_SYMBOL, 11),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11),
    (1, HUFFMAN_EMIT_SYMBOL, 12),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12),
    (1, HUFFMAN_EMIT_SYMBOL, 14),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14),
    (1, HUFFMAN_EMIT_SYMBOL, 15),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15),
    (1, HUFFMAN_EMIT_SYMBOL, 16),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16),
    (1, HUFFMAN_EMIT_SYMBOL, 17),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17),
    (1, HUFFMAN_EMIT_SYMBOL, 18),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18),

    # Node 232
    (2, HUFFMAN_EMIT_SYMBOL, 8),
    (9, HUFFMAN_EMIT_SYMBOL, 8),
    (23, HUFFMAN_EMIT_SYMBOL, 8),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8),
    (2, HUFFMAN_EMIT_SYMBOL, 11),
    (9, HUFFMAN_EMIT_SYMBOL, 11),
    (23, HUFFMAN_EMIT_SYMBOL, 11),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11),
    (2, HUFFMAN_EMIT_SYMBOL, 12),
    (9, HUFFMAN_EMIT_SYMBOL, 12),
    (23, HUFFMAN_EMIT_SYMBOL, 12),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12),
    (2, HUFFMAN_EMIT_SYMBOL, 14),
    (9, HUFFMAN_EMIT_SYMBOL, 14),
    (23, HUFFMAN_EMIT_SYMBOL, 14),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14),

    # Node 233
    (3, HUFFMAN_EMIT_SYMBOL, 8),
    (6, HUFFMAN_EMIT_SYMBOL, 8),
    (10, HUFFMAN_EMIT_SYMBOL, 8),
    (15, HUFFMAN_EMIT_SYMBOL, 8),
    (24, HUFFMAN_EMIT_SYMBOL, 8),
    (31, HUFFMAN_EMIT_SYMBOL, 8),
    (41, HUFFMAN_EMIT_SYMBOL, 8),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 8),
    (3, HUFFMAN_EMIT_SYMBOL, 11),
    (6, HUFFMAN_EMIT_SYMBOL, 11),
    (10, HUFFMAN_EMIT_SYMBOL, 11),
    (15, HUFFMAN_EMIT_SYMBOL, 11),
    (24, HUFFMAN_EMIT_SYMBOL, 11),
    (31, HUFFMAN_EMIT_SYMBOL, 11),
    (41, HUFFMAN_EMIT_SYMBOL, 11),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 11),

    # Node 234
    (3, HUFFMAN_EMIT_SYMBOL, 12),
    (6, HUFFMAN_EMIT_SYMBOL, 12),
    (10, HUFFMAN_EMIT_SYMBOL, 12),
    (15, HUFFMAN_EMIT_SYMBOL, 12),
    (24, HUFFMAN_EMIT_SYMBOL, 12),
    (31, HUFFMAN_EMIT_SYMBOL, 12),
    (41, HUFFMAN_EMIT_SYMBOL, 12),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 12),
    (3, HUFFMAN_EMIT_SYMBOL, 14),
    (6, HUFFMAN_EMIT_SYMBOL, 14),
    (10, HUFFMAN_EMIT_SYMBOL, 14),
    (15, HUFFMAN_EMIT_SYMBOL, 14),
    (24, HUFFMAN_EMIT_SYMBOL, 14),
    (31, HUFFMAN_EMIT_SYMBOL, 14),
    (41, HUFFMAN_EMIT_SYMBOL, 14),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 14),

    # Node 235
    (2, HUFFMAN_EMIT_SYMBOL, 15),
    (9, HUFFMAN_EMIT_SYMBOL, 15),
    (23, HUFFMAN_EMIT_SYMBOL, 15),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15),
    (2, HUFFMAN_EMIT_SYMBOL, 16),
    (9, HUFFMAN_EMIT_SYMBOL, 16),
    (23, HUFFMAN_EMIT_SYMBOL, 16),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16),
    (2, HUFFMAN_EMIT_SYMBOL, 17),
    (9, HUFFMAN_EMIT_SYMBOL, 17),
    (23, HUFFMAN_EMIT_SYMBOL, 17),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17),
    (2, HUFFMAN_EMIT_SYMBOL, 18),
    (9, HUFFMAN_EMIT_SYMBOL, 18),
    (23, HUFFMAN_EMIT_SYMBOL, 18),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18),

    # Node 236
    (3, HUFFMAN_EMIT_SYMBOL, 15),
    (6, HUFFMAN_EMIT_SYMBOL, 15),
    (10, HUFFMAN_EMIT_SYMBOL, 15),
    (15, HUFFMAN_EMIT_SYMBOL, 15),
    (24, HUFFMAN_EMIT_SYMBOL, 15),
    (31, HUFFMAN_EMIT_SYMBOL, 15),
    (41, HUFFMAN_EMIT_SYMBOL, 15),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 15),
    (3, HUFFMAN_EMIT_SYMBOL, 16),
    (6, HUFFMAN_EMIT_SYMBOL, 16),
    (10, HUFFMAN_EMIT_SYMBOL, 16),
    (15, HUFFMAN_EMIT_SYMBOL, 16),
    (24, HUFFMAN_EMIT_SYMBOL, 16),
    (31, HUFFMAN_EMIT_SYMBOL, 16),
    (41, HUFFMAN_EMIT_SYMBOL, 16),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 16),

    # Node 237
    (3, HUFFMAN_EMIT_SYMBOL, 17),
    (6, HUFFMAN_EMIT_SYMBOL, 17),
    (10, HUFFMAN_EMIT_SYMBOL, 17),
    (15, HUFFMAN_EMIT_SYMBOL, 17),
    (24, HUFFMAN_EMIT_SYMBOL, 17),
    (31, HUFFMAN_EMIT_SYMBOL, 17),
    (41, HUFFMAN_EMIT_SYMBOL, 17),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 17),
    (3, HUFFMAN_EMIT_SYMBOL, 18),
    (6, HUFFMAN_EMIT_SYMBOL, 18),
    (10, HUFFMAN_EMIT_SYMBOL, 18),
    (15, HUFFMAN_EMIT_SYMBOL, 18),
    (24, HUFFMAN_EMIT_SYMBOL, 18),
    (31, HUFFMAN_EMIT_SYMBOL, 18),
    (41, HUFFMAN_EMIT_SYMBOL, 18),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 18),

    # Node 238
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249),
    (253, 0, 0),

    # Node 239
    (1, HUFFMAN_EMIT_SYMBOL, 19),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19),
    (1, HUFFMAN_EMIT_SYMBOL, 20),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20),
    (1, HUFFMAN_EMIT_SYMBOL, 21),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21),
    (1, HUFFMAN_EMIT_SYMBOL, 23),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23),
    (1, HUFFMAN_EMIT_SYMBOL, 24),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24),
    (1, HUFFMAN_EMIT_SYMBOL, 25),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25),
    (1, HUFFMAN_EMIT_SYMBOL, 26),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26),
    (1, HUFFMAN_EMIT_SYMBOL, 27),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27),

    # Node 240
    (2, HUFFMAN_EMIT_SYMBOL, 19),
    (9, HUFFMAN_EMIT_SYMBOL, 19),
    (23, HUFFMAN_EMIT_SYMBOL, 19),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19),
    (2, HUFFMAN_EMIT_SYMBOL, 20),
    (9, HUFFMAN_EMIT_SYMBOL, 20),
    (23, HUFFMAN_EMIT_SYMBOL, 20),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20),
    (2, HUFFMAN_EMIT_SYMBOL, 21),
    (9, HUFFMAN_EMIT_SYMBOL, 21),
    (23, HUFFMAN_EMIT_SYMBOL, 21),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21),
    (2, HUFFMAN_EMIT_SYMBOL, 23),
    (9, HUFFMAN_EMIT_SYMBOL, 23),
    (23, HUFFMAN_EMIT_SYMBOL, 23),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23),

    # Node 241
    (3, HUFFMAN_EMIT_SYMBOL, 19),
    (6, HUFFMAN_EMIT_SYMBOL, 19),
    (10, HUFFMAN_EMIT_SYMBOL, 19),
    (15, HUFFMAN_EMIT_SYMBOL, 19),
    (24, HUFFMAN_EMIT_SYMBOL, 19),
    (31, HUFFMAN_EMIT_SYMBOL, 19),
    (41, HUFFMAN_EMIT_SYMBOL, 19),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 19),
    (3, HUFFMAN_EMIT_SYMBOL, 20),
    (6, HUFFMAN_EMIT_SYMBOL, 20),
    (10, HUFFMAN_EMIT_SYMBOL, 20),
    (15, HUFFMAN_EMIT_SYMBOL, 20),
    (24, HUFFMAN_EMIT_SYMBOL, 20),
    (31, HUFFMAN_EMIT_SYMBOL, 20),
    (41, HUFFMAN_EMIT_SYMBOL, 20),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 20),

    # Node 242
    (3, HUFFMAN_EMIT_SYMBOL, 21),
    (6, HUFFMAN_EMIT_SYMBOL, 21),
    (10, HUFFMAN_EMIT_SYMBOL, 21),
    (15, HUFFMAN_EMIT_SYMBOL, 21),
    (24, HUFFMAN_EMIT_SYMBOL, 21),
    (31, HUFFMAN_EMIT_SYMBOL, 21),
    (41, HUFFMAN_EMIT_SYMBOL, 21),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 21),
    (3, HUFFMAN_EMIT_SYMBOL, 23),
    (6, HUFFMAN_EMIT_SYMBOL, 23),
    (10, HUFFMAN_EMIT_SYMBOL, 23),
    (15, HUFFMAN_EMIT_SYMBOL, 23),
    (24, HUFFMAN_EMIT_SYMBOL, 23),
    (31, HUFFMAN_EMIT_SYMBOL, 23),
    (41, HUFFMAN_EMIT_SYMBOL, 23),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 23),

    # Node 243
    (2, HUFFMAN_EMIT_SYMBOL, 24),
    (9, HUFFMAN_EMIT_SYMBOL, 24),
    (23, HUFFMAN_EMIT_SYMBOL, 24),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24),
    (2, HUFFMAN_EMIT_SYMBOL, 25),
    (9, HUFFMAN_EMIT_SYMBOL, 25),
    (23, HUFFMAN_EMIT_SYMBOL, 25),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25),
    (2, HUFFMAN_EMIT_SYMBOL, 26),
    (9, HUFFMAN_EMIT_SYMBOL, 26),
    (23, HUFFMAN_EMIT_SYMBOL, 26),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26),
    (2, HUFFMAN_EMIT_SYMBOL, 27),
    (9, HUFFMAN_EMIT_SYMBOL, 27),
    (23, HUFFMAN_EMIT_SYMBOL, 27),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27),

    # Node 244
    (3, HUFFMAN_EMIT_SYMBOL, 24),
    (6, HUFFMAN_EMIT_SYMBOL, 24),
    (10, HUFFMAN_EMIT_SYMBOL, 24),
    (15, HUFFMAN_EMIT_SYMBOL, 24),
    (24, HUFFMAN_EMIT_SYMBOL, 24),
    (31, HUFFMAN_EMIT_SYMBOL, 24),
    (41, HUFFMAN_EMIT_SYMBOL, 24),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 24),
    (3, HUFFMAN_EMIT_SYMBOL, 25),
    (6, HUFFMAN_EMIT_SYMBOL, 25),
    (10, HUFFMAN_EMIT_SYMBOL, 25),
    (15, HUFFMAN_EMIT_SYMBOL, 25),
    (24, HUFFMAN_EMIT_SYMBOL, 25),
    (31, HUFFMAN_EMIT_SYMBOL, 25),
    (41, HUFFMAN_EMIT_SYMBOL, 25),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 25),

    # Node 245
    (3, HUFFMAN_EMIT_SYMBOL, 26),
    (6, HUFFMAN_EMIT_SYMBOL, 26),
    (10, HUFFMAN_EMIT_SYMBOL, 26),
    (15, HUFFMAN_EMIT_SYMBOL, 26),
    (24, HUFFMAN_EMIT_SYMBOL, 26),
    (31, HUFFMAN_EMIT_SYMBOL, 26),
    (41, HUFFMAN_EMIT_SYMBOL, 26),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 26),
    (3, HUFFMAN_EMIT_SYMBOL, 27),
    (6, HUFFMAN_EMIT_SYMBOL, 27),
    (10, HUFFMAN_EMIT_SYMBOL, 27),
    (15, HUFFMAN_EMIT_SYMBOL, 27),
    (24, HUFFMAN_EMIT_SYMBOL, 27),
    (31, HUFFMAN_EMIT_SYMBOL, 27),
    (41, HUFFMAN_EMIT_SYMBOL, 27),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 27),

    # Node 246
    (1, HUFFMAN_EMIT_SYMBOL, 28),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28),
    (1, HUFFMAN_EMIT_SYMBOL, 29),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29),
    (1, HUFFMAN_EMIT_SYMBOL, 30),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30),
    (1, HUFFMAN_EMIT_SYMBOL, 31),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31),
    (1, HUFFMAN_EMIT_SYMBOL, 127),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127),
    (1, HUFFMAN_EMIT_SYMBOL, 220),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220),
    (1, HUFFMAN_EMIT_SYMBOL, 249),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249),
    (254, 0, 0),
    (255, 0, 0),

    # Node 247
    (2, HUFFMAN_EMIT_SYMBOL, 28),
    (9, HUFFMAN_EMIT_SYMBOL, 28),
    (23, HUFFMAN_EMIT_SYMBOL, 28),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28),
    (2, HUFFMAN_EMIT_SYMBOL, 29),
    (9, HUFFMAN_EMIT_SYMBOL, 29),
    (23, HUFFMAN_EMIT_SYMBOL, 29),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29),
    (2, HUFFMAN_EMIT_SYMBOL, 30),
    (9, HUFFMAN_EMIT_SYMBOL, 30),
    (23, HUFFMAN_EMIT_SYMBOL, 30),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30),
    (2, HUFFMAN_EMIT_SYMBOL, 31),
    (9, HUFFMAN_EMIT_SYMBOL, 31),
    (23, HUFFMAN_EMIT_SYMBOL, 31),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31),

    # Node 248
    (3, HUFFMAN_EMIT_SYMBOL, 28),
    (6, HUFFMAN_EMIT_SYMBOL, 28),
    (10, HUFFMAN_EMIT_SYMBOL, 28),
    (15, HUFFMAN_EMIT_SYMBOL, 28),
    (24, HUFFMAN_EMIT_SYMBOL, 28),
    (31, HUFFMAN_EMIT_SYMBOL, 28),
    (41, HUFFMAN_EMIT_SYMBOL, 28),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 28),
    (3, HUFFMAN_EMIT_SYMBOL, 29),
    (6, HUFFMAN_EMIT_SYMBOL, 29),
    (10, HUFFMAN_EMIT_SYMBOL, 29),
    (15, HUFFMAN_EMIT_SYMBOL, 29),
    (24, HUFFMAN_EMIT_SYMBOL, 29),
    (31, HUFFMAN_EMIT_SYMBOL, 29),
    (41, HUFFMAN_EMIT_SYMBOL, 29),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 29),

    # Node 249
    (3, HUFFMAN_EMIT_SYMBOL, 30),
    (6, HUFFMAN_EMIT_SYMBOL, 30),
    (10, HUFFMAN_EMIT_SYMBOL, 30),
    (15, HUFFMAN_EMIT_SYMBOL, 30),
    (24, HUFFMAN_EMIT_SYMBOL, 30),
    (31, HUFFMAN_EMIT_SYMBOL, 30),
    (41, HUFFMAN_EMIT_SYMBOL, 30),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 30),
    (3, HUFFMAN_EMIT_SYMBOL, 31),
    (6, HUFFMAN_EMIT_SYMBOL, 31),
    (10, HUFFMAN_EMIT_SYMBOL, 31),
    (15, HUFFMAN_EMIT_SYMBOL, 31),
    (24, HUFFMAN_EMIT_SYMBOL, 31),
    (31, HUFFMAN_EMIT_SYMBOL, 31),
    (41, HUFFMAN_EMIT_SYMBOL, 31),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 31),

    # Node 250
    (2, HUFFMAN_EMIT_SYMBOL, 127),
    (9, HUFFMAN_EMIT_SYMBOL, 127),
    (23, HUFFMAN_EMIT_SYMBOL, 127),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127),
    (2, HUFFMAN_EMIT_SYMBOL, 220),
    (9, HUFFMAN_EMIT_SYMBOL, 220),
    (23, HUFFMAN_EMIT_SYMBOL, 220),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220),
    (2, HUFFMAN_EMIT_SYMBOL, 249),
    (9, HUFFMAN_EMIT_SYMBOL, 249),
    (23, HUFFMAN_EMIT_SYMBOL, 249),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13),
    (0, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22),
    (0, HUFFMAN_FAIL, 0),

    # Node 251
    (3, HUFFMAN_EMIT_SYMBOL, 127),
    (6, HUFFMAN_EMIT_SYMBOL, 127),
    (10, HUFFMAN_EMIT_SYMBOL, 127),
    (15, HUFFMAN_EMIT_SYMBOL, 127),
    (24, HUFFMAN_EMIT_SYMBOL, 127),
    (31, HUFFMAN_EMIT_SYMBOL, 127),
    (41, HUFFMAN_EMIT_SYMBOL, 127),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 127),
    (3, HUFFMAN_EMIT_SYMBOL, 220),
    (6, HUFFMAN_EMIT_SYMBOL, 220),
    (10, HUFFMAN_EMIT_SYMBOL, 220),
    (15, HUFFMAN_EMIT_SYMBOL, 220),
    (24, HUFFMAN_EMIT_SYMBOL, 220),
    (31, HUFFMAN_EMIT_SYMBOL, 220),
    (41, HUFFMAN_EMIT_SYMBOL, 220),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 220),

    # Node 252
    (3, HUFFMAN_EMIT_SYMBOL, 249),
    (6, HUFFMAN_EMIT_SYMBOL, 249),
    (10, HUFFMAN_EMIT_SYMBOL, 249),
    (15, HUFFMAN_EMIT_SYMBOL, 249),
    (24, HUFFMAN_EMIT_SYMBOL, 249),
    (31, HUFFMAN_EMIT_SYMBOL, 249),
    (41, HUFFMAN_EMIT_SYMBOL, 249),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 249),
    (1, HUFFMAN_EMIT_SYMBOL, 10),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10),
    (1, HUFFMAN_EMIT_SYMBOL, 13),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13),
    (1, HUFFMAN_EMIT_SYMBOL, 22),
    (22, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),

    # Node 253
    (2, HUFFMAN_EMIT_SYMBOL, 10),
    (9, HUFFMAN_EMIT_SYMBOL, 10),
    (23, HUFFMAN_EMIT_SYMBOL, 10),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10),
    (2, HUFFMAN_EMIT_SYMBOL, 13),
    (9, HUFFMAN_EMIT_SYMBOL, 13),
    (23, HUFFMAN_EMIT_SYMBOL, 13),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13),
    (2, HUFFMAN_EMIT_SYMBOL, 22),
    (9, HUFFMAN_EMIT_SYMBOL, 22),
    (23, HUFFMAN_EMIT_SYMBOL, 22),
    (40, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),

    # Node 254
    (3, HUFFMAN_EMIT_SYMBOL, 10),
    (6, HUFFMAN_EMIT_SYMBOL, 10),
    (10, HUFFMAN_EMIT_SYMBOL, 10),
    (15, HUFFMAN_EMIT_SYMBOL, 10),
    (24, HUFFMAN_EMIT_SYMBOL, 10),
    (31, HUFFMAN_EMIT_SYMBOL, 10),
    (41, HUFFMAN_EMIT_SYMBOL, 10),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 10),
    (3, HUFFMAN_EMIT_SYMBOL, 13),
    (6, HUFFMAN_EMIT_SYMBOL, 13),
    (10, HUFFMAN_EMIT_SYMBOL, 13),
    (15, HUFFMAN_EMIT_SYMBOL, 13),
    (24, HUFFMAN_EMIT_SYMBOL, 13),
    (31, HUFFMAN_EMIT_SYMBOL, 13),
    (41, HUFFMAN_EMIT_SYMBOL, 13),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 13),

    # Node 255
    (3, HUFFMAN_EMIT_SYMBOL, 22),
    (6, HUFFMAN_EMIT_SYMBOL, 22),
    (10, HUFFMAN_EMIT_SYMBOL, 22),
    (15, HUFFMAN_EMIT_SYMBOL, 22),
    (24, HUFFMAN_EMIT_SYMBOL, 22),
    (31, HUFFMAN_EMIT_SYMBOL, 22),
    (41, HUFFMAN_EMIT_SYMBOL, 22),
    (56, HUFFMAN_COMPLETE | HUFFMAN_EMIT_SYMBOL, 22),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
    (0, HUFFMAN_FAIL, 0),
]
0707010001f45a000081a40000000000000000000000016a100daf000058f3000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/hpack/hpack.py # -*- coding: utf-8 -*-
"""
hpack/hpack
~~~~~~~~~~~

Implements the HPACK header compression algorithm as detailed by the IETF.
"""
import logging

from .table import HeaderTable, table_entry_size
from .compat import to_byte, to_bytes
from .exceptions import (
    HPACKDecodingError, OversizedHeaderListError, InvalidTableSizeError
)
from .huffman import HuffmanEncoder
from .huffman_constants import (
    REQUEST_CODES, REQUEST_CODES_LENGTH
)
from .huffman_table import decode_huffman
from .struct import HeaderTuple, NeverIndexedHeaderTuple

log = logging.getLogger(__name__)

INDEX_NONE = b'\x00'
INDEX_NEVER = b'\x10'
INDEX_INCREMENTAL = b'\x40'

# Precompute 2^i for 1-8 for use in prefix calcs.
# Zero index is not used but there to save a subtraction
# as prefix numbers are not zero indexed.
_PREFIX_BIT_MAX_NUMBERS = [(2 ** i) - 1 for i in range(9)]

try:  # pragma: no cover
    basestring = basestring
except NameError:  # pragma: no cover
    basestring = (str, bytes)


# We default the maximum header list we're willing to accept to 64kB. That's a
# lot of headers, but if applications want to raise it they can do.
DEFAULT_MAX_HEADER_LIST_SIZE = 2 ** 16


def _unicode_if_needed(header, raw):
    """
    Provides a header as a unicode string if raw is False, otherwise returns
    it as a bytestring.
    """
    name = to_bytes(header[0])
    value = to_bytes(header[1])
    if not raw:
        name = name.decode('utf-8')
        value = value.decode('utf-8')
    return header.__class__(name, value)


def encode_integer(integer, prefix_bits):
    """
    This encodes an integer according to the wacky integer encoding rules
    defined in the HPACK spec.
    """
    log.debug("Encoding %d with %d bits", integer, prefix_bits)

    if integer < 0:
        raise ValueError(
            "Can only encode positive integers, got %s" % integer
        )

    if prefix_bits < 1 or prefix_bits > 8:
        raise ValueError(
            "Prefix bits must be between 1 and 8, got %s" % prefix_bits
        )

    max_number = _PREFIX_BIT_MAX_NUMBERS[prefix_bits]

    if integer < max_number:
        return bytearray([integer])  # Seriously?
    else:
        elements = [max_number]
        integer -= max_number

        while integer >= 128:
            elements.append((integer & 127) + 128)
            integer >>= 7

        elements.append(integer)

        return bytearray(elements)


def decode_integer(data, prefix_bits):
    """
    This decodes an integer according to the wacky integer encoding rules
    defined in the HPACK spec. Returns a tuple of the decoded integer and the
    number of bytes that were consumed from ``data`` in order to get that
    integer.
    """
    if prefix_bits < 1 or prefix_bits > 8:
        raise ValueError(
            "Prefix bits must be between 1 and 8, got %s" % prefix_bits
        )

    max_number = _PREFIX_BIT_MAX_NUMBERS[prefix_bits]
    index = 1
    shift = 0
    mask = (0xFF >> (8 - prefix_bits))

    try:
        number = to_byte(data[0]) & mask
        if number == max_number:
            while True:
                next_byte = to_byte(data[index])
                index += 1

                if next_byte >= 128:
                    number += (next_byte - 128) << shift
                else:
                    number += next_byte << shift
                    break
                shift += 7

    except IndexError:
        raise HPACKDecodingError(
            "Unable to decode HPACK integer representation from %r" % data
        )

    log.debug("Decoded %d, consumed %d bytes", number, index)

    return number, index


def _dict_to_iterable(header_dict):
    """
    This converts a dictionary to an iterable of two-tuples. This is a
    HPACK-specific function because it pulls "special-headers" out first and
    then emits them.
    """
    assert isinstance(header_dict, dict)
    keys = sorted(
        header_dict.keys(),
        key=lambda k: not _to_bytes(k).startswith(b':')
    )
    for key in keys:
        yield key, header_dict[key]


def _to_bytes(string):
    """
    Convert string to bytes.
    """
    if not isinstance(string, basestring):  # pragma: no cover
        string = str(string)

    return string if isinstance(string, bytes) else string.encode('utf-8')


class Encoder(object):
    """
    An HPACK encoder object. This object takes HTTP headers and emits encoded
    HTTP/2 header blocks.
    """

    def __init__(self):
        self.header_table = HeaderTable()
        self.huffman_coder = HuffmanEncoder(
            REQUEST_CODES, REQUEST_CODES_LENGTH
        )
        self.table_size_changes = []

    @property
    def header_table_size(self):
        """
        Controls the size of the HPACK header table.
        """
        return self.header_table.maxsize

    @header_table_size.setter
    def header_table_size(self, value):
        self.header_table.maxsize = value
        if self.header_table.resized:
            self.table_size_changes.append(value)

    def encode(self, headers, huffman=True):
        """
        Takes a set of headers and encodes them into a HPACK-encoded header
        block.

        :param headers: The headers to encode. Must be either an iterable of
                        tuples, an iterable of :class:`HeaderTuple
                        <hpack.struct.HeaderTuple>`, or a ``dict``.

                        If an iterable of tuples, the tuples may be either
                        two-tuples or three-tuples. If they are two-tuples, the
                        tuples must be of the format ``(name, value)``. If they
                        are three-tuples, they must be of the format
                        ``(name, value, sensitive)``, where ``sensitive`` is a
                        boolean value indicating whether the header should be
                        added to header tables anywhere. If not present,
                        ``sensitive`` defaults to ``False``.

                        If an iterable of :class:`HeaderTuple
                        <hpack.struct.HeaderTuple>`, the tuples must always be
                        two-tuples. Instead of using ``sensitive`` as a third
                        tuple entry, use :class:`NeverIndexedHeaderTuple
                        <hpack.struct.NeverIndexedHeaderTuple>` to request that
                        the field never be indexed.

                        .. warning:: HTTP/2 requires that all special headers
                            (headers whose names begin with ``:`` characters)
                            appear at the *start* of the header block. While
                            this method will ensure that happens for ``dict``
                            subclasses, callers using any other iterable of
                            tuples **must** ensure they place their special
                            headers at the start of the iterable.

                            For efficiency reasons users should prefer to use
                            iterables of two-tuples: fixing the ordering of
                            dictionary headers is an expensive operation that
                            should be avoided if possible.

        :param huffman: (optional) Whether to Huffman-encode any header sent as
                        a literal value. Except for use when debugging, it is
                        recommended that this be left enabled.

        :returns: A bytestring containing the HPACK-encoded header block.
        """
        # Transforming the headers into a header block is a procedure that can
        # be modeled as a chain or pipe. First, the headers are encoded. This
        # encoding can be done a number of ways. If the header name-value pair
        # are already in the header table we can represent them using the
        # indexed representation: the same is true if they are in the static
        # table. Otherwise, a literal representation will be used.
        log.debug("HPACK encoding %s", headers)
        header_block = []

        # Turn the headers into a list of tuples if possible. This is the
        # natural way to interact with them in HPACK. Because dictionaries are
        # un-ordered, we need to make sure we grab the "special" headers first.
        if isinstance(headers, dict):
            headers = _dict_to_iterable(headers)

        # Before we begin, if the header table size has been changed we need
        # to signal all changes since last emission appropriately.
        if self.header_table.resized:
            header_block.append(self._encode_table_size_change())
            self.header_table.resized = False

        # Add each header to the header block
        for header in headers:
            sensitive = False
            if isinstance(header, HeaderTuple):
                sensitive = not header.indexable
            elif len(header) > 2:
                sensitive = header[2]

            header = (_to_bytes(header[0]), _to_bytes(header[1]))
            header_block.append(self.add(header, sensitive, huffman))

        header_block = b''.join(header_block)

        log.debug("Encoded header block to %s", header_block)

        return header_block

    def add(self, to_add, sensitive, huffman=False):
        """
        This function takes a header key-value tuple and serializes it.
        """
        log.debug("Adding %s to the header table", to_add)

        name, value = to_add

        # Set our indexing mode
        indexbit = INDEX_INCREMENTAL if not sensitive else INDEX_NEVER

        # Search for a matching header in the header table.
        match = self.header_table.search(name, value)

        if match is None:
            # Not in the header table. Encode using the literal syntax,
            # and add it to the header table.
            encoded = self._encode_literal(name, value, indexbit, huffman)
            if not sensitive:
                self.header_table.add(name, value)
            return encoded

        # The header is in the table, break out the values. If we matched
        # perfectly, we can use the indexed representation: otherwise we
        # can use the indexed literal.
        index, name, perfect = match

        if perfect:
            # Indexed representation.
            encoded = self._encode_indexed(index)
        else:
            # Indexed literal. We are going to add header to the
            # header table unconditionally. It is a future todo to
            # filter out headers which are known to be ineffective for
            # indexing since they just take space in the table and
            # pushed out other valuable headers.
            encoded = self._encode_indexed_literal(
                index, value, indexbit, huffman
            )
            if not sensitive:
                self.header_table.add(name, value)

        return encoded

    def _encode_indexed(self, index):
        """
        Encodes a header using the indexed representation.
        """
        field = encode_integer(index, 7)
        field[0] |= 0x80  # we set the top bit
        return bytes(field)

    def _encode_literal(self, name, value, indexbit, huffman=False):
        """
        Encodes a header with a literal name and literal value. If ``indexing``
        is True, the header will be added to the header table: otherwise it
        will not.
        """
        if huffman:
            name = self.huffman_coder.encode(name)
            value = self.huffman_coder.encode(value)

        name_len = encode_integer(len(name), 7)
        value_len = encode_integer(len(value), 7)

        if huffman:
            name_len[0] |= 0x80
            value_len[0] |= 0x80

        return b''.join(
            [indexbit, bytes(name_len), name, bytes(value_len), value]
        )

    def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
        """
        Encodes a header with an indexed name and a literal value and performs
        incremental indexing.
        """
        if indexbit != INDEX_INCREMENTAL:
            prefix = encode_integer(index, 4)
        else:
            prefix = encode_integer(index, 6)

        prefix[0] |= ord(indexbit)

        if huffman:
            value = self.huffman_coder.encode(value)

        value_len = encode_integer(len(value), 7)

        if huffman:
            value_len[0] |= 0x80

        return b''.join([bytes(prefix), bytes(value_len), value])

    def _encode_table_size_change(self):
        """
        Produces the encoded form of all header table size change context
        updates.
        """
        block = b''
        for size_bytes in self.table_size_changes:
            size_bytes = encode_integer(size_bytes, 5)
            size_bytes[0] |= 0x20
            block += bytes(size_bytes)
        self.table_size_changes = []
        return block


class Decoder(object):
    """
    An HPACK decoder object.

    .. versionchanged:: 2.3.0
       Added ``max_header_list_size`` argument.

    :param max_header_list_size: The maximum decompressed size we will allow
        for any single header block. This is a protection against DoS attacks
        that attempt to force the application to expand a relatively small
        amount of data into a really large header list, allowing enormous
        amounts of memory to be allocated.

        If this amount of data is exceeded, a `OversizedHeaderListError
        <hpack.OversizedHeaderListError>` exception will be raised. At this
        point the connection should be shut down, as the HPACK state will no
        longer be usable.

        Defaults to 64kB.
    :type max_header_list_size: ``int``
    """
    def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
        self.header_table = HeaderTable()

        #: The maximum decompressed size we will allow for any single header
        #: block. This is a protection against DoS attacks that attempt to
        #: force the application to expand a relatively small amount of data
        #: into a really large header list, allowing enormous amounts of memory
        #: to be allocated.
        #:
        #: If this amount of data is exceeded, a `OversizedHeaderListError
        #: <hpack.OversizedHeaderListError>` exception will be raised. At this
        #: point the connection should be shut down, as the HPACK state will no
        #: longer be usable.
        #:
        #: Defaults to 64kB.
        #:
        #: .. versionadded:: 2.3.0
        self.max_header_list_size = max_header_list_size

        #: Maximum allowed header table size.
        #:
        #: A HTTP/2 implementation should set this to the most recent value of
        #: SETTINGS_HEADER_TABLE_SIZE that it sent *and has received an ACK
        #: for*. Once this setting is set, the actual header table size will be
        #: checked at the end of each decoding run and whenever it is changed,
        #: to confirm that it fits in this size.
        self.max_allowed_table_size = self.header_table.maxsize

    @property
    def header_table_size(self):
        """
        Controls the size of the HPACK header table.
        """
        return self.header_table.maxsize

    @header_table_size.setter
    def header_table_size(self, value):
        self.header_table.maxsize = value

    def decode(self, data, raw=False):
        """
        Takes an HPACK-encoded header block and decodes it into a header set.

        :param data: A bytestring representing a complete HPACK-encoded header
                     block.
        :param raw: (optional) Whether to return the headers as tuples of raw
                    byte strings or to decode them as UTF-8 before returning
                    them. The default value is False, which returns tuples of
                    Unicode strings
        :returns: A list of two-tuples of ``(name, value)`` representing the
                  HPACK-encoded headers, in the order they were decoded.
        :raises HPACKDecodingError: If an error is encountered while decoding
                                    the header block.
        """
        log.debug("Decoding %s", data)

        data_mem = memoryview(data)
        headers = []
        data_len = len(data)
        inflated_size = 0
        current_index = 0

        while current_index < data_len:
            # Work out what kind of header we're decoding.
            # If the high bit is 1, it's an indexed field.
            current = to_byte(data[current_index])
            indexed = True if current & 0x80 else False

            # Otherwise, if the second-highest bit is 1 it's a field that does
            # alter the header table.
            literal_index = True if current & 0x40 else False

            # Otherwise, if the third-highest bit is 1 it's an encoding context
            # update.
            encoding_update = True if current & 0x20 else False

            if indexed:
                header, consumed = self._decode_indexed(
                    data_mem[current_index:]
                )
            elif literal_index:
                # It's a literal header that does affect the header table.
                header, consumed = self._decode_literal_index(
                    data_mem[current_index:]
                )
            elif encoding_update:
                # It's an update to the encoding context. These are forbidden
                # in a header block after any actual header.
                if headers:
                    raise HPACKDecodingError(
                        "Table size update not at the start of the block"
                    )
                consumed = self._update_encoding_context(
                    data_mem[current_index:]
                )
                header = None
            else:
                # It's a literal header that does not affect the header table.
                header, consumed = self._decode_literal_no_index(
                    data_mem[current_index:]
                )

            if header:
                headers.append(header)
                inflated_size += table_entry_size(*header)

                if inflated_size > self.max_header_list_size:
                    raise OversizedHeaderListError(
                        "A header list larger than %d has been received" %
                        self.max_header_list_size
                    )

            current_index += consumed

        # Confirm that the table size is lower than the maximum. We do this
        # here to ensure that we catch when the max has been *shrunk* and the
        # remote peer hasn't actually done that.
        self._assert_valid_table_size()

        try:
            return [_unicode_if_needed(h, raw) for h in headers]
        except UnicodeDecodeError:
            raise HPACKDecodingError("Unable to decode headers as UTF-8.")

    def _assert_valid_table_size(self):
        """
        Check that the table size set by the encoder is lower than the maximum
        we expect to have.
        """
        if self.header_table_size > self.max_allowed_table_size:
            raise InvalidTableSizeError(
                "Encoder did not shrink table size to within the max"
            )

    def _update_encoding_context(self, data):
        """
        Handles a byte that updates the encoding context.
        """
        # We've been asked to resize the header table.
        new_size, consumed = decode_integer(data, 5)
        if new_size > self.max_allowed_table_size:
            raise InvalidTableSizeError(
                "Encoder exceeded max allowable table size"
            )
        self.header_table_size = new_size
        return consumed

    def _decode_indexed(self, data):
        """
        Decodes a header represented using the indexed representation.
        """
        index, consumed = decode_integer(data, 7)
        header = HeaderTuple(*self.header_table.get_by_index(index))
        log.debug("Decoded %s, consumed %d", header, consumed)
        return header, consumed

    def _decode_literal_no_index(self, data):
        return self._decode_literal(data, False)

    def _decode_literal_index(self, data):
        return self._decode_literal(data, True)

    def _decode_literal(self, data, should_index):
        """
        Decodes a header represented with a literal.
        """
        total_consumed = 0

        # When should_index is true, if the low six bits of the first byte are
        # nonzero, the header name is indexed.
        # When should_index is false, if the low four bits of the first byte
        # are nonzero the header name is indexed.
        if should_index:
            indexed_name = to_byte(data[0]) & 0x3F
            name_len = 6
            not_indexable = False
        else:
            high_byte = to_byte(data[0])
            indexed_name = high_byte & 0x0F
            name_len = 4
            not_indexable = high_byte & 0x10

        if indexed_name:
            # Indexed header name.
            index, consumed = decode_integer(data, name_len)
            name = self.header_table.get_by_index(index)[0]

            total_consumed = consumed
            length = 0
        else:
            # Literal header name. The first byte was consumed, so we need to
            # move forward.
            data = data[1:]

            length, consumed = decode_integer(data, 7)
            name = data[consumed:consumed + length]
            if len(name) != length:
                raise HPACKDecodingError("Truncated header block")

            if to_byte(data[0]) & 0x80:
                name = decode_huffman(name)
            total_consumed = consumed + length + 1  # Since we moved forward 1.

        data = data[consumed + length:]

        # The header value is definitely length-based.
        length, consumed = decode_integer(data, 7)
        value = data[consumed:consumed + length]
        if len(value) != length:
            raise HPACKDecodingError("Truncated header block")

        if to_byte(data[0]) & 0x80:
            value = decode_huffman(value)

        # Updated the total consumed length.
        total_consumed += length + consumed

        # If we have been told never to index the header field, encode that in
        # the tuple we use.
        if not_indexable:
            header = NeverIndexedHeaderTuple(name, value)
        else:
            header = HeaderTuple(name, value)

        # If we've been asked to index this, add it to the header table.
        if should_index:
            self.header_table.add(name, value)

        log.debug(
            "Decoded %s, total consumed %d bytes, indexed %s",
            header,
            total_consumed,
            should_index
        )

        return header, total_consumed
 0707010001f45c000081a40000000000000000000000016a100daf00001214000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/foreign/hpack/huffman_constants.py # -*- coding: utf-8 -*-
"""
hpack/huffman_constants
~~~~~~~~~~~~~~~~~~~~~~~

Defines the constant Huffman table. This takes up an upsetting amount of space,
but c'est la vie.
"""

REQUEST_CODES = [
    0x1ff8,
    0x7fffd8,
    0xfffffe2,
    0xfffffe3,
    0xfffffe4,
    0xfffffe5,
    0xfffffe6,
    0xfffffe7,
    0xfffffe8,
    0xffffea,
    0x3ffffffc,
    0xfffffe9,
    0xfffffea,
    0x3ffffffd,
    0xfffffeb,
    0xfffffec,
    0xfffffed,
    0xfffffee,
    0xfffffef,
    0xffffff0,
    0xffffff1,
    0xffffff2,
    0x3ffffffe,
    0xffffff3,
    0xffffff4,
    0xffffff5,
    0xffffff6,
    0xffffff7,
    0xffffff8,
    0xffffff9,
    0xffffffa,
    0xffffffb,
    0x14,
    0x3f8,
    0x3f9,
    0xffa,
    0x1ff9,
    0x15,
    0xf8,
    0x7fa,
    0x3fa,
    0x3fb,
    0xf9,
    0x7fb,
    0xfa,
    0x16,
    0x17,
    0x18,
    0x0,
    0x1,
    0x2,
    0x19,
    0x1a,
    0x1b,
    0x1c,
    0x1d,
    0x1e,
    0x1f,
    0x5c,
    0xfb,
    0x7ffc,
    0x20,
    0xffb,
    0x3fc,
    0x1ffa,
    0x21,
    0x5d,
    0x5e,
    0x5f,
    0x60,
    0x61,
    0x62,
    0x63,
    0x64,
    0x65,
    0x66,
    0x67,
    0x68,
    0x69,
    0x6a,
    0x6b,
    0x6c,
    0x6d,
    0x6e,
    0x6f,
    0x70,
    0x71,
    0x72,
    0xfc,
    0x73,
    0xfd,
    0x1ffb,
    0x7fff0,
    0x1ffc,
    0x3ffc,
    0x22,
    0x7ffd,
    0x3,
    0x23,
    0x4,
    0x24,
    0x5,
    0x25,
    0x26,
    0x27,
    0x6,
    0x74,
    0x75,
    0x28,
    0x29,
    0x2a,
    0x7,
    0x2b,
    0x76,
    0x2c,
    0x8,
    0x9,
    0x2d,
    0x77,
    0x78,
    0x79,
    0x7a,
    0x7b,
    0x7ffe,
    0x7fc,
    0x3ffd,
    0x1ffd,
    0xffffffc,
    0xfffe6,
    0x3fffd2,
    0xfffe7,
    0xfffe8,
    0x3fffd3,
    0x3fffd4,
    0x3fffd5,
    0x7fffd9,
    0x3fffd6,
    0x7fffda,
    0x7fffdb,
    0x7fffdc,
    0x7fffdd,
    0x7fffde,
    0xffffeb,
    0x7fffdf,
    0xffffec,
    0xffffed,
    0x3fffd7,
    0x7fffe0,
    0xffffee,
    0x7fffe1,
    0x7fffe2,
    0x7fffe3,
    0x7fffe4,
    0x1fffdc,
    0x3fffd8,
    0x7fffe5,
    0x3fffd9,
    0x7fffe6,
    0x7fffe7,
    0xffffef,
    0x3fffda,
    0x1fffdd,
    0xfffe9,
    0x3fffdb,
    0x3fffdc,
    0x7fffe8,
    0x7fffe9,
    0x1fffde,
    0x7fffea,
    0x3fffdd,
    0x3fffde,
    0xfffff0,
    0x1fffdf,
    0x3fffdf,
    0x7fffeb,
    0x7fffec,
    0x1fffe0,
    0x1fffe1,
    0x3fffe0,
    0x1fffe2,
    0x7fffed,
    0x3fffe1,
    0x7fffee,
    0x7fffef,
    0xfffea,
    0x3fffe2,
    0x3fffe3,
    0x3fffe4,
    0x7ffff0,
    0x3fffe5,
    0x3fffe6,
    0x7ffff1,
    0x3ffffe0,
    0x3ffffe1,
    0xfffeb,
    0x7fff1,
    0x3fffe7,
    0x7ffff2,
    0x3fffe8,
    0x1ffffec,
    0x3ffffe2,
    0x3ffffe3,
    0x3ffffe4,
    0x7ffffde,
    0x7ffffdf,
    0x3ffffe5,
    0xfffff1,
    0x1ffffed,
    0x7fff2,
    0x1fffe3,
    0x3ffffe6,
    0x7ffffe0,
    0x7ffffe1,
    0x3ffffe7,
    0x7ffffe2,
    0xfffff2,
    0x1fffe4,
    0x1fffe5,
    0x3ffffe8,
    0x3ffffe9,
    0xffffffd,
    0x7ffffe3,
    0x7ffffe4,
    0x7ffffe5,
    0xfffec,
    0xfffff3,
    0xfffed,
    0x1fffe6,
    0x3fffe9,
    0x1fffe7,
    0x1fffe8,
    0x7ffff3,
    0x3fffea,
    0x3fffeb,
    0x1ffffee,
    0x1ffffef,
    0xfffff4,
    0xfffff5,
    0x3ffffea,
    0x7ffff4,
    0x3ffffeb,
    0x7ffffe6,
    0x3ffffec,
    0x3ffffed,
    0x7ffffe7,
    0x7ffffe8,
    0x7ffffe9,
    0x7ffffea,
    0x7ffffeb,
    0xffffffe,
    0x7ffffec,
    0x7ffffed,
    0x7ffffee,
    0x7ffffef,
    0x7fffff0,
    0x3ffffee,
    0x3fffffff,
]

REQUEST_CODES_LENGTH = [
    13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
    28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
     6, 10, 10, 12, 13,  6,  8, 11, 10, 10,  8, 11,  8,  6,  6,  6,
     5,  5,  5,  6,  6,  6,  6,  6,  6,  6,  7,  8, 15,  6, 12, 10,
    13,  6,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
     7,  7,  7,  7,  7,  7,  7,  7,  8,  7,  8, 13, 19, 13, 14,  6,
    15,  5,  6,  5,  6,  5,  6,  6,  6,  5,  7,  7,  6,  6,  6,  5,
     6,  7,  6,  5,  5,  6,  7,  7,  7,  7,  7, 15, 11, 14, 13, 28,
    20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
    24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
    22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
    21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
    26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
    19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
    20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
    26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
    30,
]
0707010001f44a000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002a00000000root/usr/share/opensvc/opensvc/foreign/h2 0707010001f44b000081a40000000000000000000000016a100daf0000005a000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/h2/__init__.py # -*- coding: utf-8 -*-
"""
h2
~~

A HTTP/2 implementation.
"""
__version__ = '3.2.0dev0'
  0707010001f452000081a40000000000000000000000016a100daf00002e28000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/h2/settings.py # -*- coding: utf-8 -*-
"""
h2/settings
~~~~~~~~~~~

This module contains a HTTP/2 settings object. This object provides a simple
API for manipulating HTTP/2 settings, keeping track of both the current active
state of the settings and the unacknowledged future values of the settings.
"""
import collections
import aenum

from hyperframe.frame import SettingsFrame

from h2.errors import ErrorCodes
from h2.exceptions import InvalidSettingsValueError

try:
    from collections.abc import MutableMapping
except ImportError:  # pragma: no cover
    # Python 2.7 compatibility
    from collections import MutableMapping


class SettingCodes(aenum.IntEnum):
    """
    All known HTTP/2 setting codes.

    .. versionadded:: 2.6.0
    """

    #: Allows the sender to inform the remote endpoint of the maximum size of
    #: the header compression table used to decode header blocks, in octets.
    HEADER_TABLE_SIZE = SettingsFrame.HEADER_TABLE_SIZE

    #: This setting can be used to disable server push. To disable server push
    #: on a client, set this to 0.
    ENABLE_PUSH = SettingsFrame.ENABLE_PUSH

    #: Indicates the maximum number of concurrent streams that the sender will
    #: allow.
    MAX_CONCURRENT_STREAMS = SettingsFrame.MAX_CONCURRENT_STREAMS

    #: Indicates the sender's initial window size (in octets) for stream-level
    #: flow control.
    INITIAL_WINDOW_SIZE = SettingsFrame.INITIAL_WINDOW_SIZE

    #: Indicates the size of the largest frame payload that the sender is
    #: willing to receive, in octets.
    MAX_FRAME_SIZE = SettingsFrame.MAX_FRAME_SIZE

    #: This advisory setting informs a peer of the maximum size of header list
    #: that the sender is prepared to accept, in octets.  The value is based on
    #: the uncompressed size of header fields, including the length of the name
    #: and value in octets plus an overhead of 32 octets for each header field.
    MAX_HEADER_LIST_SIZE = SettingsFrame.MAX_HEADER_LIST_SIZE

    #: This setting can be used to enable the connect protocol. To enable on a
    #: client set this to 1.
    ENABLE_CONNECT_PROTOCOL = SettingsFrame.ENABLE_CONNECT_PROTOCOL


def _setting_code_from_int(code):
    """
    Given an integer setting code, returns either one of :class:`SettingCodes
    <h2.settings.SettingCodes>` or, if not present in the known set of codes,
    returns the integer directly.
    """
    try:
        return SettingCodes(code)
    except ValueError:
        return code


class ChangedSetting:

    def __init__(self, setting, original_value, new_value):
        #: The setting code given. Either one of :class:`SettingCodes
        #: <h2.settings.SettingCodes>` or ``int``
        #:
        #: .. versionchanged:: 2.6.0
        self.setting = setting

        #: The original value before being changed.
        self.original_value = original_value

        #: The new value after being changed.
        self.new_value = new_value

    def __repr__(self):
        return (
            "ChangedSetting(setting=%s, original_value=%s, "
            "new_value=%s)"
        ) % (
            self.setting,
            self.original_value,
            self.new_value
        )


class Settings(MutableMapping):
    """
    An object that encapsulates HTTP/2 settings state.

    HTTP/2 Settings are a complex beast. Each party, remote and local, has its
    own settings and a view of the other party's settings. When a settings
    frame is emitted by a peer it cannot assume that the new settings values
    are in place until the remote peer acknowledges the setting. In principle,
    multiple settings changes can be "in flight" at the same time, all with
    different values.

    This object encapsulates this mess. It provides a dict-like interface to
    settings, which return the *current* values of the settings in question.
    Additionally, it keeps track of the stack of proposed values: each time an
    acknowledgement is sent/received, it updates the current values with the
    stack of proposed values. On top of all that, it validates the values to
    make sure they're allowed, and raises :class:`InvalidSettingsValueError
    <h2.exceptions.InvalidSettingsValueError>` if they are not.

    Finally, this object understands what the default values of the HTTP/2
    settings are, and sets those defaults appropriately.

    .. versionchanged:: 2.2.0
       Added the ``initial_values`` parameter.

    .. versionchanged:: 2.5.0
       Added the ``max_header_list_size`` property.

    :param client: (optional) Whether these settings should be defaulted for a
        client implementation or a server implementation. Defaults to ``True``.
    :type client: ``bool``
    :param initial_values: (optional) Any initial values the user would like
        set, rather than RFC 7540's defaults.
    :type initial_vales: ``MutableMapping``
    """
    def __init__(self, client=True, initial_values=None):
        # Backing object for the settings. This is a dictionary of
        # (setting: [list of values]), where the first value in the list is the
        # current value of the setting. Strictly this doesn't use lists but
        # instead uses collections.deque to avoid repeated memory allocations.
        #
        # This contains the default values for HTTP/2.
        self._settings = {
            SettingCodes.HEADER_TABLE_SIZE: collections.deque([4096]),
            SettingCodes.ENABLE_PUSH: collections.deque([int(client)]),
            SettingCodes.INITIAL_WINDOW_SIZE: collections.deque([65535]),
            SettingCodes.MAX_FRAME_SIZE: collections.deque([16384]),
            SettingCodes.ENABLE_CONNECT_PROTOCOL: collections.deque([0]),
        }
        if initial_values is not None:
            for key, value in initial_values.items():
                invalid = _validate_setting(key, value)
                if invalid:
                    raise InvalidSettingsValueError(
                        "Setting %d has invalid value %d" % (key, value),
                        error_code=invalid
                    )
                self._settings[key] = collections.deque([value])

    def acknowledge(self):
        """
        The settings have been acknowledged, either by the user (remote
        settings) or by the remote peer (local settings).

        :returns: A dict of {setting: ChangedSetting} that were applied.
        """
        changed_settings = {}

        # If there is more than one setting in the list, we have a setting
        # value outstanding. Update them.
        for k, v in self._settings.items():
            if len(v) > 1:
                old_setting = v.popleft()
                new_setting = v[0]
                changed_settings[k] = ChangedSetting(
                    k, old_setting, new_setting
                )

        return changed_settings

    # Provide easy-access to well known settings.
    @property
    def header_table_size(self):
        """
        The current value of the :data:`HEADER_TABLE_SIZE
        <h2.settings.SettingCodes.HEADER_TABLE_SIZE>` setting.
        """
        return self[SettingCodes.HEADER_TABLE_SIZE]

    @header_table_size.setter
    def header_table_size(self, value):
        self[SettingCodes.HEADER_TABLE_SIZE] = value

    @property
    def enable_push(self):
        """
        The current value of the :data:`ENABLE_PUSH
        <h2.settings.SettingCodes.ENABLE_PUSH>` setting.
        """
        return self[SettingCodes.ENABLE_PUSH]

    @enable_push.setter
    def enable_push(self, value):
        self[SettingCodes.ENABLE_PUSH] = value

    @property
    def initial_window_size(self):
        """
        The current value of the :data:`INITIAL_WINDOW_SIZE
        <h2.settings.SettingCodes.INITIAL_WINDOW_SIZE>` setting.
        """
        return self[SettingCodes.INITIAL_WINDOW_SIZE]

    @initial_window_size.setter
    def initial_window_size(self, value):
        self[SettingCodes.INITIAL_WINDOW_SIZE] = value

    @property
    def max_frame_size(self):
        """
        The current value of the :data:`MAX_FRAME_SIZE
        <h2.settings.SettingCodes.MAX_FRAME_SIZE>` setting.
        """
        return self[SettingCodes.MAX_FRAME_SIZE]

    @max_frame_size.setter
    def max_frame_size(self, value):
        self[SettingCodes.MAX_FRAME_SIZE] = value

    @property
    def max_concurrent_streams(self):
        """
        The current value of the :data:`MAX_CONCURRENT_STREAMS
        <h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS>` setting.
        """
        return self.get(SettingCodes.MAX_CONCURRENT_STREAMS, 2**32+1)

    @max_concurrent_streams.setter
    def max_concurrent_streams(self, value):
        self[SettingCodes.MAX_CONCURRENT_STREAMS] = value

    @property
    def max_header_list_size(self):
        """
        The current value of the :data:`MAX_HEADER_LIST_SIZE
        <h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE>` setting. If not set,
        returns ``None``, which means unlimited.

        .. versionadded:: 2.5.0
        """
        return self.get(SettingCodes.MAX_HEADER_LIST_SIZE, None)

    @max_header_list_size.setter
    def max_header_list_size(self, value):
        self[SettingCodes.MAX_HEADER_LIST_SIZE] = value

    @property
    def enable_connect_protocol(self):
        """
        The current value of the :data:`ENABLE_CONNECT_PROTOCOL
        <h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL>` setting.
        """
        return self[SettingCodes.ENABLE_CONNECT_PROTOCOL]

    @enable_connect_protocol.setter
    def enable_connect_protocol(self, value):
        self[SettingCodes.ENABLE_CONNECT_PROTOCOL] = value

    # Implement the MutableMapping API.
    def __getitem__(self, key):
        val = self._settings[key][0]

        # Things that were created when a setting was received should stay
        # KeyError'd.
        if val is None:
            raise KeyError

        return val

    def __setitem__(self, key, value):
        invalid = _validate_setting(key, value)
        if invalid:
            raise InvalidSettingsValueError(
                "Setting %d has invalid value %d" % (key, value),
                error_code=invalid
            )

        try:
            items = self._settings[key]
        except KeyError:
            items = collections.deque([None])
            self._settings[key] = items

        items.append(value)

    def __delitem__(self, key):
        del self._settings[key]

    def __iter__(self):
        return self._settings.__iter__()

    def __len__(self):
        return len(self._settings)

    def __eq__(self, other):
        if isinstance(other, Settings):
            return self._settings == other._settings
        else:
            return NotImplemented

    def __ne__(self, other):
        if isinstance(other, Settings):
            return not self == other
        else:
            return NotImplemented


def _validate_setting(setting, value):  # noqa: C901
    """
    Confirms that a specific setting has a well-formed value. If the setting is
    invalid, returns an error code. Otherwise, returns 0 (NO_ERROR).
    """
    if setting == SettingCodes.ENABLE_PUSH:
        if value not in (0, 1):
            return ErrorCodes.PROTOCOL_ERROR
    elif setting == SettingCodes.INITIAL_WINDOW_SIZE:
        if not 0 <= value <= 2147483647:  # 2^31 - 1
            return ErrorCodes.FLOW_CONTROL_ERROR
    elif setting == SettingCodes.MAX_FRAME_SIZE:
        if not 16384 <= value <= 16777215:  # 2^14 and 2^24 - 1
            return ErrorCodes.PROTOCOL_ERROR
    elif setting == SettingCodes.MAX_HEADER_LIST_SIZE:
        if value < 0:
            return ErrorCodes.PROTOCOL_ERROR
    elif setting == SettingCodes.ENABLE_CONNECT_PROTOCOL:
        if value not in (0, 1):
            return ErrorCodes.PROTOCOL_ERROR

    return 0
0707010001f44d000081a40000000000000000000000016a100daf00013e8a000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/h2/connection.py   # -*- coding: utf-8 -*-
"""
h2/connection
~~~~~~~~~~~~~

An implementation of a HTTP/2 connection.
"""
import base64

from aenum import Enum, IntEnum

from hyperframe.exceptions import InvalidPaddingError
from hyperframe.frame import (
    GoAwayFrame, WindowUpdateFrame, HeadersFrame, DataFrame, PingFrame,
    PushPromiseFrame, SettingsFrame, RstStreamFrame, PriorityFrame,
    ContinuationFrame, AltSvcFrame, ExtensionFrame
)
from hpack.hpack import Encoder, Decoder
from hpack.exceptions import HPACKError, OversizedHeaderListError

from .config import H2Configuration
from .errors import ErrorCodes, _error_code_from_int
from .events import (
    WindowUpdated, RemoteSettingsChanged, PingReceived, PingAckReceived,
    SettingsAcknowledged, ConnectionTerminated, PriorityUpdated,
    AlternativeServiceAvailable, UnknownFrameReceived
)
from .exceptions import (
    ProtocolError, NoSuchStreamError, FlowControlError, FrameTooLargeError,
    TooManyStreamsError, StreamClosedError, StreamIDTooLowError,
    NoAvailableStreamIDError, RFC1122Error, DenialOfServiceError
)
from .frame_buffer import FrameBuffer
from .settings import Settings, SettingCodes
from .stream import H2Stream, StreamClosedBy
from .utilities import SizeLimitDict, guard_increment_window
from .windows import WindowManager


class ConnectionState(Enum):
    IDLE = 0
    CLIENT_OPEN = 1
    SERVER_OPEN = 2
    CLOSED = 3


class ConnectionInputs(Enum):
    SEND_HEADERS = 0
    SEND_PUSH_PROMISE = 1
    SEND_DATA = 2
    SEND_GOAWAY = 3
    SEND_WINDOW_UPDATE = 4
    SEND_PING = 5
    SEND_SETTINGS = 6
    SEND_RST_STREAM = 7
    SEND_PRIORITY = 8
    RECV_HEADERS = 9
    RECV_PUSH_PROMISE = 10
    RECV_DATA = 11
    RECV_GOAWAY = 12
    RECV_WINDOW_UPDATE = 13
    RECV_PING = 14
    RECV_SETTINGS = 15
    RECV_RST_STREAM = 16
    RECV_PRIORITY = 17
    SEND_ALTERNATIVE_SERVICE = 18  # Added in 2.3.0
    RECV_ALTERNATIVE_SERVICE = 19  # Added in 2.3.0


class AllowedStreamIDs(IntEnum):
    EVEN = 0
    ODD = 1


class H2ConnectionStateMachine(object):
    """
    A single HTTP/2 connection state machine.

    This state machine, while defined in its own class, is logically part of
    the H2Connection class also defined in this file. The state machine itself
    maintains very little state directly, instead focusing entirely on managing
    state transitions.
    """
    # For the purposes of this state machine we treat HEADERS and their
    # associated CONTINUATION frames as a single jumbo frame. The protocol
    # allows/requires this by preventing other frames from being interleved in
    # between HEADERS/CONTINUATION frames.
    #
    # The _transitions dictionary contains a mapping of tuples of
    # (state, input) to tuples of (side_effect_function, end_state). This map
    # contains all allowed transitions: anything not in this map is invalid
    # and immediately causes a transition to ``closed``.

    _transitions = {
        # State: idle
        (ConnectionState.IDLE, ConnectionInputs.SEND_HEADERS):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.IDLE, ConnectionInputs.RECV_HEADERS):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.IDLE, ConnectionInputs.SEND_SETTINGS):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.RECV_SETTINGS):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.SEND_WINDOW_UPDATE):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.RECV_WINDOW_UPDATE):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.SEND_PING):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.RECV_PING):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.SEND_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.IDLE, ConnectionInputs.RECV_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.IDLE, ConnectionInputs.SEND_PRIORITY):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.RECV_PRIORITY):
            (None, ConnectionState.IDLE),
        (ConnectionState.IDLE, ConnectionInputs.SEND_ALTERNATIVE_SERVICE):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.IDLE, ConnectionInputs.RECV_ALTERNATIVE_SERVICE):
            (None, ConnectionState.CLIENT_OPEN),

        # State: open, client side.
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_HEADERS):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_DATA):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_WINDOW_UPDATE):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_PING):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_SETTINGS):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_PRIORITY):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_HEADERS):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PUSH_PROMISE):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_DATA):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_WINDOW_UPDATE):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PING):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_SETTINGS):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.SEND_RST_STREAM):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_RST_STREAM):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN, ConnectionInputs.RECV_PRIORITY):
            (None, ConnectionState.CLIENT_OPEN),
        (ConnectionState.CLIENT_OPEN,
            ConnectionInputs.RECV_ALTERNATIVE_SERVICE):
                (None, ConnectionState.CLIENT_OPEN),

        # State: open, server side.
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_HEADERS):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PUSH_PROMISE):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_DATA):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_WINDOW_UPDATE):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PING):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_SETTINGS):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_PRIORITY):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_HEADERS):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_DATA):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_WINDOW_UPDATE):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_PING):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_SETTINGS):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_PRIORITY):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.SEND_RST_STREAM):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN, ConnectionInputs.RECV_RST_STREAM):
            (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN,
            ConnectionInputs.SEND_ALTERNATIVE_SERVICE):
                (None, ConnectionState.SERVER_OPEN),
        (ConnectionState.SERVER_OPEN,
            ConnectionInputs.RECV_ALTERNATIVE_SERVICE):
                (None, ConnectionState.SERVER_OPEN),

        # State: closed
        (ConnectionState.CLOSED, ConnectionInputs.SEND_GOAWAY):
            (None, ConnectionState.CLOSED),
        (ConnectionState.CLOSED, ConnectionInputs.RECV_GOAWAY):
            (None, ConnectionState.CLOSED),
    }

    def __init__(self):
        self.state = ConnectionState.IDLE

    def process_input(self, input_):
        """
        Process a specific input in the state machine.
        """
        if not isinstance(input_, ConnectionInputs):
            raise ValueError("Input must be an instance of ConnectionInputs")

        try:
            func, target_state = self._transitions[(self.state, input_)]
        except KeyError:
            old_state = self.state
            self.state = ConnectionState.CLOSED
            raise ProtocolError(
                "Invalid input %s in state %s" % (input_, old_state)
            )
        else:
            self.state = target_state
            if func is not None:  # pragma: no cover
                return func()

            return []


class H2Connection(object):
    """
    A low-level HTTP/2 connection object. This handles building and receiving
    frames and maintains both connection and per-stream state for all streams
    on this connection.

    This wraps a HTTP/2 Connection state machine implementation, ensuring that
    frames can only be sent/received when the connection is in a valid state.
    It also builds stream state machines on demand to ensure that the
    constraints of those state machines are met as well. Attempts to create
    frames that cannot be sent will raise a ``ProtocolError``.

    .. versionchanged:: 2.3.0
       Added the ``header_encoding`` keyword argument.

    .. versionchanged:: 2.5.0
       Added the ``config`` keyword argument. Deprecated the ``client_side``
       and ``header_encoding`` parameters.

    .. versionchanged:: 3.0.0
       Removed deprecated parameters and properties.

    :param config: The configuration for the HTTP/2 connection.

        .. versionadded:: 2.5.0

    :type config: :class:`H2Configuration <h2.config.H2Configuration>`
    """
    # The initial maximum outbound frame size. This can be changed by receiving
    # a settings frame.
    DEFAULT_MAX_OUTBOUND_FRAME_SIZE = 65535

    # The initial maximum inbound frame size. This is somewhat arbitrarily
    # chosen.
    DEFAULT_MAX_INBOUND_FRAME_SIZE = 2**24

    # The highest acceptable stream ID.
    HIGHEST_ALLOWED_STREAM_ID = 2**31 - 1

    # The largest acceptable window increment.
    MAX_WINDOW_INCREMENT = 2**31 - 1

    # The initial default value of SETTINGS_MAX_HEADER_LIST_SIZE.
    DEFAULT_MAX_HEADER_LIST_SIZE = 2**16

    # Keep in memory limited amount of results for streams closes
    MAX_CLOSED_STREAMS = 2**16

    def __init__(self, config=None):
        self.state_machine = H2ConnectionStateMachine()
        self.streams = {}
        self.highest_inbound_stream_id = 0
        self.highest_outbound_stream_id = 0
        self.encoder = Encoder()
        self.decoder = Decoder()

        # This won't always actually do anything: for versions of HPACK older
        # than 2.3.0 it does nothing. However, we have to try!
        self.decoder.max_header_list_size = self.DEFAULT_MAX_HEADER_LIST_SIZE

        #: The configuration for this HTTP/2 connection object.
        #:
        #: .. versionadded:: 2.5.0
        self.config = config
        if self.config is None:
            self.config = H2Configuration(
                client_side=True,
            )

        # Objects that store settings, including defaults.
        #
        # We set the MAX_CONCURRENT_STREAMS value to 100 because its default is
        # unbounded, and that's a dangerous default because it allows
        # essentially unbounded resources to be allocated regardless of how
        # they will be used. 100 should be suitable for the average
        # application. This default obviously does not apply to the remote
        # peer's settings: the remote peer controls them!
        #
        # We also set MAX_HEADER_LIST_SIZE to a reasonable value. This is to
        # advertise our defence against CVE-2016-6581. However, not all
        # versions of HPACK will let us do it. That's ok: we should at least
        # suggest that we're not vulnerable.
        self.local_settings = Settings(
            client=self.config.client_side,
            initial_values={
                SettingCodes.MAX_CONCURRENT_STREAMS: 100,
                SettingCodes.MAX_HEADER_LIST_SIZE:
                    self.DEFAULT_MAX_HEADER_LIST_SIZE,
            }
        )
        self.remote_settings = Settings(client=not self.config.client_side)

        # The current value of the connection flow control windows on the
        # connection.
        self.outbound_flow_control_window = (
            self.remote_settings.initial_window_size
        )

        #: The maximum size of a frame that can be emitted by this peer, in
        #: bytes.
        self.max_outbound_frame_size = self.remote_settings.max_frame_size

        #: The maximum size of a frame that can be received by this peer, in
        #: bytes.
        self.max_inbound_frame_size = self.local_settings.max_frame_size

        # Buffer for incoming data.
        self.incoming_buffer = FrameBuffer(server=not self.config.client_side)

        # A private variable to store a sequence of received header frames
        # until completion.
        self._header_frames = []

        # Data that needs to be sent.
        self._data_to_send = bytearray()

        # Keeps track of how streams are closed.
        # Used to ensure that we don't blow up in the face of frames that were
        # in flight when a RST_STREAM was sent.
        # Also used to determine whether we should consider a frame received
        # while a stream is closed as either a stream error or a connection
        # error.
        self._closed_streams = SizeLimitDict(
            size_limit=self.MAX_CLOSED_STREAMS
        )

        # The flow control window manager for the connection.
        self._inbound_flow_control_window_manager = WindowManager(
            max_window_size=self.local_settings.initial_window_size
        )

        # When in doubt use dict-dispatch.
        self._frame_dispatch_table = {
            HeadersFrame: self._receive_headers_frame,
            PushPromiseFrame: self._receive_push_promise_frame,
            SettingsFrame: self._receive_settings_frame,
            DataFrame: self._receive_data_frame,
            WindowUpdateFrame: self._receive_window_update_frame,
            PingFrame: self._receive_ping_frame,
            RstStreamFrame: self._receive_rst_stream_frame,
            PriorityFrame: self._receive_priority_frame,
            GoAwayFrame: self._receive_goaway_frame,
            ContinuationFrame: self._receive_naked_continuation,
            AltSvcFrame: self._receive_alt_svc_frame,
            ExtensionFrame: self._receive_unknown_frame
        }

    def _prepare_for_sending(self, frames):
        if not frames:
            return
        self._data_to_send += b''.join(f.serialize() for f in frames)
        assert all(f.body_len <= self.max_outbound_frame_size for f in frames)

    def _open_streams(self, remainder):
        """
        A common method of counting number of open streams. Returns the number
        of streams that are open *and* that have (stream ID % 2) == remainder.
        While it iterates, also deletes any closed streams.
        """
        count = 0
        to_delete = []

        for stream_id, stream in self.streams.items():
            if stream.open and (stream_id % 2 == remainder):
                count += 1
            elif stream.closed:
                to_delete.append(stream_id)

        for stream_id in to_delete:
            stream = self.streams.pop(stream_id)
            self._closed_streams[stream_id] = stream.closed_by

        return count

    @property
    def open_outbound_streams(self):
        """
        The current number of open outbound streams.
        """
        outbound_numbers = int(self.config.client_side)
        return self._open_streams(outbound_numbers)

    @property
    def open_inbound_streams(self):
        """
        The current number of open inbound streams.
        """
        inbound_numbers = int(not self.config.client_side)
        return self._open_streams(inbound_numbers)

    @property
    def inbound_flow_control_window(self):
        """
        The size of the inbound flow control window for the connection. This is
        rarely publicly useful: instead, use :meth:`remote_flow_control_window
        <h2.connection.H2Connection.remote_flow_control_window>`. This
        shortcut is largely present to provide a shortcut to this data.
        """
        return self._inbound_flow_control_window_manager.current_window_size

    def _begin_new_stream(self, stream_id, allowed_ids):
        """
        Initiate a new stream.

        .. versionchanged:: 2.0.0
           Removed this function from the public API.

        :param stream_id: The ID of the stream to open.
        :param allowed_ids: What kind of stream ID is allowed.
        """
        self.config.logger.debug(
            "Attempting to initiate stream ID %d", stream_id
        )
        outbound = self._stream_id_is_outbound(stream_id)
        highest_stream_id = (
            self.highest_outbound_stream_id if outbound else
            self.highest_inbound_stream_id
        )

        if stream_id <= highest_stream_id:
            raise StreamIDTooLowError(stream_id, highest_stream_id)

        if (stream_id % 2) != int(allowed_ids):
            raise ProtocolError(
                "Invalid stream ID for peer."
            )

        s = H2Stream(
            stream_id,
            config=self.config,
            inbound_window_size=self.local_settings.initial_window_size,
            outbound_window_size=self.remote_settings.initial_window_size
        )
        self.config.logger.debug("Stream ID %d created", stream_id)
        s.max_inbound_frame_size = self.max_inbound_frame_size
        s.max_outbound_frame_size = self.max_outbound_frame_size

        self.streams[stream_id] = s
        self.config.logger.debug("Current streams: %s", self.streams.keys())

        if outbound:
            self.highest_outbound_stream_id = stream_id
        else:
            self.highest_inbound_stream_id = stream_id

        return s

    def initiate_connection(self):
        """
        Provides any data that needs to be sent at the start of the connection.
        Must be called for both clients and servers.
        """
        self.config.logger.debug("Initializing connection")
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        if self.config.client_side:
            preamble = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
        else:
            preamble = b''

        f = SettingsFrame(0)
        for setting, value in self.local_settings.items():
            f.settings[setting] = value
        self.config.logger.debug(
            "Send Settings frame: %s", self.local_settings
        )

        self._data_to_send += preamble + f.serialize()

    def initiate_upgrade_connection(self, settings_header=None):
        """
        Call to initialise the connection object for use with an upgraded
        HTTP/2 connection (i.e. a connection negotiated using the
        ``Upgrade: h2c`` HTTP header).

        This method differs from :meth:`initiate_connection
        <h2.connection.H2Connection.initiate_connection>` in several ways.
        Firstly, it handles the additional SETTINGS frame that is sent in the
        ``HTTP2-Settings`` header field. When called on a client connection,
        this method will return a bytestring that the caller can put in the
        ``HTTP2-Settings`` field they send on their initial request. When
        called on a server connection, the user **must** provide the value they
        received from the client in the ``HTTP2-Settings`` header field to the
        ``settings_header`` argument, which will be used appropriately.

        Additionally, this method sets up stream 1 in a half-closed state
        appropriate for this side of the connection, to reflect the fact that
        the request is already complete.

        Finally, this method also prepares the appropriate preamble to be sent
        after the upgrade.

        .. versionadded:: 2.3.0

        :param settings_header: (optional, server-only): The value of the
             ``HTTP2-Settings`` header field received from the client.
        :type settings_header: ``bytes``

        :returns: For clients, a bytestring to put in the ``HTTP2-Settings``.
            For servers, returns nothing.
        :rtype: ``bytes`` or ``None``
        """
        self.config.logger.debug(
            "Upgrade connection. Current settings: %s", self.local_settings
        )

        frame_data = None
        # Begin by getting the preamble in place.
        self.initiate_connection()

        if self.config.client_side:
            f = SettingsFrame(0)
            for setting, value in self.local_settings.items():
                f.settings[setting] = value

            frame_data = f.serialize_body()
            frame_data = base64.urlsafe_b64encode(frame_data)
        elif settings_header:
            # We have a settings header from the client. This needs to be
            # applied, but we want to throw away the ACK. We do this by
            # inserting the data into a Settings frame and then passing it to
            # the state machine, but ignoring the return value.
            settings_header = base64.urlsafe_b64decode(settings_header)
            f = SettingsFrame(0)
            f.parse_body(settings_header)
            self._receive_settings_frame(f)

        # Set up appropriate state. Stream 1 in a half-closed state:
        # half-closed(local) for clients, half-closed(remote) for servers.
        # Additionally, we need to set up the Connection state machine.
        connection_input = (
            ConnectionInputs.SEND_HEADERS if self.config.client_side
            else ConnectionInputs.RECV_HEADERS
        )
        self.config.logger.debug("Process input %s", connection_input)
        self.state_machine.process_input(connection_input)

        # Set up stream 1.
        self._begin_new_stream(stream_id=1, allowed_ids=AllowedStreamIDs.ODD)
        self.streams[1].upgrade(self.config.client_side)
        return frame_data

    def _get_or_create_stream(self, stream_id, allowed_ids):
        """
        Gets a stream by its stream ID. Will create one if one does not already
        exist. Use allowed_ids to circumvent the usual stream ID rules for
        clients and servers.

        .. versionchanged:: 2.0.0
           Removed this function from the public API.
        """
        try:
            return self.streams[stream_id]
        except KeyError:
            return self._begin_new_stream(stream_id, allowed_ids)

    def _get_stream_by_id(self, stream_id):
        """
        Gets a stream by its stream ID. Raises NoSuchStreamError if the stream
        ID does not correspond to a known stream and is higher than the current
        maximum: raises if it is lower than the current maximum.

        .. versionchanged:: 2.0.0
           Removed this function from the public API.
        """
        try:
            return self.streams[stream_id]
        except KeyError:
            outbound = self._stream_id_is_outbound(stream_id)
            highest_stream_id = (
                self.highest_outbound_stream_id if outbound else
                self.highest_inbound_stream_id
            )

            if stream_id > highest_stream_id:
                raise NoSuchStreamError(stream_id)
            else:
                raise StreamClosedError(stream_id)

    def get_next_available_stream_id(self):
        """
        Returns an integer suitable for use as the stream ID for the next
        stream created by this endpoint. For server endpoints, this stream ID
        will be even. For client endpoints, this stream ID will be odd. If no
        stream IDs are available, raises :class:`NoAvailableStreamIDError
        <h2.exceptions.NoAvailableStreamIDError>`.

        .. warning:: The return value from this function does not change until
                     the stream ID has actually been used by sending or pushing
                     headers on that stream. For that reason, it should be
                     called as close as possible to the actual use of the
                     stream ID.

        .. versionadded:: 2.0.0

        :raises: :class:`NoAvailableStreamIDError
            <h2.exceptions.NoAvailableStreamIDError>`
        :returns: The next free stream ID this peer can use to initiate a
            stream.
        :rtype: ``int``
        """
        # No streams have been opened yet, so return the lowest allowed stream
        # ID.
        if not self.highest_outbound_stream_id:
            next_stream_id = 1 if self.config.client_side else 2
        else:
            next_stream_id = self.highest_outbound_stream_id + 2
        self.config.logger.debug(
            "Next available stream ID %d", next_stream_id
        )
        if next_stream_id > self.HIGHEST_ALLOWED_STREAM_ID:
            raise NoAvailableStreamIDError("Exhausted allowed stream IDs")

        return next_stream_id

    def send_headers(self, stream_id, headers, end_stream=False,
                     priority_weight=None, priority_depends_on=None,
                     priority_exclusive=None):
        """
        Send headers on a given stream.

        This function can be used to send request or response headers: the kind
        that are sent depends on whether this connection has been opened as a
        client or server connection, and whether the stream was opened by the
        remote peer or not.

        If this is a client connection, calling ``send_headers`` will send the
        headers as a request. It will also implicitly open the stream being
        used. If this is a client connection and ``send_headers`` has *already*
        been called, this will send trailers instead.

        If this is a server connection, calling ``send_headers`` will send the
        headers as a response. It is a protocol error for a server to open a
        stream by sending headers. If this is a server connection and
        ``send_headers`` has *already* been called, this will send trailers
        instead.

        When acting as a server, you may call ``send_headers`` any number of
        times allowed by the following rules, in this order:

        - zero or more times with ``(':status', '1XX')`` (where ``1XX`` is a
          placeholder for any 100-level status code).
        - once with any other status header.
        - zero or one time for trailers.

        That is, you are allowed to send as many informational responses as you
        like, followed by one complete response and zero or one HTTP trailer
        blocks.

        Clients may send one or two header blocks: one request block, and
        optionally one trailer block.

        If it is important to send HPACK "never indexed" header fields (as
        defined in `RFC 7451 Section 7.1.3
        <https://tools.ietf.org/html/rfc7541#section-7.1.3>`_), the user may
        instead provide headers using the HPACK library's :class:`HeaderTuple
        <hpack:hpack.HeaderTuple>` and :class:`NeverIndexedHeaderTuple
        <hpack:hpack.NeverIndexedHeaderTuple>` objects.

        This method also allows users to prioritize the stream immediately,
        by sending priority information on the HEADERS frame directly. To do
        this, any one of ``priority_weight``, ``priority_depends_on``, or
        ``priority_exclusive`` must be set to a value that is not ``None``. For
        more information on the priority fields, see :meth:`prioritize
        <h2.connection.H2Connection.prioritize>`.

        .. warning:: In HTTP/2, it is mandatory that all the HTTP/2 special
            headers (that is, ones whose header keys begin with ``:``) appear
            at the start of the header block, before any normal headers.

        .. versionchanged:: 2.3.0
           Added support for using :class:`HeaderTuple
           <hpack:hpack.HeaderTuple>` objects to store headers.

        .. versionchanged:: 2.4.0
           Added the ability to provide priority keyword arguments:
           ``priority_weight``, ``priority_depends_on``, and
           ``priority_exclusive``.

        :param stream_id: The stream ID to send the headers on. If this stream
            does not currently exist, it will be created.
        :type stream_id: ``int``

        :param headers: The request/response headers to send.
        :type headers: An iterable of two tuples of bytestrings or
            :class:`HeaderTuple <hpack:hpack.HeaderTuple>` objects.

        :param end_stream: Whether this headers frame should end the stream
            immediately (that is, whether no more data will be sent after this
            frame). Defaults to ``False``.
        :type end_stream: ``bool``

        :param priority_weight: Sets the priority weight of the stream. See
            :meth:`prioritize <h2.connection.H2Connection.prioritize>` for more
            about how this field works. Defaults to ``None``, which means that
            no priority information will be sent.
        :type priority_weight: ``int`` or ``None``

        :param priority_depends_on: Sets which stream this one depends on for
            priority purposes. See :meth:`prioritize
            <h2.connection.H2Connection.prioritize>` for more about how this
            field works. Defaults to ``None``, which means that no priority
            information will be sent.
        :type priority_depends_on: ``int`` or ``None``

        :param priority_exclusive: Sets whether this stream exclusively depends
            on the stream given in ``priority_depends_on`` for priority
            purposes. See :meth:`prioritize
            <h2.connection.H2Connection.prioritize>` for more about how this
            field workds. Defaults to ``None``, which means that no priority
            information will be sent.
        :type priority_depends_on: ``bool`` or ``None``

        :returns: Nothing
        """
        self.config.logger.debug(
            "Send headers on stream ID %d", stream_id
        )

        # Check we can open the stream.
        if stream_id not in self.streams:
            max_open_streams = self.remote_settings.max_concurrent_streams
            if (self.open_outbound_streams + 1) > max_open_streams:
                raise TooManyStreamsError(
                    "Max outbound streams is %d, %d open" %
                    (max_open_streams, self.open_outbound_streams)
                )

        self.state_machine.process_input(ConnectionInputs.SEND_HEADERS)
        stream = self._get_or_create_stream(
            stream_id, AllowedStreamIDs(self.config.client_side)
        )
        frames = stream.send_headers(
            headers, self.encoder, end_stream
        )

        # We may need to send priority information.
        priority_present = (
            (priority_weight is not None) or
            (priority_depends_on is not None) or
            (priority_exclusive is not None)
        )

        if priority_present:
            if not self.config.client_side:
                raise RFC1122Error("Servers SHOULD NOT prioritize streams.")

            headers_frame = frames[0]
            headers_frame.flags.add('PRIORITY')
            frames[0] = _add_frame_priority(
                headers_frame,
                priority_weight,
                priority_depends_on,
                priority_exclusive
            )

        self._prepare_for_sending(frames)

    def send_data(self, stream_id, data, end_stream=False, pad_length=None):
        """
        Send data on a given stream.

        This method does no breaking up of data: if the data is larger than the
        value returned by :meth:`local_flow_control_window
        <h2.connection.H2Connection.local_flow_control_window>` for this stream
        then a :class:`FlowControlError <h2.exceptions.FlowControlError>` will
        be raised. If the data is larger than :data:`max_outbound_frame_size
        <h2.connection.H2Connection.max_outbound_frame_size>` then a
        :class:`FrameTooLargeError <h2.exceptions.FrameTooLargeError>` will be
        raised.

        Hyper-h2 does this to avoid buffering the data internally. If the user
        has more data to send than hyper-h2 will allow, consider breaking it up
        and buffering it externally.

        :param stream_id: The ID of the stream on which to send the data.
        :type stream_id: ``int``
        :param data: The data to send on the stream.
        :type data: ``bytes``
        :param end_stream: (optional) Whether this is the last data to be sent
            on the stream. Defaults to ``False``.
        :type end_stream: ``bool``
        :param pad_length: (optional) Length of the padding to apply to the
            data frame. Defaults to ``None`` for no use of padding. Note that
            a value of ``0`` results in padding of length ``0``
            (with the "padding" flag set on the frame).

            .. versionadded:: 2.6.0

        :type pad_length: ``int``
        :returns: Nothing
        """
        self.config.logger.debug(
            "Send data on stream ID %d with len %d", stream_id, len(data)
        )
        frame_size = len(data)
        if pad_length is not None:
            if not isinstance(pad_length, int):
                raise TypeError("pad_length must be an int")
            if pad_length < 0 or pad_length > 255:
                raise ValueError("pad_length must be within range: [0, 255]")
            # Account for padding bytes plus the 1-byte padding length field.
            frame_size += pad_length + 1
        self.config.logger.debug(
            "Frame size on stream ID %d is %d", stream_id, frame_size
        )

        if frame_size > self.local_flow_control_window(stream_id):
            raise FlowControlError(
                "Cannot send %d bytes, flow control window is %d." %
                (frame_size, self.local_flow_control_window(stream_id))
            )
        elif frame_size > self.max_outbound_frame_size:
            raise FrameTooLargeError(
                "Cannot send frame size %d, max frame size is %d" %
                (frame_size, self.max_outbound_frame_size)
            )

        self.state_machine.process_input(ConnectionInputs.SEND_DATA)
        frames = self.streams[stream_id].send_data(
            data, end_stream, pad_length=pad_length
        )

        self._prepare_for_sending(frames)

        self.outbound_flow_control_window -= frame_size
        self.config.logger.debug(
            "Outbound flow control window size is %d",
            self.outbound_flow_control_window
        )
        assert self.outbound_flow_control_window >= 0

    def end_stream(self, stream_id):
        """
        Cleanly end a given stream.

        This method ends a stream by sending an empty DATA frame on that stream
        with the ``END_STREAM`` flag set.

        :param stream_id: The ID of the stream to end.
        :type stream_id: ``int``
        :returns: Nothing
        """
        self.config.logger.debug("End stream ID %d", stream_id)
        self.state_machine.process_input(ConnectionInputs.SEND_DATA)
        frames = self.streams[stream_id].end_stream()
        self._prepare_for_sending(frames)

    def increment_flow_control_window(self, increment, stream_id=None):
        """
        Increment a flow control window, optionally for a single stream. Allows
        the remote peer to send more data.

        .. versionchanged:: 2.0.0
           Rejects attempts to increment the flow control window by out of
           range values with a ``ValueError``.

        :param increment: The amount to increment the flow control window by.
        :type increment: ``int``
        :param stream_id: (optional) The ID of the stream that should have its
            flow control window opened. If not present or ``None``, the
            connection flow control window will be opened instead.
        :type stream_id: ``int`` or ``None``
        :returns: Nothing
        :raises: ``ValueError``
        """
        if not (1 <= increment <= self.MAX_WINDOW_INCREMENT):
            raise ValueError(
                "Flow control increment must be between 1 and %d" %
                self.MAX_WINDOW_INCREMENT
            )

        self.state_machine.process_input(ConnectionInputs.SEND_WINDOW_UPDATE)

        if stream_id is not None:
            stream = self.streams[stream_id]
            frames = stream.increase_flow_control_window(
                increment
            )

            self.config.logger.debug(
                "Increase stream ID %d flow control window by %d",
                stream_id, increment
            )
        else:
            self._inbound_flow_control_window_manager.window_opened(increment)
            f = WindowUpdateFrame(0)
            f.window_increment = increment
            frames = [f]

            self.config.logger.debug(
                "Increase connection flow control window by %d", increment
            )

        self._prepare_for_sending(frames)

    def push_stream(self, stream_id, promised_stream_id, request_headers):
        """
        Push a response to the client by sending a PUSH_PROMISE frame.

        If it is important to send HPACK "never indexed" header fields (as
        defined in `RFC 7451 Section 7.1.3
        <https://tools.ietf.org/html/rfc7541#section-7.1.3>`_), the user may
        instead provide headers using the HPACK library's :class:`HeaderTuple
        <hpack:hpack.HeaderTuple>` and :class:`NeverIndexedHeaderTuple
        <hpack:hpack.NeverIndexedHeaderTuple>` objects.

        :param stream_id: The ID of the stream that this push is a response to.
        :type stream_id: ``int``
        :param promised_stream_id: The ID of the stream that the pushed
            response will be sent on.
        :type promised_stream_id: ``int``
        :param request_headers: The headers of the request that the pushed
            response will be responding to.
        :type request_headers: An iterable of two tuples of bytestrings or
            :class:`HeaderTuple <hpack:hpack.HeaderTuple>` objects.
        :returns: Nothing
        """
        self.config.logger.debug(
            "Send Push Promise frame on stream ID %d", stream_id
        )

        if not self.remote_settings.enable_push:
            raise ProtocolError("Remote peer has disabled stream push")

        self.state_machine.process_input(ConnectionInputs.SEND_PUSH_PROMISE)
        stream = self._get_stream_by_id(stream_id)

        # We need to prevent users pushing streams in response to streams that
        # they themselves have already pushed: see #163 and RFC 7540 Â§ 6.6. The
        # easiest way to do that is to assert that the stream_id is not even:
        # this shortcut works because only servers can push and the state
        # machine will enforce this.
        if (stream_id % 2) == 0:
            raise ProtocolError("Cannot recursively push streams.")

        new_stream = self._begin_new_stream(
            promised_stream_id, AllowedStreamIDs.EVEN
        )
        self.streams[promised_stream_id] = new_stream

        frames = stream.push_stream_in_band(
            promised_stream_id, request_headers, self.encoder
        )
        new_frames = new_stream.locally_pushed()
        self._prepare_for_sending(frames + new_frames)

    def ping(self, opaque_data):
        """
        Send a PING frame.

        :param opaque_data: A bytestring of length 8 that will be sent in the
                            PING frame.
        :returns: Nothing
        """
        self.config.logger.debug("Send Ping frame")

        if not isinstance(opaque_data, bytes) or len(opaque_data) != 8:
            raise ValueError("Invalid value for ping data: %r" % opaque_data)

        self.state_machine.process_input(ConnectionInputs.SEND_PING)
        f = PingFrame(0)
        f.opaque_data = opaque_data
        self._prepare_for_sending([f])

    def reset_stream(self, stream_id, error_code=0):
        """
        Reset a stream.

        This method forcibly closes a stream by sending a RST_STREAM frame for
        a given stream. This is not a graceful closure. To gracefully end a
        stream, try the :meth:`end_stream
        <h2.connection.H2Connection.end_stream>` method.

        :param stream_id: The ID of the stream to reset.
        :type stream_id: ``int``
        :param error_code: (optional) The error code to use to reset the
            stream. Defaults to :data:`ErrorCodes.NO_ERROR
            <h2.errors.ErrorCodes.NO_ERROR>`.
        :type error_code: ``int``
        :returns: Nothing
        """
        self.config.logger.debug("Reset stream ID %d", stream_id)
        self.state_machine.process_input(ConnectionInputs.SEND_RST_STREAM)
        stream = self._get_stream_by_id(stream_id)
        frames = stream.reset_stream(error_code)
        self._prepare_for_sending(frames)

    def close_connection(self, error_code=0, additional_data=None,
                         last_stream_id=None):

        """
        Close a connection, emitting a GOAWAY frame.

        .. versionchanged:: 2.4.0
           Added ``additional_data`` and ``last_stream_id`` arguments.

        :param error_code: (optional) The error code to send in the GOAWAY
            frame.
        :param additional_data: (optional) Additional debug data indicating
            a reason for closing the connection. Must be a bytestring.
        :param last_stream_id: (optional) The last stream which was processed
            by the sender. Defaults to ``highest_inbound_stream_id``.
        :returns: Nothing
        """
        self.config.logger.debug("Close connection")
        self.state_machine.process_input(ConnectionInputs.SEND_GOAWAY)

        # Additional_data must be bytes
        if additional_data is not None:
            assert isinstance(additional_data, bytes)

        if last_stream_id is None:
            last_stream_id = self.highest_inbound_stream_id

        f = GoAwayFrame(
            stream_id=0,
            last_stream_id=last_stream_id,
            error_code=error_code,
            additional_data=(additional_data or b'')
        )
        self._prepare_for_sending([f])

    def update_settings(self, new_settings):
        """
        Update the local settings. This will prepare and emit the appropriate
        SETTINGS frame.

        :param new_settings: A dictionary of {setting: new value}
        """
        self.config.logger.debug(
            "Update connection settings to %s", new_settings
        )
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)
        self.local_settings.update(new_settings)
        s = SettingsFrame(0)
        s.settings = new_settings
        self._prepare_for_sending([s])

    def advertise_alternative_service(self,
                                      field_value,
                                      origin=None,
                                      stream_id=None):
        """
        Notify a client about an available Alternative Service.

        An Alternative Service is defined in `RFC 7838
        <https://tools.ietf.org/html/rfc7838>`_. An Alternative Service
        notification informs a client that a given origin is also available
        elsewhere.

        Alternative Services can be advertised in two ways. Firstly, they can
        be advertised explicitly: that is, a server can say "origin X is also
        available at Y". To advertise like this, set the ``origin`` argument
        and not the ``stream_id`` argument. Alternatively, they can be
        advertised implicitly: that is, a server can say "the origin you're
        contacting on stream X is also available at Y". To advertise like this,
        set the ``stream_id`` argument and not the ``origin`` argument.

        The explicit method of advertising can be done as long as the
        connection is active. The implicit method can only be done after the
        client has sent the request headers and before the server has sent the
        response headers: outside of those points, Hyper-h2 will forbid sending
        the Alternative Service advertisement by raising a ProtocolError.

        The ``field_value`` parameter is specified in RFC 7838. Hyper-h2 does
        not validate or introspect this argument: the user is required to
        ensure that it's well-formed. ``field_value`` corresponds to RFC 7838's
        "Alternative Service Field Value".

        .. note:: It is strongly preferred to use the explicit method of
                  advertising Alternative Services. The implicit method of
                  advertising Alternative Services has a number of subtleties
                  and can lead to inconsistencies between the server and
                  client. Hyper-h2 allows both mechanisms, but caution is
                  strongly advised.

        .. versionadded:: 2.3.0

        :param field_value: The RFC 7838 Alternative Service Field Value. This
            argument is not introspected by Hyper-h2: the user is responsible
            for ensuring that it is well-formed.
        :type field_value: ``bytes``

        :param origin: The origin/authority to which the Alternative Service
            being advertised applies. Must not be provided at the same time as
            ``stream_id``.
        :type origin: ``bytes`` or ``None``

        :param stream_id: The ID of the stream which was sent to the authority
            for which this Alternative Service advertisement applies. Must not
            be provided at the same time as ``origin``.
        :type stream_id: ``int`` or ``None``

        :returns: Nothing.
        """
        if not isinstance(field_value, bytes):
            raise ValueError("Field must be bytestring.")

        if origin is not None and stream_id is not None:
            raise ValueError("Must not provide both origin and stream_id")

        self.state_machine.process_input(
            ConnectionInputs.SEND_ALTERNATIVE_SERVICE
        )

        if origin is not None:
            # This ALTSVC is sent on stream zero.
            f = AltSvcFrame(stream_id=0)
            f.origin = origin
            f.field = field_value
            frames = [f]
        else:
            stream = self._get_stream_by_id(stream_id)
            frames = stream.advertise_alternative_service(field_value)

        self._prepare_for_sending(frames)

    def prioritize(self, stream_id, weight=None, depends_on=None,
                   exclusive=None):
        """
        Notify a server about the priority of a stream.

        Stream priorities are a form of guidance to a remote server: they
        inform the server about how important a given response is, so that the
        server may allocate its resources (e.g. bandwidth, CPU time, etc.)
        accordingly. This exists to allow clients to ensure that the most
        important data arrives earlier, while less important data does not
        starve out the more important data.

        Stream priorities are explained in depth in `RFC 7540 Section 5.3
        <https://tools.ietf.org/html/rfc7540#section-5.3>`_.

        This method updates the priority information of a single stream. It may
        be called well before a stream is actively in use, or well after a
        stream is closed.

        .. warning:: RFC 7540 allows for servers to change the priority of
                     streams. However, hyper-h2 **does not** allow server
                     stacks to do this. This is because most clients do not
                     adequately know how to respond when provided conflicting
                     priority information, and relatively little utility is
                     provided by making that functionality available.

        .. note:: hyper-h2 **does not** maintain any information about the
                  RFC 7540 priority tree. That means that hyper-h2 does not
                  prevent incautious users from creating invalid priority
                  trees, particularly by creating priority loops. While some
                  basic error checking is provided by hyper-h2, users are
                  strongly recommended to understand their prioritisation
                  strategies before using the priority tools here.

        .. note:: Priority information is strictly advisory. Servers are
                  allowed to disregard it entirely. Avoid relying on the idea
                  that your priority signaling will definitely be obeyed.

        .. versionadded:: 2.4.0

        :param stream_id: The ID of the stream to prioritize.
        :type stream_id: ``int``

        :param weight: The weight to give the stream. Defaults to ``16``, the
             default weight of any stream. May be any value between ``1`` and
             ``256`` inclusive. The relative weight of a stream indicates what
             proportion of available resources will be allocated to that
             stream.
        :type weight: ``int``

        :param depends_on: The ID of the stream on which this stream depends.
             This stream will only be progressed if it is impossible to
             progress the parent stream (the one on which this one depends).
             Passing the value ``0`` means that this stream does not depend on
             any other. Defaults to ``0``.
        :type depends_on: ``int``

        :param exclusive: Whether this stream is an exclusive dependency of its
            "parent" stream (i.e. the stream given by ``depends_on``). If a
            stream is an exclusive dependency of another, that means that all
            previously-set children of the parent are moved to become children
            of the new exclusively-dependent stream. Defaults to ``False``.
        :type exclusive: ``bool``
        """
        if not self.config.client_side:
            raise RFC1122Error("Servers SHOULD NOT prioritize streams.")

        self.state_machine.process_input(
            ConnectionInputs.SEND_PRIORITY
        )

        frame = PriorityFrame(stream_id)
        frame = _add_frame_priority(frame, weight, depends_on, exclusive)

        self._prepare_for_sending([frame])

    def local_flow_control_window(self, stream_id):
        """
        Returns the maximum amount of data that can be sent on stream
        ``stream_id``.

        This value will never be larger than the total data that can be sent on
        the connection: even if the given stream allows more data, the
        connection window provides a logical maximum to the amount of data that
        can be sent.

        The maximum data that can be sent in a single data frame on a stream
        is either this value, or the maximum frame size, whichever is
        *smaller*.

        :param stream_id: The ID of the stream whose flow control window is
            being queried.
        :type stream_id: ``int``
        :returns: The amount of data in bytes that can be sent on the stream
            before the flow control window is exhausted.
        :rtype: ``int``
        """
        stream = self._get_stream_by_id(stream_id)
        return min(
            self.outbound_flow_control_window,
            stream.outbound_flow_control_window
        )

    def remote_flow_control_window(self, stream_id):
        """
        Returns the maximum amount of data the remote peer can send on stream
        ``stream_id``.

        This value will never be larger than the total data that can be sent on
        the connection: even if the given stream allows more data, the
        connection window provides a logical maximum to the amount of data that
        can be sent.

        The maximum data that can be sent in a single data frame on a stream
        is either this value, or the maximum frame size, whichever is
        *smaller*.

        :param stream_id: The ID of the stream whose flow control window is
            being queried.
        :type stream_id: ``int``
        :returns: The amount of data in bytes that can be received on the
            stream before the flow control window is exhausted.
        :rtype: ``int``
        """
        stream = self._get_stream_by_id(stream_id)
        return min(
            self.inbound_flow_control_window,
            stream.inbound_flow_control_window
        )

    def acknowledge_received_data(self, acknowledged_size, stream_id):
        """
        Inform the :class:`H2Connection <h2.connection.H2Connection>` that a
        certain number of flow-controlled bytes have been processed, and that
        the space should be handed back to the remote peer at an opportune
        time.

        .. versionadded:: 2.5.0

        :param acknowledged_size: The total *flow-controlled size* of the data
            that has been processed. Note that this must include the amount of
            padding that was sent with that data.
        :type acknowledged_size: ``int``
        :param stream_id: The ID of the stream on which this data was received.
        :type stream_id: ``int``
        :returns: Nothing
        :rtype: ``None``
        """
        self.config.logger.debug(
            "Ack received data on stream ID %d with size %d",
            stream_id, acknowledged_size
        )
        if stream_id <= 0:
            raise ValueError(
                "Stream ID %d is not valid for acknowledge_received_data" %
                stream_id
            )
        if acknowledged_size < 0:
            raise ValueError("Cannot acknowledge negative data")

        frames = []

        conn_manager = self._inbound_flow_control_window_manager
        conn_increment = conn_manager.process_bytes(acknowledged_size)
        if conn_increment:
            f = WindowUpdateFrame(0)
            f.window_increment = conn_increment
            frames.append(f)

        try:
            stream = self._get_stream_by_id(stream_id)
        except StreamClosedError:
            # The stream is already gone. We're not worried about incrementing
            # the window in this case.
            pass
        else:
            # No point incrementing the windows of closed streams.
            if stream.open:
                frames.extend(
                    stream.acknowledge_received_data(acknowledged_size)
                )

        self._prepare_for_sending(frames)

    def data_to_send(self, amount=None):
        """
        Returns some data for sending out of the internal data buffer.

        This method is analogous to ``read`` on a file-like object, but it
        doesn't block. Instead, it returns as much data as the user asks for,
        or less if that much data is not available. It does not perform any
        I/O, and so uses a different name.

        :param amount: (optional) The maximum amount of data to return. If not
            set, or set to ``None``, will return as much data as possible.
        :type amount: ``int``
        :returns: A bytestring containing the data to send on the wire.
        :rtype: ``bytes``
        """
        if amount is None:
            data = bytes(self._data_to_send)
            self._data_to_send = bytearray()
            return data
        else:
            data = bytes(self._data_to_send[:amount])
            self._data_to_send = self._data_to_send[amount:]
            return data

    def clear_outbound_data_buffer(self):
        """
        Clears the outbound data buffer, such that if this call was immediately
        followed by a call to
        :meth:`data_to_send <h2.connection.H2Connection.data_to_send>`, that
        call would return no data.

        This method should not normally be used, but is made available to avoid
        exposing implementation details.
        """
        self._data_to_send = bytearray()

    def _acknowledge_settings(self):
        """
        Acknowledge settings that have been received.

        .. versionchanged:: 2.0.0
           Removed from public API, removed useless ``event`` parameter, made
           automatic.

        :returns: Nothing
        """
        self.state_machine.process_input(ConnectionInputs.SEND_SETTINGS)

        changes = self.remote_settings.acknowledge()

        if SettingCodes.INITIAL_WINDOW_SIZE in changes:
            setting = changes[SettingCodes.INITIAL_WINDOW_SIZE]
            self._flow_control_change_from_settings(
                setting.original_value,
                setting.new_value,
            )

        # HEADER_TABLE_SIZE changes by the remote part affect our encoder: cf.
        # RFC 7540 Section 6.5.2.
        if SettingCodes.HEADER_TABLE_SIZE in changes:
            setting = changes[SettingCodes.HEADER_TABLE_SIZE]
            self.encoder.header_table_size = setting.new_value

        if SettingCodes.MAX_FRAME_SIZE in changes:
            setting = changes[SettingCodes.MAX_FRAME_SIZE]
            self.max_outbound_frame_size = setting.new_value
            for stream in self.streams.values():
                stream.max_outbound_frame_size = setting.new_value

        f = SettingsFrame(0)
        f.flags.add('ACK')
        return [f]

    def _flow_control_change_from_settings(self, old_value, new_value):
        """
        Update flow control windows in response to a change in the value of
        SETTINGS_INITIAL_WINDOW_SIZE.

        When this setting is changed, it automatically updates all flow control
        windows by the delta in the settings values. Note that it does not
        increment the *connection* flow control window, per section 6.9.2 of
        RFC 7540.
        """
        delta = new_value - old_value

        for stream in self.streams.values():
            stream.outbound_flow_control_window = guard_increment_window(
                stream.outbound_flow_control_window,
                delta
            )

    def _inbound_flow_control_change_from_settings(self, old_value, new_value):
        """
        Update remote flow control windows in response to a change in the value
        of SETTINGS_INITIAL_WINDOW_SIZE.

        When this setting is changed, it automatically updates all remote flow
        control windows by the delta in the settings values.
        """
        delta = new_value - old_value

        for stream in self.streams.values():
            stream._inbound_flow_control_change_from_settings(delta)

    def receive_data(self, data):
        """
        Pass some received HTTP/2 data to the connection for handling.

        :param data: The data received from the remote peer on the network.
        :type data: ``bytes``
        :returns: A list of events that the remote peer triggered by sending
            this data.
        """
        self.config.logger.debug(
            "Process received data on connection. Received data: %r", data
        )

        events = []
        self.incoming_buffer.add_data(data)
        self.incoming_buffer.max_frame_size = self.max_inbound_frame_size

        try:
            for frame in self.incoming_buffer:
                events.extend(self._receive_frame(frame))
        except InvalidPaddingError:
            self._terminate_connection(ErrorCodes.PROTOCOL_ERROR)
            raise ProtocolError("Received frame with invalid padding.")
        except ProtocolError as e:
            # For whatever reason, receiving the frame caused a protocol error.
            # We should prepare to emit a GoAway frame before throwing the
            # exception up further. No need for an event: the exception will
            # do fine.
            self._terminate_connection(e.error_code)
            raise

        return events

    def _receive_frame(self, frame):
        """
        Handle a frame received on the connection.

        .. versionchanged:: 2.0.0
           Removed from the public API.
        """
        try:
            # I don't love using __class__ here, maybe reconsider it.
            frames, events = self._frame_dispatch_table[frame.__class__](frame)
        except StreamClosedError as e:
            # If the stream was closed by RST_STREAM, we just send a RST_STREAM
            # to the remote peer. Otherwise, this is a connection error, and so
            # we will re-raise to trigger one.
            if self._stream_is_closed_by_reset(e.stream_id):
                f = RstStreamFrame(e.stream_id)
                f.error_code = e.error_code
                self._prepare_for_sending([f])
                events = e._events
            else:
                raise
        except StreamIDTooLowError as e:
            # The stream ID seems invalid. This may happen when the closed
            # stream has been cleaned up, or when the remote peer has opened a
            # new stream with a higher stream ID than this one, forcing it
            # closed implicitly.
            #
            # Check how the stream was closed: depending on the mechanism, it
            # is either a stream error or a connection error.
            if self._stream_is_closed_by_reset(e.stream_id):
                # Closed by RST_STREAM is a stream error.
                f = RstStreamFrame(e.stream_id)
                f.error_code = ErrorCodes.STREAM_CLOSED
                self._prepare_for_sending([f])
                events = []
            elif self._stream_is_closed_by_end(e.stream_id):
                # Closed by END_STREAM is a connection error.
                raise StreamClosedError(e.stream_id)
            else:
                # Closed implicitly, also a connection error, but of type
                # PROTOCOL_ERROR.
                raise
        else:
            self._prepare_for_sending(frames)

        return events

    def _terminate_connection(self, error_code):
        """
        Terminate the connection early. Used in error handling blocks to send
        GOAWAY frames.
        """
        f = GoAwayFrame(0)
        f.last_stream_id = self.highest_inbound_stream_id
        f.error_code = error_code
        self.state_machine.process_input(ConnectionInputs.SEND_GOAWAY)
        self._prepare_for_sending([f])

    def _receive_headers_frame(self, frame):
        """
        Receive a headers frame on the connection.
        """
        # If necessary, check we can open the stream. Also validate that the
        # stream ID is valid.
        if frame.stream_id not in self.streams:
            max_open_streams = self.local_settings.max_concurrent_streams
            if (self.open_inbound_streams + 1) > max_open_streams:
                raise TooManyStreamsError(
                    "Max outbound streams is %d, %d open" %
                    (max_open_streams, self.open_outbound_streams)
                )

        # Let's decode the headers. We handle headers as bytes internally up
        # until we hang them off the event, at which point we may optionally
        # convert them to unicode.
        headers = _decode_headers(self.decoder, frame.data)

        events = self.state_machine.process_input(
            ConnectionInputs.RECV_HEADERS
        )
        stream = self._get_or_create_stream(
            frame.stream_id, AllowedStreamIDs(not self.config.client_side)
        )
        frames, stream_events = stream.receive_headers(
            headers,
            'END_STREAM' in frame.flags,
            self.config.header_encoding
        )

        if 'PRIORITY' in frame.flags:
            p_frames, p_events = self._receive_priority_frame(frame)
            stream_events[0].priority_updated = p_events[0]
            stream_events.extend(p_events)
            assert not p_frames

        return frames, events + stream_events

    def _receive_push_promise_frame(self, frame):
        """
        Receive a push-promise frame on the connection.
        """
        if not self.local_settings.enable_push:
            raise ProtocolError("Received pushed stream")

        pushed_headers = _decode_headers(self.decoder, frame.data)

        events = self.state_machine.process_input(
            ConnectionInputs.RECV_PUSH_PROMISE
        )

        try:
            stream = self._get_stream_by_id(frame.stream_id)
        except NoSuchStreamError:
            # We need to check if the parent stream was reset by us. If it was
            # then we presume that the PUSH_PROMISE was in flight when we reset
            # the parent stream. Rather than accept the new stream, just reset
            # it.
            #
            # If this was closed naturally, however, we should call this a
            # PROTOCOL_ERROR: pushing a stream on a naturally closed stream is
            # a real problem because it creates a brand new stream that the
            # remote peer now believes exists.
            if (self._stream_closed_by(frame.stream_id) ==
                    StreamClosedBy.SEND_RST_STREAM):
                f = RstStreamFrame(frame.promised_stream_id)
                f.error_code = ErrorCodes.REFUSED_STREAM
                return [f], events

            raise ProtocolError("Attempted to push on closed stream.")

        # We need to prevent peers pushing streams in response to streams that
        # they themselves have already pushed: see #163 and RFC 7540 Â§ 6.6. The
        # easiest way to do that is to assert that the stream_id is not even:
        # this shortcut works because only servers can push and the state
        # machine will enforce this.
        if (frame.stream_id % 2) == 0:
            raise ProtocolError("Cannot recursively push streams.")

        try:
            frames, stream_events = stream.receive_push_promise_in_band(
                frame.promised_stream_id,
                pushed_headers,
                self.config.header_encoding,
            )
        except StreamClosedError:
            # The parent stream was reset by us, so we presume that
            # PUSH_PROMISE was in flight when we reset the parent stream.
            # So we just reset the new stream.
            f = RstStreamFrame(frame.promised_stream_id)
            f.error_code = ErrorCodes.REFUSED_STREAM
            return [f], events

        new_stream = self._begin_new_stream(
            frame.promised_stream_id, AllowedStreamIDs.EVEN
        )
        self.streams[frame.promised_stream_id] = new_stream
        new_stream.remotely_pushed(pushed_headers)

        return frames, events + stream_events

    def _receive_data_frame(self, frame):
        """
        Receive a data frame on the connection.
        """
        flow_controlled_length = frame.flow_controlled_length

        events = self.state_machine.process_input(
            ConnectionInputs.RECV_DATA
        )
        self._inbound_flow_control_window_manager.window_consumed(
            flow_controlled_length
        )
        stream = self._get_stream_by_id(frame.stream_id)
        frames, stream_events = stream.receive_data(
            frame.data,
            'END_STREAM' in frame.flags,
            flow_controlled_length
        )
        return frames, events + stream_events

    def _receive_settings_frame(self, frame):
        """
        Receive a SETTINGS frame on the connection.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_SETTINGS
        )

        # This is an ack of the local settings.
        if 'ACK' in frame.flags:
            changed_settings = self._local_settings_acked()
            ack_event = SettingsAcknowledged()
            ack_event.changed_settings = changed_settings
            events.append(ack_event)
            return [], events

        # Add the new settings.
        self.remote_settings.update(frame.settings)
        events.append(
            RemoteSettingsChanged.from_settings(
                self.remote_settings, frame.settings
            )
        )
        frames = self._acknowledge_settings()

        return frames, events

    def _receive_window_update_frame(self, frame):
        """
        Receive a WINDOW_UPDATE frame on the connection.
        """
        # Validate the frame.
        if not (1 <= frame.window_increment <= self.MAX_WINDOW_INCREMENT):
            raise ProtocolError(
                "Flow control increment must be between 1 and %d, received %d"
                % (self.MAX_WINDOW_INCREMENT, frame.window_increment)
            )

        events = self.state_machine.process_input(
            ConnectionInputs.RECV_WINDOW_UPDATE
        )

        if frame.stream_id:
            stream = self._get_stream_by_id(frame.stream_id)
            frames, stream_events = stream.receive_window_update(
                frame.window_increment
            )
        else:
            # Increment our local flow control window.
            self.outbound_flow_control_window = guard_increment_window(
                self.outbound_flow_control_window,
                frame.window_increment
            )

            # FIXME: Should we split this into one event per active stream?
            window_updated_event = WindowUpdated()
            window_updated_event.stream_id = 0
            window_updated_event.delta = frame.window_increment
            stream_events = [window_updated_event]
            frames = []

        return frames, events + stream_events

    def _receive_ping_frame(self, frame):
        """
        Receive a PING frame on the connection.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_PING
        )
        flags = []

        if 'ACK' in frame.flags:
            evt = PingAckReceived()
        else:
            evt = PingReceived()

            # automatically ACK the PING with the same 'opaque data'
            f = PingFrame(0)
            f.flags = {'ACK'}
            f.opaque_data = frame.opaque_data
            flags.append(f)

        evt.ping_data = frame.opaque_data
        events.append(evt)

        return flags, events

    def _receive_rst_stream_frame(self, frame):
        """
        Receive a RST_STREAM frame on the connection.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_RST_STREAM
        )
        try:
            stream = self._get_stream_by_id(frame.stream_id)
        except NoSuchStreamError:
            # The stream is missing. That's ok, we just do nothing here.
            stream_frames = []
            stream_events = []
        else:
            stream_frames, stream_events = stream.stream_reset(frame)

        return stream_frames, events + stream_events

    def _receive_priority_frame(self, frame):
        """
        Receive a PRIORITY frame on the connection.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_PRIORITY
        )

        event = PriorityUpdated()
        event.stream_id = frame.stream_id
        event.depends_on = frame.depends_on
        event.exclusive = frame.exclusive

        # Weight is an integer between 1 and 256, but the byte only allows
        # 0 to 255: add one.
        event.weight = frame.stream_weight + 1

        # A stream may not depend on itself.
        if event.depends_on == frame.stream_id:
            raise ProtocolError(
                "Stream %d may not depend on itself" % frame.stream_id
            )
        events.append(event)

        return [], events

    def _receive_goaway_frame(self, frame):
        """
        Receive a GOAWAY frame on the connection.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_GOAWAY
        )

        # Clear the outbound data buffer: we cannot send further data now.
        self.clear_outbound_data_buffer()

        # Fire an appropriate ConnectionTerminated event.
        new_event = ConnectionTerminated()
        new_event.error_code = _error_code_from_int(frame.error_code)
        new_event.last_stream_id = frame.last_stream_id
        new_event.additional_data = (frame.additional_data
                                     if frame.additional_data else None)
        events.append(new_event)

        return [], events

    def _receive_naked_continuation(self, frame):
        """
        A naked CONTINUATION frame has been received. This is always an error,
        but the type of error it is depends on the state of the stream and must
        transition the state of the stream, so we need to pass it to the
        appropriate stream.
        """
        stream = self._get_stream_by_id(frame.stream_id)
        stream.receive_continuation()
        assert False, "Should not be reachable"

    def _receive_alt_svc_frame(self, frame):
        """
        An ALTSVC frame has been received. This frame, specified in RFC 7838,
        is used to advertise alternative places where the same service can be
        reached.

        This frame can optionally be received either on a stream or on stream
        0, and its semantics are different in each case.
        """
        events = self.state_machine.process_input(
            ConnectionInputs.RECV_ALTERNATIVE_SERVICE
        )
        frames = []

        if frame.stream_id:
            # Given that it makes no sense to receive ALTSVC on a stream
            # before that stream has been opened with a HEADERS frame, the
            # ALTSVC frame cannot create a stream. If the stream is not
            # present, we simply ignore the frame.
            try:
                stream = self._get_stream_by_id(frame.stream_id)
            except (NoSuchStreamError, StreamClosedError):
                pass
            else:
                stream_frames, stream_events = stream.receive_alt_svc(frame)
                frames.extend(stream_frames)
                events.extend(stream_events)
        else:
            # This frame is sent on stream 0. The origin field on the frame
            # must be present, though if it isn't it's not a ProtocolError
            # (annoyingly), we just need to ignore it.
            if not frame.origin:
                return frames, events

            # If we're a server, we want to ignore this (RFC 7838 says so).
            if not self.config.client_side:
                return frames, events

            event = AlternativeServiceAvailable()
            event.origin = frame.origin
            event.field_value = frame.field
            events.append(event)

        return frames, events

    def _receive_unknown_frame(self, frame):
        """
        We have received a frame that we do not understand. This is almost
        certainly an extension frame, though it's impossible to be entirely
        sure.

        RFC 7540 Â§ 5.5 says that we MUST ignore unknown frame types: so we
        do. We do notify the user that we received one, however.
        """
        # All we do here is log.
        self.config.logger.debug(
            "Received unknown extension frame (ID %d)", frame.stream_id
        )
        event = UnknownFrameReceived()
        event.frame = frame
        return [], [event]

    def _local_settings_acked(self):
        """
        Handle the local settings being ACKed, update internal state.
        """
        changes = self.local_settings.acknowledge()

        if SettingCodes.INITIAL_WINDOW_SIZE in changes:
            setting = changes[SettingCodes.INITIAL_WINDOW_SIZE]
            self._inbound_flow_control_change_from_settings(
                setting.original_value,
                setting.new_value,
            )

        if SettingCodes.MAX_HEADER_LIST_SIZE in changes:
            setting = changes[SettingCodes.MAX_HEADER_LIST_SIZE]
            self.decoder.max_header_list_size = setting.new_value

        if SettingCodes.MAX_FRAME_SIZE in changes:
            setting = changes[SettingCodes.MAX_FRAME_SIZE]
            self.max_inbound_frame_size = setting.new_value

        if SettingCodes.HEADER_TABLE_SIZE in changes:
            setting = changes[SettingCodes.HEADER_TABLE_SIZE]
            # This is safe across all hpack versions: some versions just won't
            # respect it.
            self.decoder.max_allowed_table_size = setting.new_value

        return changes

    def _stream_id_is_outbound(self, stream_id):
        """
        Returns ``True`` if the stream ID corresponds to an outbound stream
        (one initiated by this peer), returns ``False`` otherwise.
        """
        return (stream_id % 2 == int(self.config.client_side))

    def _stream_closed_by(self, stream_id):
        """
        Returns how the stream was closed.

        The return value will be either a member of
        ``h2.stream.StreamClosedBy`` or ``None``. If ``None``, the stream was
        closed implicitly by the peer opening a stream with a higher stream ID
        before opening this one.
        """
        if stream_id in self.streams:
            return self.streams[stream_id].closed_by
        if stream_id in self._closed_streams:
            return self._closed_streams[stream_id]
        return None

    def _stream_is_closed_by_reset(self, stream_id):
        """
        Returns ``True`` if the stream was closed by sending or receiving a
        RST_STREAM frame. Returns ``False`` otherwise.
        """
        return self._stream_closed_by(stream_id) in (
            StreamClosedBy.RECV_RST_STREAM, StreamClosedBy.SEND_RST_STREAM
        )

    def _stream_is_closed_by_end(self, stream_id):
        """
        Returns ``True`` if the stream was closed by sending or receiving an
        END_STREAM flag in a HEADERS or DATA frame. Returns ``False``
        otherwise.
        """
        return self._stream_closed_by(stream_id) in (
            StreamClosedBy.RECV_END_STREAM, StreamClosedBy.SEND_END_STREAM
        )


def _add_frame_priority(frame, weight=None, depends_on=None, exclusive=None):
    """
    Adds priority data to a given frame. Does not change any flags set on that
    frame: if the caller is adding priority information to a HEADERS frame they
    must set that themselves.

    This method also deliberately sets defaults for anything missing.

    This method validates the input values.
    """
    # A stream may not depend on itself.
    if depends_on == frame.stream_id:
        raise ProtocolError(
            "Stream %d may not depend on itself" % frame.stream_id
        )

    # Weight must be between 1 and 256.
    if weight is not None:
        if weight > 256 or weight < 1:
            raise ProtocolError(
                "Weight must be between 1 and 256, not %d" % weight
            )
        else:
            # Weight is an integer between 1 and 256, but the byte only allows
            # 0 to 255: subtract one.
            weight -= 1

    # Set defaults for anything not provided.
    weight = weight if weight is not None else 15
    depends_on = depends_on if depends_on is not None else 0
    exclusive = exclusive if exclusive is not None else False

    frame.stream_weight = weight
    frame.depends_on = depends_on
    frame.exclusive = exclusive

    return frame


def _decode_headers(decoder, encoded_header_block):
    """
    Decode a HPACK-encoded header block, translating HPACK exceptions into
    sensible hyper-h2 errors.

    This only ever returns bytestring headers: hyper-h2 may emit them as
    unicode later, but internally it processes them as bytestrings only.
    """
    try:
        return decoder.decode(encoded_header_block, raw=True)
    except OversizedHeaderListError as e:
        # This is a symptom of a HPACK bomb attack: the user has
        # disregarded our requirements on how large a header block we'll
        # accept.
        raise DenialOfServiceError("Oversized header block: %s" % e)
    except (HPACKError, IndexError, TypeError, UnicodeDecodeError) as e:
        # We should only need HPACKError here, but versions of HPACK older
        # than 2.1.0 throw all three others as well. For maximum
        # compatibility, catch all of them.
        raise ProtocolError("Error decoding header block: %s" % e)
  0707010001f455000081a40000000000000000000000016a100daf000015e3000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/h2/windows.py  # -*- coding: utf-8 -*-
"""
h2/windows
~~~~~~~~~~

Defines tools for managing HTTP/2 flow control windows.

The objects defined in this module are used to automatically manage HTTP/2
flow control windows. Specifically, they keep track of what the size of the
window is, how much data has been consumed from that window, and how much data
the user has already used. It then implements a basic algorithm that attempts
to manage the flow control window without user input, trying to ensure that it
does not emit too many WINDOW_UPDATE frames.
"""
from __future__ import division

from .exceptions import FlowControlError


# The largest acceptable value for a HTTP/2 flow control window.
LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1


class WindowManager(object):
    """
    A basic HTTP/2 window manager.

    :param max_window_size: The maximum size of the flow control window.
    :type max_window_size: ``int``
    """
    def __init__(self, max_window_size):
        assert max_window_size <= LARGEST_FLOW_CONTROL_WINDOW
        self.max_window_size = max_window_size
        self.current_window_size = max_window_size
        self._bytes_processed = 0

    def window_consumed(self, size):
        """
        We have received a certain number of bytes from the remote peer. This
        necessarily shrinks the flow control window!

        :param size: The number of flow controlled bytes we received from the
            remote peer.
        :type size: ``int``
        :returns: Nothing.
        :rtype: ``None``
        """
        self.current_window_size -= size
        if self.current_window_size < 0:
            raise FlowControlError("Flow control window shrunk below 0")

    def window_opened(self, size):
        """
        The flow control window has been incremented, either because of manual
        flow control management or because of the user changing the flow
        control settings. This can have the effect of increasing what we
        consider to be the "maximum" flow control window size.

        This does not increase our view of how many bytes have been processed,
        only of how much space is in the window.

        :param size: The increment to the flow control window we received.
        :type size: ``int``
        :returns: Nothing
        :rtype: ``None``
        """
        self.current_window_size += size

        if self.current_window_size > LARGEST_FLOW_CONTROL_WINDOW:
            raise FlowControlError(
                "Flow control window mustn't exceed %d" %
                LARGEST_FLOW_CONTROL_WINDOW
            )

        if self.current_window_size > self.max_window_size:
            self.max_window_size = self.current_window_size

    def process_bytes(self, size):
        """
        The application has informed us that it has processed a certain number
        of bytes. This may cause us to want to emit a window update frame. If
        we do want to emit a window update frame, this method will return the
        number of bytes that we should increment the window by.

        :param size: The number of flow controlled bytes that the application
            has processed.
        :type size: ``int``
        :returns: The number of bytes to increment the flow control window by,
            or ``None``.
        :rtype: ``int`` or ``None``
        """
        self._bytes_processed += size
        return self._maybe_update_window()

    def _maybe_update_window(self):
        """
        Run the algorithm.

        Our current algorithm can be described like this.

        1. If no bytes have been processed, we immediately return 0. There is
           no meaningful way for us to hand space in the window back to the
           remote peer, so let's not even try.
        2. If there is no space in the flow control window, and we have
           processed at least 1024 bytes (or 1/4 of the window, if the window
           is smaller), we will emit a window update frame. This is to avoid
           the risk of blocking a stream altogether.
        3. If there is space in the flow control window, and we have processed
           at least 1/2 of the window worth of bytes, we will emit a window
           update frame. This is to minimise the number of window update frames
           we have to emit.

        In a healthy system with large flow control windows, this will
        irregularly emit WINDOW_UPDATE frames. This prevents us starving the
        connection by emitting eleventy bajillion WINDOW_UPDATE frames,
        especially in situations where the remote peer is sending a lot of very
        small DATA frames.
        """
        # TODO: Can the window be smaller than 1024 bytes? If not, we can
        # streamline this algorithm.
        if not self._bytes_processed:
            return None

        max_increment = (self.max_window_size - self.current_window_size)
        increment = 0

        # Note that, even though we may increment less than _bytes_processed,
        # we still want to set it to zero whenever we emit an increment. This
        # is because we'll always increment up to the maximum we can.
        if (self.current_window_size == 0) and (
                self._bytes_processed > min(1024, self.max_window_size // 4)):
            increment = min(self._bytes_processed, max_increment)
            self._bytes_processed = 0
        elif self._bytes_processed >= (self.max_window_size // 2):
            increment = min(self._bytes_processed, max_increment)
            self._bytes_processed = 0

        self.current_window_size += increment
        return increment
 0707010001f44f000081a40000000000000000000000016a100daf000055ca000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/h2/events.py   # -*- coding: utf-8 -*-
"""
h2/events
~~~~~~~~~

Defines Event types for HTTP/2.

Events are returned by the H2 state machine to allow implementations to keep
track of events triggered by receiving data. Each time data is provided to the
H2 state machine it processes the data and returns a list of Event objects.
"""
import binascii

from .settings import ChangedSetting, _setting_code_from_int


class Event(object):
    """
    Base class for h2 events.
    """
    pass


class RequestReceived(Event):
    """
    The RequestReceived event is fired whenever request headers are received.
    This event carries the HTTP headers for the given request and the stream ID
    of the new stream.

    .. versionchanged:: 2.3.0
       Changed the type of ``headers`` to :class:`HeaderTuple
       <hpack:hpack.HeaderTuple>`. This has no effect on current users.

    .. versionchanged:: 2.4.0
       Added ``stream_ended`` and ``priority_updated`` properties.
    """
    def __init__(self):
        #: The Stream ID for the stream this request was made on.
        self.stream_id = None

        #: The request headers.
        self.headers = None

        #: If this request also ended the stream, the associated
        #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available
        #: here.
        #:
        #: .. versionadded:: 2.4.0
        self.stream_ended = None

        #: If this request also had associated priority information, the
        #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
        #: event will be available here.
        #:
        #: .. versionadded:: 2.4.0
        self.priority_updated = None

    def __repr__(self):
        return "<RequestReceived stream_id:%s, headers:%s>" % (
            self.stream_id, self.headers
        )


class ResponseReceived(Event):
    """
    The ResponseReceived event is fired whenever response headers are received.
    This event carries the HTTP headers for the given response and the stream
    ID of the new stream.

    .. versionchanged:: 2.3.0
       Changed the type of ``headers`` to :class:`HeaderTuple
       <hpack:hpack.HeaderTuple>`. This has no effect on current users.

   .. versionchanged:: 2.4.0
      Added ``stream_ended`` and ``priority_updated`` properties.
    """
    def __init__(self):
        #: The Stream ID for the stream this response was made on.
        self.stream_id = None

        #: The response headers.
        self.headers = None

        #: If this response also ended the stream, the associated
        #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available
        #: here.
        #:
        #: .. versionadded:: 2.4.0
        self.stream_ended = None

        #: If this response also had associated priority information, the
        #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
        #: event will be available here.
        #:
        #: .. versionadded:: 2.4.0
        self.priority_updated = None

    def __repr__(self):
        return "<ResponseReceived stream_id:%s, headers:%s>" % (
            self.stream_id, self.headers
        )


class TrailersReceived(Event):
    """
    The TrailersReceived event is fired whenever trailers are received on a
    stream. Trailers are a set of headers sent after the body of the
    request/response, and are used to provide information that wasn't known
    ahead of time (e.g. content-length). This event carries the HTTP header
    fields that form the trailers and the stream ID of the stream on which they
    were received.

    .. versionchanged:: 2.3.0
       Changed the type of ``headers`` to :class:`HeaderTuple
       <hpack:hpack.HeaderTuple>`. This has no effect on current users.

    .. versionchanged:: 2.4.0
       Added ``stream_ended`` and ``priority_updated`` properties.
    """
    def __init__(self):
        #: The Stream ID for the stream on which these trailers were received.
        self.stream_id = None

        #: The trailers themselves.
        self.headers = None

        #: Trailers always end streams. This property has the associated
        #: :class:`StreamEnded <h2.events.StreamEnded>` in it.
        #:
        #: .. versionadded:: 2.4.0
        self.stream_ended = None

        #: If the trailers also set associated priority information, the
        #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
        #: event will be available here.
        #:
        #: .. versionadded:: 2.4.0
        self.priority_updated = None

    def __repr__(self):
        return "<TrailersReceived stream_id:%s, headers:%s>" % (
            self.stream_id, self.headers
        )


class _HeadersSent(Event):
    """
    The _HeadersSent event is fired whenever headers are sent.

    This is an internal event, used to determine validation steps on
    outgoing header blocks.
    """
    pass


class _ResponseSent(_HeadersSent):
    """
    The _ResponseSent event is fired whenever response headers are sent
    on a stream.

    This is an internal event, used to determine validation steps on
    outgoing header blocks.
    """
    pass


class _RequestSent(_HeadersSent):
    """
    The _RequestSent event is fired whenever request headers are sent
    on a stream.

    This is an internal event, used to determine validation steps on
    outgoing header blocks.
    """
    pass


class _TrailersSent(_HeadersSent):
    """
    The _TrailersSent event is fired whenever trailers are sent on a
    stream. Trailers are a set of headers sent after the body of the
    request/response, and are used to provide information that wasn't known
    ahead of time (e.g. content-length).

    This is an internal event, used to determine validation steps on
    outgoing header blocks.
    """
    pass


class _PushedRequestSent(_HeadersSent):
    """
    The _PushedRequestSent event is fired whenever pushed request headers are
    sent.

    This is an internal event, used to determine validation steps on outgoing
    header blocks.
    """
    pass


class InformationalResponseReceived(Event):
    """
    The InformationalResponseReceived event is fired when an informational
    response (that is, one whose status code is a 1XX code) is received from
    the remote peer.

    The remote peer may send any number of these, from zero upwards. These
    responses are most commonly sent in response to requests that have the
    ``expect: 100-continue`` header field present. Most users can safely
    ignore this event unless you are intending to use the
    ``expect: 100-continue`` flow, or are for any reason expecting a different
    1XX status code.

    .. versionadded:: 2.2.0

    .. versionchanged:: 2.3.0
       Changed the type of ``headers`` to :class:`HeaderTuple
       <hpack:hpack.HeaderTuple>`. This has no effect on current users.

    .. versionchanged:: 2.4.0
       Added ``priority_updated`` property.
    """
    def __init__(self):
        #: The Stream ID for the stream this informational response was made
        #: on.
        self.stream_id = None

        #: The headers for this informational response.
        self.headers = None

        #: If this response also had associated priority information, the
        #: associated :class:`PriorityUpdated <h2.events.PriorityUpdated>`
        #: event will be available here.
        #:
        #: .. versionadded:: 2.4.0
        self.priority_updated = None

    def __repr__(self):
        return "<InformationalResponseReceived stream_id:%s, headers:%s>" % (
            self.stream_id, self.headers
        )


class DataReceived(Event):
    """
    The DataReceived event is fired whenever data is received on a stream from
    the remote peer. The event carries the data itself, and the stream ID on
    which the data was received.

    .. versionchanged:: 2.4.0
       Added ``stream_ended`` property.
    """
    def __init__(self):
        #: The Stream ID for the stream this data was received on.
        self.stream_id = None

        #: The data itself.
        self.data = None

        #: The amount of data received that counts against the flow control
        #: window. Note that padding counts against the flow control window, so
        #: when adjusting flow control you should always use this field rather
        #: than ``len(data)``.
        self.flow_controlled_length = None

        #: If this data chunk also completed the stream, the associated
        #: :class:`StreamEnded <h2.events.StreamEnded>` event will be available
        #: here.
        #:
        #: .. versionadded:: 2.4.0
        self.stream_ended = None

    def __repr__(self):
        return (
            "<DataReceived stream_id:%s, "
            "flow_controlled_length:%s, "
            "data:%s>" % (
                self.stream_id,
                self.flow_controlled_length,
                _bytes_representation(self.data[:20]),
            )
        )


class WindowUpdated(Event):
    """
    The WindowUpdated event is fired whenever a flow control window changes
    size. HTTP/2 defines flow control windows for connections and streams: this
    event fires for both connections and streams. The event carries the ID of
    the stream to which it applies (set to zero if the window update applies to
    the connection), and the delta in the window size.
    """
    def __init__(self):
        #: The Stream ID of the stream whose flow control window was changed.
        #: May be ``0`` if the connection window was changed.
        self.stream_id = None

        #: The window delta.
        self.delta = None

    def __repr__(self):
        return "<WindowUpdated stream_id:%s, delta:%s>" % (
            self.stream_id, self.delta
        )


class RemoteSettingsChanged(Event):
    """
    The RemoteSettingsChanged event is fired whenever the remote peer changes
    its settings. It contains a complete inventory of changed settings,
    including their previous values.

    In HTTP/2, settings changes need to be acknowledged. hyper-h2 automatically
    acknowledges settings changes for efficiency. However, it is possible that
    the caller may not be happy with the changed setting.

    When this event is received, the caller should confirm that the new
    settings are acceptable. If they are not acceptable, the user should close
    the connection with the error code :data:`PROTOCOL_ERROR
    <h2.errors.ErrorCodes.PROTOCOL_ERROR>`.

    .. versionchanged:: 2.0.0
       Prior to this version the user needed to acknowledge settings changes.
       This is no longer the case: hyper-h2 now automatically acknowledges
       them.
    """
    def __init__(self):
        #: A dictionary of setting byte to
        #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing
        #: the changed settings.
        self.changed_settings = {}

    @classmethod
    def from_settings(cls, old_settings, new_settings):
        """
        Build a RemoteSettingsChanged event from a set of changed settings.

        :param old_settings: A complete collection of old settings, in the form
                             of a dictionary of ``{setting: value}``.
        :param new_settings: All the changed settings and their new values, in
                             the form of a dictionary of ``{setting: value}``.
        """
        e = cls()
        for setting, new_value in new_settings.items():
            setting = _setting_code_from_int(setting)
            original_value = old_settings.get(setting)
            change = ChangedSetting(setting, original_value, new_value)
            e.changed_settings[setting] = change

        return e

    def __repr__(self):
        return "<RemoteSettingsChanged changed_settings:{%s}>" % (
            ", ".join(repr(cs) for cs in self.changed_settings.values()),
        )


class PingReceived(Event):
    """
    The PingReceived event is fired whenever a PING is received. It contains
    the 'opaque data' of the PING frame. A ping acknowledgment with the same
    'opaque data' is automatically emitted after receiving a ping.

    .. versionadded:: 3.1.0
    """
    def __init__(self):
        #: The data included on the ping.
        self.ping_data = None

    def __repr__(self):
        return "<PingReceived ping_data:%s>" % (
            _bytes_representation(self.ping_data),
        )


class PingAcknowledged(Event):
    """
    Same as PingAckReceived.

    .. deprecated:: 3.1.0
    """
    def __init__(self):
        #: The data included on the ping.
        self.ping_data = None

    def __repr__(self):
        return "<PingAckReceived ping_data:%s>" % (
            _bytes_representation(self.ping_data),
        )


class PingAckReceived(PingAcknowledged):
    """
    The PingAckReceived event is fired whenever a PING acknowledgment is
    received. It contains the 'opaque data' of the PING+ACK frame, allowing the
    user to correlate PINGs and calculate RTT.

    .. versionadded:: 3.1.0
    """
    pass


class StreamEnded(Event):
    """
    The StreamEnded event is fired whenever a stream is ended by a remote
    party. The stream may not be fully closed if it has not been closed
    locally, but no further data or headers should be expected on that stream.
    """
    def __init__(self):
        #: The Stream ID of the stream that was closed.
        self.stream_id = None

    def __repr__(self):
        return "<StreamEnded stream_id:%s>" % self.stream_id


class StreamReset(Event):
    """
    The StreamReset event is fired in two situations. The first is when the
    remote party forcefully resets the stream. The second is when the remote
    party has made a protocol error which only affects a single stream. In this
    case, Hyper-h2 will terminate the stream early and return this event.

    .. versionchanged:: 2.0.0
       This event is now fired when Hyper-h2 automatically resets a stream.
    """
    def __init__(self):
        #: The Stream ID of the stream that was reset.
        self.stream_id = None

        #: The error code given. Either one of :class:`ErrorCodes
        #: <h2.errors.ErrorCodes>` or ``int``
        self.error_code = None

        #: Whether the remote peer sent a RST_STREAM or we did.
        self.remote_reset = True

    def __repr__(self):
        return "<StreamReset stream_id:%s, error_code:%s, remote_reset:%s>" % (
            self.stream_id, self.error_code, self.remote_reset
        )


class PushedStreamReceived(Event):
    """
    The PushedStreamReceived event is fired whenever a pushed stream has been
    received from a remote peer. The event carries on it the new stream ID, the
    ID of the parent stream, and the request headers pushed by the remote peer.
    """
    def __init__(self):
        #: The Stream ID of the stream created by the push.
        self.pushed_stream_id = None

        #: The Stream ID of the stream that the push is related to.
        self.parent_stream_id = None

        #: The request headers, sent by the remote party in the push.
        self.headers = None

    def __repr__(self):
        return (
            "<PushedStreamReceived pushed_stream_id:%s, parent_stream_id:%s, "
            "headers:%s>" % (
                self.pushed_stream_id,
                self.parent_stream_id,
                self.headers,
            )
        )


class SettingsAcknowledged(Event):
    """
    The SettingsAcknowledged event is fired whenever a settings ACK is received
    from the remote peer. The event carries on it the settings that were
    acknowedged, in the same format as
    :class:`h2.events.RemoteSettingsChanged`.
    """
    def __init__(self):
        #: A dictionary of setting byte to
        #: :class:`ChangedSetting <h2.settings.ChangedSetting>`, representing
        #: the changed settings.
        self.changed_settings = {}

    def __repr__(self):
        return "<SettingsAcknowledged changed_settings:{%s}>" % (
            ", ".join(repr(cs) for cs in self.changed_settings.values()),
        )


class PriorityUpdated(Event):
    """
    The PriorityUpdated event is fired whenever a stream sends updated priority
    information. This can occur when the stream is opened, or at any time
    during the stream lifetime.

    This event is purely advisory, and does not need to be acted on.

    .. versionadded:: 2.0.0
    """
    def __init__(self):
        #: The ID of the stream whose priority information is being updated.
        self.stream_id = None

        #: The new stream weight. May be the same as the original stream
        #: weight. An integer between 1 and 256.
        self.weight = None

        #: The stream ID this stream now depends on. May be ``0``.
        self.depends_on = None

        #: Whether the stream *exclusively* depends on the parent stream. If it
        #: does, this stream should inherit the current children of its new
        #: parent.
        self.exclusive = None

    def __repr__(self):
        return (
            "<PriorityUpdated stream_id:%s, weight:%s, depends_on:%s, "
            "exclusive:%s>" % (
                self.stream_id,
                self.weight,
                self.depends_on,
                self.exclusive
            )
        )


class ConnectionTerminated(Event):
    """
    The ConnectionTerminated event is fired when a connection is torn down by
    the remote peer using a GOAWAY frame. Once received, no further action may
    be taken on the connection: a new connection must be established.
    """
    def __init__(self):
        #: The error code cited when tearing down the connection. Should be
        #: one of :class:`ErrorCodes <h2.errors.ErrorCodes>`, but may not be if
        #: unknown HTTP/2 extensions are being used.
        self.error_code = None

        #: The stream ID of the last stream the remote peer saw. This can
        #: provide an indication of what data, if any, never reached the remote
        #: peer and so can safely be resent.
        self.last_stream_id = None

        #: Additional debug data that can be appended to GOAWAY frame.
        self.additional_data = None

    def __repr__(self):
        return (
            "<ConnectionTerminated error_code:%s, last_stream_id:%s, "
            "additional_data:%s>" % (
                self.error_code,
                self.last_stream_id,
                _bytes_representation(
                    self.additional_data[:20]
                    if self.additional_data else None)
            )
        )


class AlternativeServiceAvailable(Event):
    """
    The AlternativeServiceAvailable event is fired when the remote peer
    advertises an `RFC 7838 <https://tools.ietf.org/html/rfc7838>`_ Alternative
    Service using an ALTSVC frame.

    This event always carries the origin to which the ALTSVC information
    applies. That origin is either supplied by the server directly, or inferred
    by hyper-h2 from the ``:authority`` pseudo-header field that was sent by
    the user when initiating a given stream.

    This event also carries what RFC 7838 calls the "Alternative Service Field
    Value", which is formatted like a HTTP header field and contains the
    relevant alternative service information. Hyper-h2 does not parse or in any
    way modify that information: the user is required to do that.

    This event can only be fired on the client end of a connection.

    .. versionadded:: 2.3.0
    """
    def __init__(self):
        #: The origin to which the alternative service field value applies.
        #: This field is either supplied by the server directly, or inferred by
        #: hyper-h2 from the ``:authority`` pseudo-header field that was sent
        #: by the user when initiating the stream on which the frame was
        #: received.
        self.origin = None

        #: The ALTSVC field value. This contains information about the HTTP
        #: alternative service being advertised by the server. Hyper-h2 does
        #: not parse this field: it is left exactly as sent by the server. The
        #: structure of the data in this field is given by `RFC 7838 Section 3
        #: <https://tools.ietf.org/html/rfc7838#section-3>`_.
        self.field_value = None

    def __repr__(self):
        return (
            "<AlternativeServiceAvailable origin:%s, field_value:%s>" % (
                self.origin.decode('utf-8', 'ignore'),
                self.field_value.decode('utf-8', 'ignore'),
            )
        )


class UnknownFrameReceived(Event):
    """
    The UnknownFrameReceived event is fired when the remote peer sends a frame
    that hyper-h2 does not understand. This occurs primarily when the remote
    peer is employing HTTP/2 extensions that hyper-h2 doesn't know anything
    about.

    RFC 7540 requires that HTTP/2 implementations ignore these frames. hyper-h2
    does so. However, this event is fired to allow implementations to perform
    special processing on those frames if needed (e.g. if the implementation
    is capable of handling the frame itself).

    .. versionadded:: 2.7.0
    """
    def __init__(self):
        #: The hyperframe Frame object that encapsulates the received frame.
        self.frame = None

    def __repr__(self):
        return "<UnknownFrameReceived>"


def _bytes_representation(data):
    """
    Converts a bytestring into something that is safe to print on all Python
    platforms.

    This function is relatively expensive, so it should not be called on the
    mainline of the code. It's safe to use in things like object repr methods
    though.
    """
    if data is None:
        return None

    hex = binascii.hexlify(data)

    # This is moderately clever: on all Python versions hexlify returns a byte
    # string. On Python 3 we want an actual string, so we just check whether
    # that's what we have.
    if not isinstance(hex, str):  # pragma: no cover
        hex = hex.decode('ascii')

    return hex
  0707010001f44e000081a40000000000000000000000016a100daf00000613000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/h2/errors.py   # -*- coding: utf-8 -*-
"""
h2/errors
~~~~~~~~~~~~~~~~~~~

Global error code registry containing the established HTTP/2 error codes.

The current registry is available at:
https://tools.ietf.org/html/rfc7540#section-11.4
"""
import aenum


class ErrorCodes(aenum.IntEnum):
    """
    All known HTTP/2 error codes.

    .. versionadded:: 2.5.0
    """
    #: Graceful shutdown.
    NO_ERROR = 0x0

    #: Protocol error detected.
    PROTOCOL_ERROR = 0x1

    #: Implementation fault.
    INTERNAL_ERROR = 0x2

    #: Flow-control limits exceeded.
    FLOW_CONTROL_ERROR = 0x3

    #: Settings not acknowledged.
    SETTINGS_TIMEOUT = 0x4

    #: Frame received for closed stream.
    STREAM_CLOSED = 0x5

    #: Frame size incorrect.
    FRAME_SIZE_ERROR = 0x6

    #: Stream not processed.
    REFUSED_STREAM = 0x7

    #: Stream cancelled.
    CANCEL = 0x8

    #: Compression state not updated.
    COMPRESSION_ERROR = 0x9

    #: TCP connection error for CONNECT method.
    CONNECT_ERROR = 0xa

    #: Processing capacity exceeded.
    ENHANCE_YOUR_CALM = 0xb

    #: Negotiated TLS parameters not acceptable.
    INADEQUATE_SECURITY = 0xc

    #: Use HTTP/1.1 for the request.
    HTTP_1_1_REQUIRED = 0xd


def _error_code_from_int(code):
    """
    Given an integer error code, returns either one of :class:`ErrorCodes
    <h2.errors.ErrorCodes>` or, if not present in the known set of codes,
    returns the integer directly.
    """
    try:
        return ErrorCodes(code)
    except ValueError:
        return code


__all__ = ['ErrorCodes']
 0707010001f454000081a40000000000000000000000016a100daf00005922000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/h2/utilities.py    # -*- coding: utf-8 -*-
"""
h2/utilities
~~~~~~~~~~~~

Utility functions that do not belong in a separate module.
"""
import collections
import re
from string import whitespace
import sys

from hpack import HeaderTuple, NeverIndexedHeaderTuple

from .exceptions import ProtocolError, FlowControlError

UPPER_RE = re.compile(b"[A-Z]")

# A set of headers that are hop-by-hop or connection-specific and thus
# forbidden in HTTP/2. This list comes from RFC 7540 Â§ 8.1.2.2.
CONNECTION_HEADERS = frozenset([
    b'connection', u'connection',
    b'proxy-connection', u'proxy-connection',
    b'keep-alive', u'keep-alive',
    b'transfer-encoding', u'transfer-encoding',
    b'upgrade', u'upgrade',
])


_ALLOWED_PSEUDO_HEADER_FIELDS = frozenset([
    b':method', u':method',
    b':scheme', u':scheme',
    b':authority', u':authority',
    b':path', u':path',
    b':status', u':status',
    b':protocol', u':protocol',
])


_SECURE_HEADERS = frozenset([
    # May have basic credentials which are vulnerable to dictionary attacks.
    b'authorization', u'authorization',
    b'proxy-authorization', u'proxy-authorization',
])


_REQUEST_ONLY_HEADERS = frozenset([
    b':scheme', u':scheme',
    b':path', u':path',
    b':authority', u':authority',
    b':method', u':method',
    b':protocol', u':protocol',
])


_RESPONSE_ONLY_HEADERS = frozenset([b':status', u':status'])


# A Set of pseudo headers that are only valid if the method is
# CONNECT, see RFC 8441 Â§ 5
_CONNECT_REQUEST_ONLY_HEADERS = frozenset([b':protocol', u':protocol'])


if sys.version_info[0] == 2:  # Python 2.X
    _WHITESPACE = frozenset(whitespace)
else:  # Python 3.3+
    _WHITESPACE = frozenset(map(ord, whitespace))


def _secure_headers(headers, hdr_validation_flags):
    """
    Certain headers are at risk of being attacked during the header compression
    phase, and so need to be kept out of header compression contexts. This
    function automatically transforms certain specific headers into HPACK
    never-indexed fields to ensure they don't get added to header compression
    contexts.

    This function currently implements two rules:

    - 'authorization' and 'proxy-authorization' fields are automatically made
      never-indexed.
    - Any 'cookie' header field shorter than 20 bytes long is made
      never-indexed.

    These fields are the most at-risk. These rules are inspired by Firefox
    and nghttp2.
    """
    for header in headers:
        if header[0] in _SECURE_HEADERS:
            yield NeverIndexedHeaderTuple(*header)
        elif header[0] in (b'cookie', u'cookie') and len(header[1]) < 20:
            yield NeverIndexedHeaderTuple(*header)
        else:
            yield header


def extract_method_header(headers):
    """
    Extracts the request method from the headers list.
    """
    for k, v in headers:
        if k in (b':method', u':method'):
            if not isinstance(v, bytes):
                return v.encode('utf-8')
            else:
                return v


def is_informational_response(headers):
    """
    Searches a header block for a :status header to confirm that a given
    collection of headers are an informational response. Assumes the header
    block is well formed: that is, that the HTTP/2 special headers are first
    in the block, and so that it can stop looking when it finds the first
    header field whose name does not begin with a colon.

    :param headers: The HTTP/2 header block.
    :returns: A boolean indicating if this is an informational response.
    """
    for n, v in headers:
        if isinstance(n, bytes):
            sigil = b':'
            status = b':status'
            informational_start = b'1'
        else:
            sigil = u':'
            status = u':status'
            informational_start = u'1'

        # If we find a non-special header, we're done here: stop looping.
        if not n.startswith(sigil):
            return False

        # This isn't the status header, bail.
        if n != status:
            continue

        # If the first digit is a 1, we've got informational headers.
        return v.startswith(informational_start)


def guard_increment_window(current, increment):
    """
    Increments a flow control window, guarding against that window becoming too
    large.

    :param current: The current value of the flow control window.
    :param increment: The increment to apply to that window.
    :returns: The new value of the window.
    :raises: ``FlowControlError``
    """
    # The largest value the flow control window may take.
    LARGEST_FLOW_CONTROL_WINDOW = 2**31 - 1

    new_size = current + increment

    if new_size > LARGEST_FLOW_CONTROL_WINDOW:
        raise FlowControlError(
            "May not increment flow control window past %d" %
            LARGEST_FLOW_CONTROL_WINDOW
        )

    return new_size


def authority_from_headers(headers):
    """
    Given a header set, searches for the authority header and returns the
    value.

    Note that this doesn't terminate early, so should only be called if the
    headers are for a client request. Otherwise, will loop over the entire
    header set, which is potentially unwise.

    :param headers: The HTTP header set.
    :returns: The value of the authority header, or ``None``.
    :rtype: ``bytes`` or ``None``.
    """
    for n, v in headers:
        # This gets run against headers that come both from HPACK and from the
        # user, so we may have unicode floating around in here. We only want
        # bytes.
        if n in (b':authority', u':authority'):
            return v.encode('utf-8') if not isinstance(v, bytes) else v

    return None


# Flags used by the validate_headers pipeline to determine which checks
# should be applied to a given set of headers.
HeaderValidationFlags = collections.namedtuple(
    'HeaderValidationFlags',
    ['is_client', 'is_trailer', 'is_response_header', 'is_push_promise']
)


def validate_headers(headers, hdr_validation_flags):
    """
    Validates a header sequence against a set of constraints from RFC 7540.

    :param headers: The HTTP header set.
    :param hdr_validation_flags: An instance of HeaderValidationFlags.
    """
    # This validation logic is built on a sequence of generators that are
    # iterated over to provide the final header list. This reduces some of the
    # overhead of doing this checking. However, it's worth noting that this
    # checking remains somewhat expensive, and attempts should be made wherever
    # possible to reduce the time spent doing them.
    #
    # For example, we avoid tuple upacking in loops because it represents a
    # fixed cost that we don't want to spend, instead indexing into the header
    # tuples.
    headers = _reject_uppercase_header_fields(
        headers, hdr_validation_flags
    )
    headers = _reject_surrounding_whitespace(
        headers, hdr_validation_flags
    )
    headers = _reject_te(
        headers, hdr_validation_flags
    )
    headers = _reject_connection_header(
        headers, hdr_validation_flags
    )
    headers = _reject_pseudo_header_fields(
        headers, hdr_validation_flags
    )
    headers = _check_host_authority_header(
        headers, hdr_validation_flags
    )
    headers = _check_path_header(headers, hdr_validation_flags)

    return headers


def _reject_uppercase_header_fields(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if any uppercase character is found in a header
    block.
    """
    for header in headers:
        if UPPER_RE.search(header[0]):
            raise ProtocolError(
                "Received uppercase header name %s." % header[0])
        yield header


def _reject_surrounding_whitespace(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if any header name or value is surrounded by
    whitespace characters.
    """
    # For compatibility with RFC 7230 header fields, we need to allow the field
    # value to be an empty string. This is ludicrous, but technically allowed.
    # The field name may not be empty, though, so we can safely assume that it
    # must have at least one character in it and throw exceptions if it
    # doesn't.
    for header in headers:
        if header[0][0] in _WHITESPACE or header[0][-1] in _WHITESPACE:
            raise ProtocolError(
                "Received header name surrounded by whitespace %r" % header[0])
        if header[1] and ((header[1][0] in _WHITESPACE) or
           (header[1][-1] in _WHITESPACE)):
            raise ProtocolError(
                "Received header value surrounded by whitespace %r" % header[1]
            )
        yield header


def _reject_te(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if the TE header is present in a header block and
    its value is anything other than "trailers".
    """
    for header in headers:
        if header[0] in (b'te', u'te'):
            if header[1].lower() not in (b'trailers', u'trailers'):
                raise ProtocolError(
                    "Invalid value for Transfer-Encoding header: %s" %
                    header[1]
                )

        yield header


def _reject_connection_header(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if the Connection header is present in a header
    block.
    """
    for header in headers:
        if header[0] in CONNECTION_HEADERS:
            raise ProtocolError(
                "Connection-specific header field present: %s." % header[0]
            )

        yield header


def _custom_startswith(test_string, bytes_prefix, unicode_prefix):
    """
    Given a string that might be a bytestring or a Unicode string,
    return True if it starts with the appropriate prefix.
    """
    if isinstance(test_string, bytes):
        return test_string.startswith(bytes_prefix)
    else:
        return test_string.startswith(unicode_prefix)


def _assert_header_in_set(string_header, bytes_header, header_set):
    """
    Given a set of header names, checks whether the string or byte version of
    the header name is present. Raises a Protocol error with the appropriate
    error if it's missing.
    """
    if not (string_header in header_set or bytes_header in header_set):
        raise ProtocolError(
            "Header block missing mandatory %s header" % string_header
        )


def _reject_pseudo_header_fields(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if duplicate pseudo-header fields are found in a
    header block or if a pseudo-header field appears in a block after an
    ordinary header field.

    Raises a ProtocolError if pseudo-header fields are found in trailers.
    """
    seen_pseudo_header_fields = set()
    seen_regular_header = False
    method = None

    for header in headers:
        if _custom_startswith(header[0], b':', u':'):
            if header[0] in seen_pseudo_header_fields:
                raise ProtocolError(
                    "Received duplicate pseudo-header field %s" % header[0]
                )

            seen_pseudo_header_fields.add(header[0])

            if seen_regular_header:
                raise ProtocolError(
                    "Received pseudo-header field out of sequence: %s" %
                    header[0]
                )

            if header[0] not in _ALLOWED_PSEUDO_HEADER_FIELDS:
                raise ProtocolError(
                    "Received custom pseudo-header field %s" % header[0]
                )

            if header[0] in (b':method', u':method'):
                if not isinstance(header[1], bytes):
                    method = header[1].encode('utf-8')
                else:
                    method = header[1]

        else:
            seen_regular_header = True

        yield header

    # Check the pseudo-headers we got to confirm they're acceptable.
    _check_pseudo_header_field_acceptability(
        seen_pseudo_header_fields, method, hdr_validation_flags
    )


def _check_pseudo_header_field_acceptability(pseudo_headers,
                                             method,
                                             hdr_validation_flags):
    """
    Given the set of pseudo-headers present in a header block and the
    validation flags, confirms that RFC 7540 allows them.
    """
    # Pseudo-header fields MUST NOT appear in trailers - RFC 7540 Â§ 8.1.2.1
    if hdr_validation_flags.is_trailer and pseudo_headers:
        raise ProtocolError(
            "Received pseudo-header in trailer %s" % pseudo_headers
        )

    # If ':status' pseudo-header is not there in a response header, reject it.
    # Similarly, if ':path', ':method', or ':scheme' are not there in a request
    # header, reject it. Additionally, if a response contains any request-only
    # headers or vice-versa, reject it.
    # Relevant RFC section: RFC 7540 Â§ 8.1.2.4
    # https://tools.ietf.org/html/rfc7540#section-8.1.2.4
    if hdr_validation_flags.is_response_header:
        _assert_header_in_set(u':status', b':status', pseudo_headers)
        invalid_response_headers = pseudo_headers & _REQUEST_ONLY_HEADERS
        if invalid_response_headers:
            raise ProtocolError(
                "Encountered request-only headers %s" %
                invalid_response_headers
            )
    elif (not hdr_validation_flags.is_response_header and
          not hdr_validation_flags.is_trailer):
        # This is a request, so we need to have seen :path, :method, and
        # :scheme.
        _assert_header_in_set(u':path', b':path', pseudo_headers)
        _assert_header_in_set(u':method', b':method', pseudo_headers)
        _assert_header_in_set(u':scheme', b':scheme', pseudo_headers)
        invalid_request_headers = pseudo_headers & _RESPONSE_ONLY_HEADERS
        if invalid_request_headers:
            raise ProtocolError(
                "Encountered response-only headers %s" %
                invalid_request_headers
            )
        if method != b'CONNECT':
            invalid_headers = pseudo_headers & _CONNECT_REQUEST_ONLY_HEADERS
            if invalid_headers:
                raise ProtocolError(
                    "Encountered connect-request-only headers %s" %
                    invalid_headers
                )


def _validate_host_authority_header(headers):
    """
    Given the :authority and Host headers from a request block that isn't
    a trailer, check that:
     1. At least one of these headers is set.
     2. If both headers are set, they match.

    :param headers: The HTTP header set.
    :raises: ``ProtocolError``
    """
    # We use None as a sentinel value.  Iterate over the list of headers,
    # and record the value of these headers (if present).  We don't need
    # to worry about receiving duplicate :authority headers, as this is
    # enforced by the _reject_pseudo_header_fields() pipeline.
    #
    # TODO: We should also guard against receiving duplicate Host headers,
    # and against sending duplicate headers.
    authority_header_val = None
    host_header_val = None

    for header in headers:
        if header[0] in (b':authority', u':authority'):
            authority_header_val = header[1]
        elif header[0] in (b'host', u'host'):
            host_header_val = header[1]

        yield header

    # If we have not-None values for these variables, then we know we saw
    # the corresponding header.
    authority_present = (authority_header_val is not None)
    host_present = (host_header_val is not None)

    # It is an error for a request header block to contain neither
    # an :authority header nor a Host header.
    if not authority_present and not host_present:
        raise ProtocolError(
            "Request header block does not have an :authority or Host header."
        )

    # If we receive both headers, they should definitely match.
    if authority_present and host_present:
        if authority_header_val != host_header_val:
            raise ProtocolError(
                "Request header block has mismatched :authority and "
                "Host headers: %r / %r"
                % (authority_header_val, host_header_val)
            )


def _check_host_authority_header(headers, hdr_validation_flags):
    """
    Raises a ProtocolError if a header block arrives that does not contain an
    :authority or a Host header, or if a header block contains both fields,
    but their values do not match.
    """
    # We only expect to see :authority and Host headers on request header
    # blocks that aren't trailers, so skip this validation if this is a
    # response header or we're looking at trailer blocks.
    skip_validation = (
        hdr_validation_flags.is_response_header or
        hdr_validation_flags.is_trailer
    )
    if skip_validation:
        return headers

    return _validate_host_authority_header(headers)


def _check_path_header(headers, hdr_validation_flags):
    """
    Raise a ProtocolError if a header block arrives or is sent that contains an
    empty :path header.
    """
    def inner():
        for header in headers:
            if header[0] in (b':path', u':path'):
                if not header[1]:
                    raise ProtocolError("An empty :path header is forbidden")

            yield header

    # We only expect to see :authority and Host headers on request header
    # blocks that aren't trailers, so skip this validation if this is a
    # response header or we're looking at trailer blocks.
    skip_validation = (
        hdr_validation_flags.is_response_header or
        hdr_validation_flags.is_trailer
    )
    if skip_validation:
        return headers
    else:
        return inner()


def _lowercase_header_names(headers, hdr_validation_flags):
    """
    Given an iterable of header two-tuples, rebuilds that iterable with the
    header names lowercased. This generator produces tuples that preserve the
    original type of the header tuple for tuple and any ``HeaderTuple``.
    """
    for header in headers:
        if isinstance(header, HeaderTuple):
            yield header.__class__(header[0].lower(), header[1])
        else:
            yield (header[0].lower(), header[1])


def _strip_surrounding_whitespace(headers, hdr_validation_flags):
    """
    Given an iterable of header two-tuples, strip both leading and trailing
    whitespace from both header names and header values. This generator
    produces tuples that preserve the original type of the header tuple for
    tuple and any ``HeaderTuple``.
    """
    for header in headers:
        if isinstance(header, HeaderTuple):
            yield header.__class__(header[0].strip(), header[1].strip())
        else:
            yield (header[0].strip(), header[1].strip())


def _strip_connection_headers(headers, hdr_validation_flags):
    """
    Strip any connection headers as per RFC7540 Â§ 8.1.2.2.
    """
    for header in headers:
        if header[0] not in CONNECTION_HEADERS:
            yield header


def _check_sent_host_authority_header(headers, hdr_validation_flags):
    """
    Raises an InvalidHeaderBlockError if we try to send a header block
    that does not contain an :authority or a Host header, or if
    the header block contains both fields, but their values do not match.
    """
    # We only expect to see :authority and Host headers on request header
    # blocks that aren't trailers, so skip this validation if this is a
    # response header or we're looking at trailer blocks.
    skip_validation = (
        hdr_validation_flags.is_response_header or
        hdr_validation_flags.is_trailer
    )
    if skip_validation:
        return headers

    return _validate_host_authority_header(headers)


def _combine_cookie_fields(headers, hdr_validation_flags):
    """
    RFC 7540 Â§ 8.1.2.5 allows HTTP/2 clients to split the Cookie header field,
    which must normally appear only once, into multiple fields for better
    compression. However, they MUST be joined back up again when received.
    This normalization step applies that transform. The side-effect is that
    all cookie fields now appear *last* in the header block.
    """
    # There is a problem here about header indexing. Specifically, it's
    # possible that all these cookies are sent with different header indexing
    # values. At this point it shouldn't matter too much, so we apply our own
    # logic and make them never-indexed.
    cookies = []
    for header in headers:
        if header[0] == b'cookie':
            cookies.append(header[1])
        else:
            yield header
    if cookies:
        cookie_val = b'; '.join(cookies)
        yield NeverIndexedHeaderTuple(b'cookie', cookie_val)


def normalize_outbound_headers(headers, hdr_validation_flags):
    """
    Normalizes a header sequence that we are about to send.

    :param headers: The HTTP header set.
    :param hdr_validation_flags: An instance of HeaderValidationFlags.
    """
    headers = _lowercase_header_names(headers, hdr_validation_flags)
    headers = _strip_surrounding_whitespace(headers, hdr_validation_flags)
    headers = _strip_connection_headers(headers, hdr_validation_flags)
    headers = _secure_headers(headers, hdr_validation_flags)

    return headers


def normalize_inbound_headers(headers, hdr_validation_flags):
    """
    Normalizes a header sequence that we have received.

    :param headers: The HTTP header set.
    :param hdr_validation_flags: An instance of HeaderValidationFlags
    """
    headers = _combine_cookie_fields(headers, hdr_validation_flags)
    return headers


def validate_outbound_headers(headers, hdr_validation_flags):
    """
    Validates and normalizes a header sequence that we are about to send.

    :param headers: The HTTP header set.
    :param hdr_validation_flags: An instance of HeaderValidationFlags.
    """
    headers = _reject_te(
        headers, hdr_validation_flags
    )
    headers = _reject_connection_header(
        headers, hdr_validation_flags
    )
    headers = _reject_pseudo_header_fields(
        headers, hdr_validation_flags
    )
    headers = _check_sent_host_authority_header(
        headers, hdr_validation_flags
    )
    headers = _check_path_header(headers, hdr_validation_flags)

    return headers


class SizeLimitDict(collections.OrderedDict):

    def __init__(self, *args, **kwargs):
        self._size_limit = kwargs.pop("size_limit", None)
        super(SizeLimitDict, self).__init__(*args, **kwargs)

        self._check_size_limit()

    def __setitem__(self, key, value):
        super(SizeLimitDict, self).__setitem__(key, value)

        self._check_size_limit()

    def _check_size_limit(self):
        if self._size_limit is not None:
            while len(self) > self._size_limit:
                self.popitem(last=False)
  0707010001f451000081a40000000000000000000000016a100daf00001a73000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/foreign/h2/frame_buffer.py # -*- coding: utf-8 -*-
"""
h2/frame_buffer
~~~~~~~~~~~~~~~

A data structure that provides a way to iterate over a byte buffer in terms of
frames.
"""
from hyperframe.exceptions import InvalidFrameError
from hyperframe.frame import (
    Frame, HeadersFrame, ContinuationFrame, PushPromiseFrame
)

from .exceptions import (
    ProtocolError, FrameTooLargeError, FrameDataMissingError
)

# To avoid a DOS attack based on sending loads of continuation frames, we limit
# the maximum number we're perpared to receive. In this case, we'll set the
# limit to 64, which means the largest encoded header block we can receive by
# default is 262144 bytes long, and the largest possible *at all* is 1073741760
# bytes long.
#
# This value seems reasonable for now, but in future we may want to evaluate
# making it configurable.
CONTINUATION_BACKLOG = 64


class FrameBuffer(object):
    """
    This is a data structure that expects to act as a buffer for HTTP/2 data
    that allows iteraton in terms of H2 frames.
    """
    def __init__(self, server=False):
        self.data = b''
        self.max_frame_size = 0
        self._preamble = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' if server else b''
        self._preamble_len = len(self._preamble)
        self._headers_buffer = []

    def add_data(self, data):
        """
        Add more data to the frame buffer.

        :param data: A bytestring containing the byte buffer.
        """
        if self._preamble_len:
            data_len = len(data)
            of_which_preamble = min(self._preamble_len, data_len)

            if self._preamble[:of_which_preamble] != data[:of_which_preamble]:
                raise ProtocolError("Invalid HTTP/2 preamble.")

            data = data[of_which_preamble:]
            self._preamble_len -= of_which_preamble
            self._preamble = self._preamble[of_which_preamble:]

        self.data += data

    def _parse_frame_header(self, data):
        """
        Parses the frame header from the data. Either returns a tuple of
        (frame, length), or throws an exception. The returned frame may be None
        if the frame is of unknown type.
        """
        try:
            frame, length = Frame.parse_frame_header(data[:9])
        except ValueError as e:
            # The frame header is invalid. This is a ProtocolError
            raise ProtocolError("Invalid frame header received: %s" % str(e))

        return frame, length

    def _validate_frame_length(self, length):
        """
        Confirm that the frame is an appropriate length.
        """
        if length > self.max_frame_size:
            raise FrameTooLargeError(
                "Received overlong frame: length %d, max %d" %
                (length, self.max_frame_size)
            )

    def _update_header_buffer(self, f):
        """
        Updates the internal header buffer. Returns a frame that should replace
        the current one. May throw exceptions if this frame is invalid.
        """
        # Check if we're in the middle of a headers block. If we are, this
        # frame *must* be a CONTINUATION frame with the same stream ID as the
        # leading HEADERS or PUSH_PROMISE frame. Anything else is a
        # ProtocolError. If the frame *is* valid, append it to the header
        # buffer.
        if self._headers_buffer:
            stream_id = self._headers_buffer[0].stream_id
            valid_frame = (
                f is not None and
                isinstance(f, ContinuationFrame) and
                f.stream_id == stream_id
            )
            if not valid_frame:
                raise ProtocolError("Invalid frame during header block.")

            # Append the frame to the buffer.
            self._headers_buffer.append(f)
            if len(self._headers_buffer) > CONTINUATION_BACKLOG:
                raise ProtocolError("Too many continuation frames received.")

            # If this is the end of the header block, then we want to build a
            # mutant HEADERS frame that's massive. Use the original one we got,
            # then set END_HEADERS and set its data appopriately. If it's not
            # the end of the block, lose the current frame: we can't yield it.
            if 'END_HEADERS' in f.flags:
                f = self._headers_buffer[0]
                f.flags.add('END_HEADERS')
                f.data = b''.join(x.data for x in self._headers_buffer)
                self._headers_buffer = []
            else:
                f = None
        elif (isinstance(f, (HeadersFrame, PushPromiseFrame)) and
                'END_HEADERS' not in f.flags):
            # This is the start of a headers block! Save the frame off and then
            # act like we didn't receive one.
            self._headers_buffer.append(f)
            f = None

        return f

    # The methods below support the iterator protocol.
    def __iter__(self):
        return self

    def next(self):  # Python 2
        # First, check that we have enough data to successfully parse the
        # next frame header. If not, bail. Otherwise, parse it.
        if len(self.data) < 9:
            raise StopIteration()

        try:
            f, length = self._parse_frame_header(self.data)
        except InvalidFrameError:  # pragma: no cover
            raise ProtocolError("Received frame with invalid frame header.")

        # Next, check that we have enough length to parse the frame body. If
        # not, bail, leaving the frame header data in the buffer for next time.
        if len(self.data) < length + 9:
            raise StopIteration()

        # Confirm the frame has an appropriate length.
        self._validate_frame_length(length)

        # Don't try to parse the body if we didn't get a frame we know about:
        # there's nothing we can do with it anyway.
        if f is not None:
            try:
                f.parse_body(memoryview(self.data[9:9+length]))
            except InvalidFrameError:
                raise FrameDataMissingError("Frame data missing or invalid")

        # At this point, as we know we'll use or discard the entire frame, we
        # can update the data.
        self.data = self.data[9+length:]

        # Pass the frame through the header buffer.
        f = self._update_header_buffer(f)

        # If we got a frame we didn't understand or shouldn't yield, rather
        # than return None it'd be better if we just tried to get the next
        # frame in the sequence instead. Recurse back into ourselves to do
        # that. This is safe because the amount of work we have to do here is
        # strictly bounded by the length of the buffer.
        return f if f is not None else self.next()

    def __next__(self):  # Python 3
        return self.next()
 0707010001f453000081a40000000000000000000000016a100daf0000da8d000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/h2/stream.py   # -*- coding: utf-8 -*-
"""
h2/stream
~~~~~~~~~

An implementation of a HTTP/2 stream.
"""
from aenum import Enum, IntEnum
from hpack import HeaderTuple
from hyperframe.frame import (
    HeadersFrame, ContinuationFrame, DataFrame, WindowUpdateFrame,
    RstStreamFrame, PushPromiseFrame, AltSvcFrame
)

from .errors import ErrorCodes, _error_code_from_int
from .events import (
    RequestReceived, ResponseReceived, DataReceived, WindowUpdated,
    StreamEnded, PushedStreamReceived, StreamReset, TrailersReceived,
    InformationalResponseReceived, AlternativeServiceAvailable,
    _ResponseSent, _RequestSent, _TrailersSent, _PushedRequestSent
)
from .exceptions import (
    ProtocolError, StreamClosedError, InvalidBodyLengthError, FlowControlError
)
from .utilities import (
    guard_increment_window, is_informational_response, authority_from_headers,
    validate_headers, validate_outbound_headers, normalize_outbound_headers,
    HeaderValidationFlags, extract_method_header, normalize_inbound_headers
)
from .windows import WindowManager


class StreamState(IntEnum):
    IDLE = 0
    RESERVED_REMOTE = 1
    RESERVED_LOCAL = 2
    OPEN = 3
    HALF_CLOSED_REMOTE = 4
    HALF_CLOSED_LOCAL = 5
    CLOSED = 6


class StreamInputs(Enum):
    SEND_HEADERS = 0
    SEND_PUSH_PROMISE = 1
    SEND_RST_STREAM = 2
    SEND_DATA = 3
    SEND_WINDOW_UPDATE = 4
    SEND_END_STREAM = 5
    RECV_HEADERS = 6
    RECV_PUSH_PROMISE = 7
    RECV_RST_STREAM = 8
    RECV_DATA = 9
    RECV_WINDOW_UPDATE = 10
    RECV_END_STREAM = 11
    RECV_CONTINUATION = 12  # Added in 2.0.0
    SEND_INFORMATIONAL_HEADERS = 13  # Added in 2.2.0
    RECV_INFORMATIONAL_HEADERS = 14  # Added in 2.2.0
    SEND_ALTERNATIVE_SERVICE = 15  # Added in 2.3.0
    RECV_ALTERNATIVE_SERVICE = 16  # Added in 2.3.0
    UPGRADE_CLIENT = 17  # Added 2.3.0
    UPGRADE_SERVER = 18  # Added 2.3.0


class StreamClosedBy(Enum):
    SEND_END_STREAM = 0
    RECV_END_STREAM = 1
    SEND_RST_STREAM = 2
    RECV_RST_STREAM = 3


# This array is initialized once, and is indexed by the stream states above.
# It indicates whether a stream in the given state is open. The reason we do
# this is that we potentially check whether a stream in a given state is open
# quite frequently: given that we check so often, we should do so in the
# fastest and most performant way possible.
STREAM_OPEN = [False for _ in range(0, len(StreamState))]
STREAM_OPEN[StreamState.OPEN] = True
STREAM_OPEN[StreamState.HALF_CLOSED_LOCAL] = True
STREAM_OPEN[StreamState.HALF_CLOSED_REMOTE] = True


class H2StreamStateMachine(object):
    """
    A single HTTP/2 stream state machine.

    This stream object implements basically the state machine described in
    RFC 7540 section 5.1.

    :param stream_id: The stream ID of this stream. This is stored primarily
        for logging purposes.
    """
    def __init__(self, stream_id):
        self.state = StreamState.IDLE
        self.stream_id = stream_id

        #: Whether this peer is the client side of this stream.
        self.client = None

        # Whether trailers have been sent/received on this stream or not.
        self.headers_sent = None
        self.trailers_sent = None
        self.headers_received = None
        self.trailers_received = None

        # How the stream was closed. One of StreamClosedBy.
        self.stream_closed_by = None

    def process_input(self, input_):
        """
        Process a specific input in the state machine.
        """
        if not isinstance(input_, StreamInputs):
            raise ValueError("Input must be an instance of StreamInputs")

        try:
            func, target_state = _transitions[(self.state, input_)]
        except KeyError:
            old_state = self.state
            self.state = StreamState.CLOSED
            raise ProtocolError(
                "Invalid input %s in state %s" % (input_, old_state)
            )
        else:
            previous_state = self.state
            self.state = target_state
            if func is not None:
                try:
                    return func(self, previous_state)
                except ProtocolError:
                    self.state = StreamState.CLOSED
                    raise
                except AssertionError as e:  # pragma: no cover
                    self.state = StreamState.CLOSED
                    raise ProtocolError(e)

            return []

    def request_sent(self, previous_state):
        """
        Fires when a request is sent.
        """
        self.client = True
        self.headers_sent = True
        event = _RequestSent()

        return [event]

    def response_sent(self, previous_state):
        """
        Fires when something that should be a response is sent. This 'response'
        may actually be trailers.
        """
        if not self.headers_sent:
            if self.client is True or self.client is None:
                raise ProtocolError("Client cannot send responses.")
            self.headers_sent = True
            event = _ResponseSent()
        else:
            assert not self.trailers_sent
            self.trailers_sent = True
            event = _TrailersSent()

        return [event]

    def request_received(self, previous_state):
        """
        Fires when a request is received.
        """
        assert not self.headers_received
        assert not self.trailers_received

        self.client = False
        self.headers_received = True
        event = RequestReceived()

        event.stream_id = self.stream_id
        return [event]

    def response_received(self, previous_state):
        """
        Fires when a response is received. Also disambiguates between responses
        and trailers.
        """
        if not self.headers_received:
            assert self.client is True
            self.headers_received = True
            event = ResponseReceived()
        else:
            assert not self.trailers_received
            self.trailers_received = True
            event = TrailersReceived()

        event.stream_id = self.stream_id
        return [event]

    def data_received(self, previous_state):
        """
        Fires when data is received.
        """
        event = DataReceived()
        event.stream_id = self.stream_id
        return [event]

    def window_updated(self, previous_state):
        """
        Fires when a window update frame is received.
        """
        event = WindowUpdated()
        event.stream_id = self.stream_id
        return [event]

    def stream_half_closed(self, previous_state):
        """
        Fires when an END_STREAM flag is received in the OPEN state,
        transitioning this stream to a HALF_CLOSED_REMOTE state.
        """
        event = StreamEnded()
        event.stream_id = self.stream_id
        return [event]

    def stream_ended(self, previous_state):
        """
        Fires when a stream is cleanly ended.
        """
        self.stream_closed_by = StreamClosedBy.RECV_END_STREAM
        event = StreamEnded()
        event.stream_id = self.stream_id
        return [event]

    def stream_reset(self, previous_state):
        """
        Fired when a stream is forcefully reset.
        """
        self.stream_closed_by = StreamClosedBy.RECV_RST_STREAM
        event = StreamReset()
        event.stream_id = self.stream_id
        return [event]

    def send_new_pushed_stream(self, previous_state):
        """
        Fires on the newly pushed stream, when pushed by the local peer.

        No event here, but definitionally this peer must be a server.
        """
        assert self.client is None
        self.client = False
        self.headers_received = True
        return []

    def recv_new_pushed_stream(self, previous_state):
        """
        Fires on the newly pushed stream, when pushed by the remote peer.

        No event here, but definitionally this peer must be a client.
        """
        assert self.client is None
        self.client = True
        self.headers_sent = True
        return []

    def send_push_promise(self, previous_state):
        """
        Fires on the already-existing stream when a PUSH_PROMISE frame is sent.
        We may only send PUSH_PROMISE frames if we're a server.
        """
        if self.client is True:
            raise ProtocolError("Cannot push streams from client peers.")

        event = _PushedRequestSent()
        return [event]

    def recv_push_promise(self, previous_state):
        """
        Fires on the already-existing stream when a PUSH_PROMISE frame is
        received. We may only receive PUSH_PROMISE frames if we're a client.

        Fires a PushedStreamReceived event.
        """
        if not self.client:
            if self.client is None:  # pragma: no cover
                msg = "Idle streams cannot receive pushes"
            else:  # pragma: no cover
                msg = "Cannot receive pushed streams as a server"
            raise ProtocolError(msg)

        event = PushedStreamReceived()
        event.parent_stream_id = self.stream_id
        return [event]

    def send_end_stream(self, previous_state):
        """
        Called when an attempt is made to send END_STREAM in the
        HALF_CLOSED_REMOTE state.
        """
        self.stream_closed_by = StreamClosedBy.SEND_END_STREAM

    def send_reset_stream(self, previous_state):
        """
        Called when an attempt is made to send RST_STREAM in a non-closed
        stream state.
        """
        self.stream_closed_by = StreamClosedBy.SEND_RST_STREAM

    def reset_stream_on_error(self, previous_state):
        """
        Called when we need to forcefully emit another RST_STREAM frame on
        behalf of the state machine.

        If this is the first time we've done this, we should also hang an event
        off the StreamClosedError so that the user can be informed. We know
        it's the first time we've done this if the stream is currently in a
        state other than CLOSED.
        """
        self.stream_closed_by = StreamClosedBy.SEND_RST_STREAM

        error = StreamClosedError(self.stream_id)

        event = StreamReset()
        event.stream_id = self.stream_id
        event.error_code = ErrorCodes.STREAM_CLOSED
        event.remote_reset = False
        error._events = [event]
        raise error

    def recv_on_closed_stream(self, previous_state):
        """
        Called when an unexpected frame is received on an already-closed
        stream.

        An endpoint that receives an unexpected frame should treat it as
        a stream error or connection error with type STREAM_CLOSED, depending
        on the specific frame. The error handling is done at a higher level:
        this just raises the appropriate error.
        """
        raise StreamClosedError(self.stream_id)

    def send_on_closed_stream(self, previous_state):
        """
        Called when an attempt is made to send data on an already-closed
        stream.

        This essentially overrides the standard logic by throwing a
        more-specific error: StreamClosedError. This is a ProtocolError, so it
        matches the standard API of the state machine, but provides more detail
        to the user.
        """
        raise StreamClosedError(self.stream_id)

    def recv_push_on_closed_stream(self, previous_state):
        """
        Called when a PUSH_PROMISE frame is received on a full stop
        stream.

        If the stream was closed by us sending a RST_STREAM frame, then we
        presume that the PUSH_PROMISE was in flight when we reset the parent
        stream. Rathen than accept the new stream, we just reset it.
        Otherwise, we should call this a PROTOCOL_ERROR: pushing a stream on a
        naturally closed stream is a real problem because it creates a brand
        new stream that the remote peer now believes exists.
        """
        assert self.stream_closed_by is not None

        if self.stream_closed_by == StreamClosedBy.SEND_RST_STREAM:
            raise StreamClosedError(self.stream_id)
        else:
            raise ProtocolError("Attempted to push on closed stream.")

    def send_push_on_closed_stream(self, previous_state):
        """
        Called when an attempt is made to push on an already-closed stream.

        This essentially overrides the standard logic by providing a more
        useful error message. It's necessary because simply indicating that the
        stream is closed is not enough: there is now a new stream that is not
        allowed to be there. The only recourse is to tear the whole connection
        down.
        """
        raise ProtocolError("Attempted to push on closed stream.")

    def window_on_closed_stream(self, previous_state):
        """
        Called when a WINDOW_UPDATE frame is received on an already-closed
        stream.

        If we sent an END_STREAM frame, we just ignore the frame, as instructed
        in RFC 7540 Section 5.1. Technically we should eventually consider
        WINDOW_UPDATE in this state an error, but we don't have access to a
        clock so we just always allow it. If we closed the stream for any other
        reason, we behave as we do for receiving any other frame on a closed
        stream.
        """
        assert self.stream_closed_by is not None

        if self.stream_closed_by == StreamClosedBy.SEND_END_STREAM:
            return []
        return self.recv_on_closed_stream(previous_state)

    def reset_on_closed_stream(self, previous_state):
        """
        Called when a RST_STREAM frame is received on an already-closed stream.

        If we sent an END_STREAM frame, we just ignore the frame, as instructed
        in RFC 7540 Section 5.1. Technically we should eventually consider
        RST_STREAM in this state an error, but we don't have access to a clock
        so we just always allow it. If we closed the stream for any other
        reason, we behave as we do for receiving any other frame on a closed
        stream.
        """
        assert self.stream_closed_by is not None

        if self.stream_closed_by is StreamClosedBy.SEND_END_STREAM:
            return []
        return self.recv_on_closed_stream(previous_state)

    def send_informational_response(self, previous_state):
        """
        Called when an informational header block is sent (that is, a block
        where the :status header has a 1XX value).

        Only enforces that these are sent *before* final headers are sent.
        """
        if self.headers_sent:
            raise ProtocolError("Information response after final response")

        event = _ResponseSent()
        return [event]

    def recv_informational_response(self, previous_state):
        """
        Called when an informational header block is received (that is, a block
        where the :status header has a 1XX value).
        """
        if self.headers_received:
            raise ProtocolError("Informational response after final response")

        event = InformationalResponseReceived()
        event.stream_id = self.stream_id
        return [event]

    def recv_alt_svc(self, previous_state):
        """
        Called when receiving an ALTSVC frame.

        RFC 7838 allows us to receive ALTSVC frames at any stream state, which
        is really absurdly overzealous. For that reason, we want to limit the
        states in which we can actually receive it. It's really only sensible
        to receive it after we've sent our own headers and before the server
        has sent its header block: the server can't guarantee that we have any
        state around after it completes its header block, and the server
        doesn't know what origin we're talking about before we've sent ours.

        For that reason, this function applies a few extra checks on both state
        and some of the little state variables we keep around. If those suggest
        an unreasonable situation for the ALTSVC frame to have been sent in,
        we quietly ignore it (as RFC 7838 suggests).

        This function is also *not* always called by the state machine. In some
        states (IDLE, RESERVED_LOCAL, CLOSED) we don't bother to call it,
        because we know the frame cannot be valid in that state (IDLE because
        the server cannot know what origin the stream applies to, CLOSED
        because the server cannot assume we still have state around,
        RESERVED_LOCAL because by definition if we're in the RESERVED_LOCAL
        state then *we* are the server).
        """
        # Servers can't receive ALTSVC frames, but RFC 7838 tells us to ignore
        # them.
        if self.client is False:
            return []

        # If we've received the response headers from the server they can't
        # guarantee we still have any state around. Other implementations
        # (like nghttp2) ignore ALTSVC in this state, so we will too.
        if self.headers_received:
            return []

        # Otherwise, this is a sensible enough frame to have received. Return
        # the event and let it get populated.
        return [AlternativeServiceAvailable()]

    def send_alt_svc(self, previous_state):
        """
        Called when sending an ALTSVC frame on this stream.

        For consistency with the restrictions we apply on receiving ALTSVC
        frames in ``recv_alt_svc``, we want to restrict when users can send
        ALTSVC frames to the situations when we ourselves would accept them.

        That means: when we are a server, when we have received the request
        headers, and when we have not yet sent our own response headers.
        """
        # We should not send ALTSVC after we've sent response headers, as the
        # client may have disposed of its state.
        if self.headers_sent:
            raise ProtocolError(
                "Cannot send ALTSVC after sending response headers."
            )

        return


# STATE MACHINE
#
# The stream state machine is defined here to avoid the need to allocate it
# repeatedly for each stream. It cannot be defined in the stream class because
# it needs to be able to reference the callbacks defined on the class, but
# because Python's scoping rules are weird the class object is not actually in
# scope during the body of the class object.
#
# For the sake of clarity, we reproduce the RFC 7540 state machine here:
#
#                          +--------+
#                  send PP |        | recv PP
#                 ,--------|  idle  |--------.
#                /         |        |         \
#               v          +--------+          v
#        +----------+          |           +----------+
#        |          |          | send H /  |          |
# ,------| reserved |          | recv H    | reserved |------.
# |      | (local)  |          |           | (remote) |      |
# |      +----------+          v           +----------+      |
# |          |             +--------+             |          |
# |          |     recv ES |        | send ES     |          |
# |   send H |     ,-------|  open  |-------.     | recv H   |
# |          |    /        |        |        \    |          |
# |          v   v         +--------+         v   v          |
# |      +----------+          |           +----------+      |
# |      |   half   |          |           |   half   |      |
# |      |  closed  |          | send R /  |  closed  |      |
# |      | (remote) |          | recv R    | (local)  |      |
# |      +----------+          |           +----------+      |
# |           |                |                 |           |
# |           | send ES /      |       recv ES / |           |
# |           | send R /       v        send R / |           |
# |           | recv R     +--------+   recv R   |           |
# | send R /  `----------->|        |<-----------'  send R / |
# | recv R                 | closed |               recv R   |
# `----------------------->|        |<----------------------'
#                          +--------+
#
#    send:   endpoint sends this frame
#    recv:   endpoint receives this frame
#
#    H:  HEADERS frame (with implied CONTINUATIONs)
#    PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
#    ES: END_STREAM flag
#    R:  RST_STREAM frame
#
# For the purposes of this state machine we treat HEADERS and their
# associated CONTINUATION frames as a single jumbo frame. The protocol
# allows/requires this by preventing other frames from being interleved in
# between HEADERS/CONTINUATION frames. However, if a CONTINUATION frame is
# received without a prior HEADERS frame, it *will* be passed to this state
# machine. The state machine should always reject that frame, either as an
# invalid transition or because the stream is closed.
#
# There is a confusing relationship around PUSH_PROMISE frames. The state
# machine above considers them to be frames belonging to the new stream,
# which is *somewhat* true. However, they are sent with the stream ID of
# their related stream, and are only sendable in some cases.
# For this reason, our state machine implementation below allows for
# PUSH_PROMISE frames both in the IDLE state (as in the diagram), but also
# in the OPEN, HALF_CLOSED_LOCAL, and HALF_CLOSED_REMOTE states.
# Essentially, for hyper-h2, PUSH_PROMISE frames are effectively sent on
# two streams.
#
# The _transitions dictionary contains a mapping of tuples of
# (state, input) to tuples of (side_effect_function, end_state). This
# map contains all allowed transitions: anything not in this map is
# invalid and immediately causes a transition to ``closed``.
_transitions = {
    # State: idle
    (StreamState.IDLE, StreamInputs.SEND_HEADERS):
        (H2StreamStateMachine.request_sent, StreamState.OPEN),
    (StreamState.IDLE, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.request_received, StreamState.OPEN),
    (StreamState.IDLE, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.IDLE, StreamInputs.SEND_PUSH_PROMISE):
        (H2StreamStateMachine.send_new_pushed_stream,
            StreamState.RESERVED_LOCAL),
    (StreamState.IDLE, StreamInputs.RECV_PUSH_PROMISE):
        (H2StreamStateMachine.recv_new_pushed_stream,
            StreamState.RESERVED_REMOTE),
    (StreamState.IDLE, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (None, StreamState.IDLE),
    (StreamState.IDLE, StreamInputs.UPGRADE_CLIENT):
        (H2StreamStateMachine.request_sent, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.IDLE, StreamInputs.UPGRADE_SERVER):
        (H2StreamStateMachine.request_received,
            StreamState.HALF_CLOSED_REMOTE),

    # State: reserved local
    (StreamState.RESERVED_LOCAL, StreamInputs.SEND_HEADERS):
        (H2StreamStateMachine.response_sent, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.RESERVED_LOCAL, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.RESERVED_LOCAL, StreamInputs.SEND_WINDOW_UPDATE):
        (None, StreamState.RESERVED_LOCAL),
    (StreamState.RESERVED_LOCAL, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_updated, StreamState.RESERVED_LOCAL),
    (StreamState.RESERVED_LOCAL, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_reset_stream, StreamState.CLOSED),
    (StreamState.RESERVED_LOCAL, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.stream_reset, StreamState.CLOSED),
    (StreamState.RESERVED_LOCAL, StreamInputs.SEND_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.send_alt_svc, StreamState.RESERVED_LOCAL),
    (StreamState.RESERVED_LOCAL, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (None, StreamState.RESERVED_LOCAL),

    # State: reserved remote
    (StreamState.RESERVED_REMOTE, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.response_received,
            StreamState.HALF_CLOSED_LOCAL),
    (StreamState.RESERVED_REMOTE, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.RESERVED_REMOTE, StreamInputs.SEND_WINDOW_UPDATE):
        (None, StreamState.RESERVED_REMOTE),
    (StreamState.RESERVED_REMOTE, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_updated, StreamState.RESERVED_REMOTE),
    (StreamState.RESERVED_REMOTE, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_reset_stream, StreamState.CLOSED),
    (StreamState.RESERVED_REMOTE, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.stream_reset, StreamState.CLOSED),
    (StreamState.RESERVED_REMOTE, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.recv_alt_svc, StreamState.RESERVED_REMOTE),

    # State: open
    (StreamState.OPEN, StreamInputs.SEND_HEADERS):
        (H2StreamStateMachine.response_sent, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.response_received, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.SEND_DATA):
        (None, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.data_received, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.SEND_END_STREAM):
        (None, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.OPEN, StreamInputs.RECV_END_STREAM):
        (H2StreamStateMachine.stream_half_closed,
         StreamState.HALF_CLOSED_REMOTE),
    (StreamState.OPEN, StreamInputs.SEND_WINDOW_UPDATE):
        (None, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_updated, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_reset_stream, StreamState.CLOSED),
    (StreamState.OPEN, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.stream_reset, StreamState.CLOSED),
    (StreamState.OPEN, StreamInputs.SEND_PUSH_PROMISE):
        (H2StreamStateMachine.send_push_promise, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_PUSH_PROMISE):
        (H2StreamStateMachine.recv_push_promise, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.SEND_INFORMATIONAL_HEADERS):
        (H2StreamStateMachine.send_informational_response, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_INFORMATIONAL_HEADERS):
        (H2StreamStateMachine.recv_informational_response, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.SEND_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.send_alt_svc, StreamState.OPEN),
    (StreamState.OPEN, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.recv_alt_svc, StreamState.OPEN),

    # State: half-closed remote
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_HEADERS):
        (H2StreamStateMachine.response_sent, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_DATA):
        (None, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_END_STREAM):
        (H2StreamStateMachine.send_end_stream, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_WINDOW_UPDATE):
        (None, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_updated, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_reset_stream, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.stream_reset, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_PUSH_PROMISE):
        (H2StreamStateMachine.send_push_promise,
            StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_PUSH_PROMISE):
        (H2StreamStateMachine.reset_stream_on_error, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_INFORMATIONAL_HEADERS):
        (H2StreamStateMachine.send_informational_response,
            StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.SEND_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.send_alt_svc, StreamState.HALF_CLOSED_REMOTE),
    (StreamState.HALF_CLOSED_REMOTE, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.recv_alt_svc, StreamState.HALF_CLOSED_REMOTE),

    # State: half-closed local
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.response_received,
            StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.data_received, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_END_STREAM):
        (H2StreamStateMachine.stream_ended, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_WINDOW_UPDATE):
        (None, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_updated, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_reset_stream, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.stream_reset, StreamState.CLOSED),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_PUSH_PROMISE):
        (H2StreamStateMachine.recv_push_promise,
            StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_INFORMATIONAL_HEADERS):
        (H2StreamStateMachine.recv_informational_response,
            StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.SEND_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.send_alt_svc, StreamState.HALF_CLOSED_LOCAL),
    (StreamState.HALF_CLOSED_LOCAL, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (H2StreamStateMachine.recv_alt_svc, StreamState.HALF_CLOSED_LOCAL),

    # State: closed
    (StreamState.CLOSED, StreamInputs.RECV_END_STREAM):
        (None, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.RECV_ALTERNATIVE_SERVICE):
        (None, StreamState.CLOSED),

    # RFC 7540 Section 5.1 defines how the end point should react when
    # receiving a frame on a closed stream with the following statements:
    #
    # > An endpoint that receives any frame other than PRIORITY after receiving
    # > a RST_STREAM MUST treat that as a stream error of type STREAM_CLOSED.
    # > An endpoint that receives any frames after receiving a frame with the
    # > END_STREAM flag set MUST treat that as a connection error of type
    # > STREAM_CLOSED.
    (StreamState.CLOSED, StreamInputs.RECV_HEADERS):
        (H2StreamStateMachine.recv_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.RECV_DATA):
        (H2StreamStateMachine.recv_on_closed_stream, StreamState.CLOSED),

    # > WINDOW_UPDATE or RST_STREAM frames can be received in this state
    # > for a short period after a DATA or HEADERS frame containing a
    # > END_STREAM flag is sent.
    (StreamState.CLOSED, StreamInputs.RECV_WINDOW_UPDATE):
        (H2StreamStateMachine.window_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.RECV_RST_STREAM):
        (H2StreamStateMachine.reset_on_closed_stream, StreamState.CLOSED),

    # > A receiver MUST treat the receipt of a PUSH_PROMISE on a stream that is
    # > neither "open" nor "half-closed (local)" as a connection error of type
    # > PROTOCOL_ERROR.
    (StreamState.CLOSED, StreamInputs.RECV_PUSH_PROMISE):
        (H2StreamStateMachine.recv_push_on_closed_stream, StreamState.CLOSED),

    # Also, users should be forbidden from sending on closed streams.
    (StreamState.CLOSED, StreamInputs.SEND_HEADERS):
        (H2StreamStateMachine.send_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.SEND_PUSH_PROMISE):
        (H2StreamStateMachine.send_push_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.SEND_RST_STREAM):
        (H2StreamStateMachine.send_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.SEND_DATA):
        (H2StreamStateMachine.send_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.SEND_WINDOW_UPDATE):
        (H2StreamStateMachine.send_on_closed_stream, StreamState.CLOSED),
    (StreamState.CLOSED, StreamInputs.SEND_END_STREAM):
        (H2StreamStateMachine.send_on_closed_stream, StreamState.CLOSED),
}


class H2Stream(object):
    """
    A low-level HTTP/2 stream object. This handles building and receiving
    frames and maintains per-stream state.

    This wraps a HTTP/2 Stream state machine implementation, ensuring that
    frames can only be sent/received when the stream is in a valid state.
    Attempts to create frames that cannot be sent will raise a
    ``ProtocolError``.
    """
    def __init__(self,
                 stream_id,
                 config,
                 inbound_window_size,
                 outbound_window_size):
        self.state_machine = H2StreamStateMachine(stream_id)
        self.stream_id = stream_id
        self.max_outbound_frame_size = None
        self.request_method = None

        # The current value of the outbound stream flow control window
        self.outbound_flow_control_window = outbound_window_size

        # The flow control manager.
        self._inbound_window_manager = WindowManager(inbound_window_size)

        # The expected content length, if any.
        self._expected_content_length = None

        # The actual received content length. Always tracked.
        self._actual_content_length = 0

        # The authority we believe this stream belongs to.
        self._authority = None

        # The configuration for this stream.
        self.config = config

    def __repr__(self):
        return "<%s id:%d state:%r>" % (
            type(self).__name__,
            self.stream_id,
            self.state_machine.state
        )

    @property
    def inbound_flow_control_window(self):
        """
        The size of the inbound flow control window for the stream. This is
        rarely publicly useful: instead, use :meth:`remote_flow_control_window
        <h2.stream.H2Stream.remote_flow_control_window>`. This shortcut is
        largely present to provide a shortcut to this data.
        """
        return self._inbound_window_manager.current_window_size

    @property
    def open(self):
        """
        Whether the stream is 'open' in any sense: that is, whether it counts
        against the number of concurrent streams.
        """
        # RFC 7540 Section 5.1.2 defines 'open' for this purpose to mean either
        # the OPEN state or either of the HALF_CLOSED states. Perplexingly,
        # this excludes the reserved states.
        # For more detail on why we're doing this in this slightly weird way,
        # see the comment on ``STREAM_OPEN`` at the top of the file.
        return STREAM_OPEN[self.state_machine.state]

    @property
    def closed(self):
        """
        Whether the stream is closed.
        """
        return self.state_machine.state == StreamState.CLOSED

    @property
    def closed_by(self):
        """
        Returns how the stream was closed, as one of StreamClosedBy.
        """
        return self.state_machine.stream_closed_by

    def upgrade(self, client_side):
        """
        Called by the connection to indicate that this stream is the initial
        request/response of an upgraded connection. Places the stream into an
        appropriate state.
        """
        self.config.logger.debug("Upgrading %r", self)

        assert self.stream_id == 1
        input_ = (
            StreamInputs.UPGRADE_CLIENT if client_side
            else StreamInputs.UPGRADE_SERVER
        )

        # This may return events, we deliberately don't want them.
        self.state_machine.process_input(input_)
        return

    def send_headers(self, headers, encoder, end_stream=False):
        """
        Returns a list of HEADERS/CONTINUATION frames to emit as either headers
        or trailers.
        """
        self.config.logger.debug("Send headers %s on %r", headers, self)

        # Because encoding headers makes an irreversible change to the header
        # compression context, we make the state transition before we encode
        # them.

        # First, check if we're a client. If we are, no problem: if we aren't,
        # we need to scan the header block to see if this is an informational
        # response.
        input_ = StreamInputs.SEND_HEADERS
        if ((not self.state_machine.client) and
                is_informational_response(headers)):
            if end_stream:
                raise ProtocolError(
                    "Cannot set END_STREAM on informational responses."
                )

            input_ = StreamInputs.SEND_INFORMATIONAL_HEADERS

        events = self.state_machine.process_input(input_)

        hf = HeadersFrame(self.stream_id)
        hdr_validation_flags = self._build_hdr_validation_flags(events)
        frames = self._build_headers_frames(
            headers, encoder, hf, hdr_validation_flags
        )

        if end_stream:
            # Not a bug: the END_STREAM flag is valid on the initial HEADERS
            # frame, not the CONTINUATION frames that follow.
            self.state_machine.process_input(StreamInputs.SEND_END_STREAM)
            frames[0].flags.add('END_STREAM')

        if self.state_machine.trailers_sent and not end_stream:
            raise ProtocolError("Trailers must have END_STREAM set.")

        if self.state_machine.client and self._authority is None:
            self._authority = authority_from_headers(headers)

        # store request method for _initialize_content_length
        self.request_method = extract_method_header(headers)

        return frames

    def push_stream_in_band(self, related_stream_id, headers, encoder):
        """
        Returns a list of PUSH_PROMISE/CONTINUATION frames to emit as a pushed
        stream header. Called on the stream that has the PUSH_PROMISE frame
        sent on it.
        """
        self.config.logger.debug("Push stream %r", self)

        # Because encoding headers makes an irreversible change to the header
        # compression context, we make the state transition *first*.

        events = self.state_machine.process_input(
            StreamInputs.SEND_PUSH_PROMISE
        )

        ppf = PushPromiseFrame(self.stream_id)
        ppf.promised_stream_id = related_stream_id
        hdr_validation_flags = self._build_hdr_validation_flags(events)
        frames = self._build_headers_frames(
            headers, encoder, ppf, hdr_validation_flags
        )

        return frames

    def locally_pushed(self):
        """
        Mark this stream as one that was pushed by this peer. Must be called
        immediately after initialization. Sends no frames, simply updates the
        state machine.
        """
        # This does not trigger any events.
        events = self.state_machine.process_input(
            StreamInputs.SEND_PUSH_PROMISE
        )
        assert not events
        return []

    def send_data(self, data, end_stream=False, pad_length=None):
        """
        Prepare some data frames. Optionally end the stream.

        .. warning:: Does not perform flow control checks.
        """
        self.config.logger.debug(
            "Send data on %r with end stream set to %s", self, end_stream
        )

        self.state_machine.process_input(StreamInputs.SEND_DATA)

        df = DataFrame(self.stream_id)
        df.data = data
        if end_stream:
            self.state_machine.process_input(StreamInputs.SEND_END_STREAM)
            df.flags.add('END_STREAM')
        if pad_length is not None:
            df.flags.add('PADDED')
            df.pad_length = pad_length

        # Subtract flow_controlled_length to account for possible padding
        self.outbound_flow_control_window -= df.flow_controlled_length
        assert self.outbound_flow_control_window >= 0

        return [df]

    def end_stream(self):
        """
        End a stream without sending data.
        """
        self.config.logger.debug("End stream %r", self)

        self.state_machine.process_input(StreamInputs.SEND_END_STREAM)
        df = DataFrame(self.stream_id)
        df.flags.add('END_STREAM')
        return [df]

    def advertise_alternative_service(self, field_value):
        """
        Advertise an RFC 7838 alternative service. The semantics of this are
        better documented in the ``H2Connection`` class.
        """
        self.config.logger.debug(
            "Advertise alternative service of %r for %r", field_value, self
        )
        self.state_machine.process_input(StreamInputs.SEND_ALTERNATIVE_SERVICE)
        asf = AltSvcFrame(self.stream_id)
        asf.field = field_value
        return [asf]

    def increase_flow_control_window(self, increment):
        """
        Increase the size of the flow control window for the remote side.
        """
        self.config.logger.debug(
            "Increase flow control window for %r by %d",
            self, increment
        )
        self.state_machine.process_input(StreamInputs.SEND_WINDOW_UPDATE)
        self._inbound_window_manager.window_opened(increment)

        wuf = WindowUpdateFrame(self.stream_id)
        wuf.window_increment = increment
        return [wuf]

    def receive_push_promise_in_band(self,
                                     promised_stream_id,
                                     headers,
                                     header_encoding):
        """
        Receives a push promise frame sent on this stream, pushing a remote
        stream. This is called on the stream that has the PUSH_PROMISE sent
        on it.
        """
        self.config.logger.debug(
            "Receive Push Promise on %r for remote stream %d",
            self, promised_stream_id
        )
        events = self.state_machine.process_input(
            StreamInputs.RECV_PUSH_PROMISE
        )
        events[0].pushed_stream_id = promised_stream_id

        hdr_validation_flags = self._build_hdr_validation_flags(events)
        events[0].headers = self._process_received_headers(
            headers, hdr_validation_flags, header_encoding
        )
        return [], events

    def remotely_pushed(self, pushed_headers):
        """
        Mark this stream as one that was pushed by the remote peer. Must be
        called immediately after initialization. Sends no frames, simply
        updates the state machine.
        """
        self.config.logger.debug("%r pushed by remote peer", self)
        events = self.state_machine.process_input(
            StreamInputs.RECV_PUSH_PROMISE
        )
        self._authority = authority_from_headers(pushed_headers)
        return [], events

    def receive_headers(self, headers, end_stream, header_encoding):
        """
        Receive a set of headers (or trailers).
        """
        if is_informational_response(headers):
            if end_stream:
                raise ProtocolError(
                    "Cannot set END_STREAM on informational responses"
                )
            input_ = StreamInputs.RECV_INFORMATIONAL_HEADERS
        else:
            input_ = StreamInputs.RECV_HEADERS

        events = self.state_machine.process_input(input_)

        if end_stream:
            es_events = self.state_machine.process_input(
                StreamInputs.RECV_END_STREAM
            )
            events[0].stream_ended = es_events[0]
            events += es_events

        self._initialize_content_length(headers)

        if isinstance(events[0], TrailersReceived):
            if not end_stream:
                raise ProtocolError("Trailers must have END_STREAM set")

        hdr_validation_flags = self._build_hdr_validation_flags(events)
        events[0].headers = self._process_received_headers(
            headers, hdr_validation_flags, header_encoding
        )
        return [], events

    def receive_data(self, data, end_stream, flow_control_len):
        """
        Receive some data.
        """
        self.config.logger.debug(
            "Receive data on %r with end stream %s and flow control length "
            "set to %d", self, end_stream, flow_control_len
        )
        events = self.state_machine.process_input(StreamInputs.RECV_DATA)
        self._inbound_window_manager.window_consumed(flow_control_len)
        self._track_content_length(len(data), end_stream)

        if end_stream:
            es_events = self.state_machine.process_input(
                StreamInputs.RECV_END_STREAM
            )
            events[0].stream_ended = es_events[0]
            events.extend(es_events)

        events[0].data = data
        events[0].flow_controlled_length = flow_control_len
        return [], events

    def receive_window_update(self, increment):
        """
        Handle a WINDOW_UPDATE increment.
        """
        self.config.logger.debug(
            "Receive Window Update on %r for increment of %d",
            self, increment
        )
        events = self.state_machine.process_input(
            StreamInputs.RECV_WINDOW_UPDATE
        )
        frames = []

        # If we encounter a problem with incrementing the flow control window,
        # this should be treated as a *stream* error, not a *connection* error.
        # That means we need to catch the error and forcibly close the stream.
        if events:
            events[0].delta = increment
            try:
                self.outbound_flow_control_window = guard_increment_window(
                    self.outbound_flow_control_window,
                    increment
                )
            except FlowControlError:
                # Ok, this is bad. We're going to need to perform a local
                # reset.
                event = StreamReset()
                event.stream_id = self.stream_id
                event.error_code = ErrorCodes.FLOW_CONTROL_ERROR
                event.remote_reset = False

                events = [event]
                frames = self.reset_stream(event.error_code)

        return frames, events

    def receive_continuation(self):
        """
        A naked CONTINUATION frame has been received. This is always an error,
        but the type of error it is depends on the state of the stream and must
        transition the state of the stream, so we need to handle it.
        """
        self.config.logger.debug("Receive Continuation frame on %r", self)
        self.state_machine.process_input(
            StreamInputs.RECV_CONTINUATION
        )
        assert False, "Should not be reachable"

    def receive_alt_svc(self, frame):
        """
        An Alternative Service frame was received on the stream. This frame
        inherits the origin associated with this stream.
        """
        self.config.logger.debug(
            "Receive Alternative Service frame on stream %r", self
        )

        # If the origin is present, RFC 7838 says we have to ignore it.
        if frame.origin:
            return [], []

        events = self.state_machine.process_input(
            StreamInputs.RECV_ALTERNATIVE_SERVICE
        )

        # There are lots of situations where we want to ignore the ALTSVC
        # frame. If we need to pay attention, we'll have an event and should
        # fill it out.
        if events:
            assert isinstance(events[0], AlternativeServiceAvailable)
            events[0].origin = self._authority
            events[0].field_value = frame.field

        return [], events

    def reset_stream(self, error_code=0):
        """
        Close the stream locally. Reset the stream with an error code.
        """
        self.config.logger.debug(
            "Local reset %r with error code: %d", self, error_code
        )
        self.state_machine.process_input(StreamInputs.SEND_RST_STREAM)

        rsf = RstStreamFrame(self.stream_id)
        rsf.error_code = error_code
        return [rsf]

    def stream_reset(self, frame):
        """
        Handle a stream being reset remotely.
        """
        self.config.logger.debug(
            "Remote reset %r with error code: %d", self, frame.error_code
        )
        events = self.state_machine.process_input(StreamInputs.RECV_RST_STREAM)

        if events:
            # We don't fire an event if this stream is already closed.
            events[0].error_code = _error_code_from_int(frame.error_code)

        return [], events

    def acknowledge_received_data(self, acknowledged_size):
        """
        The user has informed us that they've processed some amount of data
        that was received on this stream. Pass that to the window manager and
        potentially return some WindowUpdate frames.
        """
        self.config.logger.debug(
            "Acknowledge received data with size %d on %r",
            acknowledged_size, self
        )
        increment = self._inbound_window_manager.process_bytes(
            acknowledged_size
        )
        if increment:
            f = WindowUpdateFrame(self.stream_id)
            f.window_increment = increment
            return [f]

        return []

    def _build_hdr_validation_flags(self, events):
        """
        Constructs a set of header validation flags for use when normalizing
        and validating header blocks.
        """
        is_trailer = isinstance(
            events[0], (_TrailersSent, TrailersReceived)
        )
        is_response_header = isinstance(
            events[0],
            (
                _ResponseSent,
                ResponseReceived,
                InformationalResponseReceived
            )
        )
        is_push_promise = isinstance(
            events[0], (PushedStreamReceived, _PushedRequestSent)
        )

        return HeaderValidationFlags(
            is_client=self.state_machine.client,
            is_trailer=is_trailer,
            is_response_header=is_response_header,
            is_push_promise=is_push_promise,
        )

    def _build_headers_frames(self,
                              headers,
                              encoder,
                              first_frame,
                              hdr_validation_flags):
        """
        Helper method to build headers or push promise frames.
        """
        # We need to lowercase the header names, and to ensure that secure
        # header fields are kept out of compression contexts.
        if self.config.normalize_outbound_headers:
            headers = normalize_outbound_headers(
                headers, hdr_validation_flags
            )
        if self.config.validate_outbound_headers:
            headers = validate_outbound_headers(
                headers, hdr_validation_flags
            )

        encoded_headers = encoder.encode(headers)

        # Slice into blocks of max_outbound_frame_size. Be careful with this:
        # it only works right because we never send padded frames or priority
        # information on the frames. Revisit this if we do.
        header_blocks = [
            encoded_headers[i:i+self.max_outbound_frame_size]
            for i in range(
                0, len(encoded_headers), self.max_outbound_frame_size
            )
        ]

        frames = []
        first_frame.data = header_blocks[0]
        frames.append(first_frame)

        for block in header_blocks[1:]:
            cf = ContinuationFrame(self.stream_id)
            cf.data = block
            frames.append(cf)

        frames[-1].flags.add('END_HEADERS')
        return frames

    def _process_received_headers(self,
                                  headers,
                                  header_validation_flags,
                                  header_encoding):
        """
        When headers have been received from the remote peer, run a processing
        pipeline on them to transform them into the appropriate form for
        attaching to an event.
        """
        if self.config.normalize_inbound_headers:
            headers = normalize_inbound_headers(
                headers, header_validation_flags
            )

        if self.config.validate_inbound_headers:
            headers = validate_headers(headers, header_validation_flags)

        if header_encoding:
            headers = _decode_headers(headers, header_encoding)

        # The above steps are all generators, so we need to concretize the
        # headers now.
        return list(headers)

    def _initialize_content_length(self, headers):
        """
        Checks the headers for a content-length header and initializes the
        _expected_content_length field from it. It's not an error for no
        Content-Length header to be present.
        """
        if self.request_method == b'HEAD':
            self._expected_content_length = 0
            return

        for n, v in headers:
            if n == b'content-length':
                try:
                    self._expected_content_length = int(v, 10)
                except ValueError:
                    raise ProtocolError(
                        "Invalid content-length header: %s" % v
                    )

                return

    def _track_content_length(self, length, end_stream):
        """
        Update the expected content length in response to data being received.
        Validates that the appropriate amount of data is sent. Always updates
        the received data, but only validates the length against the
        content-length header if one was sent.

        :param length: The length of the body chunk received.
        :param end_stream: If this is the last body chunk received.
        """
        self._actual_content_length += length
        actual = self._actual_content_length
        expected = self._expected_content_length

        if expected is not None:
            if expected < actual:
                raise InvalidBodyLengthError(expected, actual)

            if end_stream and expected != actual:
                raise InvalidBodyLengthError(expected, actual)

    def _inbound_flow_control_change_from_settings(self, delta):
        """
        We changed SETTINGS_INITIAL_WINDOW_SIZE, which means we need to
        update the target window size for flow control. For our flow control
        strategy, this means we need to do two things: we need to adjust the
        current window size, but we also need to set the target maximum window
        size to the new value.
        """
        new_max_size = self._inbound_window_manager.max_window_size + delta
        self._inbound_window_manager.window_opened(delta)
        self._inbound_window_manager.max_window_size = new_max_size


def _decode_headers(headers, encoding):
    """
    Given an iterable of header two-tuples and an encoding, decodes those
    headers using that encoding while preserving the type of the header tuple.
    This ensures that the use of ``HeaderTuple`` is preserved.
    """
    for header in headers:
        # This function expects to work on decoded headers, which are always
        # HeaderTuple objects.
        assert isinstance(header, HeaderTuple)

        name, value = header
        name = name.decode(encoding)
        value = value.decode(encoding)
        yield header.__class__(name, value)
   0707010001f450000081a40000000000000000000000016a100daf00001503000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/h2/exceptions.py   # -*- coding: utf-8 -*-
"""
h2/exceptions
~~~~~~~~~~~~~

Exceptions for the HTTP/2 module.
"""
import h2.errors


class H2Error(Exception):
    """
    The base class for all exceptions for the HTTP/2 module.
    """


class ProtocolError(H2Error):
    """
    An action was attempted in violation of the HTTP/2 protocol.
    """
    #: The error code corresponds to this kind of Protocol Error.
    error_code = h2.errors.ErrorCodes.PROTOCOL_ERROR


class FrameTooLargeError(ProtocolError):
    """
    The frame that we tried to send or that we received was too large.
    """
    #: This error code that corresponds to this kind of Protocol Error.
    error_code = h2.errors.ErrorCodes.FRAME_SIZE_ERROR


class FrameDataMissingError(ProtocolError):
    """
    The frame that we received is missing some data.

    .. versionadded:: 2.0.0
    """
    #: The error code that corresponds to this kind of Protocol Error
    error_code = h2.errors.ErrorCodes.FRAME_SIZE_ERROR


class TooManyStreamsError(ProtocolError):
    """
    An attempt was made to open a stream that would lead to too many concurrent
    streams.
    """
    pass


class FlowControlError(ProtocolError):
    """
    An attempted action violates flow control constraints.
    """
    #: The error code that corresponds to this kind of
    #: :class:`ProtocolError <h2.exceptions.ProtocolError>`
    error_code = h2.errors.ErrorCodes.FLOW_CONTROL_ERROR


class StreamIDTooLowError(ProtocolError):
    """
    An attempt was made to open a stream that had an ID that is lower than the
    highest ID we have seen on this connection.
    """
    def __init__(self, stream_id, max_stream_id):
        #: The ID of the stream that we attempted to open.
        self.stream_id = stream_id

        #: The current highest-seen stream ID.
        self.max_stream_id = max_stream_id

    def __str__(self):
        return "StreamIDTooLowError: %d is lower than %d" % (
            self.stream_id, self.max_stream_id
        )


class NoAvailableStreamIDError(ProtocolError):
    """
    There are no available stream IDs left to the connection. All stream IDs
    have been exhausted.

    .. versionadded:: 2.0.0
    """
    pass


class NoSuchStreamError(ProtocolError):
    """
    A stream-specific action referenced a stream that does not exist.

    .. versionchanged:: 2.0.0
       Became a subclass of :class:`ProtocolError
       <h2.exceptions.ProtocolError>`
    """
    def __init__(self, stream_id):
        #: The stream ID that corresponds to the non-existent stream.
        self.stream_id = stream_id


class StreamClosedError(NoSuchStreamError):
    """
    A more specific form of
    :class:`NoSuchStreamError <h2.exceptions.NoSuchStreamError>`. Indicates
    that the stream has since been closed, and that all state relating to that
    stream has been removed.
    """
    def __init__(self, stream_id):
        #: The stream ID that corresponds to the nonexistent stream.
        self.stream_id = stream_id

        #: The relevant HTTP/2 error code.
        self.error_code = h2.errors.ErrorCodes.STREAM_CLOSED

        # Any events that internal code may need to fire. Not relevant to
        # external users that may receive a StreamClosedError.
        self._events = []


class InvalidSettingsValueError(ProtocolError, ValueError):
    """
    An attempt was made to set an invalid Settings value.

    .. versionadded:: 2.0.0
    """
    def __init__(self, msg, error_code):
        super(InvalidSettingsValueError, self).__init__(msg)
        self.error_code = error_code


class InvalidBodyLengthError(ProtocolError):
    """
    The remote peer sent more or less data that the Content-Length header
    indicated.

    .. versionadded:: 2.0.0
    """
    def __init__(self, expected, actual):
        self.expected_length = expected
        self.actual_length = actual

    def __str__(self):
        return "InvalidBodyLengthError: Expected %d bytes, received %d" % (
            self.expected_length, self.actual_length
        )


class UnsupportedFrameError(ProtocolError, KeyError):
    """
    The remote peer sent a frame that is unsupported in this context.

    .. versionadded:: 2.1.0
    """
    # TODO: Remove the KeyError in 3.0.0
    pass


class RFC1122Error(H2Error):
    """
    Emitted when users attempt to do something that is literally allowed by the
    relevant RFC, but is sufficiently ill-defined that it's unwise to allow
    users to actually do it.

    While there is some disagreement about whether or not we should be liberal
    in what accept, it is a truth universally acknowledged that we should be
    conservative in what emit.

    .. versionadded:: 2.4.0
    """
    # shazow says I'm going to regret naming the exception this way. If that
    # turns out to be true, TELL HIM NOTHING.
    pass


class DenialOfServiceError(ProtocolError):
    """
    Emitted when the remote peer exhibits a behaviour that is likely to be an
    attempt to perform a Denial of Service attack on the implementation. This
    is a form of ProtocolError that carries a different error code, and allows
    more easy detection of this kind of behaviour.

    .. versionadded:: 2.5.0
    """
    #: The error code that corresponds to this kind of
    #: :class:`ProtocolError <h2.exceptions.ProtocolError>`
    error_code = h2.errors.ErrorCodes.ENHANCE_YOUR_CALM
 0707010001f44c000081a40000000000000000000000016a100daf00001962000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/foreign/h2/config.py   # -*- coding: utf-8 -*-
"""
h2/config
~~~~~~~~~

Objects for controlling the configuration of the HTTP/2 stack.
"""


class _BooleanConfigOption(object):
    """
    Descriptor for handling a boolean config option.  This will block
    attempts to set boolean config options to non-bools.
    """
    def __init__(self, name):
        self.name = name
        self.attr_name = '_%s' % self.name

    def __get__(self, instance, owner):
        return getattr(instance, self.attr_name)

    def __set__(self, instance, value):
        if not isinstance(value, bool):
            raise ValueError("%s must be a bool" % self.name)
        setattr(instance, self.attr_name, value)


class DummyLogger(object):
    """
    An Logger object that does not actual logging, hence a DummyLogger.

    For the class the log operation is merely a no-op.  The intent is to avoid
    conditionals being sprinkled throughout the hyper-h2 code for calls to
    logging functions when no logger is passed into the corresponding object.
    """
    def __init__(self, *vargs):
        pass

    def debug(self, *vargs, **kwargs):
        """
        No-op logging. Only level needed for now.
        """
        pass


class H2Configuration(object):
    """
    An object that controls the way a single HTTP/2 connection behaves.

    This object allows the users to customize behaviour. In particular, it
    allows users to enable or disable optional features, or to otherwise handle
    various unusual behaviours.

    This object has very little behaviour of its own: it mostly just ensures
    that configuration is self-consistent.

    :param client_side: Whether this object is to be used on the client side of
        a connection, or on the server side. Affects the logic used by the
        state machine, the default settings values, the allowable stream IDs,
        and several other properties. Defaults to ``True``.
    :type client_side: ``bool``

    :param header_encoding: Controls whether the headers emitted by this object
        in events are transparently decoded to ``unicode`` strings, and what
        encoding is used to do that decoding. This defaults to ``None``,
        meaning that headers will be returned as bytes. To automatically
        decode headers (that is, to return them as unicode strings), this can
        be set to the string name of any encoding, e.g. ``'utf-8'``.

        .. versionchanged:: 3.0.0
           Changed default value from ``'utf-8'`` to ``None``

    :type header_encoding: ``str``, ``False``, or ``None``

    :param validate_outbound_headers: Controls whether the headers emitted
        by this object are validated against the rules in RFC 7540.
        Disabling this setting will cause outbound header validation to
        be skipped, and allow the object to emit headers that may be illegal
        according to RFC 7540. Defaults to ``True``.
    :type validate_outbound_headers: ``bool``

    :param normalize_outbound_headers: Controls whether the headers emitted
        by this object are normalized before sending.  Disabling this setting
        will cause outbound header normalization to be skipped, and allow
        the object to emit headers that may be illegal according to
        RFC 7540. Defaults to ``True``.
    :type normalize_outbound_headers: ``bool``

    :param validate_inbound_headers: Controls whether the headers received
        by this object are validated against the rules in RFC 7540.
        Disabling this setting will cause inbound header validation to
        be skipped, and allow the object to receive headers that may be illegal
        according to RFC 7540. Defaults to ``True``.
    :type validate_inbound_headers: ``bool``

    :param normalize_inbound_headers: Controls whether the headers received by
        this object are normalized according to the rules of RFC 7540.
        Disabling this setting may lead to hyper-h2 emitting header blocks that
        some RFCs forbid, e.g. with multiple cookie fields.

        .. versionadded:: 3.0.0

    :type normalize_inbound_headers: ``bool``

    :param logger: A logger that conforms to the requirements for this module,
        those being no I/O and no context switches, which is needed in order
        to run in asynchronous operation.

        .. versionadded:: 2.6.0

    :type logger: ``logging.Logger``
    """
    client_side = _BooleanConfigOption('client_side')
    validate_outbound_headers = _BooleanConfigOption(
        'validate_outbound_headers'
    )
    normalize_outbound_headers = _BooleanConfigOption(
        'normalize_outbound_headers'
    )
    validate_inbound_headers = _BooleanConfigOption(
        'validate_inbound_headers'
    )
    normalize_inbound_headers = _BooleanConfigOption(
        'normalize_inbound_headers'
    )

    def __init__(self,
                 client_side=True,
                 header_encoding=None,
                 validate_outbound_headers=True,
                 normalize_outbound_headers=True,
                 validate_inbound_headers=True,
                 normalize_inbound_headers=True,
                 logger=None):
        self.client_side = client_side
        self.header_encoding = header_encoding
        self.validate_outbound_headers = validate_outbound_headers
        self.normalize_outbound_headers = normalize_outbound_headers
        self.validate_inbound_headers = validate_inbound_headers
        self.normalize_inbound_headers = normalize_inbound_headers
        self.logger = logger or DummyLogger(__name__)

    @property
    def header_encoding(self):
        """
        Controls whether the headers emitted by this object in events are
        transparently decoded to ``unicode`` strings, and what encoding is used
        to do that decoding. This defaults to ``None``, meaning that headers
        will be returned as bytes. To automatically decode headers (that is, to
        return them as unicode strings), this can be set to the string name of
        any encoding, e.g. ``'utf-8'``.
        """
        return self._header_encoding

    @header_encoding.setter
    def header_encoding(self, value):
        """
        Enforces constraints on the value of header encoding.
        """
        if not isinstance(value, (bool, str, type(None))):
            raise ValueError("header_encoding must be bool, string, or None")
        if value is True:
            raise ValueError("header_encoding cannot be True")
        self._header_encoding = value
  0707010001f485000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng    0707010001f487000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext    0707010001f48b000081a40000000000000000000000016a100daf00000ba8000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/iterable.py    #
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import functools
from .. import This, DatumInContext, JSONPath


class SortedThis(This):
    """The JSONPath referring to the sorted version of the current object.

    Concrete syntax is '`sorted`' or [\\field,/field].
    """
    def __init__(self, expressions=None):
        self.expressions = expressions

    def _compare(self, left, right):
        left = DatumInContext.wrap(left)
        right = DatumInContext.wrap(right)

        for expr in self.expressions:
            field, reverse = expr
            l_datum = field.find(left)
            r_datum = field.find(right)
            if (not l_datum or not r_datum or
                    len(l_datum) > 1 or len(r_datum) > 1 or
                    l_datum[0].value == r_datum[0].value):
                # NOTE(sileht): should we do something if the expression
                # match multiple fields, for now ignore them
                continue
            elif l_datum[0].value < r_datum[0].value:
                return 1 if reverse else -1
            else:
                return -1 if reverse else 1
        return 0

    def find(self, datum):
        """Return sorted value of This if list or dict."""
        if isinstance(datum.value, dict) and self.expressions:
            return datum

        if isinstance(datum.value, dict) or isinstance(datum.value, list):
            key = (functools.cmp_to_key(self._compare)
                   if self.expressions else None)
            return [DatumInContext.wrap(
                [value for value in sorted(datum.value, key=key)])]
        return datum

    def __eq__(self, other):
        return isinstance(other, Len)

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.expressions)

    def __str__(self):
        return '[?%s]' % self.expressions


class Len(JSONPath):
    """The JSONPath referring to the len of the current object.

    Concrete syntax is '`len`'.
    """

    def find(self, datum):
        datum = DatumInContext.wrap(datum)
        try:
            value = len(datum.value)
        except TypeError:
            return []
        else:
            return [DatumInContext(value,
                                               context=None,
                                               path=Len())]

    def __eq__(self, other):
        return isinstance(other, Len)

    def __str__(self):
        return '`len`'

    def __repr__(self):
        return 'Len()'
0707010001f48a000081a40000000000000000000000016a100daf00000c68000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/filter.py  #
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import operator
from foreign.six import moves

from .. import JSONPath, DatumInContext, Index


OPERATOR_MAP = {
    '!=': operator.ne,
    '==': operator.eq,
    '=': operator.eq,
    '<=': operator.le,
    '<': operator.lt,
    '>=': operator.ge,
    '>': operator.gt,
}


class Filter(JSONPath):
    """The JSONQuery filter"""

    def __init__(self, expressions):
        self.expressions = expressions

    def find(self, datum):
        if not self.expressions:
            return datum

        datum = DatumInContext.wrap(datum)
        if not isinstance(datum.value, list):
            return []

        return [DatumInContext(datum.value[i], path=Index(i), context=datum)
                for i in moves.range(0, len(datum.value))
                if (len(self.expressions) ==
                    len(list(filter(lambda x: x.find(datum.value[i]),
                                    self.expressions))))]

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.expressions)

    def __str__(self):
        return '[?%s]' % self.expressions


class Expression(JSONPath):
    """The JSONQuery expression"""

    def __init__(self, target, op, value):
        self.target = target
        self.op = op
        self.value = value

    def find(self, datum):
        datum = self.target.find(DatumInContext.wrap(datum))

        if not datum:
            return []
        if self.op is None:
            return datum

        found = []
        for data in datum:
            value = data.value
            if isinstance(self.value, int):
                try:
                    value = int(value)
                except ValueError:
                    continue
            elif isinstance(self.value, bool):
                try:
                    value = bool(value)
                except ValueError:
                    continue

            if OPERATOR_MAP[self.op](value, self.value):
                found.append(data)

        return found

    def __eq__(self, other):
        return (isinstance(other, Filter) and
                self.target == other.target and
                self.op == other.op and
                self.value == other.value)

    def __repr__(self):
        if self.op is None:
            return '%s(%r)' % (self.__class__.__name__, self.target)
        else:
            return '%s(%r %s %r)' % (self.__class__.__name__,
                                     self.target, self.op, self.value)

    def __str__(self):
        if self.op is None:
            return '%s' % self.target
        else:
            return '%s %s %s' % (self.target, self.op, self.value)
0707010001f489000081a40000000000000000000000016a100daf0000094d000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/arithmetic.py  #
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import operator
from .. import JSONPath, DatumInContext


OPERATOR_MAP = {
    '+': operator.add,
    '-': operator.sub,
    '*': operator.mul,
    '/': operator.truediv,
}


class Operation(JSONPath):
    def __init__(self, left, op, right):
        self.left = left
        self.op = OPERATOR_MAP[op]
        self.right = right

    def find(self, datum):
        result = []
        if (isinstance(self.left, JSONPath)
                and isinstance(self.right, JSONPath)):
            left = self.left.find(datum)
            right = self.right.find(datum)
            if left and right and len(left) == len(right):
                for l, r in zip(left, right):
                    try:
                        result.append(self.op(l.value, r.value))
                    except TypeError:
                        return []
            else:
                return []
        elif isinstance(self.left, JSONPath):
            left = self.left.find(datum)
            for l in left:
                try:
                    result.append(self.op(l.value, self.right))
                except TypeError:
                    return []
        elif isinstance(self.right, JSONPath):
            right = self.right.find(datum)
            for r in right:
                try:
                    result.append(self.op(self.left, r.value))
                except TypeError:
                    return []
        else:
            try:
                result.append(self.op(self.left, self.right))
            except TypeError:
                return []
        return [DatumInContext.wrap(r) for r in result]

    def __repr__(self):
        return '%s(%r%s%r)' % (self.__class__.__name__, self.left, self.op,
                               self.right)

    def __str__(self):
        return '%s%s%s' % (self.left, self.op, self.right)
   0707010001f48c000081a40000000000000000000000016a100daf00001458000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/parser.py  #
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from .. import lexer
from .. import parser
from .. import Fields, This, Child

from . import arithmetic as _arithmetic
from . import filter as _filter
from . import iterable as _iterable
from . import string as _string


class ExtendedJsonPathLexer(lexer.JsonPathLexer):
    """Custom LALR-lexer for JsonPath"""
    literals = lexer.JsonPathLexer.literals + ['?', '@', '+', '*', '/', '-']
    tokens = (['BOOL'] +
              parser.JsonPathLexer.tokens +
              ['FILTER_OP', 'SORT_DIRECTION', 'FLOAT'])

    t_FILTER_OP = r'==?|<=|>=|!=|<|>'

    def t_BOOL(self, t):
        r'true|false'
        t.value = True if t.value == 'true' else False
        return t

    def t_SORT_DIRECTION(self, t):
        r',?\s*(/|\\)'
        t.value = t.value[-1]
        return t

    def t_ID(self, t):
        r'@?[a-zA-Z_][a-zA-Z0-9_@\-]*'
        # NOTE(sileht): This fixes the ID expression to be
        # able to use @ for `This` like any json query
        t.type = self.reserved_words.get(t.value, 'ID')
        return t

    def t_FLOAT(self, t):
        r'-?\d+\.\d+'
        t.value = float(t.value)
        return t


class ExtentedJsonPathParser(parser.JsonPathParser):
    """Custom LALR-parser for JsonPath"""

    tokens = ExtendedJsonPathLexer.tokens

    def __init__(self, debug=False, lexer_class=None):
        lexer_class = lexer_class or ExtendedJsonPathLexer
        super(ExtentedJsonPathParser, self).__init__(debug, lexer_class)

    def p_jsonpath_operator_jsonpath(self, p):
        """jsonpath : NUMBER operator NUMBER
                    | FLOAT operator FLOAT
                    | ID operator ID
                    | NUMBER operator jsonpath
                    | FLOAT operator jsonpath
                    | jsonpath operator NUMBER
                    | jsonpath operator FLOAT
                    | jsonpath operator jsonpath
        """

        # NOTE(sileht): If we have choice between a field or a string we
        # always choice string, because field can be full qualified
        # like $.foo == foo and where string can't.
        for i in [1, 3]:
            if (isinstance(p[i], Fields) and len(p[i].fields) == 1):  # noqa
                p[i] = p[i].fields[0]

        p[0] = _arithmetic.Operation(p[1], p[2], p[3])

    def p_operator(self, p):
        """operator : '+'
                    | '-'
                    | '*'
                    | '/'
        """
        p[0] = p[1]

    def p_jsonpath_named_operator(self, p):
        "jsonpath : NAMED_OPERATOR"
        if p[1] == 'len':
            p[0] = _iterable.Len()
        elif p[1] == 'sorted':
            p[0] = _iterable.SortedThis()
        elif p[1].startswith("split("):
            p[0] = _string.Split(p[1])
        elif p[1].startswith("sub("):
            p[0] = _string.Sub(p[1])
        else:
            super(ExtentedJsonPathParser, self).p_jsonpath_named_operator(p)

    def p_expression(self, p):
        """expression : jsonpath
                      | jsonpath FILTER_OP ID
                      | jsonpath FILTER_OP FLOAT
                      | jsonpath FILTER_OP NUMBER
                      | jsonpath FILTER_OP BOOL
        """
        if len(p) == 2:
            left, op, right = p[1], None, None
        else:
            __, left, op, right = p
        p[0] = _filter.Expression(left, op, right)

    def p_expressions_expression(self, p):
        "expressions : expression"
        p[0] = [p[1]]

    def p_expressions_and(self, p):
        "expressions : expressions '&' expressions"
        # TODO(sileht): implements '|'
        p[0] = p[1] + p[3]

    def p_expressions_parens(self, p):
        "expressions : '(' expressions ')'"
        p[0] = p[2]

    def p_filter(self, p):
        "filter : '?' expressions "
        p[0] = _filter.Filter(p[2])

    def p_jsonpath_filter(self, p):
        "jsonpath : jsonpath '[' filter ']'"
        p[0] = Child(p[1], p[3])

    def p_sort(self, p):
        "sort : SORT_DIRECTION jsonpath"
        p[0] = (p[2], p[1] != "/")

    def p_sorts_sort(self, p):
        "sorts : sort"
        p[0] = [p[1]]

    def p_sorts_comma(self, p):
        "sorts : sorts sorts"
        p[0] = p[1] + p[2]

    def p_jsonpath_sort(self, p):
        "jsonpath : jsonpath '[' sorts ']'"
        sort = _iterable.SortedThis(p[3])
        p[0] = Child(p[1], sort)

    def p_jsonpath_this(self, p):
        "jsonpath : '@'"
        p[0] = This()

    precedence = [
        ('left', '+', '-'),
        ('left', '*', '/'),
    ] + parser.JsonPathParser.precedence + [
        ('nonassoc', 'ID'),
    ]


def parse(path, debug=False):
    return ExtentedJsonPathParser(debug=debug).parse(path)
0707010001f48d000081a40000000000000000000000016a100daf00000a28000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/string.py  #
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import re
from .. import DatumInContext, This


SUB = re.compile(r"sub\(/(.*)/,\s+(.*)\)")
SPLIT = re.compile(r"split\((.),\s+(\d+),\s+(\d+|-1)\)")


class DefintionInvalid(Exception):
    pass


class Sub(This):
    """Regex subtituor

    Concrete syntax is '`sub(/regex/, repl)`'
    """

    def __init__(self, method=None):
        m = SUB.match(method)
        if m is None:
            raise DefintionInvalid("%s is not valid" % method)
        self.expr = m.group(1).strip()
        self.repl = m.group(2).strip()
        self.regex = re.compile(self.expr)
        self.method = method
        print("%r" % self)

    def find(self, datum):
        datum = DatumInContext.wrap(datum)
        value = self.regex.sub(self.repl, datum.value)
        if value == datum.value:
            return []
        else:
            return [DatumInContext.wrap(value)]

    def __eq__(self, other):
        return (isinstance(other, Sub) and self.method == other.method)

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.method)

    def __str__(self):
        return '`sub(/%s/, %s)`' % (self.expr, self.repl)


class Split(This):
    """String splitter

    Concrete syntax is '`split(char, segment, max_split)`'
    """

    def __init__(self, method=None):
        m = SPLIT.match(method)
        if m is None:
            raise DefintionInvalid("%s is not valid" % method)
        self.char = m.group(1)
        self.segment = int(m.group(2))
        self.max_split = int(m.group(3))
        self.method = method

    def find(self, datum):
        datum = DatumInContext.wrap(datum)
        try:
            value = datum.value.split(self.char, self.max_split)[self.segment]
        except Exception:
            return []
        return [DatumInContext.wrap(value)]

    def __eq__(self, other):
        return (isinstance(other, Sub) and self.method == other.method)

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.method)

    def __str__(self):
        return '`%s`' % self.method
0707010001f488000081a40000000000000000000000016a100daf0000025d000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/ext/__init__.py    # -*- coding: utf-8 -*-

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from .parser import parse  # noqa
   0707010001f490000081a40000000000000000000000016a100daf000015e5000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/parser.py  from __future__ import print_function, absolute_import, division, generators, nested_scopes
import sys
import os.path
import logging

import ply.yacc

from jsonpath_ng.jsonpath import *
from jsonpath_ng.lexer import JsonPathLexer

logger = logging.getLogger(__name__)

def parse(string):
    return JsonPathParser().parse(string)

class JsonPathParser(object):
    '''
    An LALR-parser for JsonPath
    '''

    tokens = JsonPathLexer.tokens

    def __init__(self, debug=False, lexer_class=None):
        if self.__doc__ == None:
            raise Exception('Docstrings have been removed! By design of PLY, jsonpath-rw requires docstrings. You must not use PYTHONOPTIMIZE=2 or python -OO.')

        self.debug = debug
        self.lexer_class = lexer_class or JsonPathLexer # Crufty but works around statefulness in PLY

    def parse(self, string, lexer = None):
        lexer = lexer or self.lexer_class()
        return self.parse_token_stream(lexer.tokenize(string))

    def parse_token_stream(self, token_iterator, start_symbol='jsonpath'):

        # Since PLY has some crufty aspects and dumps files, we try to keep them local
        # However, we need to derive the name of the output Python file :-/
        output_directory = os.path.dirname(__file__)
        try:
            module_name = os.path.splitext(os.path.split(__file__)[1])[0]
        except:
            module_name = __name__

        parsing_table_module = '_'.join([module_name, start_symbol, 'parsetab'])

        # And we regenerate the parse table every time; it doesn't actually take that long!
        new_parser = ply.yacc.yacc(module=self,
                                   debug=self.debug,
                                   tabmodule = parsing_table_module,
                                   outputdir = output_directory,
                                   write_tables=0,
                                   start = start_symbol,
                                   errorlog = logger)

        return new_parser.parse(lexer = IteratorToTokenStream(token_iterator))

    # ===================== PLY Parser specification =====================

    precedence = [
        ('left', ','),
        ('left', 'DOUBLEDOT'),
        ('left', '.'),
        ('left', '|'),
        ('left', '&'),
        ('left', 'WHERE'),
    ]

    def p_error(self, t):
        raise Exception('Parse error at %s:%s near token %s (%s)' % (t.lineno, t.col, t.value, t.type))

    def p_jsonpath_binop(self, p):
        """jsonpath : jsonpath '.' jsonpath
                    | jsonpath DOUBLEDOT jsonpath
                    | jsonpath WHERE jsonpath
                    | jsonpath '|' jsonpath
                    | jsonpath '&' jsonpath"""
        op = p[2]

        if op == '.':
            p[0] = Child(p[1], p[3])
        elif op == '..':
            p[0] = Descendants(p[1], p[3])
        elif op == 'where':
            p[0] = Where(p[1], p[3])
        elif op == '|':
            p[0] = Union(p[1], p[3])
        elif op == '&':
            p[0] = Intersect(p[1], p[3])

    def p_jsonpath_fields(self, p):
        "jsonpath : fields_or_any"
        p[0] = Fields(*p[1])

    def p_jsonpath_named_operator(self, p):
        "jsonpath : NAMED_OPERATOR"
        if p[1] == 'this':
            p[0] = This()
        elif p[1] == 'parent':
            p[0] = Parent()
        else:
            raise Exception('Unknown named operator `%s` at %s:%s' % (p[1], p.lineno(1), p.lexpos(1)))

    def p_jsonpath_root(self, p):
        "jsonpath : '$'"
        p[0] = Root()

    def p_jsonpath_idx(self, p):
        "jsonpath : '[' idx ']'"
        p[0] = p[2]

    def p_jsonpath_slice(self, p):
        "jsonpath : '[' slice ']'"
        p[0] = p[2]

    def p_jsonpath_fieldbrackets(self, p):
        "jsonpath : '[' fields ']'"
        p[0] = Fields(*p[2])

    def p_jsonpath_child_fieldbrackets(self, p):
        "jsonpath : jsonpath '[' fields ']'"
        p[0] = Child(p[1], Fields(*p[3]))

    def p_jsonpath_child_idxbrackets(self, p):
        "jsonpath : jsonpath '[' idx ']'"
        p[0] = Child(p[1], p[3])

    def p_jsonpath_child_slicebrackets(self, p):
        "jsonpath : jsonpath '[' slice ']'"
        p[0] = Child(p[1], p[3])

    def p_jsonpath_parens(self, p):
        "jsonpath : '(' jsonpath ')'"
        p[0] = p[2]

    # Because fields in brackets cannot be '*' - that is reserved for array indices
    def p_fields_or_any(self, p):
        """fields_or_any : fields
                         | '*'    """
        if p[1] == '*':
            p[0] = ['*']
        else:
            p[0] = p[1]

    def p_fields_id(self, p):
        "fields : ID"
        p[0] = [p[1]]

    def p_fields_comma(self, p):
        "fields : fields ',' fields"
        p[0] = p[1] + p[3]

    def p_idx(self, p):
        "idx : NUMBER"
        p[0] = Index(p[1])

    def p_slice_any(self, p):
        "slice : '*'"
        p[0] = Slice()

    def p_slice(self, p): # Currently does not support `step`
        "slice : maybe_int ':' maybe_int"
        p[0] = Slice(start=p[1], end=p[3])

    def p_maybe_int(self, p):
        """maybe_int : NUMBER
                     | empty"""
        p[0] = p[1]

    def p_empty(self, p):
        'empty :'
        p[0] = None

class IteratorToTokenStream(object):
    def __init__(self, iterator):
        self.iterator = iterator

    def token(self):
        try:
            return next(self.iterator)
        except StopIteration:
            return None


if __name__ == '__main__':
    logging.basicConfig()
    parser = JsonPathParser(debug=True)
    print(parser.parse(sys.stdin.read()))
   0707010001f48e000081a40000000000000000000000016a100daf0000547d000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/jsonpath.py    from __future__ import unicode_literals, print_function, absolute_import, division, generators, nested_scopes
import logging
import foreign.six as six
from foreign.six.moves import xrange
from itertools import *  # noqa

# Get logger name
logger = logging.getLogger(__name__)

# Turn on/off the automatic creation of id attributes
# ... could be a kwarg pervasively but uses are rare and simple today
auto_id_field = None


class JSONPath(object):
    """
    The base class for JSONPath abstract syntax; those
    methods stubbed here are the interface to supported
    JSONPath semantics.
    """

    def find(self, data):
        """
        All `JSONPath` types support `find()`, which returns an iterable of `DatumInContext`s.
        They keep track of the path followed to the current location, so if the calling code
        has some opinion about that, it can be passed in here as a starting point.
        """
        raise NotImplementedError()

    def update(self, data, val):
        """
        Returns `data` with the specified path replaced by `val`. Only updates
        if the specified path exists.
        """

        raise NotImplementedError()

    def filter(self, fn, data):
        """
        Returns `data` with the specified path filtering nodes according
        the filter evaluation result returned by the filter function.

        Arguments:
            fn (function): unary function that accepts one argument
                and returns bool.
            data (dict|list|tuple): JSON object to filter.
        """

        raise NotImplementedError()

    def child(self, child):
        """
        Equivalent to Child(self, next) but with some canonicalization
        """
        if isinstance(self, This) or isinstance(self, Root):
            return child
        elif isinstance(child, This):
            return self
        elif isinstance(child, Root):
            return child
        else:
            return Child(self, child)

    def make_datum(self, value):
        if isinstance(value, DatumInContext):
            return value
        else:
            return DatumInContext(value, path=Root(), context=None)


class DatumInContext(object):
    """
    Represents a datum along a path from a context.

    Essentially a zipper but with a structure represented by JsonPath,
    and where the context is more of a parent pointer than a proper
    representation of the context.

    For quick-and-dirty work, this proxies any non-special attributes
    to the underlying datum, but the actual datum can (and usually should)
    be retrieved via the `value` attribute.

    To place `datum` within another, use `datum.in_context(context=..., path=...)`
    which extends the path. If the datum already has a context, it places the entire
    context within that passed in, so an object can be built from the inside
    out.
    """
    @classmethod
    def wrap(cls, data):
        if isinstance(data, cls):
            return data
        else:
            return cls(data)

    def __init__(self, value, path=None, context=None):
        self.value = value
        self.path = path or This()
        self.context = None if context is None else DatumInContext.wrap(context)

    def in_context(self, context, path):
        context = DatumInContext.wrap(context)

        if self.context:
            return DatumInContext(value=self.value, path=self.path, context=context.in_context(path=path, context=context))
        else:
            return DatumInContext(value=self.value, path=path, context=context)

    @property
    def full_path(self):
        return self.path if self.context is None else self.context.full_path.child(self.path)

    @property
    def id_pseudopath(self):
        """
        Looks like a path, but with ids stuck in when available
        """
        try:
            pseudopath = Fields(str(self.value[auto_id_field]))
        except (TypeError, AttributeError, KeyError): # This may not be all the interesting exceptions
            pseudopath = self.path

        if self.context:
            return self.context.id_pseudopath.child(pseudopath)
        else:
            return pseudopath

    def __repr__(self):
        return '%s(value=%r, path=%r, context=%r)' % (self.__class__.__name__, self.value, self.path, self.context)

    def __eq__(self, other):
        return isinstance(other, DatumInContext) and other.value == self.value and other.path == self.path and self.context == other.context


class AutoIdForDatum(DatumInContext):
    """
    This behaves like a DatumInContext, but the value is
    always the path leading up to it, not including the "id",
    and with any "id" fields along the way replacing the prior
    segment of the path

    For example, it will make "foo.bar.id" return a datum
    that behaves like DatumInContext(value="foo.bar", path="foo.bar.id").

    This is disabled by default; it can be turned on by
    settings the `auto_id_field` global to a value other
    than `None`.
    """

    def __init__(self, datum, id_field=None):
        """
        Invariant is that datum.path is the path from context to datum. The auto id
        will either be the id in the datum (if present) or the id of the context
        followed by the path to the datum.

        The path to this datum is always the path to the context, the path to the
        datum, and then the auto id field.
        """
        self.datum = datum
        self.id_field = id_field or auto_id_field

    @property
    def value(self):
        return str(self.datum.id_pseudopath)

    @property
    def path(self):
        return self.id_field

    @property
    def context(self):
        return self.datum

    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.datum)

    def in_context(self, context, path):
        return AutoIdForDatum(self.datum.in_context(context=context, path=path))

    def __eq__(self, other):
        return isinstance(other, AutoIdForDatum) and other.datum == self.datum and self.id_field == other.id_field


class Root(JSONPath):
    """
    The JSONPath referring to the "root" object. Concrete syntax is '$'.
    The root is the topmost datum without any context attached.
    """

    def find(self, data):
        if not isinstance(data, DatumInContext):
            return [DatumInContext(data, path=Root(), context=None)]
        else:
            if data.context is None:
                return [DatumInContext(data.value, context=None, path=Root())]
            else:
                return Root().find(data.context)

    def update(self, data, val):
        return val

    def filter(self, fn, data):
        return data if fn(data) else None

    def __str__(self):
        return '$'

    def __repr__(self):
        return 'Root()'

    def __eq__(self, other):
        return isinstance(other, Root)


class This(JSONPath):
    """
    The JSONPath referring to the current datum. Concrete syntax is '@'.
    """

    def find(self, datum):
        return [DatumInContext.wrap(datum)]

    def update(self, data, val):
        return val

    def filter(self, fn, data):
        return data if fn(data) else None

    def __str__(self):
        return '`this`'

    def __repr__(self):
        return 'This()'

    def __eq__(self, other):
        return isinstance(other, This)


class Child(JSONPath):
    """
    JSONPath that first matches the left, then the right.
    Concrete syntax is <left> '.' <right>
    """

    def __init__(self, left, right):
        self.left = left
        self.right = right

    def find(self, datum):
        """
        Extra special case: auto ids do not have children,
        so cut it off right now rather than auto id the auto id
        """

        return [submatch
                for subdata in self.left.find(datum)
                if not isinstance(subdata, AutoIdForDatum)
                for submatch in self.right.find(subdata)]

    def update(self, data, val):
        for datum in self.left.find(data):
            self.right.update(datum.value, val)
        return data

    def filter(self, fn, data):
        for datum in self.left.find(data):
            self.right.filter(fn, datum.value)
        return data

    def __eq__(self, other):
        return isinstance(other, Child) and self.left == other.left and self.right == other.right

    def __str__(self):
        return '%s.%s' % (self.left, self.right)

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.left, self.right)


class Parent(JSONPath):
    """
    JSONPath that matches the parent node of the current match.
    Will crash if no such parent exists.
    Available via named operator `parent`.
    """

    def find(self, datum):
        datum = DatumInContext.wrap(datum)
        return [datum.context]

    def __eq__(self, other):
        return isinstance(other, Parent)

    def __str__(self):
        return '`parent`'

    def __repr__(self):
        return 'Parent()'


class Where(JSONPath):
    """
    JSONPath that first matches the left, and then
    filters for only those nodes that have
    a match on the right.

    WARNING: Subject to change. May want to have "contains"
    or some other better word for it.
    """

    def __init__(self, left, right):
        self.left = left
        self.right = right

    def find(self, data):
        return [subdata for subdata in self.left.find(data) if self.right.find(subdata)]

    def update(self, data, val):
        for datum in self.find(data):
            datum.path.update(data, val)
        return data

    def filter(self, fn, data):
        for datum in self.find(data):
            datum.path.filter(fn, datum.value)
        return data

    def __str__(self):
        return '%s where %s' % (self.left, self.right)

    def __eq__(self, other):
        return isinstance(other, Where) and other.left == self.left and other.right == self.right

class Descendants(JSONPath):
    """
    JSONPath that matches first the left expression then any descendant
    of it which matches the right expression.
    """

    def __init__(self, left, right):
        self.left = left
        self.right = right

    def find(self, datum):
        # <left> .. <right> ==> <left> . (<right> | *..<right> | [*]..<right>)
        #
        # With with a wonky caveat that since Slice() has funky coercions
        # we cannot just delegate to that equivalence or we'll hit an
        # infinite loop. So right here we implement the coercion-free version.

        # Get all left matches into a list
        left_matches = self.left.find(datum)
        if not isinstance(left_matches, list):
            left_matches = [left_matches]

        def match_recursively(datum):
            right_matches = self.right.find(datum)

            # Manually do the * or [*] to avoid coercion and recurse just the right-hand pattern
            if isinstance(datum.value, list):
                recursive_matches = [submatch
                                     for i in range(0, len(datum.value))
                                     for submatch in match_recursively(DatumInContext(datum.value[i], context=datum, path=Index(i)))]

            elif isinstance(datum.value, dict):
                recursive_matches = [submatch
                                     for field in datum.value.keys()
                                     for submatch in match_recursively(DatumInContext(datum.value[field], context=datum, path=Fields(field)))]

            else:
                recursive_matches = []

            return right_matches + list(recursive_matches)

        # TODO: repeatable iterator instead of list?
        return [submatch
                for left_match in left_matches
                for submatch in match_recursively(left_match)]

    def is_singular():
        return False

    def update(self, data, val):
        # Get all left matches into a list
        left_matches = self.left.find(data)
        if not isinstance(left_matches, list):
            left_matches = [left_matches]

        def update_recursively(data):
            # Update only mutable values corresponding to JSON types
            if not (isinstance(data, list) or isinstance(data, dict)):
                return

            self.right.update(data, val)

            # Manually do the * or [*] to avoid coercion and recurse just the right-hand pattern
            if isinstance(data, list):
                for i in range(0, len(data)):
                    update_recursively(data[i])

            elif isinstance(data, dict):
                for field in data.keys():
                    update_recursively(data[field])

        for submatch in left_matches:
            update_recursively(submatch.value)

        return data

    def filter(self, fn, data):
        # Get all left matches into a list
        left_matches = self.left.find(data)
        if not isinstance(left_matches, list):
            left_matches = [left_matches]

        def filter_recursively(data):
            # Update only mutable values corresponding to JSON types
            if not (isinstance(data, list) or isinstance(data, dict)):
                return

            self.right.filter(fn, data)

            # Manually do the * or [*] to avoid coercion and recurse just the right-hand pattern
            if isinstance(data, list):
                for i in range(0, len(data)):
                    filter_recursively(data[i])

            elif isinstance(data, dict):
                for field in data.keys():
                    filter_recursively(data[field])

        for submatch in left_matches:
            filter_recursively(submatch.value)

        return data

    def __str__(self):
        return '%s..%s' % (self.left, self.right)

    def __eq__(self, other):
        return isinstance(other, Descendants) and self.left == other.left and self.right == other.right

class Union(JSONPath):
    """
    JSONPath that returns the union of the results of each match.
    This is pretty shoddily implemented for now. The nicest semantics
    in case of mismatched bits (list vs atomic) is to put
    them all in a list, but I haven't done that yet.

    WARNING: Any appearance of this being the _concatenation_ is
    coincidence. It may even be a bug! (or laziness)
    """
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def is_singular(self):
        return False

    def find(self, data):
        return self.left.find(data) + self.right.find(data)

class Intersect(JSONPath):
    """
    JSONPath for bits that match *both* patterns.

    This can be accomplished a couple of ways. The most
    efficient is to actually build the intersected
    AST as in building a state machine for matching the
    intersection of regular languages. The next
    idea is to build a filtered data and match against
    that.
    """
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def is_singular(self):
        return False

    def find(self, data):
        raise NotImplementedError()


class Fields(JSONPath):
    """
    JSONPath referring to some field of the current object.
    Concrete syntax ix comma-separated field names.

    WARNING: If '*' is any of the field names, then they will
    all be returned.
    """

    def __init__(self, *fields):
        self.fields = fields

    def get_field_datum(self, datum, field):
        if field == auto_id_field:
            return AutoIdForDatum(datum)
        else:
            try:
                field_value = datum.value[field] # Do NOT use `val.get(field)` since that confuses None as a value and None due to `get`
                return DatumInContext(value=field_value, path=Fields(field), context=datum)
            except (TypeError, KeyError, AttributeError):
                return None

    def reified_fields(self, datum):
        if '*' not in self.fields:
            return self.fields
        else:
            try:
                fields = tuple(datum.value.keys())
                return fields if auto_id_field is None else fields + (auto_id_field,)
            except AttributeError:
                return ()

    def find(self, datum):
        datum  = DatumInContext.wrap(datum)

        return  [field_datum
                 for field_datum in [self.get_field_datum(datum, field) for field in self.reified_fields(datum)]
                 if field_datum is not None]

    def update(self, data, val):
        for field in self.reified_fields(DatumInContext.wrap(data)):
            if field in data:
                if hasattr(val, '__call__'):
                    val(data[field], data, field)
                else:
                    data[field] = val
        return data

    def filter(self, fn, data):
        for field in self.reified_fields(DatumInContext.wrap(data)):
            if field in data:
                if fn(data[field]):
                    data.pop(field)
        return data

    def __str__(self):
        return ','.join(map(str, self.fields))

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, ','.join(map(repr, self.fields)))

    def __eq__(self, other):
        return isinstance(other, Fields) and tuple(self.fields) == tuple(other.fields)


class Index(JSONPath):
    """
    JSONPath that matches indices of the current datum, or none if not large enough.
    Concrete syntax is brackets.

    WARNING: If the datum is None or not long enough, it will not crash but will not match anything.
    NOTE: For the concrete syntax of `[*]`, the abstract syntax is a Slice() with no parameters (equiv to `[:]`
    """

    def __init__(self, index):
        self.index = index

    def find(self, datum):
        datum = DatumInContext.wrap(datum)

        if datum.value and len(datum.value) > self.index:
            return [DatumInContext(datum.value[self.index], path=self, context=datum)]
        else:
            return []

    def update(self, data, val):
        if hasattr(val, '__call__'):
            val.__call__(data[self.index], data, self.index)
        elif len(data) > self.index:
            data[self.index] = val
        return data

    def filter(self, fn, data):
        if fn(data[self.index]):
            data.pop(self.index)  # relies on mutation :(
        return data

    def __eq__(self, other):
        return isinstance(other, Index) and self.index == other.index

    def __str__(self):
        return '[%i]' % self.index


class Slice(JSONPath):
    """
    JSONPath matching a slice of an array.

    Because of a mismatch between JSON and XML when schema-unaware,
    this always returns an iterable; if the incoming data
    was not a list, then it returns a one element list _containing_ that
    data.

    Consider these two docs, and their schema-unaware translation to JSON:

    <a><b>hello</b></a> ==> {"a": {"b": "hello"}}
    <a><b>hello</b><b>goodbye</b></a> ==> {"a": {"b": ["hello", "goodbye"]}}

    If there were a schema, it would be known that "b" should always be an
    array (unless the schema were wonky, but that is too much to fix here)
    so when querying with JSON if the one writing the JSON knows that it
    should be an array, they can write a slice operator and it will coerce
    a non-array value to an array.

    This may be a bit unfortunate because it would be nice to always have
    an iterator, but dictionaries and other objects may also be iterable,
    so this is the compromise.
    """
    def __init__(self, start=None, end=None, step=None):
        self.start = start
        self.end = end
        self.step = step

    def find(self, datum):
        datum = DatumInContext.wrap(datum)

        # Here's the hack. If it is a dictionary or some kind of constant,
        # put it in a single-element list
        if (isinstance(datum.value, dict) or isinstance(datum.value, six.integer_types) or isinstance(datum.value, six.string_types)):
            return self.find(DatumInContext([datum.value], path=datum.path, context=datum.context))

        # Some iterators do not support slicing but we can still
        # at least work for '*'
        if self.start == None and self.end == None and self.step == None:
            return [DatumInContext(datum.value[i], path=Index(i), context=datum) for i in xrange(0, len(datum.value))]
        else:
            return [DatumInContext(datum.value[i], path=Index(i), context=datum) for i in range(0, len(datum.value))[self.start:self.end:self.step]]

    def update(self, data, val):
        for datum in self.find(data):
            datum.path.update(data, val)
        return data

    def filter(self, fn, data):
        while True:
            length = len(data)
            for datum in self.find(data):
                data = datum.path.filter(fn, data)
                if len(data) < length:
                    break

            if length == len(data):
                break
        return data

    def __str__(self):
        if self.start == None and self.end == None and self.step == None:
            return '[*]'
        else:
            return '[%s%s%s]' % (self.start or '',
                                   ':%d'%self.end if self.end else '',
                                   ':%d'%self.step if self.step else '')

    def __repr__(self):
        return '%s(start=%r,end=%r,step=%r)' % (self.__class__.__name__, self.start, self.end, self.step)

    def __eq__(self, other):
        return isinstance(other, Slice) and other.start == self.start and self.end == other.end and other.step == self.step
   0707010001f486000081a40000000000000000000000016a100daf00000074000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/__init__.py    from .jsonpath import *  # noqa
from .parser import parse  # noqa


# Current package version
__version__ = '1.4.3'
0707010001f48f000081a40000000000000000000000016a100daf000014c9000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/foreign/jsonpath_ng/lexer.py   from __future__ import unicode_literals, print_function, absolute_import, division, generators, nested_scopes
import sys
import logging

import ply.lex

logger = logging.getLogger(__name__)


class JsonPathLexerError(Exception):
    pass


class JsonPathLexer(object):
    '''
    A Lexical analyzer for JsonPath.
    '''

    def __init__(self, debug=False):
        self.debug = debug
        if self.__doc__ == None:
            raise JsonPathLexerError('Docstrings have been removed! By design of PLY, jsonpath-rw requires docstrings. You must not use PYTHONOPTIMIZE=2 or python -OO.')

    def tokenize(self, string):
        '''
        Maps a string to an iterator over tokens. In other words: [char] -> [token]
        '''

        new_lexer = ply.lex.lex(module=self, debug=self.debug, errorlog=logger)
        new_lexer.latest_newline = 0
        new_lexer.string_value = None
        new_lexer.input(string)

        while True:
            t = new_lexer.token()
            if t is None: break
            t.col = t.lexpos - new_lexer.latest_newline
            yield t

        if new_lexer.string_value is not None:
            raise JsonPathLexerError('Unexpected EOF in string literal or identifier')

    # ============== PLY Lexer specification ==================
    #
    # This probably should be private but:
    #   - the parser requires access to `tokens` (perhaps they should be defined in a third, shared dependency)
    #   - things like `literals` might be a legitimate part of the public interface.
    #
    # Anyhow, it is pythonic to give some rope to hang oneself with :-)

    literals = ['*', '.', '[', ']', '(', ')', '$', ',', ':', '|', '&']

    reserved_words = { 'where': 'WHERE' }

    tokens = ['DOUBLEDOT', 'NUMBER', 'ID', 'NAMED_OPERATOR'] + list(reserved_words.values())

    states = [ ('singlequote', 'exclusive'),
               ('doublequote', 'exclusive'),
               ('backquote', 'exclusive') ]

    # Normal lexing, rather easy
    t_DOUBLEDOT = r'\.\.'
    t_ignore = ' \t'

    def t_ID(self, t):
        r'[a-zA-Z_@][a-zA-Z0-9_@\-]*'
        t.type = self.reserved_words.get(t.value, 'ID')
        return t

    def t_NUMBER(self, t):
        r'-?\d+'
        t.value = int(t.value)
        return t


    # Single-quoted strings
    t_singlequote_ignore = ''
    def t_singlequote(self, t):
        r"'"
        t.lexer.string_start = t.lexer.lexpos
        t.lexer.string_value = ''
        t.lexer.push_state('singlequote')

    def t_singlequote_content(self, t):
        r"[^'\\]+"
        t.lexer.string_value += t.value

    def t_singlequote_escape(self, t):
        r'\\.'
        t.lexer.string_value += t.value[1]

    def t_singlequote_end(self, t):
        r"'"
        t.value = t.lexer.string_value
        t.type = 'ID'
        t.lexer.string_value = None
        t.lexer.pop_state()
        return t

    def t_singlequote_error(self, t):
        raise JsonPathLexerError('Error on line %s, col %s while lexing singlequoted field: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.lexer.latest_newline, t.value[0]))


    # Double-quoted strings
    t_doublequote_ignore = ''
    def t_doublequote(self, t):
        r'"'
        t.lexer.string_start = t.lexer.lexpos
        t.lexer.string_value = ''
        t.lexer.push_state('doublequote')

    def t_doublequote_content(self, t):
        r'[^"\\]+'
        t.lexer.string_value += t.value

    def t_doublequote_escape(self, t):
        r'\\.'
        t.lexer.string_value += t.value[1]

    def t_doublequote_end(self, t):
        r'"'
        t.value = t.lexer.string_value
        t.type = 'ID'
        t.lexer.string_value = None
        t.lexer.pop_state()
        return t

    def t_doublequote_error(self, t):
        raise JsonPathLexerError('Error on line %s, col %s while lexing doublequoted field: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.lexer.latest_newline, t.value[0]))


    # Back-quoted "magic" operators
    t_backquote_ignore = ''
    def t_backquote(self, t):
        r'`'
        t.lexer.string_start = t.lexer.lexpos
        t.lexer.string_value = ''
        t.lexer.push_state('backquote')

    def t_backquote_escape(self, t):
        r'\\.'
        t.lexer.string_value += t.value[1]

    def t_backquote_content(self, t):
        r"[^`\\]+"
        t.lexer.string_value += t.value

    def t_backquote_end(self, t):
        r'`'
        t.value = t.lexer.string_value
        t.type = 'NAMED_OPERATOR'
        t.lexer.string_value = None
        t.lexer.pop_state()
        return t

    def t_backquote_error(self, t):
        raise JsonPathLexerError('Error on line %s, col %s while lexing backquoted operator: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.lexer.latest_newline, t.value[0]))


    # Counting lines, handling errors
    def t_newline(self, t):
        r'\n'
        t.lexer.lineno += 1
        t.lexer.latest_newline = t.lexpos

    def t_error(self, t):
        raise JsonPathLexerError('Error on line %s, col %s: Unexpected character: %s ' % (t.lexer.lineno, t.lexpos - t.lexer.latest_newline, t.value[0]))

if __name__ == '__main__':
    logging.basicConfig()
    lexer = JsonPathLexer(debug=True)
    for token in lexer.tokenize(sys.stdin.read()):
        print('%-20s%s' % (token.value, token.type))
   0707010001f433000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/foreign/aenum  0707010001f43a000081a40000000000000000000000016a100daf00000196000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/aenum/_py3.py  from inspect import getfullargspec as _getfullargspec

__all__ = [
        'getargspec', 'raise_with_traceback', 'raise_from_none',
        ]

def getargspec(method):
    args, varargs, keywords, defaults, _, _, _ = _getfullargspec(method)
    return args, varargs, keywords, defaults

def raise_with_traceback(exc, tb):
    raise exc.with_traceback(tb)

def raise_from_none(exc):
    raise exc from None

  0707010001f43b000081a40000000000000000000000016a100daf00004a9f000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/foreign/aenum/_tuple.py    from ._common import *
from ._constant import NamedConstant
import sys as _sys

__all__ = [
        'TupleSize', 'NamedTuple',
        ]

# NamedTuple

class NamedTupleDict(OrderedDict):
    """Track field order and ensure field names are not reused.

    NamedTupleMeta will use the names found in self._field_names to translate
    to indices.
    """
    def __init__(self, *args, **kwds):
        self._field_names = []
        super(NamedTupleDict, self).__init__(*args, **kwds)

    def __setitem__(self, key, value):
        """Records anything not dundered or not a descriptor.

        If a field name is used twice, an error is raised.

        Single underscore (sunder) names are reserved.
        """
        if is_sunder(key):
            if key not in ('_size_', '_order_', '_fields_', '_review_'):
                raise ValueError(
                        '_sunder_ names, such as %r, are reserved for future NamedTuple use'
                        % (key, )
                        )
        elif is_dunder(key):
            if key == '__order__':
                key = '_order_'
        elif key in self._field_names:
            # overwriting a field?
            raise TypeError('attempt to reuse field name: %r' % (key, ))
        elif not is_descriptor(value):
            if key in self:
                # field overwriting a descriptor?
                raise TypeError('%s already defined as: %r' % (key, self[key]))
            self._field_names.append(key)
        super(NamedTupleDict, self).__setitem__(key, value)


class _TupleAttributeAtIndex(object):

    def __init__(self, name, index, doc, default):
        self.name = name
        self.index = index
        if doc is undefined:
            doc = None
        self.__doc__ = doc
        self.default = default

    def __get__(self, instance, owner):
        if instance is None:
            return self
        if len(instance) <= self.index:
            raise AttributeError('%s instance has no value for %s' % (instance.__class__.__name__, self.name))
        return instance[self.index]

    def __repr__(self):
        return '%s(%d)' % (self.__class__.__name__, self.index)


class undefined(object):
    def __repr__(self):
        return 'undefined'
    def __bool__(self):
        return False
    __nonzero__ = __bool__
undefined = undefined()


class TupleSize(NamedConstant):
    fixed = constant('fixed', 'tuple length is static')
    minimum = constant('minimum', 'tuple must be at least x long (x is calculated during creation')
    variable = constant('variable', 'tuple length can be anything')

class NamedTupleMeta(type):
    """Metaclass for NamedTuple"""

    @classmethod
    def __prepare__(metacls, cls, bases, size=undefined, **kwds):
        return NamedTupleDict()

    def __init__(cls, *args , **kwds):
        super(NamedTupleMeta, cls).__init__(*args)

    def __new__(metacls, cls, bases, clsdict, size=undefined, **kwds):
        if bases == (object, ):
            bases = (tuple, object)
        elif tuple not in bases:
            if object in bases:
                index = bases.index(object)
                bases = bases[:index] + (tuple, ) + bases[index:]
            else:
                bases = bases + (tuple, )
        # include any fields from base classes
        base_dict = NamedTupleDict()
        namedtuple_bases = []
        for base in bases:
            if isinstance(base, NamedTupleMeta):
                namedtuple_bases.append(base)
        i = 0
        if namedtuple_bases:
            for name, index, doc, default in metacls._convert_fields(*namedtuple_bases):
                base_dict[name] = index, doc, default
                i = max(i, index)
        # construct properly ordered dict with normalized indexes
        for k, v in clsdict.items():
            base_dict[k] = v
        original_dict = base_dict
        if size is not undefined and '_size_' in original_dict:
            raise TypeError('_size_ cannot be set if "size" is passed in header')
        add_order = isinstance(clsdict, NamedTupleDict)
        clsdict = NamedTupleDict()
        clsdict.setdefault('_size_', size or TupleSize.fixed)
        unnumbered = OrderedDict()
        numbered = OrderedDict()
        _order_ = original_dict.pop('_order_', [])
        if _order_ :
            _order_ = _order_.replace(',',' ').split()
            add_order = False
        # and process this class
        for k, v in original_dict.items():
            if k not in original_dict._field_names:
                clsdict[k] = v
            else:
                # TODO:normalize v here
                if isinstance(v, baseinteger):
                    # assume an offset
                    v = v, undefined, undefined
                    i = v[0] + 1
                    target = numbered
                elif isinstance(v, basestring):
                    # assume a docstring
                    if add_order:
                        v = i, v, undefined
                        i += 1
                        target = numbered
                    else:
                        v = undefined, v, undefined
                        target = unnumbered
                elif isinstance(v, tuple) and len(v) in (2, 3) and isinstance(v[0], baseinteger) and isinstance(v[1], (basestring, NoneType)):
                    # assume an offset, a docstring, and (maybe) a default
                    if len(v) == 2:
                        v = v + (undefined, )
                    v = v
                    i = v[0] + 1
                    target = numbered
                elif isinstance(v, tuple) and len(v) in (1, 2) and isinstance(v[0], (basestring, NoneType)):
                    # assume a docstring, and (maybe) a default
                    if len(v) == 1:
                        v = v + (undefined, )
                    if add_order:
                        v = (i, ) + v
                        i += 1
                        target = numbered
                    else:
                        v = (undefined, ) + v
                        target = unnumbered
                else:
                    # refuse to guess further
                    raise ValueError('not sure what to do with %s=%r (should be OFFSET [, DOC [, DEFAULT]])' % (k, v))
                target[k] = v
        # all index values have been normalized
        # deal with _order_ (or lack thereof)
        fields = []
        aliases = []
        seen = set()
        max_len = 0
        if not _order_:
            if unnumbered:
                raise ValueError("_order_ not specified and OFFSETs not declared for %r" % (unnumbered.keys(), ))
            for name, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])):
                if index in seen:
                    aliases.append(name)
                else:
                    fields.append(name)
                    seen.add(index)
                    max_len = max(max_len, index + 1)
            offsets = numbered
        else:
            # check if any unnumbered not in _order_
            missing = set(unnumbered) - set(_order_)
            if missing:
                raise ValueError("unable to order fields: %s (use _order_ or specify OFFSET" % missing)
            offsets = OrderedDict()
            # if any unnumbered, number them from their position in _order_
            i = 0
            for k in _order_:
                try:
                    index, doc, default = unnumbered.pop(k, None) or numbered.pop(k)
                except IndexError:
                    raise ValueError('%s (from _order_) not found in %s' % (k, cls))
                if index is not undefined:
                    i = index
                if i in seen:
                    aliases.append(k)
                else:
                    fields.append(k)
                    seen.add(i)
                offsets[k] = i, doc, default
                i += 1
                max_len = max(max_len, i)
            # now handle anything in numbered
            for k, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])):
                if index in seen:
                    aliases.append(k)
                else:
                    fields.append(k)
                    seen.add(index)
                offsets[k] = index, doc, default
                max_len = max(max_len, index+1)

        # at this point fields and aliases should be ordered lists, offsets should be an
        # OrdededDict with each value an int, str or None or undefined, default or None or undefined
        assert len(fields) + len(aliases) == len(offsets), "number of fields + aliases != number of offsets"
        assert set(fields) & set(offsets) == set(fields), "some fields are not in offsets: %s" % set(fields) & set(offsets)
        assert set(aliases) & set(offsets) == set(aliases), "some aliases are not in offsets: %s" % set(aliases) & set(offsets)
        for name, (index, doc, default) in offsets.items():
            assert isinstance(index, baseinteger), "index for %s is not an int (%s:%r)" % (name, type(index), index)
            assert isinstance(doc, (basestring, NoneType)) or doc is undefined, "doc is not a str, None, nor undefined (%s:%r)" % (name, type(doc), doc)

        # create descriptors for fields
        for name, (index, doc, default) in offsets.items():
            clsdict[name] = _TupleAttributeAtIndex(name, index, doc, default)
        clsdict['__slots__'] = ()

        # create our new NamedTuple type
        namedtuple_class = super(NamedTupleMeta, metacls).__new__(metacls, cls, bases, clsdict)
        namedtuple_class._fields_ = fields
        namedtuple_class._aliases_ = aliases
        namedtuple_class._defined_len_ = max_len
        return namedtuple_class

    @staticmethod
    def _convert_fields(*namedtuples):
        "create list of index, doc, default triplets for cls in namedtuples"
        all_fields = []
        for cls in namedtuples:
            base = len(all_fields)
            for field in cls._fields_:
                desc = getattr(cls, field)
                all_fields.append((field, base+desc.index, desc.__doc__, desc.default))
        return all_fields

    def __add__(cls, other):
        "A new NamedTuple is created by concatenating the _fields_ and adjusting the descriptors"
        if not isinstance(other, NamedTupleMeta):
            return NotImplemented
        return NamedTupleMeta('%s%s' % (cls.__name__, other.__name__), (cls, other), {})

    def __call__(cls, *args, **kwds):
        """Creates a new NamedTuple class or an instance of a NamedTuple subclass.

        NamedTuple should have args of (class_name, names, module)

            `names` can be:

                * A string containing member names, separated either with spaces or
                  commas.  Values are auto-numbered from 1.
                * An iterable of member names.  Values are auto-numbered from 1.
                * An iterable of (member name, value) pairs.
                * A mapping of member name -> value.

                `module`, if set, will be stored in the new class' __module__ attribute;

                Note: if `module` is not set this routine will attempt to discover the
                calling module by walking the frame stack; if this is unsuccessful
                the resulting class will not be pickleable.

        subclass should have whatever arguments and/or keywords will be used to create an
        instance of the subclass
        """
        if cls is NamedTuple or cls._defined_len_ == 0:
            original_args = args
            original_kwds = kwds.copy()
            # create a new subclass
            try:
                if 'class_name' in kwds:
                    class_name = kwds.pop('class_name')
                else:
                    class_name, args = args[0], args[1:]
                if 'names' in kwds:
                    names = kwds.pop('names')
                else:
                    names, args = args[0], args[1:]
                if 'module' in kwds:
                    module = kwds.pop('module')
                elif args:
                    module, args = args[0], args[1:]
                else:
                    module = None
                if 'type' in kwds:
                    type = kwds.pop('type')
                elif args:
                    type, args = args[0], args[1:]
                else:
                    type = None

            except IndexError:
                raise TypeError('too few arguments to NamedTuple: %s, %s' % (original_args, original_kwds))
            if args or kwds:
                raise TypeError('too many arguments to NamedTuple: %s, %s' % (original_args, original_kwds))
            if PY2:
                # if class_name is unicode, attempt a conversion to ASCII
                if isinstance(class_name, unicode):
                    try:
                        class_name = class_name.encode('ascii')
                    except UnicodeEncodeError:
                        raise TypeError('%r is not representable in ASCII' % (class_name, ))
            # quick exit if names is a NamedTuple
            if isinstance(names, NamedTupleMeta):
                names.__name__ = class_name
                if type is not None and type not in names.__bases__:
                    names.__bases__ = (type, ) + names.__bases__
                return names

            metacls = cls.__class__
            bases = (cls, )
            clsdict = metacls.__prepare__(class_name, bases)

            # special processing needed for names?
            if isinstance(names, basestring):
                names = names.replace(',', ' ').split()
            if isinstance(names, (tuple, list)) and isinstance(names[0], basestring):
                names = [(e, i) for (i, e) in enumerate(names)]
            # Here, names is either an iterable of (name, index) or (name, index, doc, default) or a mapping.
            item = None  # in case names is empty
            for item in names:
                if isinstance(item, basestring):
                    # mapping
                    field_name, field_index = item, names[item]
                else:
                    # non-mapping
                    if len(item) == 2:
                        field_name, field_index = item
                    else:
                        field_name, field_index = item[0], item[1:]
                clsdict[field_name] = field_index
            if type is not None:
                if not isinstance(type, tuple):
                    type = (type, )
                bases = type + bases
            namedtuple_class = metacls.__new__(metacls, class_name, bases, clsdict)

            # TODO: replace the frame hack if a blessed way to know the calling
            # module is ever developed
            if module is None:
                try:
                    module = _sys._getframe(1).f_globals['__name__']
                except (AttributeError, ValueError, KeyError):
                    pass
            if module is None:
                make_class_unpicklable(namedtuple_class)
            else:
                namedtuple_class.__module__ = module

            return namedtuple_class
        else:
            # instantiate a subclass
            namedtuple_instance = cls.__new__(cls, *args, **kwds)
            if isinstance(namedtuple_instance, cls):
                namedtuple_instance.__init__(*args, **kwds)
            return namedtuple_instance

    @bltin_property
    def __fields__(cls):
        return list(cls._fields_)
    # collections.namedtuple compatibility
    _fields = __fields__

    @bltin_property
    def __aliases__(cls):
        return list(cls._aliases_)

    def __repr__(cls):
        return "<NamedTuple %r>" % (cls.__name__, )

namedtuple_dict = _Addendum(
        dict=NamedTupleMeta.__prepare__('NamedTuple', (object, )),
        doc="NamedTuple base class.\n\n    Derive from this class to define new NamedTuples.\n\n",
        ns=globals(),
        )

@namedtuple_dict
def __new__(cls, *args, **kwds):
    if cls._size_ is TupleSize.fixed and len(args) > cls._defined_len_:
        raise TypeError('%d fields expected, %d received' % (cls._defined_len_, len(args)))
    unknown = set(kwds) - set(cls._fields_) - set(cls._aliases_)
    if unknown:
        raise TypeError('unknown fields: %r' % (unknown, ))
    final_args = list(args) + [undefined] * (len(cls.__fields__) - len(args))
    for field, value in kwds.items():
        index = getattr(cls, field).index
        if final_args[index] != undefined:
            raise TypeError('field %s specified more than once' % field)
        final_args[index] = value
    cls._review_(final_args)
    missing = []
    for index, value in enumerate(final_args):
        if value is undefined:
            # look for default values
            name = cls.__fields__[index]
            default = getattr(cls, name).default
            if default is undefined:
                missing.append(name)
            else:
                final_args[index] = default
    if missing:
        if cls._size_ in (TupleSize.fixed, TupleSize.minimum):
            raise TypeError('values not provided for field(s): %s' % ', '.join(missing))
        while final_args and final_args[-1] is undefined:
            final_args.pop()
            missing.pop()
        if cls._size_ is not TupleSize.variable or undefined in final_args:
            raise TypeError('values not provided for field(s): %s' % ', '.join(missing))
    return tuple.__new__(cls, tuple(final_args))

@namedtuple_dict
def __reduce_ex__(self, proto):
    return self.__class__, tuple(getattr(self, f) for f in self._fields_)

@namedtuple_dict
def __repr__(self):
    if len(self) == len(self._fields_):
        return "%s(%s)" % (
                self.__class__.__name__, ', '.join(['%s=%r' % (f, o) for f, o in zip(self._fields_, self)])
                )
    else:
        return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(o) for o in self]))

@namedtuple_dict
def __str__(self):
    return "%s(%s)" % (
            self.__class__.__name__, ', '.join(['%r' % (getattr(self, f), ) for f in self._fields_])
            )

@namedtuple_dict
@bltin_property
def _fields_(self):
    return list(self.__class__._fields_)

    # compatibility methods with stdlib namedtuple
@namedtuple_dict
@bltin_property
def __aliases__(self):
    return list(self.__class__._aliases_)

@namedtuple_dict
@bltin_property
def _fields(self):
    return list(self.__class__._fields_)

@namedtuple_dict
@classmethod
def _make(cls, iterable, new=None, len=None):
    return cls.__new__(cls, *iterable)

@namedtuple_dict
def _asdict(self):
    return OrderedDict(zip(self._fields_, self))

@namedtuple_dict
def _replace(self, **kwds):
    current = self._asdict()
    current.update(kwds)
    return self.__class__(**current)

@namedtuple_dict
@classmethod
def _review_(cls, final_args):
    pass

NamedTuple = NamedTupleMeta('NamedTuple', (object, ), namedtuple_dict.resolve())
del namedtuple_dict



 0707010001f439000081a40000000000000000000000016a100daf000000b8000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/aenum/_py2.py  from operator import div as _div_
from inspect import getargspec

def raise_with_traceback(exc, tb):
    raise exc, None, tb

__all__ = ['_div_', 'getargspec', 'raise_with_traceback']
0707010001f436000081a40000000000000000000000016a100daf0000200b000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/foreign/aenum/_common.py   from __future__ import print_function

__all__ = [
        'pyver', 'PY2', 'PY2_6', 'PY3', 'PY3_3', 'PY3_4', 'PY3_5', 'PY3_6', 'PY3_7', 'PY3_11',
        '_or_', '_and_', '_xor_', '_inv_', '_abs_', '_add_', '_floordiv_', '_lshift_',
        '_rshift_', '_mod_', '_mul_', '_neg_', '_pos_', '_pow_', '_truediv_', '_sub_',
        'unicode', 'basestring', 'baseinteger', 'long', 'NoneType', '_Addendum',
        'is_descriptor', 'is_dunder', 'is_sunder', 'is_internal_class', 'is_private_name',
        'get_attr_from_chain', '_value', 'constant',
        'make_class_unpicklable', 'bltin_property',
        'skip', 'nonmember', 'member', 'Member', 'NonMember', 'OrderedDict',
        ]


# imports
import sys as _sys
pyver = _sys.version_info[:2]
PY2 = pyver < (3, )
PY3 = pyver >= (3, )
PY2_6 = (2, 6)
PY3_3 = (3, 3)
PY3_4 = (3, 4)
PY3_5 = (3, 5)
PY3_6 = (3, 6)
PY3_7 = (3, 7)
PY3_11 = (3, 11)

import re

from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_
from operator import abs as _abs_, add as _add_, floordiv as _floordiv_
from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_
from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_
from operator import truediv as _truediv_, sub as _sub_

if PY2:
    from . import _py2
    from ._py2 import *
    __all__.extend(_py2.__all__)
if PY3:
    from . import _py3
    from ._py3 import *
    __all__.extend(_py3.__all__)

bltin_property = property

# shims

try:
    from collections import OrderedDict
except ImportError:
    OrderedDict = dict

try:
    unicode
    unicode = unicode
except NameError:
    # In Python 3 unicode no longer exists (it's just str)
    unicode = str

try:
    basestring
    basestring = bytes, unicode
except NameError:
    # In Python 2 basestring is the ancestor of both str and unicode
    # in Python 3 it's just str, but was missing in 3.1
    basestring = str,

try:
    baseinteger = int, long
    long = long
except NameError:
    baseinteger = int,
    long = int
# deprecated
baseint = baseinteger

try:
    NoneType
except NameError:
    NoneType = type(None)

class _Addendum(object):
    def __init__(self, dict, doc, ns):
        # dict is the dict to update with functions
        # doc is the docstring to put in the dict
        # ns is the namespace to remove the function names from
        self.dict = dict
        self.ns = ns
        self.added = set()
    def __call__(self, func):
        if isinstance(func, (staticmethod, classmethod)):
            name = func.__func__.__name__
        elif isinstance(func, (property, bltin_property)):
            name = (func.fget or func.fset or func.fdel).__name__
        else:
            name = func.__name__
        self.dict[name] = func
        self.added.add(name)
        return func
    def __getitem__(self, name):
        return self.dict[name]
    def __setitem__(self, name, value):
        self.dict[name] = value
    def resolve(self):
        ns = self.ns
        for name in self.added:
            del ns[name]
        return self.dict

def is_descriptor(obj):
    """Returns True if obj is a descriptor, False otherwise."""
    return (
            hasattr(obj, '__get__') or
            hasattr(obj, '__set__') or
            hasattr(obj, '__delete__'))


def is_dunder(name):
    """Returns True if a __dunder__ name, False otherwise."""
    return (len(name) > 4 and
            name[:2] == name[-2:] == '__' and
            name[2] != '_' and
            name[-3] != '_')


def is_sunder(name):
    """Returns True if a _sunder_ name, False otherwise."""
    return (len(name) > 2 and
            name[0] == name[-1] == '_' and
            name[1] != '_' and
            name[-2] != '_')

def is_internal_class(cls_name, obj):
    # only 3.3 and up, always return False in 3.2 and below
    if pyver < PY3_3:
        return False
    else:
        qualname = getattr(obj, '__qualname__', False)
        return not is_descriptor(obj) and qualname and re.search(r"\.?%s\.\w+$" % cls_name, qualname)

def is_private_name(cls_name, name):
    pattern = r'^_%s__\w+[^_]_?$' % (cls_name, )
    return re.search(pattern, name)

def get_attr_from_chain(cls, attr):
    sentinel = object()
    for basecls in cls.mro():
        obj = basecls.__dict__.get(attr, sentinel)
        if obj is not sentinel:
            return obj

def _value(obj):
    if isinstance(obj, (auto, constant)):
        return obj.value
    else:
        return obj

class constant(object):
    '''
    Simple constant descriptor for NamedConstant and Enum use.
    '''
    def __init__(self, value, doc=None):
        self.value = value
        self.__doc__ = doc
    def __get__(self, *args):
        return self.value
    def __repr__(self):
        return '%s(%r)' % (self.__class__.__name__, self.value)
    def __and__(self, other):
        return _and_(self.value, _value(other))
    def __rand__(self, other):
        return _and_(_value(other), self.value)
    def __invert__(self):
        return _inv_(self.value)
    def __or__(self, other):
        return _or_(self.value, _value(other))
    def __ror__(self, other):
        return _or_(_value(other), self.value)
    def __xor__(self, other):
        return _xor_(self.value, _value(other))
    def __rxor__(self, other):
        return _xor_(_value(other), self.value)
    def __abs__(self):
        return _abs_(self.value)
    def __add__(self, other):
        return _add_(self.value, _value(other))
    def __radd__(self, other):
        return _add_(_value(other), self.value)
    def __neg__(self):
        return _neg_(self.value)
    def __pos__(self):
        return _pos_(self.value)
    if PY2:
        def __div__(self, other):
            return _div_(self.value, _value(other))
    def __rdiv__(self, other):
        return _div_(_value(other), (self.value))
    def __floordiv__(self, other):
        return _floordiv_(self.value, _value(other))
    def __rfloordiv__(self, other):
        return _floordiv_(_value(other), self.value)
    def __truediv__(self, other):
        return _truediv_(self.value, _value(other))
    def __rtruediv__(self, other):
        return _truediv_(_value(other), self.value)
    def __lshift__(self, other):
        return _lshift_(self.value, _value(other))
    def __rlshift__(self, other):
        return _lshift_(_value(other), self.value)
    def __rshift__(self, other):
        return _rshift_(self.value, _value(other))
    def __rrshift__(self, other):
        return _rshift_(_value(other), self.value)
    def __mod__(self, other):
        return _mod_(self.value, _value(other))
    def __rmod__(self, other):
        return _mod_(_value(other), self.value)
    def __mul__(self, other):
        return _mul_(self.value, _value(other))
    def __rmul__(self, other):
        return _mul_(_value(other), self.value)
    def __pow__(self, other):
        return _pow_(self.value, _value(other))
    def __rpow__(self, other):
        return _pow_(_value(other), self.value)
    def __sub__(self, other):
        return _sub_(self.value, _value(other))
    def __rsub__(self, other):
        return _sub_(_value(other), self.value)
    def __set_name__(self, ownerclass, name):
        self.name = name
        self.clsname = ownerclass.__name__

def make_class_unpicklable(obj):
    """
    Make the given obj un-picklable.

    obj should be either a dictionary, on an Enum
    """
    def _break_on_call_reduce(self, proto):
        raise TypeError('%r cannot be pickled' % self)
    if isinstance(obj, dict):
        obj['__reduce_ex__'] = _break_on_call_reduce
        obj['__module__'] = '<unknown>'
    else:
        setattr(obj, '__reduce_ex__', _break_on_call_reduce)
        setattr(obj, '__module__', '<unknown>')

class NonMember(object):
    """
    Protects item from becaming an Enum member during class creation.
    """
    def __init__(self, value):
        self.value = value

    def __get__(self, instance, ownerclass=None):
        return self.value
skip = nonmember = NonMember

class Member(object):
    """
    Forces item to became an Enum member during class creation.
    """
    def __init__(self, value):
        self.value = value
member = Member


 0707010001f435000081a40000000000000000000000016a100daf0000046d000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/foreign/aenum/__init__.py  """Python Advanced Enumerations & NameTuples"""
from __future__ import print_function

version = 3, 1, 16, 1

# imports
from ._common import *
from ._constant import *
from ._tuple import *
from ._enum import *


__all__ = [
        'NamedConstant', 'Constant', 'constant', 'skip', 'nonmember', 'member', 'no_arg',
        'Member', 'NonMember', 'bin', 
        'Enum', 'IntEnum', 'AutoNumberEnum', 'OrderedEnum', 'UniqueEnum',
        'StrEnum', 'UpperStrEnum', 'LowerStrEnum', 'ReprEnum',
        'Flag', 'IntFlag', 'enum_property',
        'AddValue', 'MagicValue', 'MultiValue', 'NoAlias', 'Unique',
        'AddValueEnum', 'MultiValueEnum', 'NoAliasEnum',
        'enum', 'extend_enum', 'unique', 'property',
        'NamedTuple', 'SqliteEnum', '_reduce_ex_by_name',
        'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
        'add_stdlib_integration', 'remove_stdlib_integration'
        ]

if sqlite3 is None:
    __all__.remove('SqliteEnum')


if PY2:
    from . import _py2
    __all__.extend(_py2.__all__)
else:
    from . import _py3
    __all__.extend(_py3.__all__)
    __all__.append('AutoEnum')



# helpers

   0707010001f438000081a40000000000000000000000016a100daf0001f043000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/foreign/aenum/_enum.py from __future__ import print_function
from ._common import *
from ._constant import NamedConstant
from ._tuple import NamedTuple
from collections import defaultdict
import sys as _sys

__all__ = [
        'bit_count', 'is_single_bit', 'bin', 'property', 'bits',
        'AddValue', 'MagicValue', 'MultiValue', 'NoAlias', 'Unique', 'enum', 'auto',
        'AddValueEnum', 'MultiValueEnum', 'NoAliasEnum', 'UniqueEnum', 'AutoNumberEnum',
        'OrderedEnum', 'unique', 'no_arg', 'extend_enum', 'enum_property',
        'EnumType', 'EnumMeta', 'EnumDict', 'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag',
        'LowerStrEnum', 'UpperStrEnum', 'ReprEnum', 'SqliteEnum', 'sqlite3',
        'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
        'add_stdlib_integration', 'remove_stdlib_integration',
        'export', 'cls2module', '_reduce_ex_by_name', 'show_flag_values',
        ]
        

_bltin_bin = bin

try:
    import sqlite3
except ImportError:
    sqlite3 = None
    __all__.remove('SqliteEnum')

try:
    RecursionError
except NameError:
    # python3.4
    RecursionError = RuntimeError


try:
    any
except NameError:
    def any(iterable):
        for element in iterable:
            if element:
                return True
        return False

# derive from stdlib enum if possible
stdlib_enums = ()
try:
    import enum
    if hasattr(enum, 'version'):
        raise ImportError('wrong version')
    else:
        from enum import EnumMeta as StdlibEnumMeta, Enum as StdlibEnum, IntEnum as StdlibIntEnum
        StdlibFlag = StdlibIntFlag = StdlibStrEnum = StdlibReprEnum = None
        stdlib_enums = StdlibEnum, StdlibIntEnum
except ImportError:
    StdlibEnumMeta = StdlibEnum = StdlibIntEnum = StdlibIntFlag = StdlibFlag = StdlibStrEnum = StdlibStrEnum = StdlibReprEnum = None

if StdlibEnum:
    try:
        from enum import IntFlag as StdlibIntFlag, Flag as StdlibFlag
        stdlib_enums += StdlibFlag, StdlibIntFlag
    except ImportError:
        pass
    try:
        from enum import StrEnum as StdlibStrEnum
        stdlib_enums += (StdlibStrEnum, )
    except ImportError:
        pass
    try:
        from enum import ReprEnum as StdlibReprEnum
        stdlib_enums += (StdlibReprEnum, )
    except ImportError:
        pass

# will be exported later
MagicValue = AddValue = MultiValue = NoAlias = Unique = None

def export(collection, namespace=None):
    """
    export([collection,] namespace) -> Export members to target namespace.

    If collection is not given, act as a decorator.
    """
    if namespace is None:
        namespace = collection
        def export_decorator(collection):
            return export(collection, namespace)
        return export_decorator
    elif issubclass(collection, NamedConstant):
        for n, c in collection.__dict__.items():
            if isinstance(c, NamedConstant):
                namespace[n] = c
    elif issubclass(collection, Enum) or stdlib_enums and issubclass(collection, stdlib_enums):
        data = collection.__members__.items()
        for n, m in data:
            namespace[n] = m
    else:
        raise TypeError('%r is not a supported collection' % (collection,) )
    return collection

def bit_count(num):
    """
    return number of set bits

    Counting bits set, Brian Kernighan's way*

        unsigned int v;          // count the number of bits set in v
        unsigned int c;          // c accumulates the total bits set in v
        for (c = 0; v; c++)
        {   v &= v - 1;  }       //clear the least significant bit set

    This method goes through as many iterations as there are set bits. So if we
    have a 32-bit word with only the high bit set, then it will only go once
    through the loop.

    * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988.

    This works because each subtraction "borrows" from the lowest 1-bit. For
    example:

          loop pass 1     loop pass 2
          -----------     -----------
               101000          100000
             -      1        -      1
             = 100111        = 011111
             & 101000        & 100000
             = 100000        =      0

    It is an excellent technique for Python, since the size of the integer need
    not be determined beforehand.

    (from https://wiki.python.org/moin/BitManipulation)
    """
    count = 0
    while num:
        num &= num - 1
        count += 1
    return count

def is_single_bit(value):
    """
    True if only one bit set in value (should be an int)
    """
    if value == 0:
        return False
    value &= value - 1
    return value == 0

def _iter_bits_lsb(value):
    """
    Return each bit value one at a time.

    >>> list(_iter_bits_lsb(6))
    [2, 4]
    """

    while value:
        bit = value & (~value + 1)
        yield bit
        value ^= bit

def bin(value, max_bits=None):
    """
    Like built-in bin(), except negative values are represented in
    twos-compliment, and the leading bit always indicates sign
    (0=positive, 1=negative).

    >>> bin(10)
    '0b0 1010'
    >>> bin(~10)   # ~10 is -11
    '0b1 0101'
    """

    ceiling = 2 ** (value).bit_length()
    if value >= 0:
        s = _bltin_bin(value + ceiling).replace('1', '0', 1)
    else:
        s = _bltin_bin(~value ^ (ceiling - 1) + ceiling)
    sign = s[:3]
    digits = s[3:]
    if not digits:
        digits = '0'
    if max_bits is not None:
        if len(digits) < max_bits:
            digits = (sign[-1] * max_bits + digits)[-max_bits:]
    return "%s %s" % (sign, digits)

def show_flag_values(value):
    return list(_iter_bits_lsb(value))


try:
    from types import DynamicClassAttribute
    base = DynamicClassAttribute
except ImportError:
    base = object
    DynamicClassAttribute = None

class property(base):
    """
    This is a descriptor, used to define attributes that act differently
    when accessed through an enum member and through an enum class.
    Instance access is the same as property(), but access to an attribute
    through the enum class will look in the class' _member_map_.
    """

    # inherit from DynamicClassAttribute if we can in order to get `inspect`
    # support

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        # next two lines make property act the same as bltin_property
        self.__doc__ = doc or fget.__doc__
        self.overwrite_doc = doc is None
        # support for abstract methods
        self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False))
        # names, if possible

    def getter(self, fget):
        fdoc = fget.__doc__ if self.overwrite_doc else None
        result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__)
        result.overwrite_doc = self.__doc__ is None
        return result

    def setter(self, fset):
        fdoc = fget.__doc__ if self.overwrite_doc else None
        result = type(self)(self.fget, fset, self.fdel, self.__doc__)
        result.overwrite_doc = self.__doc__ is None
        return result

    def deleter(self, fdel):
        fdoc = fget.__doc__ if self.overwrite_doc else None
        result = type(self)(self.fget, self.fset, fdel, self.__doc__)
        result.overwrite_doc = self.__doc__ is None
        return result

    def __repr__(self):
        member = self.ownerclass._member_map_.get(self.name)
        func = self.fget or self.fset or self.fdel
        strings = []
        if member:
            strings.append('%r' % member)
        if func:
            strings.append('function=%s' % func.__name__)
        return 'property(%s)' % ', '.join(strings)

    def __get__(self, instance, ownerclass=None):
        if instance is None:
            try:
                return ownerclass._member_map_[self.name]
            except KeyError:
                raise AttributeError(
                        '%r has no attribute %r' % (ownerclass, self.name)
                        )
        else:
            if self.fget is not None:
                return self.fget(instance)
            else:
                if self.fset is not None:
                    raise AttributeError(
                            'cannot read attribute %r on %r' % (self.name, ownerclass)
                            )
                else:
                    try:
                        return instance.__dict__[self.name]
                    except KeyError:
                        raise AttributeError(
                                '%r member has no attribute %r' % (ownerclass, self.name)
                                )

    def __set__(self, instance, value):
        if self.fset is None:
            if self.fget is not None:
                raise AttributeError(
                        "cannot set attribute %r on <aenum %r>" % (self.name, self.clsname)
                        )
            else:
                instance.__dict__[self.name] = value
        else:
            return self.fset(instance, value)

    def __delete__(self, instance):
        if self.fdel is None:
            if self.fget or self.fset:
                raise AttributeError(
                        "cannot delete attribute %r on <aenum %r>" % (self.name, self.clsname)
                        )
            elif self.name in instance.__dict__:
                del instance.__dict__[self.name]
            else:
                raise AttributeError(
                        "no attribute %r on <aenum %r> member" % (self.name, self.clsname)
                        )
        else:
            return self.fdel(instance)

    def __set_name__(self, ownerclass, name):
        self.name = name
        self.clsname = ownerclass.__name__
        self.ownerclass = ownerclass

_RouteClassAttributeToGetattr = property
if DynamicClassAttribute is None:
    DynamicClassAttribute = property
# deprecated
enum_property = property

# more helpers

class SentinelType(type):
    def __repr__(cls):
        return '<%s>' % cls.__name__
Sentinel = SentinelType('Sentinel', (object, ), {})

def _power_of_two(value):
    if value < 1:
        return False
    return value == 2 ** _high_bit(value)

def bits(num):
    if num in (0, 1):
        return str(num)
    negative = False
    if num < 0:
        negative = True
        num = ~num
    result = bits(num>>1) + str(num&1)
    if negative:
        result = '1' + ''.join(['10'[d=='1'] for d in result])
    return result


def bit_count(num):
    """
        return number of set bits

        Counting bits set, Brian Kernighan's way*

            unsigned int v;          // count the number of bits set in v
            unsigned int c;          // c accumulates the total bits set in v
            for (c = 0; v; c++)
            {   v &= v - 1;  }       //clear the least significant bit set

        This method goes through as many iterations as there are set bits. So if we
        have a 32-bit word with only the high bit set, then it will only go once
        through the loop.

        * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988.

        This works because each subtraction "borrows" from the lowest 1-bit. For example:

              loop pass 1     loop pass 2
              -----------     -----------
                   101000          100000
                 -      1        -      1
                 = 100111        = 011111
                 & 101000        & 100000
                 = 100000        =      0

        It is an excellent technique for Python, since the size of the integer need not
        be determined beforehand.
    """
    count = 0
    while(num):
        num &= num - 1
        count += 1
    return(count)

def bit_len(num):
    length = 0
    while num:
        length += 1
        num >>= 1
    return length

def is_single_bit(num):
    """
    True if only one bit set in num (should be an int)
    """
    return (num != 0) and (num & (num - 1)) == 0

def _check_auto_args(method):
    """check if new generate method supports *args and **kwds"""
    if isinstance(method, staticmethod):
        method = method.__get__(type)
    method = getattr(method, 'im_func', method)
    args, varargs, keywords, defaults = getargspec(method)
    return varargs is not None and keywords is not None

def enumsort(things):
    """
    sorts things by value if all same type; otherwise by name
    """
    if not things:
        return things
    sort_type = type(things[0])
    if not issubclass(sort_type, tuple):
        # direct sort or type error
        if not all((type(v) is sort_type) for v in things[1:]):
            raise TypeError('cannot sort items of different types')
        return sorted(things)
    else:
        # expecting list of (name, value) tuples
        sort_type = type(things[0][1])
        try:
            if all((type(v[1]) is sort_type) for v in things[1:]):
                return sorted(things, key=lambda i: i[1])
            else:
                raise TypeError('try name sort instead')
        except TypeError:
            return sorted(things, key=lambda i: i[0])

# Enum

    # _init_ and value and AddValue
    # -----------------------------
    # by default, when defining a member everything after the = is "the value", everything is
    #   passed to __new__, everything is passed to __init__
    #
    # if _init_ is present then
    #   if `value` is not in _init_, everything is "the value", defaults apply
    #   if `value` is in _init_, only the first thing after the = is the value, and the rest will
    #       be passed to __init__
    #   if fewer values are present for member assignment than _init_ calls for, _generate_next_value_
    #       will be called in an attempt to generate them
    #
    # if AddValue is present then
    #   _generate_next_value_ is always called, and any generated values are prepended to provided
    #       values (custom _gnv_s can change that)
    #   default _init_ rules apply


    # Constants used in Enum

@export(globals())
class EnumConstants(NamedConstant):
    AddValue = constant('addvalue', 'prepends value(s) from _generate_next_value_ to each member')
    MagicValue = constant('magicvalue', 'calls _generate_next_value_ when no arguments are given')
    MultiValue = constant('multivalue', 'each member can have several values')
    NoAlias = constant('noalias', 'duplicate valued members are distinct, not aliased')
    Unique = constant('unique', 'duplicate valued members are not allowed')
    def __repr__(self):
        return self._name_


    # Dummy value for Enum as EnumType explicity checks for it, but of course until
    # EnumType finishes running the first time the Enum class doesn't exist.  This
    # is also why there are checks in EnumType like `if Enum is not None`.
    #
    # Ditto for Flag.

Enum = ReprEnum = IntEnum = StrEnum = Flag = IntFlag = EJECT = KEEP = None

class enum(object):
    """
    Helper class to track args, kwds.
    """
    def __init__(self, *args, **kwds):
        self._args = args
        self._kwds = dict(kwds.items())
        self._hash = hash(args)
        self.name = None

    @bltin_property
    def args(self):
        return self._args

    @bltin_property
    def kwds(self):
        return self._kwds.copy()

    def __hash__(self):
        return self._hash

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.args == other.args and self.kwds == other.kwds

    def __ne__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented
        return self.args != other.args or self.kwds != other.kwds

    def __repr__(self):
        final = []
        args = ', '.join(['%r' % (a, ) for a in self.args])
        if args:
            final.append(args)
        kwds = ', '.join([('%s=%r') % (k, v) for k, v in enumsort(list(self.kwds.items()))])
        if kwds:
            final.append(kwds)
        return '%s(%s)' % (self.__class__.__name__, ', '.join(final))

_auto_null = SentinelType('no_value', (object, ), {})
class auto(enum):
    """
    Instances are replaced with an appropriate value in Enum class suites.
    """
    enum_member = _auto_null
    _value = _auto_null
    _operations = []

    def __and__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_and_, (self, other)))
        return new_auto

    def __rand__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_and_, (other, self)))
        return new_auto

    def __invert__(self):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_inv_, (self,)))
        return new_auto

    def __or__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_or_, (self, other)))
        return new_auto

    def __ror__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_or_, (other, self)))
        return new_auto

    def __xor__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_xor_, (self, other)))
        return new_auto

    def __rxor__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_xor_, (other, self)))
        return new_auto

    def __abs__(self):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_abs_, (self, )))
        return new_auto

    def __add__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_add_, (self, other)))
        return new_auto

    def __radd__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_add_, (other, self)))
        return new_auto

    def __neg__(self):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_neg_, (self, )))
        return new_auto

    def __pos__(self):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_pos_, (self, )))
        return new_auto

    if PY2:
        def __div__(self, other):
            new_auto = self.__class__()
            new_auto._operations = self._operations[:]
            new_auto._operations.append((_div_, (self, other)))
            return new_auto

    def __rdiv__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_div_, (other, self)))
        return new_auto

    def __floordiv__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_floordiv_, (self, other)))
        return new_auto

    def __rfloordiv__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_floordiv_, (other, self)))
        return new_auto

    def __truediv__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_truediv_, (self, other)))
        return new_auto

    def __rtruediv__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_truediv_, (other, self)))
        return new_auto

    def __lshift__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_lshift_, (self, other)))
        return new_auto

    def __rlshift__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_lshift_, (other, self)))
        return new_auto

    def __rshift__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_rshift_, (self, other)))
        return new_auto

    def __rrshift__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_rshift_, (other, self)))
        return new_auto

    def __mod__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_mod_, (self, other)))
        return new_auto

    def __rmod__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_mod_, (other, self)))
        return new_auto

    def __mul__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_mul_, (self, other)))
        return new_auto

    def __rmul__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_mul_, (other, self)))
        return new_auto

    def __pow__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_pow_, (self, other)))
        return new_auto

    def __rpow__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_pow_, (other, self)))
        return new_auto

    def __sub__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_sub_, (self, other)))
        return new_auto

    def __rsub__(self, other):
        new_auto = self.__class__()
        new_auto._operations = self._operations[:]
        new_auto._operations.append((_sub_, (other, self)))
        return new_auto

    def __repr__(self):
        if self._operations:
            return 'auto(...)'
        else:
            return 'auto(%r, *%r, **%r)' % (self._value, self._args, self._kwds)

    @bltin_property
    def value(self):
        if self._value is not _auto_null and self._operations:
            raise TypeError('auto() object out of sync')
        elif self._value is _auto_null and not self._operations:
            return self._value
        elif self._value is not _auto_null:
            return self._value
        else:
            return self._resolve()

    @value.setter
    def value(self, value):
        if self._operations:
            value = self._resolve(value)
        self._value = value

    def _resolve(self, base_value=None):
            cls = self.__class__
            for op, params in self._operations:
                values = []
                for param in params:
                    if isinstance(param, cls):
                        if param.value is _auto_null:
                            if base_value is None:
                                return _auto_null
                            else:
                                values.append(base_value)
                        else:
                            values.append(param.value)
                    else:
                        values.append(param)
                value = op(*values)
            self._operations[:] = []
            self._value = value
            return value

from . import _common
_common.property = property
_common.auto = auto
del _common


class _EnumArgSpec(NamedTuple):
    args = 0, 'all args except *args and **kwds'
    varargs = 1, 'the name of the *args variable'
    keywords = 2, 'the name of the **kwds variable'
    defaults = 3, 'any default values'
    required = 4, 'number of required values (no default available)'

    def __new__(cls, _new_func):
        argspec = getargspec(_new_func)
        args, varargs, keywords, defaults = argspec
        if defaults:
            reqs = args[1:-len(defaults)]
        else:
            reqs = args[1:]
        return tuple.__new__(_EnumArgSpec, (args, varargs, keywords, defaults, reqs))


class _proto_member:
    """
    intermediate step for enum members between class execution and final creation
    """

    def __init__(self, value):
        self.value = value

    def __set_name__(self, enum_class, member_name):
        """
        convert each quasi-member into an instance of the new enum class
        """
        # first step: remove ourself from enum_class
        delattr(enum_class, member_name)
        # second step: create member based on enum_class
        value = self.value
        kwds = {}
        args = ()
        init_args = ()
        extra_mv_args = ()
        multivalue = None
        if isinstance(value, tuple) and value and isinstance(value[0], auto):
            multivalue = value
            value = value[0]
        if isinstance(value, auto) and value.value is _auto_null:
            args = value.args
            kwds = value.kwds
        elif isinstance(value, auto):
            kwds = value.kwds
            args = (value.value, ) + value.args
            value = value.value
        elif isinstance(value, enum):
            args = value.args
            kwds = value.kwds
        elif isinstance(value, Member):
            value = value.value
            args = (value, )
        elif not isinstance(value, tuple):
            args = (value, )
        else:
            args = value
        if multivalue is not None:
            value = (value, ) + multivalue[1:]
            kwds = {}
            args = value
            del multivalue
        # possibilities
        #
        # - no init, multivalue  -> __new__[0], __init__(*[:]), extra=[1:]
        # - init w/o value, multivalue  -> __new__[0], __init__(*[:]), extra=[1:]
        #
        # - init w/value, multivalue  -> __new__[0], __init__(*[1:]),  extra=[1:]
        #
        # - init w/value, no multivalue  -> __new__[0], __init__(*[1:]), extra=[]
        #
        # - init w/o value, no multivalue  -> __new__[:], __init__(*[:]), extra=[]
        # - no init, no multivalue  ->  __new__[:], __init__(*[:]), extra=[]
        if enum_class._multivalue_ or 'value' in enum_class._creating_init_:
            if enum_class._multivalue_:
                # when multivalue is True, creating_init can be anything
                mv_arg = args[0]
                extra_mv_args = args[1:]
                if 'value' in enum_class._creating_init_:
                    init_args = args[1:]
                else:
                    init_args = args
                args = args[0:1]
                value = args[0]
            else:
                # 'value' is definitely in creating_init
                if enum_class._auto_init_ and enum_class._new_args_:
                    # we have a custom __new__ and an auto __init__
                    # divvy up according to number of params in each
                    init_args = args[-len(enum_class._creating_init_)+1:]
                    if not enum_class._auto_args_:
                        args = args[:len(enum_class._new_args_.args)]
                    value = args[0]
                elif enum_class._auto_init_:
                    # don't pass in value
                    init_args = args[1:]
                    args = args[0:1]
                    value = args[0]
                elif enum_class._new_args_:
                    # do not modify args
                    value = args[0]
                else:
                    # keep all args for user-defined __init__
                    # keep value as-is
                    init_args = args
        else:
            # either no creating_init, or it doesn't have 'value'
            init_args = args
        if enum_class._member_type_ is tuple:   # special case for tuple enums
            args = (args, )     # wrap it one more time
        if not enum_class._use_args_:
            enum_member = enum_class._new_member_(enum_class)
        else:
            enum_member = enum_class._new_member_(enum_class, *args, **kwds)
        if not hasattr(enum_member, '_value_'):
            if enum_class._member_type_ is object:
                enum_member._value_ = value
            else:
                try:
                    enum_member._value_ = enum_class._member_type_(*args, **kwds)
                except Exception as exc:
                    te = TypeError('_value_ not set in __new__, unable to create it')
                    te.__cause__ = exc
                    raise te
        value = enum_member._value_
        enum_member._name_ = member_name
        enum_member.__objclass__ = enum_class
        enum_member.__init__(*init_args, **kwds)
        enum_member._sort_order_ = len(enum_class._member_names_)

        if Flag is not None and issubclass(enum_class, Flag):
            enum_class._flag_mask_ |= value
            if is_single_bit(value):
                enum_class._singles_mask_ |= value
            enum_class._all_bits_ = 2 ** ((enum_class._flag_mask_).bit_length()) - 1

        # If another member with the same value was already defined, the
        # new member becomes an alias to the existing one.
        if enum_class._noalias_:
            # unless NoAlias was specified
            enum_class._member_names_.append(member_name)
        else:
            nonunique = defaultdict(list)
            try:
                try:
                    # try to do a fast lookup to avoid the quadratic loop
                    enum_member = enum_class._value2member_map_[value]
                    if enum_class._unique_:
                        nonunique[enum_member.name].append(member_name)
                except TypeError:
                    # unhashable members are stored elsewhere
                    for unhashable_value, canonical_member in enum_class._value2member_seq_:
                        name = canonical_member.name
                        if unhashable_value == enum_member._value_:
                            if enum_class._unique_:
                                nonunique[name].append(member_name)
                            enum_member = canonical_member
                            break
                    else:
                        raise KeyError
            except KeyError:
                # this could still be an alias if the value is multi-bit and the
                # class is a flag class
                if (
                        Flag is None
                        or not issubclass(enum_class, Flag)
                    ):
                    # no other instances found, record this member in _member_names_
                    enum_class._member_names_.append(member_name)
                elif (
                        Flag is not None
                        and issubclass(enum_class, Flag)
                        and is_single_bit(value)
                    ):
                    # no other instances found, record this member in _member_names_
                    enum_class._member_names_.append(member_name)
            if nonunique:
                # duplicates not allowed if Unique specified
                message = []
                for name, aliases in nonunique.items():
                    bad_aliases = ','.join(aliases)
                    message.append('%s --> %s [%r]' % (name, bad_aliases, enum_class[name].value))
                raise ValueError(
                        '%s: duplicate names found: %s' %
                            (enum_class.__name__, ';  '.join(message))
                        )
        # if self.value is an `auto()`, replace the value attribute with the new enum member
        if isinstance(self.value, auto):
            self.value.enum_member = enum_member
        # if necessary, get redirect in place and then add it to _member_map_
        found_descriptor = None
        descriptor_type = None
        class_type = None
        for base in enum_class.__mro__[1:]:
            attr = base.__dict__.get(member_name)
            if attr is not None:
                if isinstance(attr, (property, DynamicClassAttribute)):
                    found_descriptor = attr
                    class_type = base
                    descriptor_type = 'enum'
                    break
                elif is_descriptor(attr):
                    found_descriptor = attr
                    descriptor_type = descriptor_type or 'desc'
                    class_type = class_type or base
                    continue
                else:
                    descriptor_type = 'attr'
                    class_type = base
        if found_descriptor:
            redirect = property()
            redirect.member = enum_member
            redirect.__set_name__(enum_class, member_name)
            if descriptor_type in ('enum','desc'):
                # earlier descriptor found; copy fget, fset, fdel to this one.
                redirect.fget = getattr(found_descriptor, 'fget', None)
                redirect._get = getattr(found_descriptor, '__get__', None)
                redirect.fset = getattr(found_descriptor, 'fset', None)
                redirect._set = getattr(found_descriptor, '__set__', None)
                redirect.fdel = getattr(found_descriptor, 'fdel', None)
                redirect._del = getattr(found_descriptor, '__delete__', None)
            redirect._attr_type = descriptor_type
            redirect._cls_type = class_type
            setattr(enum_class, member_name, redirect)
        else:
            setattr(enum_class, member_name, enum_member)
        # now add to _member_map_ (even aliases)
        enum_class._member_map_[member_name] = enum_member
        #
        # process (possible) MultiValues
        values = (value, ) + extra_mv_args
        if enum_class._multivalue_ and mv_arg not in values:
            values += (mv_arg, )
        enum_member._values_ = values
        for value in values:
            # first check if value has already been used
            if enum_class._multivalue_ and (
                    value in enum_class._value2member_map_
                    or any(v == value for (v, m) in enum_class._value2member_seq_)
                    ):
                raise ValueError('%r has already been used' % (value, ))
            try:
                # This may fail if value is not hashable. We can't add the value
                # to the map, and by-value lookups for this value will be
                # linear.
                if enum_class._noalias_:
                    raise TypeError('cannot use dict to store value')
                enum_class._value2member_map_[value] = enum_member
            except TypeError:
                enum_class._value2member_seq_ += ((value, enum_member), )

class EnumDict(dict):
    """Track enum member order and ensure member names are not reused.

    EnumType will use the names found in self._member_names as the
    enumeration member names.
    """
    def __init__(self, cls_name, settings, start, constructor_init, constructor_start, constructor_boundary):
        super(EnumDict, self).__init__()
        self._cls_name = cls_name
        self._constructor_init = constructor_init
        self._constructor_start = constructor_start
        self._constructor_boundary = constructor_boundary
        self._generate_next_value = None
        self._member_names = []
        self._member_names_set = set()
        self._settings = settings
        self._addvalue = addvalue = AddValue in settings
        self._magicvalue = MagicValue in settings
        self._multivalue = MultiValue in settings
        if self._addvalue and self._magicvalue:
            raise TypeError('%r: AddValue and MagicValue are mutually exclusive' % cls_name)
        if self._multivalue and self._magicvalue:
            raise TypeError('%r: MultiValue and MagicValue are mutually exclusive' % cls_name)
        self._start = start
        self._addvalue_value = start
        self._new_args = ()
        self._auto_args = False
        # when the magic turns off
        self._locked = MagicValue not in settings
        # if init fields are specified
        self._init = []
        # list of temporary names
        self._ignore = []
        if self._magicvalue:
            self._ignore = ['property', 'staticmethod', 'classmethod']
        self._ignore_init_done = False
        # if _sunder_ values can be changed via the class body
        self._allow_init = True
        self._last_values = []
        self._auto_called = False

    def __getitem__(self, key):
        if key == self._cls_name and self._cls_name not in self:
            return enum
        elif (
                self._locked
                or key in self
                or key in self._ignore
                or is_sunder(key)
                or is_dunder(key)
                ):
            return super(EnumDict, self).__getitem__(key)
        elif self._magicvalue:
            value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:])
            self.__setitem__(key, value)
            return value
        else:
            raise Exception('Magic is not set -- why am I here?')

    def __setitem__(self, key, value):
        """Changes anything not sundured, dundered, nor a descriptor.

        If an enum member name is used twice, an error is raised; duplicate
        values are not checked for.

        Single underscore (sunder) names are reserved.
        """
        # Flag classes that have MagicValue and __new__ will get a generated _gnv_
        #
        # if auto() is used in a tuple, auto_store becomes False
        auto_store = True
        if is_internal_class(self._cls_name, value):
            pass
        elif is_private_name(self._cls_name, key):
            pass
        elif is_sunder(key):
            if key not in (
                    '_init_', '_settings_', '_order_', '_ignore_', '_start_',
                    '_create_pseudo_member_', '_create_pseudo_member_values_',
                    '_generate_next_value_', '_boundary_', '_numeric_repr_',
                    '_missing_', '_missing_value_', '_missing_name_',
                    '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
                    ):
                raise ValueError('%r: _sunder_ names, such as %r, are reserved for future Enum use'
                        % (self._cls_name, key)
                        )
            elif not self._allow_init and key not in (
                    'create_pseudo_member_', '_missing_', '_missing_value_', '_missing_name_',
                ):
                # sunder is used during creation, must be specified first
                raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key))
            elif key == '_ignore_':
                if self._ignore_init_done:
                    raise TypeError('%r: ignore can only be specified once' % self._cls_name)
                if isinstance(value, basestring):
                    value = value.split()
                else:
                    value = list(value)
                self._ignore = value
                already = set(value) & self._member_names_set
                if already:
                    raise ValueError('%r: _ignore_ cannot specify already set names %s' % (
                            self._cls_name,
                            ', '.join(repr(a) for a in already)
                            ))
                self._ignore_init_done = True
            elif key == '_boundary_':
                if self._constructor_boundary:
                    raise TypeError('%r: boundary specified in constructor and class body' % self._cls_name)
            elif key == '_start_':
                if self._constructor_start:
                    raise TypeError('%r: start specified in constructor and class body' % self._cls_name)
                self._start = value
            elif key == '_settings_':
                if not isinstance(value, (set, tuple)):
                    value = (value, )
                if not isinstance(value, set):
                    value = set(value)
                self._settings |= value
                if NoAlias in value and Unique in value:
                    raise TypeError('%r: NoAlias and Unique are mutually exclusive' % self._cls_name)
                elif MultiValue in value and NoAlias in value:
                    raise TypeError('cannot specify both MultiValue and NoAlias' % self._cls_name)
                allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue'])
                for arg in self._settings:
                    if arg not in allowed_settings:
                        raise TypeError('%r: unknown qualifier %r (from %r)' % (self._cls_name, arg, value))
                    allowed_settings[arg] = True
                self._multivalue = allowed_settings['multivalue']
                self._addvalue = allowed_settings['addvalue']
                self._magicvalue = allowed_settings['magicvalue']
                self._locked = not self._magicvalue
                if self._magicvalue and not self._ignore_init_done:
                    self._ignore = ['property', 'classmethod', 'staticmethod']
                if self._addvalue and self._init and 'value' not in self._init:
                    self._init.insert(0, 'value')
                value = tuple(self._settings)
            elif key == '_init_':
                if self._constructor_init:
                    raise TypeError('%r: init specified in constructor and in class body' % self._cls_name)
                _init_ = value
                if isinstance(_init_, basestring):
                    _init_ = _init_.replace(',',' ').split()
                if self._addvalue and 'value' not in self._init:
                    self._init.insert(0, 'value')
                if self._magicvalue:
                    raise TypeError("%r: _init_ and MagicValue are mutually exclusive" % self._cls_name)
                self._init = _init_
                value = _init_
            elif key == '_generate_next_value_':
                # check if members already defined as auto()
                if self._auto_called:
                    raise TypeError("_generate_next_value_ must be defined before members")
                gnv = value
                if value is not None:
                    if isinstance(value, staticmethod):
                        gnv = value.__func__
                    elif isinstance(value, classmethod):
                        raise TypeError('%r: _generate_next_value must be a staticmethod, not a classmethod' % self._cls_name)
                    else:
                        gnv = value
                        value = staticmethod(value)
                    self._auto_args = _check_auto_args(value)
                setattr(self, '_generate_next_value', gnv)
        elif is_dunder(key):
            if key == '__order__':
                key = '_order_'
                if not self._allow_init:
                    # _order_ is used during creation, must be specified first
                    raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key))
            elif key == '__new__':  # and self._new_to_init:
                if isinstance(value, staticmethod):
                    value = value.__func__
                self._new_args = _EnumArgSpec(value)
            elif key == '__init_subclass__':
                if not isinstance(value, classmethod):
                    value = classmethod(value)
            if is_descriptor(value):
                self._locked = True
        elif key in self._member_names_set:
            # descriptor overwriting an enum?
            raise TypeError('%r: attempt to reuse name: %r' % (self._cls_name, key))
        elif key in self._ignore:
            pass
        elif not is_descriptor(value):
            self._allow_init = False
            if key in self:
                # enum overwriting a descriptor?
                raise TypeError('%r: %s already defined as %r' % (self._cls_name, key, self[key]))
            if type(value) is enum:
                value.name = key
                if self._addvalue:
                    raise TypeError('%r: enum() and AddValue are incompatible' % self._cls_name)
            elif self._addvalue and not self._multivalue:
                # generate a value
                value = self._gnv(key, value)
            elif self._multivalue:
                # make sure it's a tuple
                if not type(value) is tuple:
                    value = (value, )
                if isinstance(value[0], auto):
                    value = (self._convert_auto(key, value[0]), ) + value[1:]
                if self._addvalue:
                    value = self._gnv(key, value)
            elif isinstance(value, auto):
                value = self._convert_auto(key, value)
            elif type(value) is tuple and any(isinstance(v, auto) for v in value):
                # insist on an actual tuple, no subclasses, in keeping with only supporting
                # top-level auto() usage (not contained in any other data structure)
                auto_valued = []
                for v in value:
                    if isinstance(v, auto):
                        auto_store = False
                        v = self._convert_auto(key, v)
                        v = v.value
                        self._last_values.append(v)
                    auto_valued.append(v)
                value = tuple(auto_valued)
            elif not isinstance(value, auto):
                # call generate maybe if
                # - init is specified; or
                # - __new__ is specified;
                # and either of them call for more values than are present
                new_args = () or self._new_args and self._new_args.required
                target_len = len(self._init or new_args)
                if isinstance(value, tuple):
                    source_len = len(value)
                else:
                    source_len = 1
                multi_args = len(self._init) > 1 or new_args
                if source_len < target_len :
                    value = self._gnv(key, value)
            else:
                pass
            if self._init:
                if isinstance(value, auto):
                    test_value = value.args
                elif not isinstance(value, tuple):
                    test_value = (value, )
                else:
                    test_value = value
                if len(self._init) != len(test_value):
                    raise TypeError(
                            '%s.%s: number of fields provided do not match init [%r != %r]'
                            % (self._cls_name, key, self._init, test_value)
                            )
            self._member_names.append(key)
            self._member_names_set.add(key)
        else:
            # not a new member, turn off the autoassign magic
            self._locked = True
            self._allow_init = False
        if not (is_sunder(key) or is_dunder(key) or is_private_name(self._cls_name, key) or is_descriptor(value)):
            if not auto_store:
                # reset for next pass
                auto_store = True
            elif isinstance(value, auto):
                self._last_values.append(value.value)
            elif type(value) is tuple:
                if value:
                    if isinstance(value[0], auto):
                        self._last_values.append(value[0].value)
                    else:
                        self._last_values.append(value[0])
            else:
                self._last_values.append(value)
        super(EnumDict, self).__setitem__(key, value)

    def _convert_auto(self, key, value):
        # if auto.args or auto.kwds, compare to _init_ and __new__ -- if lacking, call gnv
        # if not auto.args|kwds but auto.value is _auto_null -- call gnv
        if value.args or value.kwds or value.value is _auto_null:
            if value.args or value.kwds:
                values = value.args
            else:
                values = ()
            new_args = () or self._new_args and self._new_args.required
            target_len = len(self._init or new_args) or 1
            if type(values) is tuple:
                source_len = len(values)
            else:
                source_len = 1
            multi_args = len(self._init) > 1 or new_args
            if source_len < target_len :
                values = self._gnv(key, values)
                self._auto_called = True
                if value.args:
                    value._args = values
                else:
                    value.value = values
        return value

    def _gnv(self, key, value):
        # generate a value
        if self._auto_args:
            if not isinstance(value, tuple):
                value = (value, )
            value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:], *value)
        else:
            value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:])
        if isinstance(value, tuple) and len(value) == 1:
            value = value[0]
        return value


no_arg = SentinelType('no_arg', (type, ), {})
class EnumType(type):
    """Metaclass for Enum"""

    @classmethod
    def __prepare__(metacls, cls, bases, init=None, start=None, settings=(), boundary=None, **kwds):
        metacls._check_for_existing_members_(cls, bases)
        if Flag is None and cls == 'Flag':
            initial_flag = True
        else:
            initial_flag = False
        # settings are a combination of current and all past settings
        constructor_init = init is not None
        constructor_start = start is not None
        constructor_boundary = boundary is not None
        if not isinstance(settings, tuple):
            settings = settings,
        settings = set(settings)
        generate = None
        order = None
        # inherit previous flags
        member_type, first_enum = metacls._get_mixins_(cls, bases)
        if first_enum is not None:
            generate = getattr(first_enum, '_generate_next_value_', None)
            generate = getattr(generate, 'im_func', generate)
            settings |= metacls._get_settings_(bases)
            init = init or first_enum._auto_init_[:]
            order = first_enum._order_function_
            if start is None:
                start = first_enum._start_
        else:
            # first time through -- creating Enum itself
            start = 1
        # check for custom settings
        if AddValue in settings and init and 'value' not in init:
            if isinstance(init, list):
                init.insert(0, 'value')
            else:
                init = 'value ' + init
        if NoAlias in settings and Unique in settings:
            raise TypeError('%r: NoAlias and Unique are mutually exclusive' % cls)
        if MultiValue in settings and NoAlias in settings:
            raise TypeError('%r: MultiValue and NoAlias are mutually exclusive' % cls)
        allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue'])
        for arg in settings:
            if arg not in allowed_settings:
                raise TypeError('%r: unknown qualifier %r' % (cls, arg))
        enum_dict = EnumDict(cls_name=cls, settings=settings, start=start, constructor_init=constructor_init, constructor_start=constructor_start, constructor_boundary=constructor_boundary)
        enum_dict._member_type = member_type
        enum_dict._base_type = ('enum', 'flag')[
                Flag is None and cls == 'Flag'
                or
                Flag is not None and any(issubclass(b, Flag) for b in bases)
                ]
        if Flag is not None and any(b is Flag for b in bases) and member_type not in (baseinteger + (object, )):
            if Flag in bases:
                # when a non-int data type is mixed in with Flag, we end up
                # needing two values for two `__new__`s:
                # - the integer value for the Flag itself; and
                # - the mix-in value for the mix-in
                #
                # we provide a default `_generate_next_value_` to supply the int
                # argument, and a default `__new__` to keep the two straight
                def _generate_next_value_(name, start, count, values, *args, **kwds):
                    return (2 ** count, ) + args
                enum_dict['_generate_next_value_'] = staticmethod(_generate_next_value_)
                def __new__(cls, flag_value, type_value):
                    obj = member_type.__new__(cls, type_value)
                    obj._value_ = flag_value
                    return obj
                enum_dict['__new__'] = __new__
            else:
                try:
                    enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
                except TypeError:
                    pass
        elif not initial_flag:
            if hasattr(first_enum, '__new_member__'):
                enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
            if generate:
                enum_dict['_generate_next_value_'] = generate
                enum_dict._inherited_gnv = True
            if init is not None:
                if isinstance(init, basestring):
                    init = init.replace(',',' ').split()
                enum_dict._init = init
        elif hasattr(first_enum, '__new_member__'):
            enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__)
        if order is not None:
            enum_dict['_order_'] = staticmethod(order)
        return enum_dict

    def __init__(cls, *args , **kwds):
        pass

    def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=(), boundary=None, **kwds):
        # handle py2 case first
        if type(clsdict) is not EnumDict:
            # py2 and/or functional API gyrations
            init = clsdict.pop('_init_', None)
            start = clsdict.pop('_start_', None)
            settings = clsdict.pop('_settings_', ())
            _order_ = clsdict.pop('_order_', clsdict.pop('__order__', None))
            _ignore_ = clsdict.pop('_ignore_', None)
            _create_pseudo_member_ = clsdict.pop('_create_pseudo_member_', None)
            _create_pseudo_member_values_ = clsdict.pop('_create_pseudo_member_values_', None)
            _generate_next_value_ = clsdict.pop('_generate_next_value_', None)
            _missing_ = clsdict.pop('_missing_', None)
            _missing_value_ = clsdict.pop('_missing_value_', None)
            _missing_name_ = clsdict.pop('_missing_name_', None)
            _boundary_ = clsdict.pop('_boundary_', None)
            _iter_member_ = clsdict.pop('_iter_member_', None)
            _iter_member_by_value_ = clsdict.pop('_iter_member_by_value_', None)
            _iter_member_by_def_ = clsdict.pop('_iter_member_by_def_', None)
            __new__ = clsdict.pop('__new__', None)
            __new__ = getattr(__new__, 'im_func', __new__)
            __new__ = getattr(__new__, '__func__', __new__)
            enum_members = dict([
                    (k, v) for (k, v) in clsdict.items()
                    if not (is_sunder(k) or is_dunder(k) or is_private_name(cls, k) or is_descriptor(v))
                    ])
            original_dict = clsdict
            clsdict = metacls.__prepare__(cls, bases, init=init, start=start)
            if settings:
                clsdict['_settings_'] = settings
            init = init or clsdict._init
            if _order_ is None:
                _order_ = clsdict.get('_order_')
                if _order_ is not None:
                    _order_ = _order_.__get__(cls)
            if isinstance(original_dict, OrderedDict):
                calced_order = original_dict
            elif _order_ is None:
                calced_order = [name for (name, value) in enumsort(list(enum_members.items()))]
            elif isinstance(_order_, basestring):
                calced_order = _order_ = _order_.replace(',', ' ').split()
            elif callable(_order_):
                if init:
                    if not isinstance(init, basestring):
                        init = ' '.join(init)
                member = NamedTuple('member', init and 'name ' + init or ['name', 'value'])
                calced_order = []
                for name, value in enum_members.items():
                    if init:
                        if not isinstance(value, tuple):
                            value = (value, )
                        name_value = (name, ) + value
                    else:
                        name_value = tuple((name, value))
                    if member._defined_len_ != len(name_value):
                        raise TypeError('%d values expected (%s), %d received (%s)' % (
                            member._defined_len_,
                            ', '.join(member._fields_),
                            len(name_value),
                            ', '.join([repr(v) for v in name_value]),
                            ))
                    calced_order.append(member(*name_value))
                calced_order = [m.name for m in sorted(calced_order, key=_order_)]
            else:
                calced_order = _order_
            for name in (
                    '_missing_', '_missing_value_', '_missing_name_',
                    '_ignore_', '_create_pseudo_member_', '_create_pseudo_member_values_',
                    '_generate_next_value_', '_order_', '__new__',
                    '_missing_', '_missing_value_', '_missing_name_',
                    '_boundary_',
                    '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
                ):
                attr = locals()[name]
                if attr is not None:
                    clsdict[name] = attr
            # now add members
            for k in calced_order:
                try:
                    clsdict[k] = original_dict[k]
                except KeyError:
                    # this error will be handled when _order_ is checked
                    pass
            for k, v in original_dict.items():
                if k not in calced_order:
                    clsdict[k] = v
            del _order_, _ignore_, _create_pseudo_member_, _create_pseudo_member_values_,
            del _generate_next_value_, _missing_, _missing_value_, _missing_name_
        #
        # resume normal path
        clsdict._locked = True
        #
        # check for illegal enum names (any others?)
        member_names = clsdict._member_names
        invalid_names = set(member_names) & set(['mro', ''])
        if invalid_names:
            raise ValueError('invalid enum member name(s): %s' % (
                ', '.join(invalid_names), ))
        _order_ = clsdict.pop('_order_', None)
        if isinstance(_order_, basestring):
            _order_ = _order_.replace(',',' ').split()
        init = clsdict._init
        start = clsdict._start
        settings = clsdict._settings
        creating_init = []
        new_args = clsdict._new_args
        auto_args = clsdict._auto_args
        auto_init = False
        if init is not None:
            auto_init = True
            creating_init = init[:]
        if 'value' in creating_init and creating_init[0] != 'value':
            raise TypeError("'value', if specified, must be the first item in 'init'")
        magicvalue = MagicValue in settings
        multivalue = MultiValue in settings
        noalias = NoAlias in settings
        unique = Unique in settings
        # an Enum class cannot be mixed with other types (int, float, etc.) if
        #   it has an inherited __new__ unless a new __new__ is defined (or
        #   the resulting class will fail).
        # an Enum class is final once enumeration items have been defined;
        #
        # remove any keys listed in _ignore_
        clsdict.setdefault('_ignore_', []).append('_ignore_')
        ignore = clsdict['_ignore_']
        for key in ignore:
            clsdict.pop(key, None)
        #
        boundary = boundary or clsdict.pop('_boundary_', None)
        _gnv = clsdict.get('_generate_next_value_')
        if _gnv is not None and type(_gnv) is not staticmethod:
            _gnv = staticmethod(_gnv)
        # convert to regular dict
        clsdict = dict(clsdict.items())
        if _gnv is not None:
            clsdict['_generate_next_value_'] = _gnv
        member_type, first_enum = metacls._get_mixins_(cls, bases)
        # get the method to create enum members
        __new__, save_new, new_uses_args = metacls._find_new_(
                clsdict,
                member_type,
                first_enum,
                )
        clsdict['_new_member_'] = staticmethod(__new__)
        clsdict['_use_args_'] = new_uses_args
        #
        # convert future enum members into temporary _proto_members
        for name in member_names:
            value = clsdict[name]
            clsdict[name] = _proto_member(value)
        #
        # temp stuff
        clsdict['_creating_init_'] = creating_init
        clsdict['_multivalue_'] = multivalue
        clsdict['_magicvalue_'] = magicvalue
        clsdict['_noalias_'] = noalias
        clsdict['_unique_'] = unique
        #
        # house-keeping structures
        clsdict['_member_names_'] = []
        clsdict['_member_map_'] = OrderedDict()
        clsdict['_member_type_'] = member_type
        clsdict['_value2member_map_'] = {}
        clsdict['_value2member_seq_'] = ()
        clsdict['_settings_'] = settings
        clsdict['_start_'] = start
        clsdict['_auto_init_'] = init
        clsdict['_new_args_'] = new_args
        clsdict['_auto_args_'] = auto_args
        clsdict['_order_function_'] = None
        # now set the __repr__ for the value
        clsdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)
        #
        # Flag structures (will be removed if final class is not a Flag
        clsdict['_boundary_'] = (
                boundary
                or getattr(first_enum, '_boundary_', None)
                )
        clsdict['_flag_mask_'] = 0
        clsdict['_singles_mask_'] = 0
        clsdict['_all_bits_'] = 0
        clsdict['_inverted_'] = None
        #
        # move skipped values out of the descriptor
        for name, obj in clsdict.items():
            if isinstance(obj, nonmember):
                clsdict[name] = obj.value
        #
        # If a custom type is mixed into the Enum, and it does not know how
        # to pickle itself, pickle.dumps will succeed but pickle.loads will
        # fail.  Rather than have the error show up later and possibly far
        # from the source, sabotage the pickle protocol for this class so
        # that pickle.dumps also fails.
        #
        # However, if the new class implements its own __reduce_ex__, do not
        # sabotage -- it's on them to make sure it works correctly.  We use
        # __reduce_ex__ instead of any of the others as it is preferred by
        # pickle over __reduce__, and it handles all pickle protocols.
        unpicklable = False
        if '__reduce_ex__' not in clsdict:
            if member_type is not object:
                methods = ('__getnewargs_ex__', '__getnewargs__',
                        '__reduce_ex__', '__reduce__')
                if not any(m in member_type.__dict__ for m in methods):
                    make_class_unpicklable(clsdict)
                    unpicklable = True
        #
        # create a default docstring if one has not been provided
        if '__doc__' not in clsdict:
            clsdict['__doc__'] = 'An enumeration.'
        #
        # create our new Enum type
        try:
            exc = None
            enum_class = type.__new__(metacls, cls, bases, clsdict)
        except RuntimeError as e:
            # any exceptions raised by _proto_member (aka member.__new__) will get converted to
            # a RuntimeError, so get that original exception back and raise
            # it instead
            exc = e.__cause__ or e
        if exc is not None:
            raise exc
        #
        # if Python 3.5 or ealier, implement the __set_name__ and
        # __init_subclass__ protocols
        if pyver < PY3_6:
            for name in member_names:
                enum_class.__dict__[name].__set_name__(enum_class, name)
            for name, obj in enum_class.__dict__.items():
                if name in member_names:
                    continue
                if hasattr(obj, '__set_name__'):
                    obj.__set_name__(enum_class, name)
            if Enum is not None and hasattr(enum_class, '__init_subclass__'):
                super(enum_class, enum_class).__init_subclass__()
        #
        # double check that repr and friends are not the mixin's or various
        # things break (such as pickle)
        #
        # Also, special handling for ReprEnum
        if ReprEnum is not None and ReprEnum in bases:
            if member_type is object:
                raise TypeError(
                        'ReprEnum subclasses must be mixed with a data type (i.e.'
                        ' int, str, float, etc.)'
                        )
            if '__format__' not in clsdict:
                enum_class.__format__ = member_type.__format__
                clsdict['__format__'] = enum_class.__format__
            if '__str__' not in clsdict:
                method = member_type.__str__
                if method is object.__str__:
                    # if member_type does not define __str__, object.__str__ will use
                    # its __repr__ instead, so we'll also use its __repr__
                    method = member_type.__repr__
                enum_class.__str__ = method
                clsdict['__str__'] = enum_class.__str__

        for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'):
            if name in clsdict:
                # class has defined/imported/copied the method
                continue
            class_method = getattr(enum_class, name)
            obj_method = getattr(member_type, name, None)
            enum_method = getattr(first_enum, name, None)
            if obj_method is not None and obj_method == class_method:
                if name == '__reduce_ex__' and unpicklable:
                    continue
                setattr(enum_class, name, enum_method)
                clsdict[name] = enum_method
        #
        # for Flag, add __or__, __and__, __xor__, and __invert__
        if Flag is not None and issubclass(enum_class, Flag):
            for name in (
                    '__or__', '__and__', '__xor__',
                    '__ror__', '__rand__', '__rxor__',
                    '__invert__'
                ):
                if name not in clsdict:
                    setattr(enum_class, name, getattr(Flag, name))
                    clsdict[name] = enum_method
        #
        # method resolution and int's are not playing nice
        # Python's less than 2.6 use __cmp__
        if pyver < PY2_6:
            #
            if issubclass(enum_class, int):
                setattr(enum_class, '__cmp__', getattr(int, '__cmp__'))
            #
        elif PY2:
            #
            if issubclass(enum_class, int):
                for method in (
                        '__le__',
                        '__lt__',
                        '__gt__',
                        '__ge__',
                        '__eq__',
                        '__ne__',
                        '__hash__',
                        ):
                    setattr(enum_class, method, getattr(int, method))
        #
        # replace any other __new__ with our own (as long as Enum is not None,
        # anyway) -- again, this is to support pickle
        if Enum is not None:
            # if the user defined their own __new__, save it before it gets
            # clobbered in case they subclass later
            if save_new:
                setattr(enum_class, '__new_member__', enum_class.__dict__['__new__'])
            setattr(enum_class, '__new__', Enum.__dict__['__new__'])
        #
        # _order_ checking is spread out into three/four steps
        # - ensure _order_ is a list, not a string nor a function
        # - if enum_class is a Flag:
        #   - remove any non-single-bit flags from _order_
        # - remove any aliases from _order_
        # - check that _order_ and _member_names_ match
        #
        # _order_ step 1: ensure _order_ is a list
        if _order_:
            if isinstance(_order_, staticmethod):
                _order_ = _order_.__func__
            if callable(_order_):
                # save order for future subclasses
                enum_class._order_function_ = staticmethod(_order_)
                # create ordered list for comparison
                _order_ = [m.name for m in sorted(enum_class, key=_order_)]
        #
        # remove Flag structures if final class is not a Flag
        if (
                Flag is None and cls != 'Flag'
                or Flag is not None and not issubclass(enum_class, Flag)
            ):
            delattr(enum_class, '_boundary_')
            delattr(enum_class, '_flag_mask_')
            delattr(enum_class, '_singles_mask_')
            delattr(enum_class, '_all_bits_')
            delattr(enum_class, '_inverted_')
        elif Flag is not None and issubclass(enum_class, Flag):
            # set correct __iter__
            if [m._value_ for m in enum_class] != sorted([m._value_ for m in enum_class]):
                enum_class._iter_member_ = enum_class._iter_member_by_def_
            if _order_:
                # _order_ step 2: remove any items from _order_ that are not single-bit
                _order_ = [
                        o
                        for o in _order_
                        if o not in enum_class._member_map_ or is_single_bit(enum_class[o]._value_)
                        ]
        #
        # check for constants with auto() values
        for k, v in enum_class.__dict__.items():
            if isinstance(v, constant) and isinstance(v.value, auto):
                v.value = enum_class(v.value.value)
        #
        if _order_:
            # _order_ step 3: remove aliases from _order_
            _order_ = [
                    o
                    for o in _order_
                    if (
                        o not in enum_class._member_map_
                        or
                        (o in enum_class._member_map_ and o in enum_class._member_names_)
                        )]
            # _order_ step 4: verify that _order_ and _member_names_ match
            if _order_ != enum_class._member_names_:
                raise TypeError(
                        'member order does not match _order_:\n%r\n%r'
                        % (enum_class._member_names_, _order_)
                        )
        return enum_class

    def __bool__(cls):
        """
        classes/types should always be True.
        """
        return True

    def __call__(cls, value=no_arg, names=None, module=None, qualname=None, type=None, start=1, boundary=None):
        """Either returns an existing member, or creates a new enum class.

        This method is used both when an enum class is given a value to match
        to an enumeration member (i.e. Color(3)) and for the functional API
        (i.e. Color = Enum('Color', names='red green blue')).

        When used for the functional API: `module`, if set, will be stored in
        the new class' __module__ attribute; `type`, if set, will be mixed in
        as the first base class.

        Note: if `module` is not set this routine will attempt to discover the
        calling module by walking the frame stack; if this is unsuccessful
        the resulting class will not be pickleable.
        """
        if names is None:  # simple value lookup
            return cls.__new__(cls, value)
        # otherwise, functional API: we're creating a new Enum type
        return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start, boundary=boundary)

    def __contains__(cls, value):
        """Return True if `value` is in `cls`.

        `value` is in `cls` if:
        1) `value` is a member of `cls`, or
        2) `value` is the value of one of the `cls`'s members.
        """
        if isinstance(value, cls):
            return True
        try:
            return value in cls._value2member_map_
        except TypeError:
            return value in [v for v,m in cls._value2member_seq_]

    def __delattr__(cls, attr):
        # nicer error message when someone tries to delete an attribute
        # (see issue19025).
        if attr in cls._member_map_:
            raise AttributeError(
                    "%s: cannot delete Enum member %r." % (cls.__name__, attr),
                    )
        found_attr = get_attr_from_chain(cls, attr)
        if isinstance(found_attr, constant):
            raise AttributeError(
                    "%s: cannot delete constant %r" % (cls.__name__, attr),
                    )
        elif isinstance(found_attr, property):
            raise AttributeError(
                    "%s: cannot delete property %r" % (cls.__name__, attr),
                    )
        type.__delattr__(cls, attr)

    def __dir__(cls):
        interesting = set(cls._member_names_ + [
                    '__class__', '__contains__', '__doc__', '__getitem__',
                    '__iter__', '__len__', '__members__', '__module__',
                    '__name__',
                    ])
        if cls._new_member_ is not object.__new__:
            interesting.add('__new__')
        if cls.__init_subclass__ is not Enum.__init_subclass__:
            interesting.add('__init_subclass__')
        if hasattr(object, '__qualname__'):
            interesting.add('__qualname__')
        for method in ('__init__', '__format__', '__repr__', '__str__'):
            if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)):
                interesting.add(method)
        if cls._member_type_ is object:
            return sorted(interesting)
        else:
            # return whatever mixed-in data type has
            return sorted(set(dir(cls._member_type_)) | interesting)

    @bltin_property
    def __members__(cls):
        """Returns a mapping of member name->value.

        This mapping lists all enum members, including aliases. Note that this
        is a copy of the internal mapping.
        """
        return cls._member_map_.copy()

    def __getitem__(cls, name):
        try:
            return cls._member_map_[name]
        except KeyError:
            exc = _sys.exc_info()[1]
        if Flag is not None and issubclass(cls, Flag) and '|' in name:
            try:
                # may be an __or__ed name
                result = cls(0)
                for n in name.split('|'):
                    result |= cls[n]
                return result
            except KeyError:
                raise exc
        result = cls._missing_name_(name)
        if isinstance(result, cls):
            return result
        else:
            raise exc

    def __iter__(cls):
        return (cls._member_map_[name] for name in cls._member_names_)

    def __reversed__(cls):
        return (cls._member_map_[name] for name in reversed(cls._member_names_))

    def __len__(cls):
        return len(cls._member_names_)

    __nonzero__ = __bool__

    def __repr__(cls):
        return "<aenum %r>" % (cls.__name__, )

    def __setattr__(cls, name, value):
        """Block attempts to reassign Enum members/constants.

        A simple assignment to the class namespace only changes one of the
        several possible ways to get an Enum member from the Enum class,
        resulting in an inconsistent Enumeration.
        """
        member_map = cls.__dict__.get('_member_map_', {})
        if name in member_map:
            raise AttributeError(
                    '%s: cannot rebind member %r.' % (cls.__name__, name),
                    )
        found_attr = get_attr_from_chain(cls, name)
        if isinstance(found_attr, constant):
            raise AttributeError(
                    "%s: cannot rebind constant %r" % (cls.__name__, name),
                    )
        elif isinstance(found_attr, (bltin_property, property)) and not isinstance(value, property):
            raise AttributeError(
                    "%s: cannot rebind property %r" % (cls.__name__, name),
                    )
        type.__setattr__(cls, name, value)

    def _convert(cls, *args, **kwds):
        import warnings
        warnings.warn("_convert is deprecated and will be removed, use"
                      " _convert_ instead.", DeprecationWarning, stacklevel=2)
        return cls._convert_(*args, **kwds)

    def _convert_(cls, name, module, filter, source=None, boundary=None, as_global=False):
        """
        Create a new Enum subclass that replaces a collection of global constants
        """
        # convert all constants from source (or module) that pass filter() to
        # a new Enum called name, and export the enum and its members back to
        # module;
        # also, replace the __reduce_ex__ method so unpickling works in
        # previous Python versions
        module_globals = vars(_sys.modules[module])
        if source:
            source = vars(source)
        else:
            source = module_globals
        members = [(key, source[key]) for key in source.keys() if filter(key)]
        try:
            # sort by value, name
            members.sort(key=lambda t: (t[1], t[0]))
        except TypeError:
            # unless some values aren't comparable, in which case sort by just name
            members.sort(key=lambda t: t[0])
        cls = cls(name, members, module=module, boundary=boundary or KEEP)
        cls.__reduce_ex__ = _reduce_ex_by_name
        if as_global:
            global_enum(cls)
        else:
            module_globals.update(cls.__members__)
        module_globals[name] = cls
        return cls

    def _create_(cls, class_name, names, module=None, qualname=None, type=None, start=1, boundary=None):
        """Convenience method to create a new Enum class.

        `names` can be:

        * A string containing member names, separated either with spaces or
          commas.  Values are auto-numbered from 1.
        * An iterable of member names.  Values are auto-numbered from 1.
        * An iterable of (member name, value) pairs.
        * A mapping of member name -> value.
        """
        if PY2:
            # if class_name is unicode, attempt a conversion to ASCII
            if isinstance(class_name, unicode):
                try:
                    class_name = class_name.encode('ascii')
                except UnicodeEncodeError:
                    raise TypeError('%r is not representable in ASCII' % (class_name, ))
        metacls = cls.__class__
        if type is None:
            bases = (cls, )
        else:
            bases = (type, cls)
        _, first_enum = cls._get_mixins_(class_name, bases)
        generate = getattr(first_enum, '_generate_next_value_', None)
        generate = getattr(generate, 'im_func', generate)
        # special processing needed for names?
        if isinstance(names, basestring):
            names = names.replace(',', ' ').split()
        if isinstance(names, (tuple, list)) and names and isinstance(names[0], basestring):
            original_names, names = names, []
            last_values = []
            for count, name in enumerate(original_names):
                value = generate(name, start, count, last_values[:])
                last_values.append(value)
                names.append((name, value))
        # Here, names is either an iterable of (name, value) or a mapping.
        item = None  # in case names is empty
        clsdict = None
        for item in names:
            if clsdict is None:
                # first time initialization
                if isinstance(item, basestring):
                    clsdict = {}
                else:
                    # remember the order
                    clsdict = metacls.__prepare__(class_name, bases)
            if isinstance(item, basestring):
                member_name, member_value = item, names[item]
            else:
                member_name, member_value = item
            clsdict[member_name] = member_value
        if clsdict is None:
            # in case names was empty
            clsdict = metacls.__prepare__(class_name, bases)
        enum_class = metacls.__new__(metacls, class_name, bases, clsdict, boundary=boundary)
        # TODO: replace the frame hack if a blessed way to know the calling
        # module is ever developed
        if module is None:
            try:
                module = _sys._getframe(2).f_globals['__name__']
            except (AttributeError, KeyError):
                pass
        if module is None:
            make_class_unpicklable(enum_class)
        else:
            enum_class.__module__ = module
        if qualname is not None:
            enum_class.__qualname__ = qualname
        return enum_class

    @classmethod
    def _check_for_existing_members_(mcls, class_name, bases):
        if Enum is None:
            return
        for chain in bases:
            for base in chain.__mro__:
                if issubclass(base, Enum) and base._member_names_:
                    raise TypeError(
                            "<aenum %r> cannot extend %r"
                            % (class_name, base)
                            )
    @classmethod
    def _get_mixins_(mcls, class_name, bases):
        """Returns the type for creating enum members, and the first inherited
        enum class.

        bases: the tuple of bases that was given to __new__
        """
        if not bases or Enum is None:
            return object, Enum

        mcls._check_for_existing_members_(class_name, bases)

        # ensure final parent class is an Enum derivative, find any concrete
        # data type, and check that Enum has no members
        first_enum = bases[-1]
        if first_enum in stdlib_enums:
            first_enum = bases[-2]
        if not issubclass(first_enum, Enum):
            raise TypeError("new enumerations should be created as "
                    "`EnumName([mixin_type, ...] [data_type,] enum_type)`")
        member_type = mcls._find_data_type_(class_name, bases) or object
        if first_enum._member_names_:
            raise TypeError("cannot extend enumerations via subclassing")
        #
        return member_type, first_enum

    @classmethod
    def _find_data_repr_(mcls, class_name, bases):
        for chain in bases:
            for base in chain.__mro__:
                if base in ((object, ) + stdlib_enums):
                    continue
                elif isinstance(base, EnumType):
                    # if we hit an Enum, use it's _value_repr_
                    return base._value_repr_
                elif '__repr__' in base.__dict__:
                    # this is our data repr
                    # double-check if a dataclass with a default __repr__
                    if (
                            '__dataclass_fields__' in base.__dict__
                            and '__dataclass_params__' in base.__dict__
                            and base.__dict__['__dataclass_params__'].repr
                        ):
                        return _dataclass_repr
                    else:
                        return base.__dict__['__repr__']
        return None

    @classmethod
    def _find_data_type_(mcls, class_name, bases):
        data_types = set()
        for chain in bases:
            candidate = None
            for base in chain.__mro__:
                if base in ((object, ) + stdlib_enums):
                    continue
                elif isinstance(base, EnumType):
                    if base._member_type_ is not object:
                        data_types.add(base._member_type_)
                        break
                elif '__new__' in base.__dict__ or '__dataclass_fields__' in base.__dict__:
                    if isinstance(base, EnumType):
                        continue
                    elif StdlibFlag is not None and issubclass(base, StdlibFlag):
                        continue
                    data_types.add(candidate or base)
                    break
                else:
                    candidate = candidate or base
        if len(data_types) > 1:
            raise TypeError('%r: too many data types: %r' % (class_name, data_types))
        elif data_types:
            return data_types.pop()
        else:
            return None

    @staticmethod
    def _get_settings_(bases):
        """Returns the combined _settings_ of all Enum base classes

        bases: the tuple of bases given to __new__
        """
        settings = set()
        for chain in bases:
            for base in chain.__mro__:
                if issubclass(base, Enum):
                    for s in base._settings_:
                        settings.add(s)
        return settings

    @classmethod
    def _find_new_(mcls, clsdict, member_type, first_enum):
        """Returns the __new__ to be used for creating the enum members.

        clsdict: the class dictionary given to __new__
        member_type: the data type whose __new__ will be used by default
        first_enum: enumeration to check for an overriding __new__
        """
        # now find the correct __new__, checking to see of one was defined
        # by the user; also check earlier enum classes in case a __new__ was
        # saved as __new_member__
        __new__ = clsdict.get('__new__', None)
        #
        # should __new__ be saved as __new_member__ later?
        save_new = first_enum is not None and __new__ is not None
        #
        if __new__ is None:
            # check all possibles for __new_member__ before falling back to
            # __new__
            for method in ('__new_member__', '__new__'):
                for possible in (member_type, first_enum):
                    target = getattr(possible, method, None)
                    if target not in (
                            None,
                            None.__new__,
                            object.__new__,
                            Enum.__new__,
                            StdlibEnum.__new__,
                            ):
                        __new__ = target
                        break
                if __new__ is not None:
                    break
            else:
                __new__ = object.__new__
        # if a non-object.__new__ is used then whatever value/tuple was
        # assigned to the enum member name will be passed to __new__ and to the
        # new enum member's __init__
        if first_enum is None or __new__ in (Enum.__new__, object.__new__):
            new_uses_args = False
        else:
            new_uses_args = True
        #
        return __new__, save_new, new_uses_args


    # In order to support Python 2 and 3 with a single
    # codebase we have to create the Enum methods separately
    # and then use the `type(name, bases, dict)` method to
    # create the class.

if StdlibEnumMeta:
    class EnumType(EnumType, StdlibEnumMeta):
        pass
EnumMeta = EnumType

enum_dict = _Addendum(
        dict=EnumType.__prepare__('Enum', (object, )),
        doc="Generic enumeration.\n\n    Derive from this class to define new enumerations.\n\n",
        ns=globals(),
        )

@enum_dict
@classmethod
def __signature__(cls):
    if cls._member_names_:
        return '(*values)'
    else:
        return '(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)'

@enum_dict
def __init__(self, *args, **kwds):
    # auto-init method
    _auto_init_ = self._auto_init_
    if _auto_init_ is None:
        return
    if 'value' in _auto_init_:
        # remove 'value' from _auto_init_ as it has already been handled
        _auto_init_ = _auto_init_[1:]
    if _auto_init_:
        if len(_auto_init_) < len(args):
            raise TypeError('%d arguments expected (%s), %d received (%s)'
                    % (len(_auto_init_), _auto_init_, len(args), args))
        for name, arg in zip(_auto_init_, args):
            setattr(self, name, arg)
        if len(args) < len(_auto_init_):
            remaining_args = _auto_init_[len(args):]
            for name in remaining_args:
                value = kwds.pop(name, undefined)
                if value is undefined:
                    raise TypeError('missing value for: %r' % (name, ))
                setattr(self, name, value)
            if kwds:
                # too many keyword arguments
                raise TypeError('invalid keyword(s): %s' % ', '.join(kwds.keys()))

@enum_dict
def __new__(cls, value):
    # all enum instances are actually created during class construction
    # without calling this method; this method is called by the metaclass'
    # __call__ (i.e. Color(3) ), and by pickle
    if NoAlias in cls._settings_:
        raise TypeError('NoAlias enumerations cannot be looked up by value')
    if type(value) is cls:
        # For lookups like Color(Color.red)
        # value = value.value
        return value
    # by-value search for a matching enum member
    # see if it's in the reverse mapping (for hashable values)
    try:
        return cls._value2member_map_[value]
    except KeyError:
        # Not found, no need to do long O(n) search
        pass
    except TypeError:
        # not there, now do long search -- O(n) behavior
        for member_value, member in cls._value2member_seq_:
            if member_value == value:
                return member
    # still not found -- try _missing_ hook
    result = cls._missing_value_(value)
    if isinstance(result, cls):
        return result
    elif result is not None and getattr(cls, '_boundary_', None) is EJECT:
        return result
    else:
        if result is None:
            if value is no_arg:
                raise ValueError('%s() should be called with a value' % (cls.__name__, ))
            else:
                raise ValueError("%r is not a valid %s" % (value, cls.__name__))
        else:
            raise TypeError(
                    'error in %s._missing_: returned %r instead of None or a valid member'
                    % (cls.__name__, result)
                    )

@enum_dict
@classmethod
def __init_subclass__(cls, **kwds):
    if pyver < PY3_6:
        # end of the line
        if kwds:
            raise TypeError('unconsumed keyword arguments: %r' % (kwds, ))
    else:
        super(Enum, cls).__init_subclass__(**kwds)

@enum_dict
@staticmethod
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
    for last_value in reversed(last_values):
        try:
            new_value = last_value + 1
            break
        except TypeError:
            pass
    else:
        new_value = start
    if args:
        return (new_value, ) + args
    else:
        return new_value

@enum_dict
@classmethod
def _missing_(cls, value):
    "deprecated, use _missing_value_ instead"
    return None

@enum_dict
@classmethod
def _missing_value_(cls, value):
    "used for failed value access"
    return cls._missing_(value)

@enum_dict
@classmethod
def _missing_name_(cls, name):
    "used for failed item access"
    return None

@enum_dict
def __repr__(self):
    v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__
    return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_))

@enum_dict
def __str__(self):
    return "%s.%s" % (self.__class__.__name__, self._name_)

if PY3:
    @enum_dict
    def __dir__(self):
        """
        Returns all members and all public methods
        """
        if self.__class__._member_type_ is object:
            interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__'])
        else:
            interesting = set(n for n in object.__dir__(self) if n not in self._member_map_)
        for name in getattr(self, '__dict__', []):
            if name[0] != '_':
                interesting.add(name)
        for cls in self.__class__.mro():
            for name, obj in cls.__dict__.items():
                if name[0] == '_':
                    continue
                if isinstance(obj, property):
                    # that's an enum.property
                    if obj.fget is not None or name not in self._member_map_:
                        interesting.add(name)
                    else:
                        # in case it was added by `dir(self)`
                        interesting.discard(name)
                elif not isinstance(obj, self.__class__):
                    interesting.add(name)
        return sorted(interesting)

@enum_dict
def __format__(self, format_spec):
    return str.__format__(str(self), format_spec)

@enum_dict
def __hash__(self):
    return hash(self._name_)

@enum_dict
def __reduce_ex__(self, proto):
    return self.__class__, (self._value_, )

@enum_dict
def __le__(self, other):
    raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__))

@enum_dict
def __lt__(self, other):
    raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__))

@enum_dict
def __ge__(self, other):
    raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__))

@enum_dict
def __gt__(self, other):
    raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__))


@enum_dict
def __eq__(self, other):
    if isinstance(other, self.__class__):
        return self is other
    return NotImplemented

@enum_dict
def __ne__(self, other):
    if isinstance(other, self.__class__):
        return self is not other
    return NotImplemented


    # enum.property is used to provide access to the `name`, `value', etc.,
    # properties of enum members while keeping some measure of protection
    # from modification, while still allowing for an enumeration to have
    # members named `name`, `value`, etc..  This works because enumeration
    # members are not set directly on the enum class -- enum.property will
    # look them up in _member_map_.

@enum_dict
@property
def name(self):
    return self._name_

@enum_dict
@property
def value(self):
    return self._value_

@enum_dict
@property
def values(self):
    return self._values_

_enum_base = StdlibEnum or object
Enum = EnumType('Enum', (_enum_base, ), enum_dict.resolve())
del enum_dict

    # Enum has now been created

def pickle_by_global_name(self, proto):
    # should not be used with Flag-type enums
    return self.name
_reduce_ex_by_name = pickle_by_global_name

def pickle_by_enum_name(self, proto):
    # should not be used with Flag-type enums
    return getattr, (self.__class__, self._name_)

def _dataclass_repr(self):
    dcf = self.__dataclass_fields__
    return ', '.join(
            '%s=%r' % (k, getattr(self, k))
            for k in dcf.keys()
            if dcf[k].repr
            )

# ReprEnum
if StdlibReprEnum:
    _repr_bases = Enum, StdlibReprEnum
else:
    _repr_bases = (Enum, )
ReprEnum = EnumType('ReprEnum', _repr_bases, {
        '__doc__': "Only changes the repr(), leaving str() and format() to the mixed-in type."
        })

# IntEnum

class IntEnum(int, ReprEnum):
    """
    Enum where members are also (and must be) ints
    """


# StrEnums

class StrEnum(str, ReprEnum):
    """
    Enum where members are also (and must already be) strings

    default value is member name, lower-cased
    """

    def __new__(cls, *values, **kwds):
        if kwds:
            raise TypeError('%r: keyword arguments not supported' % (cls.__name__))
        if values:
            if not isinstance(values[0], str):
                raise TypeError('%s: values must be str [%r is a %r]' % (cls.__name__, values[0], type(values[0])))
        value = str(*values)
        member = str.__new__(cls, value)
        member._value_ = value
        return member

    __str__ = str.__str__

    def _generate_next_value_(name, start, count, last_values):
        """
        Return the lower-cased version of the member name.
        """
        return name.lower()


class LowerStrEnum(StrEnum):
    """
    Enum where members are also (and must already be) lower-case strings

    default value is member name, lower-cased
    """

    def __new__(cls, value, *args, **kwds):
        obj = StrEnum.__new_member__(cls, value, *args, **kwds)
        if value != value.lower():
            raise ValueError('%r is not lower-case' % value)
        return obj


class UpperStrEnum(StrEnum):
    """
    Enum where members are also (and must already be) upper-case strings

    default value is member name, upper-cased
    """

    def __new__(cls, value, *args, **kwds):
        obj = StrEnum.__new_member__(cls, value, *args, **kwds)
        if value != value.upper():
            raise ValueError('%r is not upper-case' % value)
        return obj

    def _generate_next_value_(name, start, count, last_values, *args, **kwds):
        return name.upper()


# Specialty Enums
if PY3:
    class AutoEnum(Enum):
        """
        automatically use _generate_next_value_ when values are missing (Python 3 only)
        """
        _settings_ = MagicValue
    __all__.append('AutoEnum')


class AutoNumberEnum(Enum):
    """
    Automatically assign increasing values to members.

    Py3: numbers match creation order
    Py2: numbers are assigned alphabetically by member name
         (unless `_order_` is specified)
    """

    def __new__(cls, *args, **kwds):
        value = len(cls.__members__) + 1
        if cls._member_type_ is int:
            obj = int.__new__(cls, value)
        elif cls._member_type_ is long:
            obj = long.__new__(cls, value)
        else:
            obj = object.__new__(cls)
        obj._value_ = value
        return obj


class AddValueEnum(Enum):
    _settings_ = AddValue


class MultiValueEnum(Enum):
    """
    Multiple values can map to each member.
    """
    _settings_ = MultiValue


class NoAliasEnum(Enum):
    """
    Duplicate value members are distinct, but cannot be looked up by value.
    """
    _settings_ = NoAlias


class OrderedEnum(Enum):
    """
    Add ordering based on values of Enum members.
    """

    def __ge__(self, other):
        if self.__class__ is other.__class__:
            return self._value_ >= other._value_
        return NotImplemented

    def __gt__(self, other):
        if self.__class__ is other.__class__:
            return self._value_ > other._value_
        return NotImplemented

    def __le__(self, other):
        if self.__class__ is other.__class__:
            return self._value_ <= other._value_
        return NotImplemented

    def __lt__(self, other):
        if self.__class__ is other.__class__:
            return self._value_ < other._value_
        return NotImplemented


if sqlite3:
    class SqliteEnum(Enum):
        def __conform__(self, protocol):
            if protocol is sqlite3.PrepareProtocol:
                return self.name


class UniqueEnum(Enum):
    """
    Ensure no duplicate values exist.
    """
    _settings_ = Unique


def convert(enum, name, module, filter, source=None):
    """
    Create a new Enum subclass that replaces a collection of global constants

    enum: Enum, IntEnum, ...
    name: name of new Enum
    module: name of module (__name__ in global context)
    filter: function that returns True if name should be converted to Enum member
    source: namespace to check (defaults to 'module')
    """
    # convert all constants from source (or module) that pass filter() to
    # a new Enum called name, and export the enum and its members back to
    # module;
    # also, replace the __reduce_ex__ method so unpickling works in
    # previous Python versions
    module_globals = vars(_sys.modules[module])
    if source:
        source = vars(source)
    else:
        source = module_globals
    members = dict((name, value) for name, value in source.items() if filter(name))
    enum = enum(name, members, module=module)
    enum.__reduce_ex__ = _reduce_ex_by_name
    module_globals.update(enum.__members__)
    module_globals[name] = enum

def extend_enum(enumeration, name, *args, **kwds):
    """
    Add a new member to an existing Enum.
    """
    # there are four possibilities:
    # - extending an aenum Enum or 3.11+ enum Enum
    # - extending an aenum Flag or 3.11+ enum Flag
    # - extending a pre-3.11 stdlib Enum Flag
    # - extending a 3.11+ stdlib Flag
    #
    # fail early if name is already in the enumeration
    if (
            name in enumeration.__dict__
            or name in enumeration._member_map_
            or name in [t[1] for t in getattr(enumeration, '_value2member_seq_', ())]
        ):
        raise TypeError('%r already in use as %r' % (name, enumeration.__dict__.get(name, enumeration[name])))
    # and check for other instances in parent classes
    descriptor = None
    for base in enumeration.__mro__[1:]:
        descriptor = base.__dict__.get(name)
        if descriptor is not None:
            if isinstance(descriptor, (property, DynamicClassAttribute)):
                break
            else:
                raise TypeError('%r already in use in superclass %r' % (name, base.__name__))
    try:
        _member_map_ = enumeration._member_map_
        _member_names_ = enumeration._member_names_
        _member_type_ = enumeration._member_type_
        _value2member_map_ = enumeration._value2member_map_
        base_attributes = set([a for b in enumeration.mro() for a in b.__dict__])
    except AttributeError:
        raise TypeError('%r is not a supported Enum' % (enumeration, ))
    try:
        _value2member_seq_ = enumeration._value2member_seq_
        _multi_value_ = MultiValue in enumeration._settings_
        _no_alias_ = NoAlias in enumeration._settings_
        _unique_ = Unique in enumeration._settings_
        _auto_init_ = enumeration._auto_init_ or []
    except AttributeError:
        # standard Enum
        _value2member_seq_ = []
        _multi_value_ = False
        _no_alias_ = False
        _unique_ = False
        _auto_init_ = []
    if _multi_value_ and not args:
        # must specify values for multivalue enums
        raise ValueError('no values specified for MultiValue enum %r' % enumeration.__name__)
    mt_new = _member_type_.__new__
    _new = getattr(enumeration, '_new_member_', None) or getattr(enumeration, '__new_member__', None) or mt_new
    if not args:
        last_values = [m.value for m in enumeration]
        count = len(enumeration)
        start = getattr(enumeration, '_start_', None)
        if start is None:
            start = last_values and (last_values[-1] + 1) or 1
        _gnv = getattr(enumeration, '_generate_next_value_', None)
        if _gnv is not None:
            args = ( _gnv(name, start, count, last_values), )
        else:
            # must be a 3.4 or 3.5 Enum
            args = (start, )
    if _new is object.__new__:
        new_uses_args = False
    else:
        new_uses_args = True
    if len(args) == 1:
        [value] = args
    else:
        value = args
    more_values = ()
    kwds = {}
    if isinstance(value, enum):
        args = value.args
        kwds = value.kwds
    if not isinstance(value, tuple):
        args = (value, )
    else:
        args = value
    # tease value out of auto-init if specified
    if 'value' in _auto_init_:
        if 'value' in kwds:
            value = kwds.pop('value')
        else:
            value, args = args[0], args[1:]
    elif _multi_value_:
        value, more_values, args = args[0], args[1:], ()
        if new_uses_args:
            args = (value, )
    if _member_type_ is tuple:
        args = (args, )
    if not new_uses_args:
        new_member = _new(enumeration)
        if not hasattr(new_member, '_value_'):
            new_member._value_ = value
    else:
        new_member = _new(enumeration, *args, **kwds)
        if not hasattr(new_member, '_value_'):
            new_member._value_ = _member_type_(*args)
    value = new_member._value_
    if _multi_value_:
        if 'value' in _auto_init_:
            args = more_values
        else:
        # put all the values back into args for the init call
            args = (value, ) + more_values
    new_member._name_ = name
    new_member.__objclass__ = enumeration.__class__
    new_member.__init__(*args)
    new_member._values_ = (value, ) + more_values
    new_member._sort_order_ = len(enumeration._member_names_)
    # do final checks before modifying enum structures:
    # - is new member a flag?
    #   - does the new member fit in the enum's declared _boundary_?
    # - is new member an alias?
    #
    _all_bits_ = _flag_mask_ = None
    if hasattr(enumeration, '_all_bits_'):
        _all_bits_ = enumeration._all_bits_ | value
        _flag_mask_ = enumeration._flag_mask_ | value
        if enumeration._boundary_ != 'keep':
            missed = list(_iter_bits_lsb(_flag_mask_ & ~_all_bits_))
            if missed:
                raise TypeError(
                        'invalid Flag %r -- missing values: %s'
                        % (cls, ', '.join((str(i) for i in missed)))
                        )
    # If another member with the same value was already defined, the
    # new member becomes an alias to the existing one.
    if _no_alias_:
        # unless NoAlias was specified
        return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_)
    else:
        # handle "normal" aliases
        new_values = new_member._values_
        for canonical_member in _member_map_.values():
            canonical_values_ = getattr(canonical_member, '_values_', [canonical_member._value_])
            for canonical_value in canonical_values_:
                for new_value in new_values:
                    if canonical_value == new_value:
                        # name is an alias
                        if _unique_ or _multi_value_:
                            # aliases not allowed in Unique and MultiValue enums
                            raise ValueError('%r is a duplicate of %r' % (new_member, canonical_member))
                        else:
                            # aliased name can be added, remaining checks irrelevant
                            # aliases don't appear in member names (only in __members__ and _member_map_).
                            return _finalize_extend_enum(enumeration, canonical_member, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
        # not a standard alias, but maybe a flag alias
        if pyver < PY3_6:
            flag_bases = Flag,
        else:
            flag_bases = Flag, StdlibFlag
        if issubclass(enumeration, flag_bases) and hasattr(enumeration, '_all_bits_'):
            # handle the new flag type
            if is_single_bit(value):
                # a new member!  (an aliase would have been discovered in the previous loop)
                return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_)
            else:
                # might be an 3.11 Flag alias
                if value & enumeration._flag_mask_ == value and _value2member_map_.get(value) is not None:
                    # yup, it's an alias to existing members... and its an alias of an alias
                    canonical = _value2member_map_.get(value)
                    return _finalize_extend_enum(enumeration, canonical, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
                else:
                    return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_, is_alias=True)
        else:
            # if we get here, we have a brand new member
            return _finalize_extend_enum(enumeration, new_member)

def _finalize_extend_enum(enumeration, new_member, name=None, bits=None, mask=None, is_alias=False):
    name = name or new_member.name
    descriptor = None
    for base in enumeration.__mro__[1:]:
        descriptor = base.__dict__.get(name)
        if descriptor is not None:
            if isinstance(descriptor, (property, DynamicClassAttribute)):
                break
            else:
                raise TypeError('%r already in use in superclass %r' % (name, base.__name__))
    if not descriptor:
        # get redirect in place before adding to _member_map_
        redirect = property()
        redirect.__set_name__(enumeration, name)
        setattr(enumeration, name, redirect)
    if not is_alias:
        enumeration._member_names_.append(name)
    enumeration._member_map_[name] = new_member
    for v in getattr(new_member, '_values_', [new_member._value_]):
        try:
            enumeration._value2member_map_[v] = new_member
        except TypeError:
            enumeration._value2member_seq_ += ((v, new_member), )
    if bits:
        enumeration._all_bits_ = bits
        enumeration._flag_mask_ = mask
        if is_single_bit(new_member._value_):
            enumeration._singles_mask_ |= new_member._value_
    return new_member

def unique(enumeration):
    """
    Class decorator that ensures only unique members exist in an enumeration.
    """
    duplicates = []
    for name, member in enumeration.__members__.items():
        if name != member.name:
            duplicates.append((name, member.name))
    if duplicates:
        duplicate_names = ', '.join(
                ["%s -> %s" % (alias, name) for (alias, name) in duplicates]
                )
        raise ValueError('duplicate names found in %r: %s' %
                (enumeration, duplicate_names)
                )
    return enumeration

# Flag

try:
    from enum import FlagBoundary
except ImportError:
    class FlagBoundary(StrEnum):
        """
        control how out of range values are handled
        "strict" -> error is raised  [default]
        "conform" -> extra bits are discarded
        "eject" -> lose flag status (becomes a normal integer)
        """
        STRICT = auto()
        CONFORM = auto()
        EJECT = auto()
        KEEP = auto()
export(FlagBoundary, globals())

if StdlibFlag:
    _flag_bases = Enum, StdlibFlag
else:
    _flag_bases = (Enum, )

flag_dict = _Addendum(
        dict=EnumType.__prepare__('Flag', _flag_bases),
        doc="Generic flag enumeration.\n\nDerive from this class to define new flag enumerations.",
        ns=globals(),
        )

flag_dict['_boundary_'] = STRICT
flag_dict['_numeric_repr_'] = repr

@flag_dict
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
    """
    Generate the next value when not given.

    name: the name of the member
    start: the initital start value or None
    count: the number of existing members
    last_value: the last value assigned or None
    """
    if not count:
        if args:
            return ((1, start)[start is not None], ) + args
        else:
            return (1, start)[start is not None]
    else:
        last_value = max(last_values)
        try:
            high_bit = _high_bit(last_value)
            result = 2 ** (high_bit+1)
            if args:
                return (result,)  + args
            else:
                return result
        except Exception:
            pass
        raise TypeError('invalid Flag value: %r' % last_value)

@flag_dict
@classmethod
def _iter_member_by_value_(cls, value):
    """
    Extract all members from the value in definition (i.e. increasing value) order.
    """
    for val in _iter_bits_lsb(value & cls._singles_mask_):
        yield cls._value2member_map_.get(val)

flag_dict['_iter_member_'] = _iter_member_by_value_

@flag_dict
@classmethod
def _iter_member_by_def_(cls, value):
    """
    Extract all members from the value in definition order.
    """
    members = list(cls._iter_member_by_value_(value))
    members.sort(key=lambda m: m._sort_order_)
    for member in members:
        yield member

@flag_dict
@classmethod
def _missing_(cls, value):
    """
    return a member matching the given value, or None
    """
    return cls._create_pseudo_member_(value)

@flag_dict
@classmethod
def _create_pseudo_member_(cls, *values):
    """
    Create a composite member.
    """
    # if we get here, no exact match was found
    # STRICT - must be composed of single-bit flags
    value = error_value = values[0]
    if not isinstance(value, baseinteger):
        raise ValueError(
                "%r is not a valid %s" % (error_value, cls.__name__)
                )
    # check boundaries
    # - value must be in range (e.g. -16 <-> +15, i.e. ~15 <-> 15)
    # - value must not include any skipped flags (e.g. if bit 2 is not
    #   defined, then 0d10 is invalid)
    flag_mask = cls._flag_mask_
    singles_mask = cls._singles_mask_
    all_bits = cls._all_bits_
    unknown_bits = all_bits ^ flag_mask
    unnamed_bits = all_bits & ~flag_mask
    boundary = cls._boundary_
    neg_value = (None, value)[value < 0]
    #
    if neg_value:
        if neg_value <= ~all_bits:
            if boundary is EJECT:
                return error_value
            elif boundary is KEEP:
                value = 2**(neg_value.bit_length()) + neg_value
            elif boundary is CONFORM:
                value = (2**(neg_value.bit_length()) + neg_value) & flag_mask
            else:
                raise ValueError(
                        "%r is not a valid %s" % (error_value, cls.__name__)
                        )
        else:
            value = all_bits + 1 + neg_value
    #
    member_value = value & singles_mask                     # all named single-bit values
    unnamed_value = value & flag_mask & ~singles_mask       # all unnamed single-bit values
    unknown_value = value & ~flag_mask
    #
    if boundary is EJECT:
        if unknown_value:
            return error_value
    elif boundary is CONFORM:
        unknown_value = 0
    elif boundary is STRICT:
        if unknown_value:
            raise ValueError(
                "%r is not a valid %s" % (error_value, cls.__name__)
                    )
    #
    members = list(cls._iter_member_(member_value))
    final_value = member_value
    still_unknown = 0
    if unnamed_value:
        found = 0
        for n, pm in cls._member_map_.items():
            if (
                    pm not in members
                and pm._value_
                and pm._value_ & final_value == pm._value_
                and pm._value_ & unnamed_value
                ):
                members.append(pm)
                final_value |= pm._value_
                found |= pm._value_
        # anything still unnamed becomes unknown
        still_unknown = (found & ~unnamed_value) ^ unnamed_value
        if still_unknown:
            if boundary is KEEP:
                pass
            elif boundary is CONFORM:
                still_unknown = 0
            elif boundary is EJECT:
                return error_value
            else: # strict
                raise ValueError(
                    "%r is not a valid %s" % (error_value, cls.__name__)
                        )
    unknown_value |= still_unknown
    final_value |= unknown_value
    values = (final_value, ) + values[1:]
    members.sort(key=lambda m: m._sort_order_)
    # let class adjust values
    values = cls._create_pseudo_member_values_(members, *values)
    __new__ = getattr(cls, '__new_member__', None)
    if cls._member_type_ is object and not __new__:
        # construct a singleton enum pseudo-member
        pseudo_member = object.__new__(cls)
    else:
        pseudo_member = (__new__ or cls._member_type_.__new__)(cls, *values)
    if not hasattr(pseudo_member, '_value_'):
        pseudo_member._value_ = final_value
    if members:
        pseudo_member._name_ = '|'.join([m._name_ for m in members])
        if unknown_value:
            pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown_value)
    else:
        pseudo_member._name_ = None
    # use setdefault in case another thread already created a composite
    # with this value, but only if all members are known
    # note: zero is a special case -- always add it
    pseudo_member = cls._value2member_map_.setdefault(final_value, pseudo_member)
    if neg_value is not None:
        cls._value2member_map_[neg_value] = pseudo_member
    return pseudo_member

@flag_dict
@classmethod
def _create_pseudo_member_values_(cls, members, *values):
    """
    Return values to be fed to __new__ to create new member.
    """
    if cls._member_type_ in (baseinteger + (object, )):
        return values
    elif len(values) < 2:
        return values + (cls._member_type_(), )
    else:
        return values

@flag_dict
def __contains__(self, other):
    """
    Returns True if self has at least the same flags set as other.
    """
    if not isinstance(other, self.__class__):
        raise TypeError(
            "unsupported operand type(s) for 'in': '%s' and '%s'" % (
                type(other).__name__, self.__class__.__name__))
    if other._value_ == 0 or self._value_ == 0:
        return False
    return other._value_ & self._value_ == other._value_

@flag_dict
def __iter__(self):
    """
    Returns flags in definition order.
    """
    for member in self._iter_member_(self._value_):
        yield member

@flag_dict
def __len__(self):
    return bit_count(self._value_)

@flag_dict
def __repr__(self):
    cls = self.__class__
    if self._name_ is None:
        # only zero is unnamed by default
        return '<%s: %r>' % (cls.__name__, self._value_)
    else:
        return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_)

@flag_dict
def __str__(self):
    cls = self.__class__
    if self._name_ is None:
        return '%s(%s)' % (cls.__name__, self._value_)
    else:
        return '%s.%s' % (cls.__name__, self._name_)

if PY2:
    @flag_dict
    def __nonzero__(self):
        return bool(self._value_)
else:
    @flag_dict
    def __bool__(self):
        return bool(self._value_)

@flag_dict
def __or__(self, other):
    if isinstance(other, self.__class__):
        other_value = other._value_
    elif self._member_type_ is not object and isinstance(other, self._member_type_):
        other_value = other
    else:
        return NotImplemented
    return self.__class__(self._value_ | other_value)

@flag_dict
def __and__(self, other):
    if isinstance(other, self.__class__):
        other_value = other._value_
    elif self._member_type_ is not object and isinstance(other, self._member_type_):
        other_value = other
    else:
        return NotImplemented
    return self.__class__(self._value_ & other_value)

@flag_dict
def __xor__(self, other):
    if isinstance(other, self.__class__):
        other_value = other._value_
    elif self._member_type_ is not object and isinstance(other, self._member_type_):
        other_value = other
    else:
        return NotImplemented
    return self.__class__(self._value_ ^ other_value)

@flag_dict
def __invert__(self):
    if self._inverted_ is None:
        self._inverted_ = self.__class__(self._singles_mask_ & ~self._value_)
        if isinstance(self._inverted_, self.__class__):
            self._inverted_._inverted_ = self
    return self._inverted_

flag_dict['__ror__'] = __or__
flag_dict['__rand__'] = __and__
flag_dict['__rxor__'] = __xor__

Flag = EnumType('Flag', _flag_bases, flag_dict.resolve())
del(flag_dict)

# IntFlag

class IntFlag(int, ReprEnum, Flag):
    "Support for integer-based Flags"

    _boundary_ = KEEP

    def __contains__(self, other):
        """
        Returns True if self has at least the same flags set as other.
        """
        if isinstance(other, int):
            other = self.__class__(other)
        elif not isinstance(other, self.__class__):
            raise TypeError(
                "unsupported operand type(s) for 'in': '%s' and '%s'" % (
                    type(other).__name__, self.__class__.__name__))
        if other._value_ == 0 or self._value_ == 0:
            return False
        return other._value_ & self._value_ == other._value_


# helpers

def _high_bit(value):
    """returns index of highest bit, or -1 if value is zero or negative"""
    return value.bit_length() - 1

def global_enum_repr(self):
    """
    use module.enum_name instead of class.enum_name

    the module is the last module in case of a multi-module name
    """
    module = self.__class__.__module__.split('.')[-1]
    return '%s.%s' % (module, self._name_)

def global_flag_repr(self):
    """
    use module.flag_name instead of class.flag_name

    the module is the last module in case of a multi-module name
    """
    module = self.__class__.__module__.split('.')[-1]
    cls_name = self.__class__.__name__
    if self._name_ is None:
        return "%s.%s(%r)" % (module, cls_name, self._value_)
    if is_single_bit(self):
        return '%s.%s' % (module, self._name_)
    if self._boundary_ is not FlagBoundary.KEEP:
        return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
    else:
        name = []
        for n in self._name_.split('|'):
            if n[0].isdigit():
                name.append(n)
            else:
                name.append('%s.%s' % (module, n))
        return '|'.join(name)

def global_str(self):
    """
    use enum_name instead of class.enum_name
    """
    if self._name_ is None:
        cls_name = self.__class__.__name__
        return "%s(%r)" % (cls_name, self._value_)
    else:
        return self._name_

def global_enum(cls, update_str=False):
    """
    decorator that makes the repr() of an enum member reference its module
    instead of its class; also exports all members to the enum's module's
    global namespace
    """
    if issubclass(cls, Flag):
        cls.__repr__ = global_flag_repr
    else:
        cls.__repr__ = global_enum_repr
    if not issubclass(cls, ReprEnum) or update_str:
        cls.__str__ = global_str
    _sys.modules[cls.__module__].__dict__.update(cls.__members__)
    return cls

if StdlibEnumMeta:

    from _weakrefset import WeakSet

    def __subclasscheck__(cls, subclass):
        """
        Override for issubclass(subclass, cls).
        """
        if not isinstance(subclass, type):
            raise TypeError('issubclass() arg 1 must be a class (got %r)' % (subclass, ))
        # Check cache
        try:
            cls.__dict__['_subclass_cache_']
        except KeyError:
            cls._subclass_cache_ = WeakSet()
            cls._subclass_negative_cache_ = WeakSet()
        except RecursionError:
            import sys
            exc, cls, tb = sys.exc_info()
            exc = RecursionError('possible causes for endless recursion:\n    - __getattribute__ is not ignoring __dunder__ attibutes\n    - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n    see `aenum.remove_stdlib_integration` for temporary work-around')
            raise_from_none(exc)
        if subclass in cls._subclass_cache_:
            return True
        # Check negative cache
        elif subclass in cls._subclass_negative_cache_:
            return False
        if cls is subclass:
            cls._subclass_cache_.add(subclass)
            return True
        # Check if it's a direct subclass
        if cls in getattr(subclass, '__mro__', ()):
            cls._subclass_cache_.add(subclass)
            return True
        # Check if it's an aenum.Enum|IntEnum|IntFlag|Flag subclass
        if cls is StdlibIntFlag and issubclass(subclass, IntFlag):
            cls._subclass_cache_.add(subclass)
            return True
        elif cls is StdlibFlag and issubclass(subclass, Flag):
            cls._subclass_cache_.add(subclass)
            return True
        elif cls is StdlibIntEnum and issubclass(subclass, IntEnum):
            cls._subclass_cache_.add(subclass)
            return True
        if cls is StdlibEnum and issubclass(subclass, Enum):
            cls._subclass_cache_.add(subclass)
            return True
        # No dice; update negative cache
        cls._subclass_negative_cache_.add(subclass)
        return False

    def __instancecheck__(cls, instance):
        subclass = instance.__class__
        try:
            return cls.__subclasscheck__(subclass)
        except RecursionError:
            import sys
            exc, cls, tb = sys.exc_info()
            exc = RecursionError('possible causes for endless recursion:\n    - __getattribute__ is not ignoring __dunder__ attibutes\n    - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n    see `aenum.remove_stdlib_integration` for temporary work-around')
            raise_from_none(exc)


def add_stdlib_integration():
    if StdlibEnum:
        StdlibEnumMeta.__subclasscheck__ = __subclasscheck__
        StdlibEnumMeta.__instancecheck__ = __instancecheck__

def remove_stdlib_integration():
    """
    Remove the __instancecheck__ and __subclasscheck__ overrides from the stdlib Enum.

    Those overrides are in place so that code detecting stdlib enums will also detect
    aenum enums.  If a buggy __getattribute__, __instancecheck__, or __subclasscheck__
    is defined on a custom EnumMeta then RecursionErrors can result; using this
    function after importing aenum will solve that problem, but the better solution is
    to fix the buggy method.
    """
    if StdlibEnum:
        del StdlibEnumMeta.__instancecheck__
        del StdlibEnumMeta.__subclasscheck__

class cls2module(object):
    def __init__(self, cls, *args):
        self.__name__ = cls.__name__
        self._parent_module = cls.__module__
        self.__all__ = []
        all_objects = cls.__dict__
        if not args:
            args = [k for k, v in all_objects.items() if isinstance(v, (NamedConstant, Enum))]
        for name in args:
            self.__dict__[name] = all_objects[name]
            self.__all__.append(name)
    def register(self):
        _sys.modules["%s.%s" % (self._parent_module, self.__name__)] = self


 0707010001f437000081a40000000000000000000000016a100daf000014fa000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/foreign/aenum/_constant.py from ._common import *

__all__ = [
       	'NamedConstant', 'Constant',
        ]

# NamedConstant

NamedConstant = None

class NamedConstantDict(dict):
    """Track constant order and ensure names are not reused.

    NamedConstantMeta will use the names found in self._names as the
    Constant names.
    """
    def __init__(self):
        super(NamedConstantDict, self).__init__()
        self._names = []

    def __setitem__(self, key, value):
        """Changes anything not dundered or not a constant descriptor.

        If an constant name is used twice, an error is raised; duplicate
        values are not checked for.

        Single underscore (sunder) names are reserved.
        """
        if is_sunder(key):
            raise ValueError(
                    '_sunder_ names, such as %r, are reserved for future NamedConstant use'
                    % (key, )
                    )
        elif is_dunder(key):
            pass
        elif key in self._names:
            # overwriting an existing constant?
            raise TypeError('attempt to reuse name: %r' % (key, ))
        elif isinstance(value, constant) or not is_descriptor(value):
            if key in self:
                # overwriting a descriptor?
                raise TypeError('%s already defined as: %r' % (key, self[key]))
            self._names.append(key)
        super(NamedConstantDict, self).__setitem__(key, value)


class NamedConstantMeta(type):
    """
    Block attempts to reassign NamedConstant attributes.
    """

    @classmethod
    def __prepare__(metacls, cls, bases, **kwds):
        return NamedConstantDict()

    def __new__(metacls, cls, bases, clsdict):
        if type(clsdict) is dict:
            original_dict = clsdict
            clsdict = NamedConstantDict()
            for k, v in original_dict.items():
                clsdict[k] = v
        newdict = {}
        constants = {}
        for name, obj in clsdict.items():
            if name in clsdict._names:
                constants[name] = obj
                continue
            elif isinstance(obj, nonmember):
                obj = obj.value
            newdict[name] = obj
        newcls = super(NamedConstantMeta, metacls).__new__(metacls, cls, bases, newdict)
        newcls._named_constant_cache_ = {}
        newcls._members_ = {}
        for name, obj in constants.items():
            new_k = newcls.__new__(newcls, name, obj)
            newcls._members_[name] = new_k
        return newcls

    def __bool__(cls):
        return True

    def __delattr__(cls, attr):
        cur_obj = cls.__dict__.get(attr)
        if NamedConstant is not None and isinstance(cur_obj, NamedConstant):
            raise AttributeError('cannot delete constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
        super(NamedConstantMeta, cls).__delattr__(attr)

    def __iter__(cls):
        return (k for k in cls._members_.values())

    def __reversed__(cls):
        return (k for k in reversed(cls._members_.values()))

    def __len__(cls):
        return len(cls._members_)

    __nonzero__ = __bool__

    def __setattr__(cls, name, value):
        """Block attempts to reassign NamedConstants.
        """
        cur_obj = cls.__dict__.get(name)
        if NamedConstant is not None and isinstance(cur_obj, NamedConstant):
            raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
        super(NamedConstantMeta, cls).__setattr__(name, value)

constant_dict = _Addendum(
        dict=NamedConstantMeta.__prepare__('NamedConstant', (object, )),
        doc="NamedConstants protection.\n\n    Derive from this class to lock NamedConstants.\n\n",
        ns=globals(),
        )

@constant_dict
def __new__(cls, name, value=None, doc=None):
    if value is None:
        # lookup, name is value
        value = name
        for name, obj in cls.__dict__.items():
            if isinstance(obj, cls) and obj._value_ == value:
                return obj
        else:
            raise ValueError('%r does not exist in %r' % (value, cls.__name__))
    cur_obj = cls.__dict__.get(name)
    if isinstance(cur_obj, NamedConstant):
        raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_))
    elif isinstance(value, constant):
        doc = doc or value.__doc__
        value = value.value
    metacls = cls.__class__
    if isinstance(value, NamedConstant):
        # constants from other classes are reduced to their actual value
        value = value._value_
    actual_type = type(value)
    value_type = cls._named_constant_cache_.get(actual_type)
    if value_type is None:
        value_type = type(cls.__name__, (cls, type(value)), {})
        cls._named_constant_cache_[type(value)] = value_type
    obj = actual_type.__new__(value_type, value)
    obj._name_ = name
    obj._value_ = value
    obj.__doc__ = doc
    cls._members_[name] = obj
    metacls.__setattr__(cls, name, obj)
    return obj

@constant_dict
def __repr__(self):
    return "<%s.%s: %r>" % (
            self.__class__.__name__, self._name_, self._value_)

@constant_dict
def __reduce_ex__(self, proto):
    return getattr, (self.__class__, self._name_)

NamedConstant = NamedConstantMeta('NamedConstant', (object, ), constant_dict.resolve())
Constant = NamedConstant
del constant_dict


  0707010001f434000081a40000000000000000000000016a100daf000005f5000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/foreign/aenum/LICENSE  Copyright (c) 2015, 2016, 2017, 2018 Ethan Furman.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    Redistributions of source code must retain the above
    copyright notice, this list of conditions and the
    following disclaimer.

    Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials
    provided with the distribution.

    Neither the name Ethan Furman nor the names of any
    contributors may be used to endorse or promote products
    derived from this software without specific prior written
    permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
   0707010001f4c0000081a40000000000000000000000016a100daf00003112000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/foreign/winstats.py    # pylint: skip-file

'''
    Windows Stats - (C) 2013-2017 Mike Miller, fix: Qian Deng
    A simple pip-able Windows status retrieval module with no additional
    dependencies.

    License:
        LGPL, Version 3 (or later)
'''
import ctypes, string
from ctypes import (Structure, Union, WinError, byref, c_double, c_longlong,
                    c_ulong, c_ulonglong, c_size_t, sizeof)
from ctypes.wintypes import HANDLE, LONG, LPCSTR, LPCWSTR, DWORD
from collections import namedtuple

__version__      = '0.70'
LPDWORD = PDWORD = ctypes.POINTER(DWORD)

try:
    unicode
except NameError:
    unicode = str


# Mem Stats-------------------------------------------------------------------
kernel32 = ctypes.windll.kernel32

class MemoryStatusEX(Structure):
    ''' I/O Struct for Windows .GlobalMemoryStatusEx() call.

        Docs:
            http://msdn.microsoft.com/en-us/library/windows/desktop/aa366770
    '''
    _fields_ = [
        ('Length', c_ulong),
        ('MemoryLoad', c_ulong),
        ('TotalPhys', c_ulonglong),
        ('AvailPhys', c_ulonglong),
        ('TotalPageFile', c_ulonglong),
        ('AvailPageFile', c_ulonglong),
        ('TotalVirtual', c_ulonglong),
        ('AvailVirtual', c_ulonglong),
        ('AvailExtendedVirtual', c_ulonglong),
    ]
    def __init__(self):
        # have to initialize this to the size of MemoryStatusEX
        self.Length = sizeof(self)
        super(MemoryStatusEX, self).__init__()


def get_mem_info():
    ''' Returns a Windows Memory Status info object.

        Docs:
            http://msdn.microsoft.com/en-us/library/windows/desktop/aa366589
    '''
    meminfo = MemoryStatusEX()
    kernel32.GlobalMemoryStatusEx(byref(meminfo))
    return meminfo


# Perf Stats -----------------------------------------------------------------
psapi = ctypes.windll.psapi

class PerformanceInfo(Structure):
    ''' I/O struct for Windows .GetPerformanceInfo() call.

        Docs:
            http://msdn.microsoft.com/en-us/library/ms684824
    '''
    _fields_ = [
        ('size',        c_ulong),
        ('CommitTotal', c_size_t),
        ('CommitLimit', c_size_t),
        ('CommitPeak', c_size_t),
        ('PhysicalTotal', c_size_t),
        ('PhysicalAvailable', c_size_t),
        ('SystemCache', c_size_t),
        ('KernelTotal', c_size_t),
        ('KernelPaged', c_size_t),
        ('KernelNonpaged', c_size_t),
        ('PageSize', c_size_t),
        ('HandleCount', c_ulong),
        ('ProcessCount', c_ulong),
        ('ThreadCount', c_ulong),
    ]
    def __init__(self):
        self.size = sizeof(self)
        super(PerformanceInfo, self).__init__()


def get_perf_info():
    ''' Returns a Windows Performance info object.

        Docs:
            http://msdn.microsoft.com/en-us/library/ms683210
        Note:
            Has an extra convenience member: SystemCacheBytes
    '''
    pinfo = PerformanceInfo()
    psapi.GetPerformanceInfo(byref(pinfo), pinfo.size)
    pinfo.SystemCacheBytes = (pinfo.SystemCache * pinfo.PageSize)
    return pinfo


# Disk Stats -----------------------------------------------------------------
_diskusage = namedtuple('disk_usage', 'total used free')

def get_fs_usage(drive):
    ''' Return stats for the given drive.

        Arguments:
            drive       Drive letter.
        Returns:
            A named tuple with total, used, and free members.
        Raises:
            ctypes.WinError
        Recipe:
            http://code.activestate.com/recipes/577972-disk-usage/
    '''
    if len(drive) < 3:
        drive = drive + ':\\'
    _, total, free = c_ulonglong(), c_ulonglong(), c_ulonglong()
    if isinstance(drive, unicode):
        fun = kernel32.GetDiskFreeSpaceExW
    else:
        fun = kernel32.GetDiskFreeSpaceExA

    ret = fun(str(drive), byref(_), byref(total), byref(free))
    if ret == 0:
        raise WinError()
    used = total.value - free.value

    return _diskusage(total.value, used, free.value)


def get_drives():
    ''' Return a list of current drive letters.
        Recipe:
            http://stackoverflow.com/a/827398/450917
    '''
    drives = []
    bitmask = kernel32.GetLogicalDrives()
    try:
        letters = string.uppercase
    except AttributeError as err:  # Py3
        letters = string.ascii_uppercase

    for letter in letters:
        if bitmask & 1:
            drives.append(letter)
        bitmask >>= 1

    return drives


_drive_types = {
    0: 'UNKNOWN',
    1: 'NO_ROOT_DIR',
    2: 'REMOVABLE',
    3: 'FIXED',
    4: 'REMOTE',
    5: 'CDROM',
    6: 'RAMDISK',
}

def get_drive_type(drive):
    ''' Return the type of the given drive, such as
            Fixed, Remote, Optical, etc as a string.

        Docs:
            http://msdn.microsoft.com/en-us/library/windows/desktop/aa364939
    '''
    result = kernel32.GetDriveTypeA(drive)
    return result, _drive_types.get(result, 'UNKNOWN')


_volinfo = namedtuple('vol_info', 'name fstype serialno length flags')

def get_vol_info(drive):
    ''' Retrieve Volume Info for the given drive.

        Docs:
            http://msdn.microsoft.com/en-us/library/windows/desktop/aa364993
        Returns:
            A named tuple containing name and fstype members.
        Note:
            Could use some improvement, such as implementing filesystem flags.
        Recipe:
            http://stackoverflow.com/a/12056414/450917
    '''
    if len(drive) < 3:
        drive = drive + ':\\'
    drive = unicode(drive)
    nameBuf = ctypes.create_unicode_buffer(1024)
    fsTypeBuf = ctypes.create_unicode_buffer(1024)
    serialno = LPDWORD()
    max_component_length = None
    file_system_flags = None

    kernel32.GetVolumeInformationW(
        ctypes.c_wchar_p(drive),
        nameBuf,
        sizeof(nameBuf),
        serialno,
        max_component_length,
        file_system_flags,
        fsTypeBuf,
        sizeof(fsTypeBuf)
    )
    try:                serialno = serialno.contents  # NULL pointer access
    except ValueError:  serialno = None               # not sure what to do
    return _volinfo(nameBuf.value, fsTypeBuf.value, serialno, None, None)


# PerfMon --------------------------------------------------------------------
HQUERY = HCOUNTER = HANDLE
pdh             = ctypes.windll.pdh
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa372637
PDH_FMT_RAW     = 16  # L
PDH_FMT_ANSI    = 32  # L
PDH_FMT_UNICODE = 64  # L
PDH_FMT_LONG    = 256  # L
PDH_FMT_DOUBLE  = 512  # L
PDH_FMT_LARGE   = 1024  # L
PDH_FMT_1000    = 8192  # L
PDH_FMT_NODATA  = 16384  # L
PDH_FMT_NOSCALE = 4096  # L

# http://msdn.microsoft.com/en-us/library/aa373046
_pdh_errcodes = {
    0x00000000: 'PDH_CSTATUS_VALID_DATA',
    0x800007d0: 'PDH_CSTATUS_NO_MACHINE',
    0x800007d2: 'PDH_MORE_DATA',
    0x800007d5: 'PDH_NO_DATA',
    0xc0000bb8: 'PDH_CSTATUS_NO_OBJECT',
    0xc0000bb9: 'PDH_CSTATUS_NO_COUNTER',
    0xc0000bbb: 'PDH_MEMORY_ALLOCATION_FAILURE',
    0xc0000bbc: 'PDH_INVALID_HANDLE',
    0xc0000bbd: 'PDH_INVALID_ARGUMENT',
    0xc0000bc0: 'PDH_CSTATUS_BAD_COUNTERNAME',
    0xc0000bc2: 'PDH_INSUFFICIENT_BUFFER',
    0xc0000bc6: 'PDH_INVALID_DATA',
    0xc0000bd3: 'PDH_NOT_IMPLEMENTED',
    0xc0000bd4: 'PDH_STRING_NOT_FOUND',
}

class PDH_Counter_Union(Union):
    'http://msdn.microsoft.com/en-us/library/windows/desktop/aa373050'
    _fields_ = [
        ('longValue', LONG),
        ('doubleValue', c_double),
        ('largeValue', c_longlong),
        ('ansiValue', LPCSTR),              # aka AnsiString...
        ('unicodeValue', LPCWSTR)           # aka WideString..
    ]

class PDHFmtCounterValue(Structure):
    'http://msdn.microsoft.com/en-us/library/aa373050'
    _fields_ = [
        ('CStatus', DWORD),
        ('union', PDH_Counter_Union),
    ]

def get_pd_err(code):
    'Convert a PDH error code to a human readable string.'
    code &= 2 ** 32 - 1  # signed to unsigned :/
    return _pdh_errcodes.get(code, code)


getfmt = lambda fmt: globals().get('PDH_FMT_' + fmt.upper(), PDH_FMT_LONG)

def get_perf_data(counters, fmts='long', english=False, delay=0):
    ''' Wrap up PerfMon's low-level API.

        Arguments:
            counters        Localized Windows PerfMon counter name, or list of.
            fmts            One of 'long', 'double', 'large', 'ansi', 'unicode'
                            If a list, must match the length of counters.
            english         Add locale-neutral counters in English.
            delay           Some metrics need a second attempt after a delay
                            to acquire (as int ms).
        Returns:
            Tuple of requested counter value(s).
        Raises:
            WindowsError
        Recipes:
            http://msdn.microsoft.com/en-us/library/windows/desktop/aa373214
            http://code.activestate.com/recipes/
                576631-get-cpu-usage-by-using-ctypes-win32-platform/
    '''
    if type(counters) is list:  counters = [ unicode(c)  for c in counters ]
    else:                       counters = [ unicode(counters) ]
    if type(fmts) is list:
        ifmts = [ getfmt(fmt)  for fmt in fmts ]
    else:
        ifmts = [getfmt(fmts)]
        fmts  = [fmts]
    if english:  addfunc = pdh.PdhAddEnglishCounterW
    else:        addfunc = pdh.PdhAddCounterW
    hQuery = HQUERY()
    hCounters = []
    values = []

    # Open Sie, bitte
    errs = pdh.PdhOpenQueryW(None, 0, byref(hQuery))
    if errs:
        pdh.PdhCloseQuery(hQuery)
        raise WindowsError('PdhOpenQueryW failed: %s' % get_pd_err(errs))

    # Add Counter
    for counter in counters:
        hCounter = HCOUNTER()
        errs = addfunc(hQuery, counter, 0, byref(hCounter))
        if errs:
            pdh.PdhCloseQuery(hQuery)
            raise WindowsError('PdhAddCounterW failed: %s' % get_pd_err(errs))
        hCounters.append(hCounter)

    # Collect
    errs = pdh.PdhCollectQueryData(hQuery)
    if errs:
        pdh.PdhCloseQuery(hQuery)
        raise WindowsError('PdhCollectQueryData failed: %s' % get_pd_err(errs))
    if delay:
        kernel32.Sleep(delay)
        errs = pdh.PdhCollectQueryData(hQuery)
        if errs:
            pdh.PdhCloseQuery(hQuery)
            raise WindowsError('PdhCollectQueryData failed: %s' %
                                 get_pd_err(errs))

    # Format
    for i, hCounter in enumerate(hCounters):
        value = PDHFmtCounterValue()
        errs = pdh.PdhGetFormattedCounterValue(hCounter, ifmts[i], None,
                                               byref(value))
        if errs:
            pdh.PdhCloseQuery(hQuery)
            raise WindowsError(('PdhGetFormattedCounterValue failed: %s' %
                                 get_pd_err(errs)))
        values.append(value)

    # Close
    errs = pdh.PdhCloseQuery(hQuery)
    if errs:
        raise WindowsError('PdhCloseQuery failed: %s' % get_pd_err(errs))

    return tuple([ getattr(value.union, fmts[i] + 'Value')  # tuple makes it
                   for i, value in enumerate(values) ])     # possible to use %


# ----------------------------------------------------------------------------

if __name__ == '__main__':

    import locale
    locale.setlocale(locale.LC_ALL, '')
    fmt = lambda x: locale.format('%d', x, True)

    print('Memory Stats:')
    meminfo = get_mem_info()
    print('    Total: %s b' % fmt(meminfo.TotalPhys))
    print('    usage: %s%%' % fmt(meminfo.MemoryLoad))
    print()

    print('Performance Stats:')
    pinfo = get_perf_info()
    print('    Cache: %s p' % fmt(pinfo.SystemCache,))
    print('    Cache: %s b' % fmt(pinfo.SystemCacheBytes))
    print()

    print('Disk Stats:')
    drives = get_drives()
    drive = drives[0]
    print('    Disks:', ', '.join(drives))
    fsinfo = get_fs_usage('%s:\\' % drive)
    vinfo = get_vol_info(drive)
    print('    %s:\\' % drive)
    print('        Name:', vinfo.name, vinfo.serialno)
    print('        Type:', vinfo.fstype)
    print('        Total:', fmt(fsinfo.total))
    print('        Used: ', fmt(fsinfo.used))
    print('        Free: ', fmt(fsinfo.free))
    print()

    print('PerfMon queries:')
    # take a second snapshot 100ms after the first:
    usage = get_perf_data(r'\Processor(_Total)\% Processor Time',
                                   fmts='double', delay=100)
    print('    CPU Usage: %.02f %%' % usage)

    # query multiple at once:
    counters = [ r'\Paging File(_Total)\% Usage', r'\Memory\Available MBytes']
    results = get_perf_data(counters, fmts='double large'.split())
    print('    Pagefile Usage: %.2f %%, Mem Avail: %s MB' % results)

  0707010001f140000041ed00000000000000000000000e6a102a9200000000000000e600010003ffffffffffffffff0000002800000000root/usr/share/opensvc/opensvc/commands   0707010001f14e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/commands/network   0707010001f150000081a40000000000000000000000016a100daf00000734000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/commands/network/parser.py """
The node management command actions and options.
"""
from utilities.storage import Storage
from utilities.optparser import OptParser, Option
from commands.node.parser import GLOBAL_OPT, GLOBAL_OPTS

PROG = "om network"

OPT = Storage({
    "name": Option(
        "--name", action="store", dest="name",
        help="The name of the object."),
    "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:`om node print schedule`."),
})
OPT.update(GLOBAL_OPT)

ACTIONS = {
    "Network actions": {
        "ls": {
            "msg": "List the available networks.",
        },
        "setup": {
            "msg": "Create bridges, assign host address, update host routes to node backend networks. This action is executed on node configuration changes. Useful for troubleshoot.",
        },
        "show": {
            "msg": "Show network configuration.",
            "options": [
                OPT.name,
            ],
        },
        "status": {
            "msg": "Show allocated ip address in networks.",
            "options": [
                OPT.name,
                OPT.verbose,
            ],
        },
    },
}

class NetworkOptParser(OptParser):
    """
    The pool management command 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,
                           global_options=GLOBAL_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent)

0707010001f14f000081a40000000000000000000000016a100daf000004c3000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/commands/network/__init__.py   from __future__ import print_function

import os
import sys

import utilities.render.color
import core.exceptions as ex
from commands.network.parser import NetworkOptParser
from core.node import Node


def _main(node, argv=None):
    optparser = NetworkOptParser(argv)
    options, action = optparser.parse_args(argv)

    utilities.render.color.use_color = options.color
    node.options.update(options.__dict__)

    node.check_privs(action)

    err = 0
    try:
        err = node.action("network_"+action)
    except KeyboardInterrupt:
        sys.stderr.write("Keybord Interrupt\n")
        err = 1
    except ex.Error:
        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()

    try:
        return _main(node, argv=argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        return 1
    except ex.Version as exc:
        print(exc)
        return 0
    finally:
        node.close()

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)

 0707010001f160000081a40000000000000000000000016a100daf0000312e000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/commands/svcmon.py from __future__ import print_function
import os
import sys
import optparse
import time
import datetime
import threading

#
# add project lib to path
#
prog = "om mon"

import core.exceptions as ex
import utilities.render.color
from core.node import Node
from foreign.six.moves import queue
from utilities.journaled_data import JournaledData
from utilities.render.cluster import format_cluster

CLEAREOL = "\x1b[K"
CLEAREOLNEW = "\x1b[K\n"
CLEAREOS = "\x1b[J"
CURSORHOME = "\x1b[H"

PATCH_Q = queue.Queue()

def setup_parser(node):
    __ver = prog + " version " + node.agent_version
    __usage = prog + \
        " [ OPTIONS ]\n" \
        "\n" \
        "Flags:\n" \
        "  O  up\n" \
        "  S  stdby up\n" \
        "  X  down\n" \
        "  s  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("--format", default=None,
                      action="store", dest="format",
                      help="Specify a data formatter. Possible values are compact or "
                           "matrix. The compact mode is best for large cluster. If "
                           "not specified, the cluster.default_mon_format value is "
                           "used as the default. If cluster.default_mon_format is not "
                           "set, the default is the matrix renderer."),
    parser.add_option("--server", default="", action="store", dest="server",
                      help="The server uri to send a request to. If not "
                           "specified the local node is targeted. Supported "
                           "schemes are https and raw. The default scheme is "
                           "https. The default port is 1214 for the raw "
                           "scheme, and 1215 for https. The uri can be a "
                           "fullpath to a listener socket. In this case, "
                           "the scheme is deduced from the socket. "
                           "Examples: raw://1.2.3.4:1214, "
                           "https://relay.opensvc.com, "
                           "/var/lib/opensvc/lsnr/h2.sock."),
    parser.add_option("--node", action="store", dest="node",
                      help="The nodes to display information for. If not specified, "
                           "all nodes are displayed."),
    parser.add_option("--namespace", action="store", dest="namespace",
                      help="The namespace to switch to for the action. "
                           "Namespaces are cluster partitions. A default "
                           "namespace can be set for the session setting the "
                           "OSVC_NAMESPACE environment variable."),
    parser.add_option("--stats", default=False,
                      action="store_true", dest="stats",
                      help="refresh the information every --interval.")
    parser.add_option("-w", "--watch", default=False,
                      action="store_true", dest="watch",
                      help="refresh the information every --interval.")
    parser.add_option("-i", "--interval", default=0, action="store",
                      dest="interval", type="int",
                      help="with --watch, set the refresh interval. defaults "
                           "to 0, to refresh on event only.")
    parser.add_option("--sections", action="store",
                      dest="sections",
                      help="the comma-separated list of sections to display. "
                           "if not set, all sections are displayed. sections "
                           "names are: threads,arbitrators,nodes,services.")
    parser.add_option(
        "-s", "--service", default="*",
        action="store", dest="parm_svcs",
        help="An object 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")
    return parser

def events(node, nodename, selector, namespace):
    global PATCH_Q
    for msg in node.daemon_events(nodename, selector=selector, namespace=namespace):
        if msg.get("kind") != "patch":
            continue
        PATCH_Q.put(msg)

def start_events_thread(node, nodename, selector, namespace):
    thr = threading.Thread(target=events, args=(node, nodename, selector, namespace))
    thr.daemon = True
    thr.start()
    return thr

def get_stats(options, node, paths):
    try:
        if options.stats:
            return node._daemon_stats(paths=paths, server=options.server, node=options.node)
        else:
            return None
    except Exception:
        return None

def nodes_info_from_cluster_data(data):
    info = {}
    for node in data.get("cluster", {}).get("nodes", []):
        info[node] = {}
    for node, _data in data.get("monitor", {}).get("nodes", {}).items():
        info[node] = {
            "labels": _data.get("labels", {}),
            "targets": _data.get("targets", {}),
        }
    return info

def _main(node, argv=None):
    parser = setup_parser(node)
    (options, args) = parser.parse_args(argv)
    node.check_privs(argv)
    svcmon(node, options)

def svcmon(node, options=None):
    global PATCH_Q
    utilities.render.color.use_color = options.color
    if not options.node:
        options.node = "*"
    chars = 0
    last_refresh = 0
    last_patch_id = None

    namespace = options.namespace if options.namespace else os.environ.get("OSVC_NAMESPACE")

    if options.stats and not options.interval:
        options.interval = 3
    if options.interval:
        options.watch = True
    nodes = []

    node.options.update({
        "color": options.color,
    })

    if options.parm_svcs is None:
        kind = os.environ.get("OSVC_KIND", "svc")
        options.parm_svcs = "*/%s/*" % kind
    dataset = JournaledData()
    result = node._daemon_status(server=options.server, selector=options.parm_svcs, namespace=namespace)
    if result is None or result.get("status", 0) != 0:
        status, error, info = node.parse_result(result)
        raise ex.Error(error)
    dataset.set([], result)
    status_data = dataset.get()
    nodes_info = nodes_info_from_cluster_data(status_data)
    expanded_svcs = [p for p in status_data.get("monitor", {}).get("services", {})]
    if options.format is None:
        options.format = node.oget("cluster", "default_mon_format")
    if options.format == "compact":
        nodes = []
    elif not nodes:
        nodes = node.nodes_selector(options.node, data=nodes_info)

    if options.watch:
        start_events_thread(node, options.server, options.parm_svcs, namespace)
        preamble = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        stats_data = get_stats(options, node, expanded_svcs)
        prev_stats_data = None
        outs = format_cluster(paths=expanded_svcs, node=nodes,
                              data=status_data, sections=options.sections,
                              selector=options.parm_svcs,
                              namespace=namespace)

        if outs is not None:
            print(CURSORHOME+preamble+CLEAREOLNEW+CLEAREOL)
            print(CLEAREOLNEW.join(outs.split("\n"))+CLEAREOS)

        while True:
            now = time.time()
            try:
                patch = PATCH_Q.get(False, 0.5)
                #for change in patch["data"]:
                #    print(change)
            except Exception as exc:
                # queue empty
                patch = None

            if patch:
                if last_patch_id and patch["id"] != last_patch_id + 1:
                    try:
                        dataset.set([], node._daemon_status(server=options.server, selector=options.parm_svcs, namespace=namespace))
                        status_data = dataset.get()
                        last_patch_id = patch["id"]
                    except Exception:
                        # seen on solaris under high load: decode_msg() raising on invalid json
                        pass
                else:
                    try:
                        dataset.patch([], patch["data"])
                        status_data = dataset.get()
                        last_patch_id = patch["id"]
                    except Exception as exc:
                        try:
                            dataset.set([], node._daemon_status(server=options.server, selector=options.parm_svcs, namespace=namespace))
                            status_data = dataset.get()
                            last_patch_id = patch["id"]
                        except Exception:
                            # seen on solaris under high load: decode_msg() raising on invalid json
                            pass

            stats_changed = options.interval and now - last_refresh >= options.interval
            if not patch and not stats_changed:
                time.sleep(0.2)
                continue
            if patch:
                if status_data is None:
                    # can happen when the secret is being reset on daemon join
                    time.sleep(0.2)
                    continue
                expanded_svcs = [p for p in status_data.get("monitor", {}).get("services", {})]
                nodes_info = nodes_info_from_cluster_data(status_data)
                if options.format != "compact":
                    nodes = node.nodes_selector(options.node, data=nodes_info)
            if stats_changed:
                prev_stats_data = stats_data
                stats_data = get_stats(options, node, expanded_svcs)
            if chars == 0:
                print(CURSORHOME+CLEAREOS)
                chars = 1
            preamble = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            outs = format_cluster(
                paths=expanded_svcs,
                node=nodes,
                data=status_data,
                prev_stats_data=prev_stats_data,
                stats_data=stats_data,
                sections=options.sections,
                selector=options.parm_svcs,
                namespace=namespace,
            )
            if outs is not None:
                print(CURSORHOME+preamble+CLEAREOLNEW+CLEAREOL)
                print(CLEAREOLNEW.join(outs.split("\n"))+CLEAREOS)
            # min delay
            last_refresh = now
            time.sleep(0.2)
    else:
        outs = format_cluster(paths=expanded_svcs, node=nodes,
                              data=status_data, sections=options.sections,
                              selector=options.parm_svcs,
                              namespace=namespace)
        if outs is not None:
            print(outs)


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.Error as e:
        if str(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)

  0707010001f164000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/vol   0707010001f166000081a40000000000000000000000016a100daf00000106000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/vol/parser.py """
The volume management command actions and options.
"""
import commands.svc.parser as mp

PROG = "om vol"
ACTIONS = mp.ACTIONS
OPT = mp.OPT

class VolOptParser(mp.SvcOptParser):
    """
    The volume management command options parser class.
    """
    pass
  0707010001f165000081a40000000000000000000000016a100daf0000011d000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/vol/__init__.py   # coding: utf8

import sys
from commands.vol.parser import VolOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=VolOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

   0707010001f154000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002e00000000root/usr/share/opensvc/opensvc/commands/nscfg 0707010001f155000081a40000000000000000000000016a100daf00000123000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/commands/nscfg/__init__.py # coding: utf8

import sys
from commands.nscfg.parser import NscfgOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=NscfgOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

 0707010001f156000081a40000000000000000000000016a100daf000004c7000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/nscfg/parser.py   """
The namespace configuration management command actions and options.
"""
import commands.mgr.parser as mp
import commands.svc.parser as svcp
from core.objects.svc import ACTION_ASYNC
from utilities.optparser import OptParser

PROG = "om nscfg"

OPT = mp.OPT
ACTIONS = mp.ACTIONS
ACTIONS["Object actions"] = svcp.PG_ACTIONS

DEPRECATED_OPTIONS = [
]

DEPRECATED_ACTIONS = [
]

ACTIONS_TRANSLATIONS = {
}

class NscfgOptParser(OptParser):
    """
    The namespace configuration management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

 0707010001f15d000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/svc   0707010001f15e000081a40000000000000000000000016a100daf0000011c000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/svc/__init__.py   # coding: utf8

import sys
from commands.mgr import Mgr as BaseMgr
from commands.svc.parser import SvcOptParser

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=SvcOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())
0707010001f15f000081a40000000000000000000000016a100daf000087c9000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/svc/parser.py """
The service management command actions and options
"""
import commands.mgr.parser as mp
from core.objects.svc import ACTION_ASYNC
from utilities.optparser import OptParser, Option
from utilities.storage import Storage

PROG = "om svc"

OPT = Storage()
OPT.update(mp.OPT)
OPT.update({
    "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."),
    "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"),
    "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."),
    "comment": Option(
        "--comment", default=None,
        action="store", dest="comment",
        help="A comment to log when acknowldging action log error entries."),
    "confirm": Option(
        "--confirm", default=False,
        action="store_true", dest="confirm",
        help="Confirm a run action configured to ask for confirmation. "
             "This can be used when scripting the run or triggering it "
             "from the api."),
    "downto": Option(
        "--downto", default=None,
        action="store", dest="downto",
        help="Stop the service down to the specified rid or driver group."),
    "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."),
    "id": Option(
        "--id", default=0,
        action="store", dest="id", type="int",
        help="Specify an object id to act on"),
    "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."),
    "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."),
    "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."),
    "sync": Option(
        "--sync", default=False,
        action="store_true", dest="syncrpc",
        help="Use synchronous collector communication. For example, "
             ":cmd:`push resinfo --sync` before a compliance run makes sure "
             "the pushed data has hit the collector database before the "
             "rulesets are contextualized."),
    "tag_name": Option(
        "--name", default=None,
        action="store", dest="name",
        help="The tag name, as shown by :cmd:`collector list tags`."),
    "tag_data": Option( 
        "--data", default=None,
        action="store", dest="data",
        help="The data stored with the tag. Typed tags, like <name>::<type> expect a particular data structure."),
    "tag_attach_data": Option(
        "--attach-data", default=None,
        action="store", dest="attach_data",
        help="The data stored with the tag attachment. Typed tags, like <name>::<type> expect a particular data structure."),
    "to": Option(
        "--to", default=None,
        action="store", dest="to",
        help="The remote node to start or migrate the service to. Or the "
             "target number of instance to scale to."),
    "upto": Option(
        "--upto", default=None,
        action="store", dest="upto",
        help="Start the service up to the specified rid or driver group."),
    "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:`print schedule`."),
})

START_ACTION_OPTS = [
    OPT.disable_rollback,
]

ACTIONS = Storage()
ACTIONS.update(mp.ACTIONS)
ACTIONS["Service and volume object actions"] = {
    "abort": {
        "msg": "Abort the action asynchronously done by the cluster daemons.",
        "options": mp.ASYNC_ACTION_OPTS,
    },
    "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": [
		    OPT.slave,
		    OPT.slaves,
        ],
    },
    "dns_update": {
        "msg": "Update the collector dns records for the service. The "
               "managed dns record is <name>.<app>.<collector "
               "domain>``.",
        "options": mp.ACTION_OPTS,
    },
    "enter": {
        "msg": "Enter the container specified by --rid <rid>, executing "
               "a shell.",
        "options": [
            OPT.rid,
        ],
    },
    "boot": {
        "msg": "Clean up actions executed before the daemon starts. For "
               "example scsi reservation release and vg tags removal. "
               "Never execute this action manually.",
        "options": mp.ACTION_OPTS,
    },
    "shutdown": {
        "msg": "Stop a service, including its standby resources. The log "
               "shipping to the collector is synchronous.",
        "options": mp.ACTION_OPTS + mp.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 the number of started instances is between "
               "``<flex_min>`` and ``<flex_max>``.",
        "options": mp.ACTION_OPTS + START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS + [
            OPT.upto,
        ],
    },
    "startstandby": {
        "msg": "Start local service instance resources flagged standby.",
        "options": mp.ACTION_OPTS + START_ACTION_OPTS,
    },
    "stop": {
        "msg": "Stop all service instances. The standby resources "
               "are not stopped, unless :opt:`--force` is specified.",
        "options": mp.ACTION_OPTS + mp.ASYNC_ACTION_OPTS + [
            OPT.downto,
        ],
    },
    "provision": {
        "msg": "Provision the service. Leave the service in frozen, stdby up state.",
        "options": mp.ASYNC_ACTION_OPTS + mp.ACTION_OPTS + START_ACTION_OPTS + [
            OPT.leader,
        ],
    },
    "unprovision": {
        "msg": "Shutdown and unprovision all service instances. Beware, data will be "
               "lost upon fs and disk unprovisioning.",
        "options": mp.ASYNC_ACTION_OPTS + mp.ACTION_OPTS + [
            OPT.leader,
        ],
    },
    "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,
        ],
    },
    "install_data": {
        "msg": "Install secrets and configurations in volume resources with "
               "secrets or configurations mapping configured.",
        "options": [
            OPT.rid,
            OPT.tags,
            OPT.subsets,
        ],
    },
    "print_resource_status": {
        "msg": "Display a specific service resource status, pointed by"
               " --rid",
        "options": [
            OPT.filter,
            OPT.format,
            OPT.refresh,
            OPT.rid,
        ],
    },
    "set_provisioned": {
        "msg": "Set the resources as provisioned.",
        "options": mp.ACTION_OPTS,
    },
    "set_unprovisioned": {
        "msg": "Set the resources as unprovisioned.",
        "options": mp.ACTION_OPTS,
    },
    "freeze": {
        "msg": "Block orchestration on the service.",
        "options": mp.ASYNC_ACTION_OPTS + [
            OPT.master,
            OPT.slave,
            OPT.slaves,
        ],
    },
    "thaw": {
        "msg": "Unblock orchestration on the service.",
        "options": mp.ASYNC_ACTION_OPTS + [
            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": mp.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": mp.ACTION_OPTS + [
            OPT.cron,
            OPT.confirm,
        ],
    },
    "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": mp.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": mp.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": mp.ACTION_OPTS + START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS,
    },
    "resync": {
        "msg": "Chain a local service instance  stop, sync_resync and start",
        "options": mp.ACTION_OPTS + START_ACTION_OPTS,
    },
    "snooze": {
        "msg": "Snooze alerts on the node for :opt:`--duration`",
        "options": [
            OPT.duration,
        ],
    },
    "unsnooze": {
        "msg": "Unsnooze alerts on the node",
        "options": [],
    },
    "support": {
        "msg": "Create a tarball archive of config, var and log files, and upload it to the OpenSVC support site.",
    },
    "sync_nodes": {
        "msg": "Run the synchronization method of each local service instance sync resource, targetting the peer nodes.",
        "options": mp.ACTION_OPTS,
    },
    "sync_drp": {
        "msg": "Run the synchronization method of each local service instance sync resource, targetting the drp nodes.",
        "options": mp.ACTION_OPTS,
    },
    "sync_quiesce": {
        "msg": "Pause replication of sync.netapp and sync.symsrdf resources.",
        "options": mp.ACTION_OPTS,
    },
    "sync_break": {
        "msg": "Break the disk replication of sync.hp3par, sync.ibmdssnap, sync.netapp, sync.symclone, sync.symsrdf resources.",
        "options": mp.ACTION_OPTS,
    },
    "sync_split": {
        "msg": "Split the disk replication of sync.symsrdf resources.",
        "options": mp.ACTION_OPTS,
    },
    "sync_establish": {
        "msg": "Establish disk replication of sync.symsrdf resources.",
        "options": mp.ACTION_OPTS,
    },
    "sync_resync": {
        "msg": "Like :cmd:`sync update`, but not triggered by the scheduler "
               "(thus adapted for clone/snap operations).",
        "options": mp.ACTION_OPTS,
    },
    "sync_full": {
        "msg": "Trigger a full copy of the volume to its target.",
        "options": mp.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": mp.ACTION_OPTS,
    },
    "sync_update": {
        "msg": "Trigger a one-time resync of the volume to its target.",
        "options": mp.ACTION_OPTS,
    },
    "sync_resume": {
        "msg": "Re-establish a broken storage hardware-assisted "
               "synchronization.",
        "options": mp.ACTION_OPTS,
    },
    "sync_revert": {
        "msg": "Revert to the pre-failover data (looses current data).",
        "options": mp.ACTION_OPTS,
    },
    "sync_verify": {
        "msg": "Trigger a one-time checksum-based verify of the volume "
               "and its target.",
        "options": mp.ACTION_OPTS,
    },
    "sync_all": {
        "msg": "Chain sync nodes, sync drp and sync update.",
        "options": mp.ACTION_OPTS + [
            OPT.cron,
        ],
    },
    "push_config": {
        "msg": "Push service configuration to the collector.",
        "options": [
            OPT.cron,
        ],
    },
    "push_status": {
        "msg": "Push service instance status to the collector synchronously.",
    },
    "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,
            OPT.sync,
        ],
    },
    "print_base_devs": {
        "msg": "Print the list of base devices the local service instance or the "
               "specified resources are layered on.",
        "options": [
            OPT.filter,
            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.filter,
            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.filter,
            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.filter,
            OPT.format,
            OPT.rid,
            OPT.tags,
            OPT.subsets,
        ],
    },
    "scale": {
        "msg": "Create-provision or delete-unprovision instances to meet "
               "service scale target.",
        "options": [
            OPT.to
        ],
    },
    "switch": {
        "msg": "Stop the running failover service instance and start the "
               "instance on the peer node specified by :opt:`--to "
               "<nodename>`.",
        "options": START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS + [
            OPT.to,
        ],
    },
    "takeover": {
        "msg": "Stop the service on its current node and start on the "
               "local node.",
        "options": START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS
    },
    "giveback": {
        "msg": "Stop the service on its current node and start on the "
               "node chosen by the placement policy.",
        "options": START_ACTION_OPTS + mp.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": START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS + [
            OPT.to,
        ],
    },
    "move": {
        "msg": "Ask the daemon to orchestrate a stop of the service instances "
               "running on nodes not in the specified target, and start "
               "instances on the specified target nodes. The target is "
               "specified by :opt:`--to <nodename>,<nodename>`.",
        "options": START_ACTION_OPTS + mp.ASYNC_ACTION_OPTS + [
            OPT.to,
        ],
    },
    "resource_monitor": {
        "msg": "Refresh the monitored resource status. This action is "
               "scheduleable, usually every minute.",
        "options": mp.ACTION_OPTS + [
            OPT.cron,
        ],
    },
    "oci": {
        "msg": "Wrap the podman or docke client command, setting automatically "
               "the namespace, cni-config-dir options and eventually "
               "the --root and --runroot options for services configured "
               "for private storage. 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 <path>@<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 podman "
               "container 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.",
    },
    "podman": {
        "msg": "Wrap the podman client command, setting automatically "
               "the namespace, cni-config-dir options and eventually "
               "the --root and --runroot options for services configured "
               "for private storage. 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 <path>@<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 podman "
               "container 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.",
    },
    "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 <path>@<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_resinfo": {
        "msg": "Display the service resource and env section key/val pairs "
               "pushed to the collector.",
        "options": [
            OPT.filter,
            OPT.format,
        ],
    },
    "print_schedule": {
        "msg": "Print the service tasks schedule.",
        "options": [
            OPT.filter,
            OPT.format,
            OPT.verbose,
        ],
    },
}

PG_ACTIONS = {
    "pg_pids": {
        "msg": "Display the tasks of the service process groups or selected resources process groups.",
        "options": [
            OPT.filter,
            OPT.format,
        ],
    },
    "pg_freeze": {
        "msg": "Freeze the tasks of a process group.",
        "options": mp.ACTION_OPTS,
    },
    "pg_thaw": {
        "msg": "Thaw the tasks of a process group.",
        "options": mp.ACTION_OPTS,
    },
    "pg_kill": {
        "msg": "Kill the tasks of a process group.",
        "options": mp.ACTION_OPTS,
    },
    "pg_update": {
        "msg": "Update cappings of process groups to reflect configuration changes.",
        "options": mp.ACTION_OPTS,
    },
    "pg_remove": {
        "msg": "Remove the process group, if empty.",
        "options": mp.ACTION_OPTS,
    },
    "pg_stats": {
        "msg": "Display key statistics of the process group.",
        "options": mp.ACTION_OPTS + [
            OPT.format,
        ],
    },
}
ACTIONS["Service and volume object actions"].update(PG_ACTIONS)

ACTIONS.update({
    "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.filter,
                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.filter,
                OPT.format,
            ],
        },
        "collector_checks": {
            "msg": "Display service checks.",
            "options": [
                OPT.filter,
                OPT.format,
            ],
        },
        "collector_disks": {
            "msg": "Display service disks.",
            "options": [
                OPT.filter,
                OPT.format,
            ],
        },
        "collector_log": {
            "msg": "Log a message in the collector service log.",
            "options": [
                OPT.message,
            ],
        },
        "collector_alerts": {
            "msg": "Display service alerts.",
            "options": [
                OPT.filter,
                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.filter,
                OPT.format,
            ],
        },
        "collector_asset": {
            "msg": "Display asset information known to the collector.",
            "options": [
                OPT.filter,
                OPT.format,
            ],
        },
        "collector_networks": {
            "msg": "Display network information known to the collector for "
                   "each service ip.",
            "options": [
                OPT.filter,
                OPT.format,
            ],
        },
        "collector_tag_attach": {
            "msg": "Set a node tag (pointed by --tag).",
            "options": [
                OPT.tag_name,
                OPT.tag_attach_data,
            ],
        },
        "collector_tag_detach": {
            "msg": "Unset a node tag (pointed by --tag).",
            "options": [
                OPT.tag_name,
            ],
        },
        "collector_tag_show": {
            "msg": "list all node tags",
            "options": [
                OPT.verbose,
            ],
        },
        "collector_tag_list": {
            "msg": "List all available tags. Use :opt:`--like` to filter the output.",
            "options": [
                OPT.like,
            ],
        },
        "collector_tag_create": {
            "msg": "Create a new tag with name specified by :opt:`--tag`.",
            "options": [
                OPT.tag_name,
                OPT.tag_data,
            ],
        },
    },
})

DEPRECATED_OPTIONS = [
    "daemon",
]

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 SvcOptParser(OptParser):
    """
    The service management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

   0707010001f141000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/commands/__init__.py   0707010001f15a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/sec   0707010001f15b000081a40000000000000000000000016a100daf0000011d000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/sec/__init__.py   # coding: utf8

import sys
from commands.sec.parser import SecOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=SecOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

   0707010001f15c000081a40000000000000000000000016a100daf0000134a000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/sec/parser.py """
Secret management command actions and options
"""
import commands.mgr.parser as mp
from utilities.optparser import OptParser, Option
from utilities.storage import Storage
from core.objects.svc import ACTION_ASYNC

PROG = "om sec"

OPT = Storage()
OPT.update(mp.OPT)
OPT.update({
    "key": Option(
        "--key", default=None,
        action="store", dest="key",
        help="The secret key name."),
    "value_from": Option(
        "--from", default=None,
        action="store", dest="value_from",
        help="Read the secret value from a file or a directory. If set to '-' or '/dev/stdin', the value is read from stdin, and the --key is mandatory. If set to a file path, the key name is the file basename. If set to a directory, one key per file is added, and the keyname is the relative path, the --key value being used as the relative path prefix."),
    "password": Option(
        "--password", default=None,
        action="store", dest="password",
        help="The pkcs bundle encryption password."),
    "path": Option(
        "--path", default=None,
        action="store", dest="path",
        help="The path where to install secret keys."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The secret value."),
    "match": Option(
        "--match", default="**",
        action="store", dest="match",
        help="A glob pattern to filter the keys with. Defaults to '**'."),
})

ACTIONS = Storage()
ACTIONS.update(mp.ACTIONS)
ACTIONS.update({
    "Secret object actions": {
        "add": {
            "msg": "Add a secret key/value to the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "change": {
            "msg": "Add a key/value to the configuration object. The key is created if it doesn't already exists.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "append": {
            "msg": "Append data to a secret key in the object.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "edit": {
            "msg": "Edit the configuration or the current value of a key.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "keys": {
            "msg": "Show all keys available in this secret.",
            "options": [
                OPT.match,
            ],
        },
        "gen_cert": {
            "msg": "Create a x509 certificate using information in the secret configuration.",
        },
        "fullpem" : {
            "msg": "Print to stdout a ascii pem-formatted concatenation of the private key and certificate. This format is accepted by opensvc context configuration. If certificate and private key are not generated yet, run the gen_cert action.",
        },
        "pkcs12" : {
            "msg": "Print to stdout a binary pkcs12-formatted concatenation of the private key and certificate. This format is accepted by most browsers certificate store. If certificate and private key are not generated yet, run the gen_cert action. A password is prompted if not already provided by --password.",
            "options": [
                OPT.password,
            ],
        },
        "decode": {
            "msg": "Decode a secret key from the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "install": {
            "msg": "Install or update secret key or secret tree in consuming volumes.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "remove": {
            "msg": "Remove a secret key from the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
    },
})

DEPRECATED_OPTIONS = [
]

DEPRECATED_ACTIONS = [
]

ACTIONS_TRANSLATIONS = {
}

class SecOptParser(OptParser):
    """
    The secret management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

  0707010001f161000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/usr   0707010001f162000081a40000000000000000000000016a100daf0000011d000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/usr/__init__.py   # coding: utf8

import sys
from commands.usr.parser import UsrOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=UsrOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

   0707010001f163000081a40000000000000000000000016a100daf000012b6000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/usr/parser.py """
The user management command actions and options
"""
import commands.mgr.parser as mp
from utilities.optparser import OptParser, Option
from utilities.storage import Storage
from core.objects.svc import ACTION_ASYNC

PROG = "om usr"

OPT = Storage()
OPT.update(mp.OPT)
OPT.update({
    "key": Option(
        "--key", default=None,
        action="store", dest="key",
        help="The secret key name."),
    "value_from": Option(
        "--from", default=None,
        action="store", dest="value_from",
        help="Read the secret value from a file or a directory. If set to '-' or '/dev/stdin', the value is read from stdin, and the --key is mandatory. If set to a file path, the key name is the file basename. If set to a directory, one key per file is added, and the keyname is the relative path, the --key value being used as the relative path prefix."),
    "password": Option(
        "--password", default=None,
        action="store", dest="password",
        help="The pkcs bundle encryption password."),
    "path": Option(
        "--path", default=None,
        action="store", dest="path",
        help="The path where to install secret keys."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The secret value."),
})

ACTIONS = mp.ACTIONS
ACTIONS.update({
    "User object actions": {
        "add": {
            "msg": "Add a secret key/value to the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "change": {
            "msg": "Add a key/value to the configuration object. The key is created if it doesn't already exists.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "append": {
            "msg": "Append data to a secret key in the object.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "edit": {
            "msg": "Edit the configuration or the current value of a key.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "keys": {
            "msg": "Show all keys available in this secret.",
        },
        "gen_cert": {
            "msg": "Create a x509 certificate using information in the secret configuration.",
        },
        "fullpem" : {
            "msg": "Print to stdout a ascii pem-formatted concatenation of the private key and certificate. This format is accepted by opensvc context configuration. If certificate and private key are not generated yet, run the gen_cert action.",
        },
        "pkcs12" : {
            "msg": "Print to stdout a binary pkcs12-formatted concatenation of the private key and certificate. This format is accepted by most browsers certificate store. If certificate and private key are not generated yet, run the gen_cert action. A password is prompted if not already provided by --password.",
            "options": [
                OPT.password,
            ],
        },
        "revoke": {
            "msg": "Revoke the user certificate, if generated by the cluster CA.",
        },
        "decode": {
            "msg": "Decode a secret key from the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "install": {
            "msg": "Install or update secret key or secret tree in consuming volumes.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "remove": {
            "msg": "Remove a secret key from the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
    },
})

DEPRECATED_OPTIONS = [
]

DEPRECATED_ACTIONS = [
]

ACTIONS_TRANSLATIONS = {
}

class UsrOptParser(OptParser):
    """
    The user management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

  0707010001f142000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/commands/ccfg  0707010001f144000081a40000000000000000000000016a100daf00000475000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/commands/ccfg/parser.py    """
The cluster configuration management command actions and options.
"""
import commands.mgr.parser as mp
from core.objects.svc import ACTION_ASYNC
from utilities.optparser import OptParser

PROG = "om cluster"

OPT = mp.OPT
ACTIONS = mp.ACTIONS

DEPRECATED_OPTIONS = [
]

DEPRECATED_ACTIONS = [
]

ACTIONS_TRANSLATIONS = {
}

class CcfgOptParser(OptParser):
    """
    The cluster configuration management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

   0707010001f143000081a40000000000000000000000016a100daf00000120000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/commands/ccfg/__init__.py  # coding: utf8

import sys
from commands.ccfg.parser import CcfgOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=CcfgOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

0707010001f157000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/commands/pool  0707010001f158000081a40000000000000000000000016a100daf000004b7000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/commands/pool/__init__.py  from __future__ import print_function

import os
import sys

import utilities.render.color
import core.exceptions as ex
from commands.pool.parser import PoolOptParser
from core.node import Node


def _main(node, argv=None):
    optparser = PoolOptParser(argv)
    options, action = optparser.parse_args(argv)

    utilities.render.color.use_color = options.color
    node.options.update(options.__dict__)

    node.check_privs(action)

    err = 0
    try:
        err = node.action("pool_"+action)
    except KeyboardInterrupt:
        sys.stderr.write("Keybord Interrupt\n")
        err = 1
    except ex.Error:
        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()

    try:
        return _main(node, argv=argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        return 1
    except ex.Version as exc:
        print(exc)
        return 0
    finally:
        node.close()

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)

 0707010001f159000081a40000000000000000000000016a100daf00000bc2000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/commands/pool/parser.py    """
The node management command actions and options.
"""
from utilities.storage import Storage
from utilities.optparser import OptParser, Option
from commands.node.parser import GLOBAL_OPT, GLOBAL_OPTS

PROG = "om pool"

OPT = Storage({
    "access": Option(
        "--access", default="rwo", action="store", dest="access",
        help="The access mode of the volume. rwo, roo, rwx, rox."),
    "blk": Option(
        "--blk", default=False,
        action="store_true", dest="blk",
        help="Create a block volume instead of a formatted volume."),
    "name": Option(
        "--name", action="store", dest="name",
        help="The name of the object."),
    "namespace": Option(
        "--namespace", action="store", dest="namespace",
        help="The namespace to switch to for the action. Namespaces are cluster partitions. A default namespace can be set for the session setting the OSVC_NAMESPACE environment variable."),
    "nodes": Option(
        "--nodes", default="",
        action="store", dest="nodes",
        help="A node selector expression. Used as the created volume nodes."),
    "pool": Option(
        "--pool", default=None,
        action="store", dest="pool",
        help="The name of the storage pool."),
    "shared": Option(
        "--shared", default=False,
        action="store_true", dest="shared",
        help="Create a volume service for a shared volume resource."),
    "size": Option(
        "--size", default="rwo", action="store", dest="size",
        help="The size mode of the volume. ex: 10gi, 10g."),
    "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:`om node print schedule`."),
})
OPT.update(GLOBAL_OPT)

ACTIONS = {
    "Pool actions": {
        "ls": {
            "msg": "List the available pools.",
        },
        "status": {
            "msg": "Show pools status.",
            "options": [
                OPT.name,
                OPT.verbose,
            ],
        },
        "create_volume": {
            "msg": "Create a volume in the pool.",
            "options": [
                OPT.access,
                OPT.blk,
                OPT.name,
                OPT.namespace,
                OPT.pool,
                OPT.shared,
                OPT.size,
                OPT.nodes,
            ],
        },
    },
}

class PoolOptParser(OptParser):
    """
    The pool management command 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,
                           global_options=GLOBAL_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent)

  0707010001f14b000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/mgr   0707010001f14c000081a40000000000000000000000016a100daf00003111000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/mgr/__init__.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 importlib
import os
import sys

import core.status
import core.exceptions as ex
import utilities.render.color
from core.node import Node
from env import Env
from utilities.naming import split_path, validate_kind
from utilities.proc import check_privs, get_option
from utilities.storage import Storage


class Mgr(object):
    def __init__(self, parser=None, node=None, selector=None):
        self.parser = parser
        self.node = node
        self.selector = selector
        self.expanded_svcs = None

    @staticmethod
    def get_extra_argv(argv=None):
        """
        Extract oci/docker/podman/... passed-through argv from the command main argv.

        The service management command acts as a wrapper for podman and docker commands,
        setting the service-specific socket if necessary.
        """
        commands = ["oci", "podman", "docker"]
        if argv is None:
            argv = sys.argv[1:]
        if len(argv) < 2:
            return argv, []
        for command in commands:
            try:
                pos = argv.index(command)
                extra_argv = argv[pos + 1:]
                argv = argv[:pos + 1]
                return argv, extra_argv
            except Exception:
                extra_argv = []
        return argv, []

    @staticmethod
    def get_build_kwargs(options, action):
        """
        Return the service build function keyword arguments, deduced from
        parsed command options.
        """
        build_kwargs = {}

        if len(set(["paths", "status"]) & set(build_kwargs.keys())) == 0:
            if hasattr(options, "svcs") and options.svcs is not None:
                build_kwargs["paths"] = options.svcs

        if hasattr(options, "status") and options.status is not None:
            build_kwargs["status"] = [core.status.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(self, 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
        env = {}
        env.update(os.environ)
        env["OSVC_DETACHED"] = "1"
        env["OSVC_PARENT_SESSION_UUID"] = Env.session_uuid
        import signal
        try:
            import subprocess
            kwargs = {}
            try:
                kwargs["preexec_fn"] = os.setsid
            except AttributeError:
                pass
            prog = os.path.dirname(os.path.abspath(__file__))
            prog = os.path.join(prog, "..", "..", "__main__.py")
            prog = os.path.realpath(prog)
            if self.selector:
                executable = [sys.executable, prog, self.selector]
            elif os.environ.get("OSVC_KIND"):
                executable = [sys.executable, prog, os.environ.get("OSVC_KIND")]
            else:
                executable = [sys.executable, prog]

            proc = subprocess.Popen(executable + argv,
                                    stdout=None, stderr=None, stdin=None,
                                    close_fds=True, cwd=os.sep,
                                    env=env, **kwargs)
            proc.wait()
            ret = proc.returncode
        except KeyboardInterrupt as exc:
            os.killpg(os.getpgid(proc.pid), signal.SIGINT)
            print("kill detached process")
            ret = 1
        except ex.Signal 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(self, 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 \
                os.environ.get("OSVC_DETACHED") != "1" and \
                (action in ("stop", "shutdown", "unprovision") or
                 (action == "delete" and options.unprovision is True)):
            ret = self.do_svcs_action_detached(argv)
        else:
            try:
                ret = self.node.do_svcs_action(action, options)
            except ex.Error as exc:
                print(exc, file=sys.stderr)
                ret = 1
        return ret

    def prepare_options(self, options):
        """
        Prepare and return the options Storage() as expected by the Svc::action
        and Node::do_svcs_action methods.
        """
        opts = Storage()
        # preserve parm_svcs, as svcs will be expanded
        opts.parm_svcs = options.parm_svcs
        for key, val in options.__dict__.items():
            opts[key.replace("parm_", "")] = val
        try:
            namespace = options.namespace
        except AttributeError:
            namespace = None
        if namespace:
            opts.namespace = namespace
        elif "OSVC_NAMESPACE" in os.environ:
            opts.namespace = os.environ["OSVC_NAMESPACE"]
        if self.selector:
            opts.parm_svcs = self.selector
            opts.svcs = self.selector
        if opts.eval and not opts.format:
            opts.format = "json"
        return opts

    @staticmethod
    def split_env(arg):
        idx = arg.index("=")
        option = arg[:idx]
        value = arg[idx + 1:]
        return option, value

    def export_env_from_options(self, options):
        if options.get("daemon"):
            os.environ["OSVC_DETACHED"] = "1"
        for arg in options.get("env", []):
            if arg in ("-", "stdin", "/dev/stdin"):
                continue
            option, value = self.split_env(arg)
            option = option.upper()
            os.environ[option] = value

    @staticmethod
    def dispatch_svcs(paths):
        data = {}
        for path in paths:
            try:
                _, _, kind = split_path(path)
            except ValueError:
                continue
            try:
                validate_kind(kind)
            except ValueError as exc:
                raise ex.Error(str(exc))
            try:
                data[kind].append(path)
            except KeyError:
                data[kind] = [path]
        return data

    def get_action(self, argv):
        action = []
        _in = False
        for word in argv:
            if _in and word.startswith("-"):
                break
            if word.startswith("-"):
                continue
            action.append(word)
            _in = True
        return "_".join(action)

    def dispatch(self, argv):
        if self.selector is None:
            yield
        namespace = get_option("--namespace", argv)
        if namespace is None:
            namespace = os.environ.get("OSVC_NAMESPACE")
        action = self.get_action(argv)
        if action in ("create", "deploy"):
            expanded_svcs = self.selector.split(",")
        elif action == "ls":
            from commands.svc.parser import SvcOptParser
            parser = SvcOptParser()
            expanded_svcs = None
            yield parser
        else:
            local = action == "boot"
            expanded_svcs = self.node.svcs_selector(self.selector, namespace, local=local)
            self.expanded_svcs = expanded_svcs
        if expanded_svcs is not None:
            svc_by_kind = self.dispatch_svcs(expanded_svcs)
            for kind, paths in svc_by_kind.items():
                modname = "commands.{kind}.parser".format(kind=kind)
                mod = importlib.import_module(modname)
                parser = getattr(mod, kind.capitalize() + "OptParser")()
                yield parser

    def parse_args(self, argv):
        if self.parser:
            self.optparser = self.parser()
            return self.optparser.parse_args(argv)
        err = []
        for parser in self.dispatch(argv):
            if not parser:
                continue
            self.optparser = parser
            try:
                options, action = self.optparser.parse_args(argv)
                return options, action
            except Exception as exc:
                # if any parser accepts this argv, don't display errors
                # raised by other parsers. keep them around in case no
                # parser accepts.
                buff = str(exc)
                if not buff.startswith(self.optparser.prog):
                    buff = "%s: %s" % (self.optparser.prog, buff)
                err.append(buff)
                pass
        # no parser matched. display a per-parser errorlog
        raise ex.Error("\n".join(err))

    def _main(self, argv=None):
        """
        Build the service list.
        Execute action-specific codepaths.
        """
        build_err = False
        ret = 0

        argv, extra_argv = self.get_extra_argv(argv)
        try:
            options, action = self.parse_args(argv)
        except ex.Error as exc:
            if str(exc):
                raise
            else:
                raise ex.Error("no match")
        if action == "deploy":
            action = "create"
            options.provision = True
        options = self.prepare_options(options)
        self.export_env_from_options(options)
        options.extra_argv = extra_argv
        utilities.render.color.use_color = options.color
        try:
            self.node.options.format = options.format
            self.node.options.jsonpath_filter = options.jsonpath_filter
        except AttributeError:
            pass
        if action not in ("ls", "monitor", "create") and options.svcs is None and options.status is None:
            raise ex.Error("no object selected.")
        if action in ("ls", "monitor") and options.svcs is None:
            kind = os.environ.get("OSVC_KIND", "svc")
            options.svcs = "*/%s/*" % kind

        if action == "create":
            if options.svcs:
                options.svcs = options.svcs.split(",")
        else:
            if self.expanded_svcs is None:
                local = action == "boot"
                expanded_svcs = self.node.svcs_selector(options.svcs, options.namespace, local=local)
            else:
                expanded_svcs = self.expanded_svcs
            if options.svcs in (None, "*") and expanded_svcs == []:
                return
            options.svcs = expanded_svcs

        self.node.set_rlimit()
        build_kwargs = self.get_build_kwargs(options, action)

        if action != "create":
            try:
                self.node.build_services(**build_kwargs)
            except ex.Error as exc:
                if len(str(exc)) > 0:
                    print(exc, file=sys.stderr)
                build_err = True

        if action not in ("ls", "create") and len(options.svcs) == 0:
            if not build_err:
                sys.stderr.write("no match\n")
            return 1

        if action == "create":
            return self.node.create_service(options.svcs, options)

        ret = self.do_svcs_action(options, action, argv=argv)

        try:
            import logging
            logging.shutdown()
        except:
            pass

        return ret

    def __call__(self, argv=None):
        """
        Instanciate a Node object.
        Call the real deal making sure the node is finally freed.
        """
        ret = 0

        if self.node is None:
            try:
                self.node = Node()
            except Exception as exc:
                print(exc, file=sys.stderr)
                return 1

        check_privs()

        try:
            ret = self._main(argv=argv)
        except ex.Error as exc:
            print(exc, file=sys.stderr)
            return 1
        except ex.Version as exc:
            print(exc)
            return 0
        except KeyboardInterrupt:
            return 1
        finally:
            self.node.close()

        if ret is None:
            ret = 0

        return ret


if __name__ == "__main__":
    selector = sys.argv[1]
    del sys.argv[1]
    sys.exit(Mgr(selector=selector)())
   0707010001f14d000081a40000000000000000000000016a100daf0000598f000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/mgr/parser.py from utilities.optparser import Option
from utilities.storage import Storage

OPT = Storage({
    "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."),
    "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."),
    "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"),
    "config": Option(
        "--config", default=None,
        action="store", dest="parm_config",
        help="The configuration to use as template when creating or "
             "installing a service. The value can be ``-`` or ``/dev/stdin`` "
             "to read the json-formatted configuration from stdin, or a file "
             "path, or uri pointing to a ini-formatted configuration, or a "
             "service selector expression (ATTENTION with cloning existing live "
             "services that include more than containers, volumes and backend "
             "ip addresses ... this could cause disruption on the cloned 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 and file log verbosity up to the debug level."),
    "daemon": Option(
        "--daemon", default=False,
        action="store_true", dest="daemon",
        help="A flag inhibiting the command 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."),
    "dry_run": Option(
        "--dry-run", default=False,
        action="store_true", dest="dry_run",
        help="Show the action execution plan."),
    "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:`get` action, the printed value of "
             ":opt:`--param` is evaluated, scoped and dereferenced. If set "
             "with the :cmd:`set` action, the current value is "
             "evaluated before mangling."),
    "filter": Option(
        "--filter", default="",
        action="store", dest="jsonpath_filter",
        help="A JSONPath expression to filter a JSON output."),
    "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, flat_json, "
             "csv or table. csv and table formatters are available only for "
             "commands returning tabular data."),
    "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."),
    "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."),
    "interval": Option(
        "--interval", default=0, action="store",
        dest="interval", type="int",
        help="with --watch, set the refresh interval. defaults "
             "to 0, to refresh on event only."),
    "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, ...). For the set and unset actions only, set the keyword for all matching resources.\n"
             "* <op> can be:\n\n"
             "  * ``=``  set as new value\n"
             "  * ``-=`` remove value from the current list\n"
             "  * ``+=`` append value to the current list\n"
             "  * ``|=`` append value to the current list if not already included\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"),
    "leader": Option(
        "--leader", default=None,
        action="store_true", dest="leader",
        help="Switch the provision action behaviour to leader, ie provision shared resources that are not provisionned by default."),
    "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."),
    "namespace": Option(
        "--namespace",
        action="store", dest="namespace",
        help="The namespace to switch to for the action. Namespaces are cluster partitions. A default namespace can be set for the session setting the OSVC_NAMESPACE environment variable."),
    "node": Option(
        "--node", default="",
        action="store", dest="node",
        help="The node to send a request to. If not specified the local node is targeted."),
    "nolock": Option(
        "--nolock", default=False,
        action="store_true", dest="nolock",
        help="Don't acquire the action lock. Dangerous, but can be useful to set parameters from an action trigger."),
    "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, ...). For the set and unset actions only, set the keyword for 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."),
    "purge_collector": Option(
        "--purge-collector", default=False,
        action="store_true", dest="purge_collector",
        help="On service delete, also remove the service collector-side"),
    "recover": Option(
        "--recover", default=False,
        action="store_true", dest="recover",
        help="Recover the stashed erroneous configuration file "
             "in a :cmd:`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",
        action="append",
        help="A resource definition in json dictionary format fed to create "
             "or update. The ``rtype`` key point the driver group name, and "
             "the ``type`` key the driver name (translated to type in the "
             "configuration file section)."),
    "restore": Option(
        "--restore", default=False,
        action="store_true", dest="restore",
        help="Keep the same service id as the template or config file referenced by the create action. The default behaviour is to generate a new id."),
    "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"
    ),
    "sections": Option(
        "--sections",
        action="store", dest="sections",
        help="the comma-separated list of sections to display. "
             "if not set, all sections are displayed. sections "
             "names are: threads,arbitrators,nodes,services."),
    "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"
             "      - ``<single value jsonpath expression on the $.monitor.services.<path> dictionary extended under the 'nodes' key by each instance 'status' and 'config' data>``\n\n"
             "    - ``<op>`` can be:\n\n"
             "      - ``<``  ``>``  ``<=``  ``>=``  ``=``\n\n"
             "      - ``~`` the string or any list element matches the regexp value\n\n"
             "      - ``~=`` the string matches regexp value or any list element is the value\n\n"
             "Examples:\n\n"
             "- ``*dns,ha*+app.timeout>1``\n\n"
             "- ``ip:+task:``\n\n"
             "- ``!*excluded``\n\n"
             "- ``$.avail=warn``\n\n"
             "- ``$.nodes.*.status.avail=warn``\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."),
    "sid": Option(
        "--sid", default=None,
        action="store", dest="sid",
        help="Session id to filter "
             "from the log file tail. Default is None."),
    "since": Option(
        "--since", default=None,
        action="store", dest="since",
        help="Limit result to log since timestamp "
             "from the log file tail. Default is None."),
    "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."),
    "stats": Option(
        "--stats", default=False,
        action="store_true", dest="stats",
        help="Show system resources usage metrics and refresh the information every --interval."),
    "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."),
    "unprovision": Option(
        "--unprovision", default=False,
        action="store_true", dest="unprovision",
        help="Unprovision the service resources before config files file deletion. "
             "Defaults to False."),
    "until": Option(
        "--until", default=None,
        action="store", dest="until",
        help="Limit result to log until timestamp "
             "from the log file tail. Default is None."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The value to set for the keyword pointed by :opt:`--param`"),
    "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."),
    "watch": Option(
        "-w", "--watch", default=False,
        action="store_true", dest="watch",
        help="refresh the information every --interval."),
})

SVC_SELECT_OPTS = [
    OPT.namespace,
    OPT.service,
    OPT.status,
    OPT.node,
    OPT.local,
]

GLOBAL_OPTS = SVC_SELECT_OPTS + [
    OPT.color,
    OPT.daemon,
    OPT.debug,
    OPT.env,
    OPT.parallel,
    OPT.waitlock,
    OPT.help,
]

ACTION_OPTS = [
    OPT.dry_run,
    OPT.force,
    OPT.master,
    OPT.nolock,
    OPT.rid,
    OPT.slave,
    OPT.slaves,
    OPT.subsets,
    OPT.tags,
]

CONFIG_OPTS = [
    OPT.force,
    OPT.master,
    OPT.nolock,
    OPT.rid,
    OPT.slave,
    OPT.slaves,
    OPT.subsets,
    OPT.tags,
]

ASYNC_ACTION_OPTS = [
    OPT.time,
    OPT.wait,
    OPT.stats,
    OPT.watch,
    OPT.interval,
]


ACTIONS = {
    "Common object actions": {
        "deploy": {
            "msg": "Create and provision a new service.",
            "options": CONFIG_OPTS + [
                OPT.config,
                OPT.disable_rollback,
                OPT.interactive,
                OPT.kw,
                OPT.leader,
                OPT.restore,
                OPT.template,
            ],
        },
        "logs": {
            "msg": "Display the service logs. All service instances logs are aggregated.",
            "options": [
                OPT.backlog,
                OPT.follow,
                OPT.nopager,
                OPT.sid,
                OPT.since,
                OPT.until,
            ]
        },
        "ls": {
            "msg": "List the service names with a local instance. Most useful to test "
                   "a service selector expression before running an action.",
            "options": [
                OPT.filter,
                OPT.format,
            ],
        },
        "monitor": {
            "msg": "Display or watch the synthetic service status, and perf metrics.",
            "options": [
                OPT.sections,
                OPT.stats,
                OPT.watch,
                OPT.interval,
                OPT.format,
            ],
        },
        "print_status": {
            "msg": "Display the service status, with a detailed view of the local "
                   "instance.\n\n"
                   "Resources Flags:\n\n"
                   "(1) ``R``   Running,           ``.`` Not Running\n\n"
                   "(2) ``M``   Monitored,         ``.`` Not Monitored\n\n"
                   "(3) ``D``   Disabled,          ``.`` Enabled\n\n"
                   "(4) ``O``   Optional,          ``.`` Not Optional\n\n"
                   "(5) ``E``   Encap,             ``.`` Not Encap\n\n"
                   "(6) ``P``   Not Provisioned,   ``.`` Provisioned\n\n"
                   "(7) ``S``   Standby,           ``.`` Not Standby\n\n"
                   "(8) ``<n>`` Remaining Restart, ``+`` if more than 10,  ``.``   No Restart\n\n"
                   "(8) ``X``   User Stopped Scope (No Restart)\n\n"
                   "",
            "options": [
                OPT.filter,
                OPT.format,
                OPT.hide_disabled,
                OPT.refresh,
                OPT.show_disabled,
            ],
        },
        "print_config_mtime": {
            "msg": "Display the service local configuration file modification time",
        },
        "purge": {
            "msg": "Unprovision and delete selected services.",
            "options": ASYNC_ACTION_OPTS + ACTION_OPTS + [
                OPT.purge_collector,
                OPT.leader,
            ],
        },
        "status": {
            "msg": "Return the local service instance overall status code.",
            "options": [
                OPT.cron,
                OPT.refresh,
            ],
        },
    },
    "Object configuration": {
        "print_config": {
            "msg": "Display the service current configuration.",
            "options": [
                OPT.filter,
                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": CONFIG_OPTS + [
                OPT.config,
                OPT.disable_rollback,
                OPT.kw,
                OPT.interactive,
                OPT.leader,
                OPT.provision,
                OPT.resource,
                OPT.restore,
                OPT.template,
            ],
        },
        "update": {
            "msg": "Update definitions in an existing service configuration "
                   "file.",
            "options": CONFIG_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 + CONFIG_OPTS + [
                OPT.purge_collector,
                OPT.unprovision,
            ],
        },
        "eval": {
            "msg": "Evaluate the value of a service configuration keyword.",
            "options": CONFIG_OPTS + [
                OPT.format,
                OPT.impersonate,
                OPT.kw,
            ],
        },
        "set": {
            "msg": "Set a service configuration parameter",
            "options": CONFIG_OPTS + [
                OPT.kw,
                OPT.add,
                OPT.eval,
                OPT.index,
                OPT.param,
                OPT.remove,
                OPT.value,
            ],
        },
        "get": {
            "msg": "Get the raw value of a service configuration keyword.",
            "options": CONFIG_OPTS + [
                OPT.eval,
                OPT.format,
                OPT.impersonate,
                OPT.param,
                OPT.kw,
            ],
        },
        "unset": {
            "msg": "Unset a node configuration keyword.",
            "options": CONFIG_OPTS + [
                OPT.kw,
                OPT.param,
            ],
        },
    },
}
 0707010001f145000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/commands/cfg   0707010001f147000081a40000000000000000000000016a100daf00000fe1000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/commands/cfg/parser.py """
The configmap management command actions and options.
"""
import commands.mgr.parser as mp
from utilities.optparser import OptParser, Option
from utilities.storage import Storage
from core.objects.svc import ACTION_ASYNC

PROG = "om cfg"

OPT = Storage()
OPT.update(mp.OPT)
OPT.update({
    "key": Option(
        "--key", default=None,
        action="store", dest="key",
        help="The configuration key name."),
    "value_from": Option(
        "--from", default=None,
        action="store", dest="value_from",
        help="Read the configuration values from a file or a directory. If set to '-' or '/dev/stdin', the value is read from stdin, and the --key is mandatory. If set to a file path, the key name is the file basename. If set to a directory, one key per file is added, and the keyname is the relative path, the --key value being used as the relative path prefix."),
    "path": Option(
        "--path", default=None,
        action="store", dest="path",
        help="The path where to install configuration keys."),
    "value": Option(
        "--value", default=None,
        action="store", dest="value",
        help="The configuration key value."),
    "match": Option(
        "--match", default="**",
        action="store", dest="match",
        help="A glob pattern to filter the keys with. Defaults to '**'."),
})

ACTIONS = Storage()
ACTIONS.update(mp.ACTIONS)
ACTIONS.update({
    "Configuration object actions": {
        "add": {
            "msg": "Add a key/value to the configuration object. Raise an error if the key already exists.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "change": {
            "msg": "Add a key/value to the configuration object. The key is created if it doesn't already exists.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "edit": {
            "msg": "Edit the configuration or the current value of a key.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "append": {
            "msg": "Append data to a key in the object.",
            "options": mp.ACTION_OPTS + [
                OPT.value_from,
                OPT.key,
                OPT.value,
            ],
        },
        "keys": {
            "msg": "Show all keys available in this configuration.",
            "options": [
                OPT.match,
            ],
        },
        "decode": {
            "msg": "Decode a key from the configuration object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "install": {
            "msg": "Install or update configuration key or configuration tree in consuming volumes.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
        "remove": {
            "msg": "Remove a secret key from the secret object.",
            "options": mp.ACTION_OPTS + [
                OPT.key,
            ],
        },
    },
})

DEPRECATED_OPTIONS = [
]

DEPRECATED_ACTIONS = [
]

ACTIONS_TRANSLATIONS = {
}

class CfgOptParser(OptParser):
    """
    The configmap management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=mp.GLOBAL_OPTS,
                           svc_select_options=mp.SVC_SELECT_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

   0707010001f146000081a40000000000000000000000016a100daf0000011d000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/commands/cfg/__init__.py   # coding: utf8

import sys
from commands.cfg.parser import CfgOptParser
from commands.mgr import Mgr as BaseMgr

class Mgr(BaseMgr):
    def __init__(self, node=None):
        super(Mgr, self).__init__(parser=CfgOptParser, node=node)

if __name__ == "__main__":
    sys.exit(Mgr()())

   0707010001f148000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/commands/daemon    0707010001f14a000081a40000000000000000000000016a100daf0000123a000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/commands/daemon/parser.py  """
The node management command actions and options.
"""
from utilities.storage import Storage
from utilities.optparser import OptParser, Option
from commands.node.parser import GLOBAL_OPT, GLOBAL_OPTS

PROG = "om daemon"

OPT = Storage({
    "foreground": Option(
        "-f", "--foreground", default=False,
        action="store_true", dest="foreground",
        help="Run the deamon in foreground mode."),
    "id": Option(
        "--id", default=0,
        action="store", dest="id",
        help="Specify an id to act on."),
    "name": Option(
        "--name", action="store", dest="name",
        help="The name of the object."),
    "secret": Option(
        "--secret", default=None,
        action="store", dest="secret",
        help="The cluster secret used as the AES key in the cluster "
             "communications."),
    "session_id": Option(
        "--session-id", default=None, action="store", dest="session_id",
        help="Specify an alive daemon listener client session id, as listed:cmd:`om daemon "
             "status` output."),
    "thr_id": Option(
        "--thread-id", default=None, action="store", dest="thr_id",
        help="Specify a daemon thread, as listed in the :cmd:`om daemon "
             "status --format <json|flat_json>` output."),
    "timeout": Option(
        "--timeout",
        action="store", dest="time",
        help="Maximum wait time."),
})
OPT.update(GLOBAL_OPT)

ACTIONS = {
    "Daemon management actions": {
        "relay_status": {
            "msg": "Show the daemon relay clients and last update timestamp.",
        },
        "blacklist_status": {
            "msg": "Show the content of the daemon senders blacklist.",
        },
        "blacklist_clear": {
            "msg": "Empty the content of the daemon senders blacklist.",
        },
        "lock_show": {
            "msg": "Show cluster locks.",
        },
        "lock_release": {
            "msg": "Release a lock. Beware locks should be released automatically.",
            "options": [
                OPT.id,
                OPT.name,
                OPT.timeout,
            ],
        },
        "restart": {
            "msg": "Restart the daemon.",
        },
        "running": {
            "msg": "Return with code 0 if the daemon is running, else return "
                   "with code 1",
        },
        "shutdown": {
            "msg": "Stop all local services instances then stop the daemon.",
        },
        "status": {
            "msg": "Display the daemon status.",
        },
        "stats": {
            "msg": "Display the daemon stats.",
        },
        "start": {
            "msg": "Start the daemon or a daemon thread pointed by :opt:`--thread-id`.",
            "options": [
                OPT.thr_id,
                OPT.foreground,
            ],
        },
        "stop": {
            "msg": ("Stop the daemon or a daemon thread pointed by :opt:`--thread-id`."
                    " listener clients can be marked for stop with session id `xxx` can be marked for stop using"
                    " `--thread-id session-id-xxx`."),
            "options": [
                OPT.thr_id,
                OPT.session_id,
            ],
        },
        "join": {
            "msg": "Join the cluster of the node specified by :opt:`--node <node>`, authenticating with :opt:`--secret <secret>`.",
            "options": [
                OPT.secret,
            ],
        },
        "rejoin": {
            "msg": "Rejoin the cluster of the node specified by :opt:`--node <node>`, authenticating with the already known secret. This will re-merge the remote node cluster-wide configurations in the local node configuration file.",
        },
        "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.",
        },
        "dns_dump": {
            "msg": "Dump the content of the cluster zone.",
        },
        "mutex_status": {
            "msg": "Show daemon mutex status.",
        },
    },
}

class DaemonOptParser(OptParser):
    """
    The daemon management command 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,
                           global_options=GLOBAL_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent)

  0707010001f149000081a40000000000000000000000016a100daf0000048f000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/commands/daemon/__init__.py    from __future__ import print_function

import sys

import core.exceptions as ex
import utilities.render.color
from commands.daemon.parser import DaemonOptParser
from core.node import Node


def _main(node, argv=None):
    optparser = DaemonOptParser(argv)
    options, action = optparser.parse_args(argv)

    utilities.render.color.use_color = options.color
    node.options.update(options.__dict__)

    node.check_privs(action)

    try:
        return node.action("daemon_"+action)
    except KeyboardInterrupt:
        sys.stderr.write("Keyboard Interrupt\n")
        return 1
    except ex.Error:
        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')
        return 1
    except:
        raise


def main(argv=None):
    node = Node()

    try:
        return _main(node, argv=argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        return 1
    except ex.Version as exc:
        print(exc)
        return 0
    finally:
        node.close()


if __name__ == "__main__":
    ret = main()
    sys.exit(ret)
 0707010001f151000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/commands/node  0707010001f153000081a40000000000000000000000016a100daf00009f29000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/commands/node/parser.py    """
The node management command actions and options.
"""
from utilities.storage import Storage
from utilities.optparser import OptParser, Option
from core.node.node import ACTION_ASYNC

PROG = "om node"

GLOBAL_OPT = Storage({
    "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"),
    "debug": Option(
        "--debug", default=False,
        action="store_true", dest="debug",
        help="Increase stream log verbosity up to the debug level."),
    "format": Option(
        "--format", default=None,
        action="store", dest="format",
        help="Specify a data formatter. Possible values are json, flat_json, "
              "csv or table. csv and table formatters are available only for "
              "commands returning tabular data."),
    "filter": Option(
        "--filter", default="",
        action="store", dest="jsonpath_filter",
        help="A JSONPath expression to filter a JSON output."),
    "help": Option(
        "-h", "--help", default=None,
        action="store_true", dest="parm_help",
        help="Show this help message and exit"),
    "local": Option(
        "--local", default=False,
        action="store_true", dest="local",
        help="Set to disable cluster-wide operations."),
    "node": Option(
        "--node", default="",
        action="store", dest="node",
        help="A node selector expression. Embedded in requests for the daemon to route and multiplex the request to a list of nodes. If not specified the local node is targeted."),
    "server": Option(
        "--server", default="",
        action="store", dest="server",
        help="The server uri to send a request to. If not specified the local node is targeted. Supported schemes are https and raw. The default scheme is https. The default port is 1214 for the raw scheme, and 1215 for https. The uri can be a fullpath to a listener socket. In this case, the scheme is deduced from the socket. Examples: raw://1.2.3.4:1214, https://relay.opensvc.com, /var/lib/opensvc/lsnr/h2.sock."),
})

GLOBAL_OPTS = [GLOBAL_OPT[opt] for opt in GLOBAL_OPT]

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."),
    "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'."),
    "cluster": Option(
        "--cluster", default=False,
        action="store_true", dest="cluster",
        help="If set, the action is executed on all cluster nodes via each "
             "node's listener."),
    "cron": Option(
        "--cron", default=False,
        action="store_true", dest="cron",
        help="If set, the action is actually executed impersonating the "
             "scheduler thread."),
    "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:`om node get` action, the printed value of "
             ":opt:`--param` is evaluated, scoped and dereferenced. If set "
             "with the :cmd:`om node set` action, the current value is "
             "evaluated before mangling."),
    "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."),
    "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."),
    "id": Option(
        "--id", default=0,
        action="store", dest="id",
        help="Specify an 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"),
    "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"
             "  * ``-=`` remove value from the current list\n"
             "  * ``+=`` append value to the current list\n"
             "  * ``|=`` append value to the current list if not already included\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."),
    "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."),
    "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:`om node 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."),
    "since": Option(
        "--since", default=None,
        action="store", dest="since",
        help="Limit result to log since timestamp "
             "from the log file tail. Default is None."),
    "stats_dir": Option(
        "--stats-dir", default=None,
        action="store", dest="stats_dir",
        help="Points the directory where the metrics files are "
             "stored for pushstats."),
    "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:`om node pushasset --sync` before a compliance run makes sure "
             "the pushed data has hit the collector database before the "
             "rulesets are contextualized."),
    "tag_name": Option(
        "--name", default=None,
        action="store", dest="name",
        help="The tag name, as shown by :cmd:`om node collector list tags`."),
    "tag_data": Option(
        "--data", default=None,
        action="store", dest="data",
        help="The data stored with the tag. Typed tags, like <name>::<type> expect a particular data structure."),
    "tag_attach_data": Option(
        "--attach-data", default=None,
        action="store", dest="attach_data",
        help="The data stored with the tag attachment. Typed tags, like <name>::<type> expect a particular data structure."),
    "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."),
    "time": Option(
        "--time", default="5m",
        action="store", dest="time",
        help="Number of seconds to wait for an async action to "
             "finish. The default is 5 minutes."),
    "until": Option(
        "--until", default=None,
        action="store", dest="until",
        help="Limit result to log until timestamp "
             "from the log file tail. Default is None."),
    "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:`om node 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:`om node print schedule`."),
    "wait": Option(
        "--wait", default=False,
        action="store_true", dest="wait",
        help="Wait for asynchronous action termination"),
})

OPT.update(GLOBAL_OPT)

ASYNC_OPTS = [
    OPT.time,
    OPT.wait,
]

ACTIONS = {
    "Node actions": {
        "auto_reboot": {
            "msg": "Reboot the node if in the specified schedule.",
            "options": [
                OPT.cron,
            ],
        },
        "drain": {
            "msg": "Freeze the selected nodes and shutdown all object "
                   "instances they run. If not specified with --node, "
                   "the local node is selected for drain.",
            "options": [
                OPT.wait,
                OPT.time,
            ],
        },
        "events": {
            "msg": "Follow the daemon events feed. Two kinds of event "
                   "can be received: event and patch. Patch data "
                   "applies to the daemon status structure.",
        },
        "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.",
            "options": ASYNC_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.",
            "options": ASYNC_OPTS,
        },
        "logs": {
            "msg": "Display the node and daemon logs.",
            "options": [
                OPT.backlog,
                OPT.follow,
                OPT.since,
                OPT.until,
            ]
        },
        "ping": {
            "msg": "Ping a cluster node or arbitrator node. The ping "
                   "validates the remote is functional.",
        },
        "print_capabilities": {
            "msg": "Display the node capabilities scanned and cached by the "
                   "agent.",
        },
        "shutdown": {
            "msg": "Shutdown the node to powered off state.",
        },
        "reboot": {
            "msg": "Reboot the node.",
        },
        "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 the node or cluster "
                   "configuration.",
        },
        "array_ls": {
            "msg": "List the configured arrays.",
        },
        "array_show": {
            "msg": "Show the configured arrays with their name and type properties.",
        },
        "updatepkg": {
            "msg": "Upgrade the opensvc agent version. the packages must be "
                   "available behind the node.repo/packages url, or behind "
                   "a mirrored node.repopkg url.",
        },
        "updateclumgr": {
            "msg": "Upgrade the opensvc cluster manager version. the bundles "
                   "must be available behind the node.repo/cluster-manager "
                   "url, or behind a mirrored node.repopkg url.",
        },
        "updatecomp": {
            "msg": "Upgrade the opensvc compliance modules. The modules must "
                   "be available as a tarball behind the :kw:`node.repocomp` "
                   "url.",
        },
        "update_ssh_authorized_keys": {
            "msg": "Install the root pubkey of each node in the local root "
                   "user authorized_keys.",
        },
        "scan_capabilities": {
            "msg": "Scan the node for capabilities. Capabilities are normaly "
                   "scanned at daemon startup and when the installed system "
                   "packages change, so admins only have to use this when "
                   "they want manually installed software to be discovered "
                   "without restarting the daemon.",
            "options": [],
        },
        "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,
            ],
        },
        "ls": {
            "msg": "List the cluster nodes matching the --node selector. "
                   "Useful to validate selector expressions.",
        },
        "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.",
        },
        "snooze": {
            "msg": "Snooze alerts on the node for :opt:`--duration`",
            "options": [
                OPT.duration,
            ],
        },
        "unsnooze": {
            "msg": "Unsnooze alerts on the node",
            "options": [],
        },
        "wait": {
            "msg": "Wait for the condition given by --filter <condition> to "
                   "become true.\n\n"
                   "condition := [!]<jsonpath>[<op><val>]\n"
                   "jsonpath : a jsonpath in the cluster data as reported by "
                   "'om node daemon status --format flat_json'\n"
                   "op := { = | > | >= | < | <= | ~ | in }\n\n"
                   "'~' is a fullmatch of the <val> regular expression unless "
                   "'^' or '$' are specified.\n"
                   "'in' supports comma-separated or json list format.\n"
                   "'!' is the negation operator.\n"
                   "If '<op><val>' is not specified, any value evaluated as "
                   "True is considered a match (non-zero numerics, non-empty "
                   "lists, non-emptry strings).",
            "options": [
                OPT.duration,
                OPT.verbose,
            ]
        },
        "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,
            ],
        },
    },
    "Node configuration": {
        "print_config": {
            "msg": "Display the node current configuration.",
        },
        "edit_config": {
            "msg": "Edit the node configuration.",
            "options": [
                OPT.discard,
                OPT.recover,
            ],
        },
        "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,
            ],
        },
        "eval": {
            "msg": "Evaluate the value of a service configuration keyword.",
            "options": [
                OPT.impersonate,
                OPT.kw,
            ],
        },
        "delete": {
            "msg": "Delete node configuration sections pointed by --kw options.",
            "options": [
                OPT.kw,
            ],
        },
        "get": {
            "msg": "Get the raw value of a node configuration keyword.",
            "options": [
                OPT.eval,
                OPT.impersonate,
                OPT.param,
                OPT.kw,
            ],
        },
        "set": {
            "msg": "Set a service configuration parameter.",
            "options": [
                OPT.add,
                OPT.eval,
                OPT.kw,
                OPT.index,
                OPT.param,
                OPT.remove,
                OPT.value,
            ],
        },
        "unset": {
            "msg": "Unset a node configuration parameter.",
            "options": [
                OPT.param,
                OPT.kw,
            ],
        },
        "validate_config": {
            "msg": "Check the section names and keywords are valid.",
        },
    },
    "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,
            ],
        },
        "pushdorado": {
            "msg": "Push dorado configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
        },
        "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,
            ],
        },
        "pushhcs": {
            "msg": "Push Hitachi Command Suite array configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
        },
        "pushfreenas": {
            "msg": "Push FreeNAS configurations to the collector.",
            "options": [
                OPT.cron,
                OPT.opt_object,
            ],
        },
        "pushpure": {
            "msg": "Push Pure Storage 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. The --force option resend all monitored "
                   "files and outputs to the collector instead of only those "
                   "that changed since the last sysreport.",
            "options": [
                OPT.cron,
                OPT.force,
            ],
        },
        "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`.",
            "options": [
                OPT.filterset,
            ],
        },
        "collector_list_services": {
            "msg": "Show the list of services matching the filterset pointed "
                   "by :opt:`--filterset`.",
            "options": [
                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_attach": {
            "msg": "Set a node tag (pointed by --tag).",
            "options": [
                OPT.tag_name,
                OPT.tag_attach_data,
            ],
        },
        "collector_tag_detach": {
            "msg": "Unset a node tag (pointed by --tag).",
            "options": [
                OPT.tag_name,
            ],
        },
        "collector_tag_show": {
            "msg": "list all node tags",
            "options": [
                OPT.verbose,
            ],
        },
        "collector_tag_list": {
            "msg": "List all available tags. Use :opt:`--like` to filter the output.",
            "options": [
                OPT.like,
            ],
        },
        "collector_tag_create": {
            "msg": "Create a new tag with name specified by :opt:`--tag`.",
            "options": [
                OPT.tag_name,
                OPT.tag_data,
            ],
        },
        "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_OPTIONS = []

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 NodeOptParser(OptParser):
    """
    The node management command 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_options=DEPRECATED_OPTIONS,
                           deprecated_actions=DEPRECATED_ACTIONS,
                           actions_translations=ACTIONS_TRANSLATIONS,
                           global_options=GLOBAL_OPTS,
                           colorize=colorize, width=width,
                           formatter=formatter, indent=indent, async_actions=ACTION_ASYNC)

   0707010001f152000081a40000000000000000000000016a100daf0000074f000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/commands/node/__init__.py  from __future__ import print_function
from __future__ import absolute_import

import os
import sys

import utilities.render.color
import core.exceptions as ex
from env import Env
from commands.node.parser import NodeOptParser
from core.node import Node
from utilities.proc import get_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 = NodeOptParser(argv)
    options, action = optparser.parse_args(argv)
    options.extra_argv = extra_argv

    utilities.render.color.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.Error:
        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()

    try:
        return _main(node, argv=argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        return 1
    except ex.Version as exc:
        print(exc)
        return 0
    finally:
        node.close()

if __name__ == "__main__":
    ret = main()
    sys.exit(ret)

 0707010001f27e000041ed00000000000000000000000a6a102a9300000000000000e600010003ffffffffffffffff0000002700000000root/usr/share/opensvc/opensvc/drivers    0707010001f300000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/drivers/cloud  0707010001f305000081a40000000000000000000000016a100daf00000760000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/cloud/vcloud.py    import core.cloud
import core.exceptions 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.InitError("apache-libcloud module must be installed")

class Cloud(core.cloud.BaseCloud):
    mode = 'vcloud'

    def __init__(self, s, auth):
        super(Cloud, self).__init__(s, auth)
        if 'username' not in auth:
            raise ex.InitError("option 'username' is mandatory in vcloud section")
        if 'password' not in auth:
            raise ex.InitError("option 'password' is mandatory in vcloud section")
        if 'manager' not in auth:
            raise ex.InitError("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, name):
        return name.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_names(self):
        l = []
        _id = self.app_cloud_id()
        try:
            vapps = self.driver.list_nodes()
        except socket.error as e:
            raise ex.Error("error connecting to %s cloud manager" % self.cid)
        for vapp in vapps:
            __id = '.'.join((vapp.name, _id))
            for vm in vapp.extra['vms']:
                name = '.'.join((vm['name'], __id))
                l.append((vm['name'], name))
        return l

0707010001f301000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/cloud/__init__.py  0707010001f302000081a40000000000000000000000016a100daf000004ee000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/cloud/amazon.py    import core.cloud
import core.exceptions as ex

try:
    #from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    #import libcloud.security
except ImportError:
    raise ex.InitError("apache-libcloud module must be installed")

class Cloud(core.cloud.BaseCloud):
    mode = 'amazon'

    def __init__(self, s, auth):
        super(Cloud, self).__init__(s, auth)
        if 'access_key_id' not in auth:
            raise ex.InitError("option 'access_key_id' is mandatory in amazon section")
        if 'provider' not in auth:
            raise ex.InitError("option 'provider' is mandatory in amazon section")
        if 'secret_key' not in auth:
            raise ex.InitError("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, name):
        return name.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_names(self):
        l = []
        return l

  0707010001f304000081a40000000000000000000000016a100daf0000084e000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/cloud/openstack.py import core.cloud
import core.exceptions as ex

try:
    from libcloud.compute.types import Provider
    from libcloud.compute.providers import get_driver
    import libcloud.security
except ImportError:
    raise ex.InitError("apache-libcloud module must be installed")

class Cloud(core.cloud.BaseCloud):
    mode = 'openstack'

    def __init__(self, s, auth):
        super(Cloud, self).__init__(s, auth)
        kwargs = {}

        if 'username' not in auth:
            raise ex.InitError("option 'username' is mandatory in %s section"%self.mode)

        if 'password' not in auth:
            raise ex.InitError("option 'password' is mandatory in %s section"%self.mode)

        if 'url' not in auth:
            raise ex.InitError("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, name=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_names(self):
        l = []
        _id = self.app_cloud_id()
        for node in self.list_nodes():
            name = '.'.join((node.name, _id))
            l.append((node.name, name))
        return l

  0707010001f303000081a40000000000000000000000016a100daf0000054f000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/cloud/gandi.py import core.cloud
import core.exceptions 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.InitError("apache-libcloud module must be installed")

class Cloud(core.cloud.BaseCloud):
    mode = 'gandi'

    def __init__(self, s, auth):
        super(Cloud, self).__init__(s, auth)
        if 'key' not in auth:
            raise ex.InitError("option 'key' is mandatory in gandi section")
        gandi = get_driver(Provider.GANDI)
        try:
            self.driver = gandi(auth['key'])
        except Exception as e:
            raise ex.InitError("error login to gandi cloud %s: %s"%(s, str(e)))

    def app_id(self):
        return ''

    def cloud_id(self):
        return self.mode

    def app_cloud_id(self):
        return self.mode

    def list_names(self):
        l = []
        _id = self.app_cloud_id()
        try:
            vapps = self.driver.list_nodes()
        except socket.error as e:
            raise ex.Error("error connecting to %s cloud manager" % self.cid)
        for vapp in vapps:
            __id = '.'.join((vapp.name, _id))
            for vm in vapp.extra['vms']:
                name = '.'.join((vm['name'], __id))
                l.append((vm['name'], name))
        return l

 0707010001f294000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/drivers/backupsrv  0707010001f295000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/backupsrv/__init__.py  0707010001f296000081a40000000000000000000000016a100daf000004bf000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/backupsrv/networker.py import os
import socket

import foreign.six as six
import core.exceptions as ex
from utilities.proc import justcall, which

class Nsr(object):
    def __init__(self):
        if not which('mminfo'):
            raise ex.Error('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 six.text_type('\n'.join(lines), errors='ignore')


if __name__ == "__main__":
    o = Nsr()
    print(o.get_mminfo())
 0707010001f27f000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/__init__.py    0707010001f309000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000002c00000000root/usr/share/opensvc/opensvc/drivers/pool   0707010001f318000081a40000000000000000000000016a100daf0000097c000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/pool/virtual.py    from __future__ import print_function

import core.exceptions as ex
from utilities.naming import is_service, split_path, factory
from utilities.lazy import lazy
from core.pool import BasePool

class Pool(BasePool):
    type = "virtual"
    capabilities = []

    def __init__(self, *args, **kwargs):
        super(Pool, self).__init__(*args, **kwargs)
        self.capabilities = self.oget("capabilities")

    @lazy
    def template(self):
        return self.oget("template")

    @lazy
    def volume_env(self):
        return self.oget("volume_env")

    @lazy
    def optional_volume_env(self):
        return self.oget("optional_volume_env")

    def configure_volume(self, volume, size=None, fmt=True, access="rwo", shared=False, nodes=None, env=None):
        if self.template is None:
            raise ex.Error("pool#%s.template is not set" % self.name)
        if not is_service(self.template):
            raise ex.Error("%s template volume not found" % self.template)
        name = self.default_disk_name(volume)
        tname, tnamespace, tkind = split_path(self.template)
        if tkind != "vol":
            raise ex.Error("%s template kind is not vol")
        svc = factory(tkind)(tname, tnamespace, volatile=True, node=self.node)
        config = svc.print_config_data()
        try:
            del config["DEFAULT"]["disable"]
        except KeyError:
            pass
        if "DEFAULT" not in config:
            config["DEFAULT"] = {}
        if "env" not in config:
            config["env"] = {}
        config["DEFAULT"]["pool"] = self.name
        config["DEFAULT"]["access"] = access
        if access in ("rox", "rwx"):
            config["DEFAULT"]["topology"] = "flex"
            config["DEFAULT"]["flex_min"] = 0
        if nodes:
            config["DEFAULT"]["nodes"] = nodes
        config["env"]["size"] = size
        if env:
            config["env"].update(env)
        if volume.volatile:
            return volume
        self.node.install_svc_conf_from_data(volume.name, volume.namespace, volume.kind, config)
        vol = factory("vol")(name=volume.name, namespace=volume.namespace, node=self.node)
        vol.freezer.thaw()
        return vol

    def pool_status(self, usage=True):
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.template,
        }
        return data

0707010001f315000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002f00000000root/usr/share/opensvc/opensvc/drivers/pool/vg    0707010001f316000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/pool/vg/__init__.py    0707010001f317000081a40000000000000000000000016a100daf000005af000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/pool/vg/linux.py   from __future__ import print_function

from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "vg"
    capabilities = ["rox", "rwx", "roo", "rwo", "snap", "blk"]

    @lazy
    def vg(self):
        return self.oget("name")

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        disk = {
            "rtype": "disk",
            "type": "lv",
            "name": name,
            "vg": self.vg,
            "size": size,
        }
        if self.mkblk_opt:
            disk["create_options"] = " ".join(self.mkblk_opt)
        data.append(disk)
        if fmt:
            data += self.add_fs(name, shared)
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.vg,
        }
        if not usage:
            return data
        cmd = ["vgs", "-o", "Size,Free", "--units", "k", "--noheadings", self.vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return data
        l = out.splitlines()[-1].split()
        data["free"] = int(l[1].split(".")[0].split("k")[0])
        data["size"] = int(l[0].split(".")[0].split("k")[0])
        data["used"] = data["size"] - data["free"]
        return data

 0707010001f311000081a40000000000000000000000016a100daf00001187000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/pool/pure.py   from __future__ import print_function

import sys

import core.exceptions as ex
from utilities.lazy import lazy
from env import Env
from drivers.array.pure import Arrays
from core.pool import BasePool

class Pool(BasePool):
    type = "pure"
    capabilities = ["rox", "rwx", "roo", "rwo", "shared", "blk", "fc"]


    @lazy
    def lock_name(self):
        return "pure_%s_create_disk" % self.array_name


    def delete_disk(self, name=None, disk_id=None):
        result = self.delete_disk_simple(disk_id=disk_id)
        return result

    def serial_from_disk_id(self, disk_id):
        if disk_id is None:
            return
        return disk_id[8:]

    def delete_disk_simple(self, disk_id=None):
        serial = self.serial_from_disk_id(disk_id)
        return self.array.del_disk(serial=serial, now=self.delete_now)

    def array_nodes(self, nodes=None):
        if nodes is None:
            nodes = self.node.cluster_nodes
        data = {}
        for node in nodes:
            an = self.oget("array", impersonate=node)
            if an not in data:
                data[an] = [node]
            else:
                data[an] += [node]
        return data


    def default_disk_name(self, volume):
        return "%s%s" % (
            self.oget("label_prefix") or self.node.cluster_id.split("-")[0] + "-",
            volume.id.split("-")[0],
        )

    def sep(self):
        return "-"

    def create_disk(self, name, size, nodes=None):
        return self.create_disk_simple(name, size, nodes=nodes)

    def create_disk_simple(self, name, size, nodes=None, array=None):
        if array is None:
            array = self.array
        if nodes:
            mappings = self.get_mappings(nodes)
            if not mappings:
                raise ex.Error("refuse to create a disk with no mappings")
        else:
            mappings = None
        lock_id = None
        if self.pod:
            name = self.pod + "::" + name
        elif self.volumegroup:
            name = self.volumegroup + "/" + name
        result = array.add_disk(name=name,
                                size=size,
                                pod=self.pod,
                                volumegroup=self.volumegroup,
                                mappings=mappings)
        return result


    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        disk = {
            "rtype": "disk",
            "type": "disk",
            "name": name,
            "scsireserv": True,
            "shared": shared,
            "size": size,
        }
        data.append(disk)
        if fmt:
            data += self.add_fs(name, shared)
        return data


    @property
    def delete_now(self):
        return self.oget("delete_now")

    @property
    def volumegroup(self):
        return self.oget("volumegroup")

    @property
    def pod(self):
        return self.oget("pod")

    @property
    def array_name(self):
        return self.oget("array")

    @lazy
    def array(self):
        o = Arrays()
        array = o.get_array(self.array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array

    @property
    def head(self):
        if self.pod:
            return self.pod
        elif self.volumegroup:
            return self.volumegroup
        else:
            return ""

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "head": "array://%s/%s" % (self.array_name, self.head),
            "capabilities": self.capabilities,
        }
        if not usage:
            return data
        try:
            status = self.array.get_arrays()[0]
            space = status["space"]
        except Exception as exc:
            print(exc)
            data["error"] = str(exc)
            return data
        data["size"] = status["capacity"] / 1024
        data["used"] = space["total_physical"] / 1024
        data["free"] = data["size"] - data["used"]
        return data


    def get_targets(self):
        qfilter = "services='scsi-fc' and enabled='true'"
        tgts = [tgt["fc"]["wwn"].replace(":", "").lower() for tgt in self.array.get_network_interfaces(qfilter=qfilter)]
        return tgts

    def get_mappings(self, nodes):
        return self._get_mappings(nodes, transport="fc")
 0707010001f30b000081a40000000000000000000000016a100daf000006eb000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/pool/directory.py  from __future__ import print_function

import os

from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "directory"
    capabilities = ["rox", "rwx", "roo", "rwo", "blk"]

    @lazy
    def path(self):
        return self.oget("path")

    def translate_blk(self, name=None, size=None, shared=False):
        data = [
            {
                "rtype": "disk",
                "type": "loop",
                "file": os.path.join(self.path, "%s.img" % name),
                "size": size,
            }
        ]
        return data

    def translate(self, name=None, size=None, fmt=True, shared=False):
        if not fmt:
            return self.translate_blk(name=name, size=size, shared=shared)
        data = []
        path = os.path.join(self.path, name)
        data.append({
            "rtype": "fs",
            "type": "flag",
        })
        data.append({
            "rtype": "fs",
            "type": "directory",
            "path": path,
        })
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        if not os.path.exists(self.path):
            os.makedirs(self.path)
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.path,
        }
        if not usage:
            return data
        cmd = ["df", "-P", self.path]
        out, err, ret = justcall(cmd)
        if ret != 0:
            data["error"] = err
            return data
        l = out.splitlines()[-1].split()
        data["free"] = int(l[3])
        data["used"] = int(l[2])
        data["size"] = int(l[1])
        return data

 0707010001f30d000081a40000000000000000000000016a100daf00001999000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/pool/drbd.py   from __future__ import print_function

import os

from env import Env
from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "drbd"
    capabilities = ["rox", "rwx", "roo", "rwo", "snap", "blk", "shared"]

    @lazy
    def vg(self):
        return self.oget("vg")

    @lazy
    def zpool(self):
        return self.oget("zpool")

    @lazy
    def max_peers(self):
        return self.oget("max_peers")

    @lazy
    def network(self):
        return self.oget("network")

    @lazy
    def addrs(self):
        return self.oget_scopes("addr")

    @lazy
    def path(self):
        return self.oget("path") or os.path.join(Env.paths.pathvar, "pool", self.name)

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        if self.vg:
            disk = {
                "rtype": "disk",
                "type": "lv",
                "name": name,
                "vg": self.vg,
                "size": size,
                "standby": True,
            }
            if self.mkblk_opt:
                disk["create_options"] = " ".join(self.mkblk_opt)
            data.append(disk)
            disk = {
                "rtype": "disk",
                "type": "drbd",
                "res": name,
                "disk": "/dev/%s/%s" % (self.vg, name),
                "standby": True,
            }
            if self.network:
                disk["network"] = self.network
            for nodename, v in self.addrs.items():
                if v:
                    disk["addr@"+nodename] = v
            if self.max_peers != 0:
                disk["max_peers"] = self.max_peers
            data.append(disk)
            dev = "disk#2"
        elif self.zpool:
            disk = {
                "rtype": "disk",
                "type": "zvol",
                "name": "/".join([self.zpool, name]),
                "size": size,
                "standby": True,
            }
            if self.mkblk_opt:
                disk["create_options"] = " ".join(self.mkblk_opt)
            data.append(disk)
            disk = {
                "rtype": "disk",
                "type": "drbd",
                "res": name,
                "disk": "/dev/zvol/%s/%s" % (self.zpool, name),
                "standby": True,
            }
            if self.network:
                disk["network"] = self.network
            for nodename, v in self.addrs.items():
                if v:
                    disk["addr@"+nodename] = v
            if self.max_peers != 0:
                disk["max_peers"] = self.max_peers
            data.append(disk)
            dev = "disk#2"
        else:
            disk = {
                "rtype": "disk",
                "type": "loop",
                "file": os.path.join(self.path, name + ".img"),
                "size": size,
                "standby": True,
            }
            data.append(disk)
            disk = {
                "rtype": "disk",
                "type": "vg",
                "pvs": os.path.join(self.path, name + ".img"),
                "name": name,
                "standby": True,
            }
            if self.max_peers != 0:
                disk["max_peers"] = self.max_peers
            data.append(disk)
            disk = {
                "rtype": "disk",
                "type": "lv",
                "name": "lv",
                "vg": name,
                "size": "100%FREE",
                "standby": True,
            }
            if self.mkblk_opt:
                disk["create_options"] = " ".join(self.mkblk_opt)
            data.append(disk)
            disk = {
                "rtype": "disk",
                "type": "drbd",
                "res": name,
                "disk": "/dev/%s/lv" % name,
                "standby": True,
            }
            if self.network:
                disk["network"] = self.network
            for nodename, v in self.addrs.items():
                if v:
                    disk["addr@"+nodename] = v
            if self.max_peers != 0:
                disk["max_peers"] = self.max_peers
            data.append(disk)
            dev = "disk#4"

        if fmt:
            data += self.add_fs(name, shared, dev=dev)
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        if self.zpool:
            data = {
                "type": self.type,
                "name": self.name,
                "capabilities": self.capabilities,
                "head": self.zpool,
            }
            if not usage:
                return data
            cmd = ["zpool", "get", "-H", "size,alloc,free", "-p", self.zpool]
            out, err, ret = justcall(cmd)
            if ret != 0:
                data["error"] = err
                return data
            lines = out.splitlines()
            data["size"] = convert_size(lines[0].split()[2], default_unit="", _to="kb")
            data["used"] = convert_size(lines[1].split()[2], default_unit="", _to="kb")
            data["free"] = convert_size(lines[2].split()[2], default_unit="", _to="kb")
            return data
        elif self.vg:
            data = {
                "type": self.type,
                "name": self.name,
                "capabilities": self.capabilities,
                "head": self.vg,
            }
            if not usage:
                return data
            cmd = ["vgs", "-o", "Size,Free", "--units", "k", "--noheadings", self.vg]
            out, err, ret = justcall(cmd)
            if ret != 0:
                return data
            l = out.splitlines()[-1].split()
            data["free"] = int(l[1].split(".")[0].rstrip("k"))
            data["size"] = int(l[0].split(".")[0].rstrip("k"))
            data["used"] = data["size"] - data["free"]
            return data
        else:
            if not os.path.exists(self.path):
                os.makedirs(self.path)
            data = {
                "type": self.type,
                "name": self.name,
                "capabilities": self.capabilities,
                "head": self.path,
            }
            if not usage:
                return data
            cmd = ["df", "-P", self.path]
            out, err, ret = justcall(cmd)
            if ret != 0:
                return data
            l = out.splitlines()[-1].split()
            data["free"] = int(l[3])
            data["used"] = int(l[2])
            data["size"] = int(l[1])
            return data

   0707010001f30a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/pool/__init__.py   0707010001f310000081a40000000000000000000000016a100daf000005cb000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/pool/loop.py   from __future__ import print_function

import os

from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "loop"
    capabilities = ["rox", "rwx", "roo", "rwo", "blk"]

    @lazy
    def path(self):
        return self.oget("path")

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = [
            {
                "rtype": "disk",
                "type": "loop",
                "file": os.path.join(self.path, "%s.img" % name),
                "size": size,
            }
        ]
        if fmt:
            data += self.add_fs(name, shared)
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        if not os.path.exists(self.path):
            os.makedirs(self.path)
        data = {
            "name": self.name,
            "type": self.type,
            "capabilities": self.capabilities,
            "head": self.path,
        }
        if not usage:
            return data
        cmd = ["df", "-P", self.path]
        out, err, ret = justcall(cmd)
        if ret != 0:
            data["err"] = err
            return data
        l = out.splitlines()[-1].split()
        data["free"] = convert_size(l[3], default_unit="K", _to="k")
        data["used"] = convert_size(l[2], default_unit="K", _to="k")
        data["size"] = convert_size(l[1], default_unit="K", _to="k")
        return data

 0707010001f30f000081a40000000000000000000000016a100daf000012a1000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/pool/hcs.py    from __future__ import print_function

import sys

import core.exceptions as ex
from utilities.lazy import lazy
from env import Env
from drivers.array.hcs import Hcss
from core.pool import BasePool

def session(fn):
    """
    A decorator for caching the result of a function
    """
    attr_name = '_fcache_' + fn.__name__

    def wrapper(self, *args, **kwargs):
        try:
            data = fn(self, *args, **kwargs)
        finally:
            self.array.close_session()
        return data

    return wrapper

class Pool(BasePool):
    type = "hcs"
    capabilities = ["rox", "rwx", "roo", "rwo", "shared", "blk", "fc"]


    @lazy
    def lock_name(self):
        return "hcs_%s_create_disk" % self.array_name


    def delete_disk(self, name=None, disk_id=None):
        result = self.delete_disk_simple(name=name, disk_id=disk_id)
        return result


    @session
    def delete_disk_simple(self, name=None, disk_id=None):
        return self.array.del_disk(name=name, naa=disk_id)


    def array_nodes(self, nodes=None):
        if nodes is None:
            nodes = self.node.cluster_nodes
        data = {}
        for node in nodes:
            an = self.oget("array", impersonate=node)
            if an not in data:
                data[an] = [node]
            else:
                data[an] += [node]
        return data


    def default_disk_name(self, volume):
        return "%s%s" % (
            self.oget("label_prefix") or self.node.cluster_id.split("-")[0] + "-",
            volume.id.split("-")[0],
        )

    def create_disk(self, name, size, nodes=None):
        return self.create_disk_simple(name, size, nodes=nodes)



    @session
    def create_disk_simple(self, name, size, nodes=None, array=None):
        if array is None:
            array = self.array
        if nodes:
            mappings = self.get_mappings(nodes)
            if not mappings:
                raise ex.Error("refuse to create a disk with no mappings")
        else:
            mappings = None
        lock_id = None
        result = array.create_disk(name=name, size=size,
                                   pool=self.storagepool,
                                   start_ldev_id=self.start_ldev_id,
                                   end_ldev_id=self.end_ldev_id,
                                   resource_group=self.resource_group,
                                   compression=self.compression,
                                   dedup=self.dedup,
                                   mappings=mappings)
        return result


    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        disk = {
            "rtype": "disk",
            "type": "disk",
            "name": name,
            "scsireserv": True,
            "shared": shared,
            "size": size,
        }
        data.append(disk)
        if fmt:
            data += self.add_fs(name, shared)
        return data


    @property
    def resource_group(self):
        return self.oget("resource_group")

    @property
    def end_ldev_id(self):
        return self.oget("end_ldev_id")

    @property
    def start_ldev_id(self):
        return self.oget("start_ldev_id")

    @property
    def storagepool(self):
        return self.oget("diskgroup")

    @property
    def array_name(self):
        return self.oget("array")

    @property
    def compression(self):
        return self.oget("compression")

    @property
    def dedup(self):
        return self.oget("dedup")

    @lazy
    def array(self):
        o = Hcss(log=self.log)
        array = o.get_hcs(self.array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array


    @session
    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "head": "array://%s/%s" % (self.array_name, self.storagepool),
            "capabilities": self.capabilities,
        }
        if not usage:
            return data
        try:
            status = self.array.get_pool_by_name(name=self.storagepool)
        except Exception as exc:
            data["error"] = str(exc)
            return data
        data["size"] = convert_size(int(status["totalPhysicalCapacity"])*1024*1024, _to="KB")
        data["free"] = convert_size(int(status["availablePhysicalVolumeCapacity"])*1024*1024, _to="KB")
        data["used"] = data["size"] - data["free"]
        return data


    def get_targets(self):
        return [tgt["wwn"] for tgt in self.array.list_fc_port()]


    def get_mappings(self, nodes):
        return self._get_mappings(nodes, transport="fc")
   0707010001f314000081a40000000000000000000000016a100daf0000182a000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/pool/symmetrix.py  from __future__ import print_function

import sys

import core.exceptions as ex
from utilities.lazy import lazy
from env import Env
from drivers.array.symmetrix import Arrays
from core.pool import BasePool

LOCK_NAME = "symmetrix_create_disk"

class Pool(BasePool):
    type = "symmetrix"
    capabilities = ["roo", "rwo", "shared", "blk", "fc"]

    @lazy
    def slo(self):
        return self.oget("slo")

    @lazy
    def srp(self):
        return self.oget("srp")

    @lazy
    def srdf(self):
        return self.oget("srdf")

    @lazy
    def rdfg(self):
        return self.oget("rdfg")

    def delete_disk(self, name=None, disk_id=None):
        self.array.del_disk(dev=disk_id)

    def create_disk(self, name, size, nodes=None):
        if self.rdfg:
            return self.create_disk_srdf(name, size, nodes=nodes)
        else:
            return self.create_disk_simple(name, size, nodes=nodes)

    def create_disk_simple(self, name, size, nodes=None):
        mappings = self.get_mappings(nodes)
        if not mappings:
            raise ex.Error("refuse to create a disk with no mappings")
        lock_id = None
        try:
            lock_id = self.node._daemon_lock(LOCK_NAME, timeout=120, on_error="raise")
            self.log.info("lock acquired: name=%s id=%s", LOCK_NAME, lock_id)
            result = self.array.add_disk(name=name, size=size,
                                         srp=self.srp,
                                         srdf=self.srdf,
                                         rdfg=self.rdfg,
                                         mappings=mappings)
        finally:
            self.node._daemon_unlock(LOCK_NAME, lock_id)
            self.log.info("lock released: name=%s id=%s", LOCK_NAME, lock_id)
        return result

    def array_nodes(self, nodes=None):
        if nodes is None:
            nodes = self.node.cluster_nodes
        data = {}
        for node in nodes:
            an = self.oget("array", impersonate=node)
            if an not in data:
                data[an] = [node]
            else:
                data[an] += [node]
        return data

    def create_disk_srdf(self, name, size, nodes=None):
        ans = self.array_nodes(nodes)
        r1_nodes = ans[self.array_name]
        r2_nodes = ans[self.remote_array_name]
        r1_result = self.create_disk_simple(name=name, size=size, nodes=r1_nodes)
        dev_id = r1_result["disk_devid"]
        remote_mappings = self.get_mappings(r2_nodes)
        remote_dev_id = self.array.remote_dev_id(dev_id)
        self.log.info("local dev=%s.%s remote dev=%s.%s",
                      self.array.sid, dev_id,
                      self.remote_array.sid, remote_dev_id)
        try:
            lock_id = self.node._daemon_lock(LOCK_NAME, timeout=120, on_error="raise")
            self.log.info("lock acquired: name=%s id=%s", LOCK_NAME, lock_id)
            r2_result = self.remote_array.add_map(dev=remote_dev_id,
                                                  mappings=remote_mappings,
                                                  slo=self.slo,
                                                  srp=self.srp)
            self.log.info("%s", r2_result)
        finally:
            self.node._daemon_unlock(LOCK_NAME, lock_id)
            self.log.info("lock released: name=%s id=%s", LOCK_NAME, lock_id)
        result = {
            "r1": r1_result,
            "r2": r2_result,
            "disk_ids": {}
        }
        for node in r1_nodes:
            result["disk_ids"][node] = r1_result["disk_id"]
        for node in r2_nodes:
            result["disk_ids"][node] = r2_result["disk_id"]
        return result

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        data.append({
            "rtype": "disk",
            "type": "disk",
            "name": name,
            "scsireserv": True,
            "shared": shared,
            "size": size,
        })
        if fmt:
            data += self.add_fs(name, shared)
        return data

    @lazy
    def array_name(self):
        return self.oget("array")

    @lazy
    def remote_array_name(self):
        for node in self.node.cluster_nodes:
             if node == Env.nodename:
                 continue
             an = self.oget("array", impersonate=node)
             if an and an != self.array_name:
                 return an

    @lazy
    def array(self):
        o = Arrays()
        array = o.get_array(self.array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array

    @lazy
    def remote_array(self):
        o = Arrays()
        array = o.get_array(self.remote_array_name)
        if array is None:
            raise ex.Error("remote array %s not found" % self.remote_array_name)
        array.node = self.node
        return array

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "head": "array://%s/%s" % (self.array_name, self.srp),
            "capabilities": self.capabilities,
        }
        if not usage:
            return data
        try:
            dg = [dg for dg in self.array.get_srps() if dg["name"] == self.srp][0]
        except Exception as exc:
            data["error"] = str(exc)
            return data
        data["free"] = convert_size(dg["free_capacity_gigabytes"], default_unit="G", _to="KB")
        data["used"] = convert_size(dg["used_capacity_gigabytes"], default_unit="G", _to="KB")
        data["size"] = convert_size(dg["usable_capacity_gigabytes"], default_unit="G", _to="KB")
        return data

    def get_targets(self):
        tgts = []
        for director in self.array.get_directors():
            for port in director.get("Port", []):
                pinfo = port.get("Port_Info", {})
                if "node_wwn" in pinfo and "port_wwn" in pinfo:
                    tgts.append(pinfo["port_wwn"].lower())
        return tgts

    def get_mappings(self, nodes):
        return self._get_mappings(nodes, transport="fc")

  0707010001f30e000081a40000000000000000000000016a100daf00000f51000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/pool/freenas.py    from __future__ import print_function

import core.exceptions as ex
from utilities.lazy import lazy
from drivers.array.freenas import Freenass
from core.pool import BasePool

LOCK_NAME = "freenas_update_disk"
LOCK_TIMEOUT = 120


class Pool(BasePool):
    type = "freenas"
    capabilities = ["roo", "rwo", "rox", "rwx", "shared", "blk", "iscsi"]

    @lazy
    def insecure_tpc(self):
        return self.oget("insecure_tpc")

    @lazy
    def compression(self):
        return self.oget("compression")

    @lazy
    def sparse(self):
        return self.oget("sparse")

    @lazy
    def blocksize(self):
        return self.oget("blocksize")

    def delete_disk(self, name=None, disk_id=None):
        lock_id = None
        try:
            lock_id = self.node._daemon_lock(LOCK_NAME, timeout=LOCK_TIMEOUT, on_error="raise")
            self.log.info("lock acquired: name=%s id=%s", LOCK_NAME, lock_id)
            return self.array.del_iscsi_zvol(name=name, volume=self.diskgroup)
        finally:
            self.node._daemon_unlock(LOCK_NAME, lock_id)
            self.log.info("lock released: name=%s id=%s", LOCK_NAME, lock_id)

    def create_disk(self, name, size, nodes=None):
        mappings = self.get_mappings(nodes)
        if not mappings:
            raise ex.Error("refuse to create a disk with no mappings")
        lock_id = None
        try:
            lock_id = self.node._daemon_lock(LOCK_NAME, timeout=LOCK_TIMEOUT, on_error="raise")
            self.log.info("lock acquired: name=%s id=%s", LOCK_NAME, lock_id)
            return self.array.add_iscsi_zvol(name=name, size=size,
                                             volume=self.diskgroup,
                                             mappings=mappings,
                                             insecure_tpc=self.insecure_tpc,
                                             compression=self.compression,
                                             sparse=self.sparse,
                                             blocksize=self.blocksize)
        finally:
            self.node._daemon_unlock(LOCK_NAME, lock_id)
            self.log.info("lock released: name=%s id=%s", LOCK_NAME, lock_id)

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        disk = {
            "rtype": "disk",
            "type": "disk",
            "name": name,
            "scsireserv": True,
            "shared": shared,
            "size": size,
        }
        data.append(disk)
        if fmt:
            data += self.add_fs(name, shared)
        return data

    @lazy
    def array_name(self):
        return self.oget("array")

    @lazy
    def diskgroup(self):
        return self.oget("diskgroup")

    @lazy
    def array(self):
        o = Freenass()
        array = o.get_freenas(self.array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "head": "array://%s/%s" % (self.array_name, self.diskgroup),
            "capabilities": self.capabilities,
        }
        if not usage:
            return data
        try:
            dg = [dg for dg in self.array.list_pools() if dg["name"] == self.diskgroup][0]
        except Exception as exc:
            data["error"] = str(exc)
            return data
        data["free"] = convert_size(dg["avail"], _to="KB")
        data["used"] = convert_size(dg["used"], _to="KB")
        data["size"] = convert_size(dg["avail"] + dg["used"], _to="KB")
        return data

    def get_targets(self):
        return [tgt["name"] for tgt in self.array.list_iscsi_target()]

    def get_mappings(self, nodes):
        return self._get_mappings(nodes, transport="iscsi")
   0707010001f319000081a40000000000000000000000016a100daf00000771000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/pool/zpool.py  from __future__ import print_function

from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "zpool"
    capabilities = ["rox", "rwx", "roo", "rwo", "snap", "blk"]

    @lazy
    def zpool(self):
        return self.oget("name")

    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        if fmt:
            fs = {
                "rtype": "fs",
                "type": "zfs",
                "dev": self.zpool + "/" + name,
                "mnt": self.mount_point(name),
            }
            if self.mkfs_opt:
                fs["mkfs_opt"] = " ".join(self.mkfs_opt)
            if self.mnt_opt:
                fs["mnt_opt"] = self.mnt_opt
            data.append(fs)
        else:
            zvol = {
                "rtype": "disk",
                "type": "zvol",
                "dev": self.zpool + "/" + name,
            }
            if self.mkblk_opt:
                zvol["create_options"] = " ".join(self.mkblk_opt)
            data.append(zvol)
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.zpool,
        }
        if not usage:
            return data
        cmd = ["zpool", "get", "-H", "size,alloc,free", "-p", self.zpool]
        out, err, ret = justcall(cmd)
        if ret != 0:
            data["error"] = err
            return data
        lines = out.splitlines()
        data["size"] = convert_size(lines[0].split()[2], default_unit="", _to="kb")
        data["used"] = convert_size(lines[1].split()[2], default_unit="", _to="kb")
        data["free"] = convert_size(lines[2].split()[2], default_unit="", _to="kb")
        return data

   0707010001f312000081a40000000000000000000000016a100daf000006ca000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/pool/share.py  from __future__ import print_function

import os

from utilities.lazy import lazy
from utilities.proc import justcall
from core.pool import BasePool

class Pool(BasePool):
    type = "share"
    capabilities = ["rox", "rwx", "roo", "rwo", "blk", "shared"]

    @lazy
    def path(self):
        return self.oget("path")

    def translate_blk(self, path=None, size=None, shared=False):
        data = [
            {
                "rtype": "disk",
                "type": "loop",
                "file": "%s.img" % path,
                "size": size,
            }
        ]
        return data

    def translate(self, name=None, size=None, fmt=True, shared=False):
        if shared:
            path = os.path.join(self.path, name)
        else:
            path = os.path.join(self.path, "%s.{nodename}" % name)
        if not fmt:
            return self.translate_blk(path, size=size, shared=shared)

        fs = {
            "rtype": "fs",
            "type": "directory",
            "path": path,
        }
        return [fs]

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        if not os.path.exists(self.path):
            os.makedirs(self.path)
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.path,
        }
        if not usage:
            return data
        cmd = ["df", "-P", self.path]
        out, err, ret = justcall(cmd)
        if ret != 0:
            data["error"] = err
            return data
        l = out.splitlines()[-1].split()
        data["free"] = int(l[3])
        data["used"] = int(l[2])
        data["size"] = int(l[1])
        return data

  0707010001f313000081a40000000000000000000000016a100daf000009e7000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/pool/shm.py    from __future__ import print_function

import os

import core.exceptions as ex

from utilities.lazy import lazy
from utilities.converters import convert_size
from utilities.proc import justcall
from core.pool import BasePool
from env import Env

class Pool(BasePool):
    type = "shm"
    capabilities = ["rox", "rwx", "roo", "rwo", "blk"]

    @lazy
    def path(self):
        if Env.sysname == "FreeBSD":
            path = os.path.join(Env.paths.pathvar, "pool", "shm")
            self.freebsd_mount(path)
            return path
        return "/dev/shm"

    def freebsd_mount(self, path):
        from utilities.mounts.freebsd import Mounts
        m = Mounts()
        if m.has_mount("tmpfs", path):
            return
        out, err, ret = justcall(["mount", "-t", "tmpfs", "none", path])
        if ret:
            raise ex.Error("can not mount the 'shm' pool tmpfs in %s: %s" % (path, err))

    def translate_blk(self, name=None, size=None, shared=False):
        data = [
            {
                "rtype": "disk",
                "type": "loop",
                "file": os.path.join(self.path, "%s.img" % name),
                "size": size,
            }
        ]
        return data

    def translate(self, name=None, size=None, fmt=True, shared=False):
        if not fmt:
            return self.translate_blk(name=name, size=size, shared=shared)
        data = []
        path = os.path.join(self.path, name)
        size_opt = "size=%dm" % convert_size(size, _to="m")
        if self.mnt_opt:
            mnt_opt = ",".join((self.mnt_opt, size_opt))
        else:
            mnt_opt = size_opt
        data.append({
            "rtype": "fs",
            "type": "tmpfs",
            "dev": "shmfs",
            "mnt": self.mount_point(name),
            "mnt_opt": mnt_opt,
        })
        return data

    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        if not os.path.exists(self.path):
            os.makedirs(self.path)
        data = {
            "type": self.type,
            "name": self.name,
            "capabilities": self.capabilities,
            "head": self.path,
        }
        if not usage:
            return data
        cmd = ["df", "-P", self.path]
        out, err, ret = justcall(cmd)
        if ret != 0:
            data["error"] = err
            return data
        l = out.splitlines()[-1].split()
        data["free"] = int(l[3])
        data["used"] = int(l[2])
        data["size"] = int(l[1])
        return data

 0707010001f30c000081a40000000000000000000000016a100daf00001be0000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/pool/dorado.py from __future__ import print_function

import sys

import core.exceptions as ex
from utilities.lazy import lazy
from env import Env
from drivers.array.dorado import Dorados
from core.pool import BasePool

def session(fn):
    """
    A decorator for caching the result of a function
    """
    attr_name = '_fcache_' + fn.__name__

    def wrapper(self, *args, **kwargs):
        data = fn(self, *args, **kwargs)
        self.array.close_session()
        if self.hypermetrodomain:
            self.remote_array.close_session()
        return data

    return wrapper

class Pool(BasePool):
    type = "dorado"
    capabilities = ["roo", "rwo", "shared", "blk", "fc"]


    @lazy
    def lock_name(self):
        return "dorado_%s_create_disk" % self.array_name


    def delete_disk(self, name=None, disk_id=None):
        if self.hypermetrodomain:
            result = self.delete_disk_hypermetro(name=name, disk_id=disk_id)
        else:
            result = self.delete_disk_simple(name=name, disk_id=disk_id)
        return result


    @session
    def delete_disk_simple(self, name=None, disk_id=None):
        return self.array.del_disk(name=name, naa=disk_id)


    @session
    def delete_disk_hypermetro(self, name=None, disk_id=None):
        data = {}
        response = self.array.unmap(name=name, naa=disk_id)
        data["unmap_local"] = response
        response = self.remote_array.unmap(name=name, naa=disk_id)
        data["unmap_remote"] = response
        response = self.array.del_disk(name=name, naa=disk_id)
        data["del_disk_local"] = response
        response = self.remote_array.del_disk(name=name, naa=disk_id)
        data["del_disk_remote"] = response
        return data


    def array_nodes(self, nodes=None):
        if nodes is None:
            nodes = self.node.cluster_nodes
        data = {}
        for node in nodes:
            an = self.oget("array", impersonate=node)
            if an not in data:
                data[an] = [node]
            else:
                data[an] += [node]
        return data


    def create_disk(self, name, size, nodes=None):
        if self.hypermetrodomain:
            return self.create_disk_hypermetro(name, size, nodes=nodes)
        else:
            return self.create_disk_simple(name, size, nodes=nodes)


    @session
    def create_disk_simple(self, name, size, nodes=None, array=None):
        if array is None:
            array = self.array
        if nodes:
            mappings = self.get_mappings(nodes)
            if not mappings:
                raise ex.Error("refuse to create a disk with no mappings")
        else:
            mappings = None
        lock_id = None
        result = array.create_disk(name=name, size=size,
                                   storagepool=self.storagepool,
                                   compression=self.compression,
                                   hypermetrodomain=self.hypermetrodomain,
                                   dedup=self.dedup,
                                   mappings=mappings)
        return result


    @session
    def create_disk_hypermetro(self, name, size, nodes=None):
        if False:
            ans = self.array_nodes(nodes)
            r1_nodes = ans[self.array_name]
            r2_nodes = ans[self.remote_array_name]
            r2_mappings = self.get_mappings(r2_nodes)
        else:
            r1_nodes = nodes
            r2_nodes = nodes
            r2_mappings = self.get_mappings(r2_nodes)
        r1_result = self.create_disk_simple(name=name, size=size, nodes=r1_nodes, array=self.array)
        dev_id = r1_result["disk_devid"]
        self.log.info("local lun %s.%s created and mapped", self.array.name, dev_id)
        r2_result = self.create_disk_simple(name=name, size=size, nodes=None, array=self.remote_array)
        remote_dev_id = r2_result["disk_devid"]
        self.log.info("remote lun %s.%s created", self.remote_array.name, remote_dev_id)
        pair_result = self.array.pair_luns(dev_id, remote_dev_id, self.hypermetrodomain)
        self.log.info("paired")
        pair_result = self.array.synchronize_lun_hypermetropair(dev_id)
        self.log.info("synchronized")
        r2_mapping_result = self.remote_array.map_lun(id=remote_dev_id, mappings=r2_mappings)
        self.log.info("remote lun %s.%s mapped", self.remote_array.name, remote_dev_id)
        result = {
            "r1": r1_result,
            "r2": r2_result,
            "pair": pair_result,
            "r2_mapping": r2_mapping_result,
            "disk_id": r1_result.get("disk_id", ""),
        }
        return result


    def translate(self, name=None, size=None, fmt=True, shared=False):
        data = []
        disk = {
            "rtype": "disk",
            "type": "disk",
            "name": name,
            "scsireserv": True,
            "shared": shared,
            "size": size,
        }
        data.append(disk)
        if fmt:
            data += self.add_fs(name, shared)
        return data


    @lazy
    def hypermetrodomain(self):
        return self.oget("hypermetrodomain")


    @lazy
    def storagepool(self):
        return self.oget("diskgroup")


    @lazy
    def array_name(self):
        return self.oget("array")


    @lazy
    def remote_array_name(self):
        for node in self.node.cluster_nodes:
             if node == Env.nodename:
                 continue
             an = self.oget("array", impersonate=node)
             if an and an != self.array_name:
                 return an


    @lazy
    def compression(self):
        return self.oget("compression")


    @lazy
    def dedup(self):
        return self.oget("dedup")


    @lazy
    def array(self):
        o = Dorados()
        array = o.get_dorado(self.array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array


    @lazy
    def remote_array(self):
        o = Dorados()
        array = o.get_dorado(self.remote_array_name)
        if array is None:
            raise ex.Error("array %s not found" % self.array_name)
        array.node = self.node
        return array


    @session
    def pool_status(self, usage=True):
        from utilities.converters import convert_size
        data = {
            "type": self.type,
            "name": self.name,
            "head": "array://%s/%s" % (self.array_name, self.storagepool),
            "capabilities": self.capabilities,
        }
        if not usage:
            return data
        try:
            status = self.array.get_storagepool(name=self.storagepool)
        except Exception as exc:
            data["error"] = str(exc)
            return data
        data["size"] = convert_size(int(status["USERTOTALCAPACITY"])*512, _to="KB")
        data["free"] = convert_size(int(status["USERFREECAPACITY"])*512, _to="KB")
        data["used"] = data["size"] - data["free"]
        return data


    def get_targets(self):
        return [tgt["WWN"] for tgt in self.array.list_fc_port()]


    def get_mappings(self, nodes):
        return self._get_mappings(nodes, transport="fc")
0707010001f306000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002a00000000root/usr/share/opensvc/opensvc/drivers/pg 0707010001f307000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/pg/__init__.py 0707010001f308000081a40000000000000000000000016a100daf00005872000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/pg/linux.py    import os
import re
import glob
import core.exceptions as ex
from core.resource import Resource
from utilities.converters import convert_size

DRIVER_BASENAME = 'pg'

if os.path.exists("/sys/fs/cgroup/cgroup.procs"):
    UNIFIED_MNT = "/sys/fs/cgroup"
    UNIFIED = True
else:
    UNIFIED_MNT = "/sys/fs/cgroup/unified"
    UNIFIED = False

CONTROLLERS = [
    "blkio",
    "cpu",
    "cpuacct",
    "cpuset",
    "devices",
    "freezer",
    "hugetlb",
    "memory",
    "net_cls",
    "perf_event",
    "pids",
    "rdma",
    "systemd",
]

def get_cgroup_mntpt(t):
    if UNIFIED:
        return UNIFIED_MNT
    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(r'\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 get_task_file(cgp):
    if cgp.startswith(UNIFIED_MNT):
        return os.path.join(cgp, "cgroup.procs")
    else:
        return os.path.join(cgp, "tasks")

def set_task(o, t, has_subtree=False):
    o.log.debug("set_task : start %s" %(t))
    cgp = get_cgroup_path(o, t, has_subtree=has_subtree)
    path = get_task_file(cgp)
    if UNIFIED and not isinstance(o, Resource):
        # don't add a pid to a non-leaf cgroup to not block
        # cgroup.subtree_control writes (device busy error)
        return
    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 getattr(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, *args, **kwargs):
    try:
        _set_cgroup(o, *args, **kwargs)
    except Exception as exc:
        o.log.warning(exc)

def set_cgroup_value(o, *args, **kwargs):
    try:
        _set_cgroup_value(o, *args, **kwargs)
    except Exception as exc:
        o.log.warning(exc)

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]
    if value is None:
        return
    _set_cgroup_value(o, t, name, value, force=force)

def _set_cgroup_value(o, t, name, value, force=False):
    cgp = get_cgroup_path(o, t)
    cocgp = glob.glob(cgp+"/lxc.payload.*")
    err = None
    for _cgp in cocgp + [cgp] + cocgp:
        try:
            __set_cgroup(o, _cgp, value, t, name, force=force)
            err = None
        except Exception as e:
            err = e
    if err:
        o.log.warning("%s", err)

def __set_cgroup(o, cgp, value, t, name, force=False):
    if name == "memory.oom_control":
        current = get_current(cgp, name).split(os.linesep)[0].split()[-1]
    else:
        current = get_current(cgp, name).strip()
    if not force and current == str(value):
        return
    path = os.path.join(cgp, name)
    if not os.path.exists(path):
        raise ex.Error("can not find %s"%path)
    log = get_log(o)
    try:
        with open(path, 'w') as f:
            f.write(str(value))
        if log: log.info('/bin/echo %s > %s'%(value, path))
    except Exception:
        raise Exception("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)
    return get_current(cgp, name)

def get_current(cgp, name):
    path = os.path.join(cgp, name)
    if not os.path.exists(path):
        raise ex.Error("can not find %s"%path)
    with open(path, 'r') as f:
        buff = f.read()
    return buff

def set_cpu_quota_v2(o):
    try:
        v = str(o.pg_settings["cpu_quota"])
    except (AttributeError, KeyError):
        return
    o.log.debug("set_cpu_quota : start <%s>", v)

    period = 100000

    if "@" in v:
        try:
            quota, threads = v.split("@")
        except Exception as e:
            raise ex.Error("malformed cpu quota: %s (%s)" % (v, str(e)))
    else:
        threads = 1
        quota = v

    if "%" in quota:
        quota = int(quota.strip("%"))
    else:
        raise ex.Error("malformed cpu quota: %s (need 10%%, 10%%@2 or 10%%@all expressions)" % v)

    from utilities.asset import Asset
    total_threads = int(Asset(None)._get_cpu_threads())

    if threads == "all":
        threads = total_threads
    else:
        threads = int(threads)

    share = (quota * period * threads) // 100
    tgt_val = "%d %d" % (share, period)

    cur_val = get_cgroup(o, 'cpu', 'cpu.max')

    if tgt_val == cur_val:
        return

    set_cgroup_value(o, 'cpu', 'cpu.max', tgt_val)

def set_cpu_quota_v1(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, threads = v.split("@")
        except Exception as e:
            raise ex.Error("malformed cpu quota: %s (%s)" % (v, str(e)))
    else:
        threads = 1
        quota = v

    if threads == "all":
        from utilities.asset import Asset
        threads = int(Asset(None)._get_cpu_threads())
    else:
        threads = int(threads)

    total_us = period * threads

    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

    log = get_log(o)

    #
    # validate memory limits sanity and order adequately the resize
    # depending on increase/decrease of limits
    #
    if mem_limit is not None and vmem_limit is not None:
        if mem_limit > vmem_limit:
            if log: log.error("pg_vmem_limit must be greater than pg_mem_limit")
            raise ex.Error

    if UNIFIED:
        try:
            cur_vmem_limit = int(get_cgroup(o, 'memory', 'memory.max')) + int(get_cgroup(o, 'memory', 'memory.swap.max'))
        except (ValueError, ex.Error):
            cur_vmem_limit = None
        if mem_limit is not None and vmem_limit is not None:
            if vmem_limit < mem_limit:
                vmem_limit = mem_limit
            set_cgroup_value(o, 'memory', 'memory.swap.max', vmem_limit-mem_limit)
            set_cgroup_value(o, 'memory', 'memory.max', mem_limit)
        elif mem_limit is not None:
            set_cgroup_value(o, 'memory', 'memory.max', mem_limit)
        elif vmem_limit is not None:
                if log: log.error("pg_vmem_limit must not be set without pg_mem_limit")
                raise ex.Error
    else:
        try:
            cur_vmem_limit = int(get_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes'))
        except ex.Error:
            cur_vmem_limit = None
        if mem_limit is not None and vmem_limit is not None:
            if cur_vmem_limit and 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:
                if log: log.error("pg_mem_limit must not be greater than current pg_vmem_limit (%d)"%cur_vmem_limit)
                raise ex.Error
            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:
                if log: log.error("pg_vmem_limit must not be lesser than current pg_mem_limit (%d)"%cur_mem_limit)
                raise ex.Error
            set_cgroup(o, 'memory', 'memory.memsw.limit_in_bytes', 'vmem_limit')

def get_namespace(o):
    if hasattr(o, "namespace"):
        buff = o.namespace
    else:
        buff = o.svc.namespace
    return buff

def get_name(o):
    if hasattr(o, "path"):
        buff = o.name
    else:
        buff = o.svc.name
    return buff

def get_log(o):
    if hasattr(o, "log"):
        log = o.log
    elif hasattr(o, "svc"):
        log = o.svc.log
    else:
        log = None
    return log

def get_cgroup_ns_relpath(o, suffix=".slice"):
    namespace = get_namespace(o)
    elements = ["opensvc" + suffix]
    if namespace:
        elements.append(namespace + suffix)
    return os.path.join(*elements)

def get_cgroup_svc_relpath(o, suffix=".slice"):
    name = get_name(o)
    elements = [get_cgroup_ns_relpath(o, suffix=suffix)]
    elements.append(name + suffix)
    return os.path.join(*elements)

def get_cgroup_relpath(o, suffix=".slice"):
    if hasattr(o, "type") and o.type == "container.lxc" and \
       hasattr(o, "name") and not o.capable("cgroup_dir"):
        return os.path.join("lxc", o.name)

    if hasattr(o, "kind") and o.kind == "nscfg":
        elements = [get_cgroup_ns_relpath(o, suffix=suffix)]
    else:
        elements = [get_cgroup_svc_relpath(o, suffix=suffix)]
        if hasattr(o, "rset") and o.rset is not None:
            elements.append(o.rset.rid.replace(":", ".") + suffix)
        if hasattr(o, "rid") and o.rid is not None:
            elements.append(o.rid.replace("#", ".") + suffix)
    return os.path.join(*elements)

def get_cgroup_path(o, t, create=True, has_subtree=False):
    o.log.debug("get_cgroup_path : t=%s, create=%s"%(t, create))
    cgroup_mntpt = get_cgroup_mntpt(t)
    if cgroup_mntpt is None:
        raise ex.Error("cgroup fs with option %s is not mounted" % t)
    relpath = get_cgroup_relpath(o)
    cgp = os.sep.join([cgroup_mntpt, relpath])
    log = get_log(o)

    if not os.path.exists(cgp) and create:
        if hasattr(o, "cleanup_cgroup"):
            o.cleanup_cgroup(t)
        create_cgroup(cgp, log=log, has_subtree=has_subtree)
    return cgp

def remove_pg(o):
    log = o.log
    if UNIFIED:
        cgp = os.path.join(os.sep, "sys", "fs", "cgroup", get_cgroup_relpath(o))
        remove_cgroup(cgp, log)
        cgp = os.path.join(os.sep, "sys", "fs", "cgroup", get_cgroup_relpath(o, suffix=""))
        remove_cgroup(cgp, log)
        return
    for t in CONTROLLERS:
        cgp = os.path.join(os.sep, "sys", "fs", "cgroup", t, get_cgroup_relpath(o))
        remove_cgroup(cgp, log)
        cgp = os.path.join(os.sep, "sys", "fs", "cgroup", t, get_cgroup_relpath(o, suffix=""))
        remove_cgroup(cgp, log)

def remove_cgroup(cgp, log):
    if not os.path.exists(cgp):
        return
    todo = [cgp]
    for dirpath, subdirs, _ in os.walk(cgp):
        for subdir in subdirs:
            path = os.path.join(dirpath, subdir)
            todo.append(path)
    for path in sorted(todo, reverse=True):
        try:
            os.rmdir(path)
            #log.info("removed %s", path)
        except Exception as exc:
            #print("lingering %s (%s)" % (path, exc))
            pass

def create_cgroup(cgp, log=None, has_subtree=False):
    if UNIFIED:
        parent_cgp = os.path.dirname(os.path.realpath(cgp))
        if parent_cgp != "/sys/fs/cgroup":
            try:
                set_sysfs(parent_cgp+"/cgroup.subtree_control", "+cpuset +cpu +io +memory +pids", log=log)
            except Exception as exc:
                raise
    try:
        os.makedirs(cgp)
    except OSError as exc:
        if exc.errno == 17:
            pass
        else:
            raise
    if UNIFIED:
        if has_subtree:
            try:
                set_sysfs(cgp+"/cgroup.subtree_control", "+cpuset +cpu +io +memory +pids", log=log)
            except Exception as exc:
                raise
    else:
        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):
    try:
        with open(path, "r") as f:
            return f.read().rstrip("\n")
    except Exception:
        return

def set_sysfs(path, val, log=None):
    if val is None:
        return
    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 pids(o, controller="memory"):
    cgp = get_cgroup_path(o, controller, create=False)
    if not os.path.exists(cgp):
        return []
    _pids = set()
    fname = "cgroup.procs"
    for path, _, files in os.walk(cgp):
        fpath = os.path.join(path, fname)
        if fname not in files:
            continue
        try:
            with open(fpath, "r") as f:
                for pid in f.readlines():
                    _pids.add(pid.strip())
        except Exception:
            pass
    return list(_pids)

def kill(o):
    _pids = pids(o, controller="freezer")
    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, "path"):
        # 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, "path"):
        # 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")
    log = get_log(o)
    if not os.path.exists(path):
        raise ex.Error("freezer control file not found: %s"%path)
    try:
        with open(path, "r") as f:
            buff = f.read()
    except Exception as e:
        raise ex.Error(str(e))
    buff = buff.strip()
    if buff == a:
        if log: log.info("%s is already %s" % (path, a))
        return
    elif buff == "FREEZING":
        if log: 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.Error(str(e))
    if log: 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.name
            except:
                name = o.rid
            res.status_log("process group %s is %s" % (name, state))
            return True
    return False

def create_pg(res, has_subtree=False):
    if not cgroup_capable(res):
        return

    _create_pg(res.svc)
    _create_pg(res.rset)
    _create_pg(res, has_subtree=has_subtree)

def set_controllers_task(o):
    for controller in CONTROLLERS:
        try:
            set_task(o, controller)
        except ex.Error:
            pass

def _create_pg(o, has_subtree=False):
    if o is None:
        return
    try:
        if UNIFIED:
            set_task(o, None, has_subtree=has_subtree)
            set_cgroup(o, 'cpuset', 'cpuset.cpus', 'cpus')
            set_cgroup(o, 'cpu', 'cpu.weight', 'cpu_shares')
            set_cgroup(o, 'cpuset', 'cpuset.mems', 'mems')
            set_cgroup(o, 'blkio', 'io.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_v2(o)
        else:
            try:
                set_task(o, 'systemd')
            except:
                pass
            set_controllers_task(o)
            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_v1(o)
    except Exception as e:
        # not configured in kernel
        pass

##############################################################################
#
# Cgroup Stats
#
##############################################################################

def get_stats_mem(o):
    data = {}
    _data = {}
    cgp = get_cgroup_path(o, "memory", create=False)
    buff = get_sysfs(cgp+"/memory.stat")
    for line in buff.splitlines():
        k, v = line.split()
        _data[k] = int(v)
    data["total"] = _data.get("total_cache", 0) + _data.get("total_rss", 0) + _data.get("total_rss_huge", 0) + _data.get("total_shmem", 0)
    return data

def get_stats_cpu(o):
    data = {}
    cgp = get_cgroup_path(o, "cpu", create=False)
    data["time"] = int(get_sysfs(cgp+"/cpuacct.usage")) / 1000000000
    return data

def get_stats_blk(o):
    data = {}
    cgp = get_cgroup_path(o, "blkio", create=False)
    if not os.path.exists(cgp):
        raise ex.Error

    rb = 0
    wb = 0
    rio = 0
    wio = 0
    leaves = 0

    def get(path, filename):
        buff = get_sysfs(path+"/" + filename)
        r = 0
        w = 0
        for line in buff.splitlines():
            l = line.split()
            try:
                if l[1] == "Read":
                    r = int(l[2])
                elif l[1] == "Write":
                    w = int(l[2])
            except IndexError:
                pass
        return r, w

    for path, subdirs, _ in os.walk(cgp):
        if subdirs:
            continue
        leaves += 1
        r, w = get(path, "blkio.throttle.io_serviced")
        rio += r
        wio += w
        r, w = get(path, "blkio.throttle.io_service_bytes")
        rb += r
        wb += w
    if not leaves:
        raise IndexError
    return {
        "r": rio,
        "w": wio,
        "rb": rb,
        "wb": wb,
    }

def get_stats_tasks(o):
    cgp = get_cgroup_path(o, "cpu", create=False)
    if not os.path.exists(cgp):
        raise ex.Error
    count = 0
    for path, subdirs, _ in os.walk(cgp):
        if subdirs:
            continue
        count += len(get_sysfs(path+"/tasks").splitlines())
    return count

def get_stats_net(o):
    _pids = pids(o)
    if not _pids:
        raise IndexError
    ns_done = []
    data = {
        "r": 0,
        "w": 0,
        "rb": 0,
        "wb": 0,
    }
    for pid in _pids:
        try:
            fpath = "/proc/%s/ns/net" % pid
            ns = os.readlink(fpath)
            if ns in ns_done:
                continue
            fpath = "/proc/%s/net/dev" % pid
            with open(fpath, "r") as f:
                lines = f.read().split("\n")[2:-1]
            for line in lines:
                l = line.split()
                data["r"] += int(l[2])
                data["w"] += int(l[10])
                data["rb"] += int(l[1])
                data["wb"] += int(l[9])
            ns_done.append(ns)
        except Exception as exc:
            #print(fpath, exc, line, pid)
            continue
    return data

def get_stats_created(o):
    data = {}
    cgp = get_cgroup_path(o, "cpu", create=False)
    return os.path.getmtime(cgp)

STATS = [
    ("cpu", get_stats_cpu),
    ("mem", get_stats_mem),
    ("blk", get_stats_blk),
    ("tasks", get_stats_tasks),
    ("net", get_stats_net),
    ("created", get_stats_created),
]

def get_stats(o):
    """
    Return all cgroup stats
    """
    data = {}
    for key, fn in STATS:
        try:
            data[key] = fn(o)
        except Exception:
            pass
    return data
  0707010001f280000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/drivers/array  0707010001f28a000081a40000000000000000000000016a100daf0000380c000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/hp3par.py    from __future__ import print_function

import os
import json
import time
import re
import datetime
from subprocess import *

import core.exceptions as ex
from core.node import Node
from env import Env
from utilities.cache import cache, clear_cache
from foreign.six.moves.urllib.request import Request, build_opener # pylint: disable=import-error
from foreign.six.moves.urllib.parse import urlencode # pylint: disable=import-error
from utilities.proc import justcall, which
from utilities.string import bdecode

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.paths.pathbin

def reformat(s):
    lines = bdecode(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):
    def __init__(self, objects=None, log=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.arrays = []

        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "hp3par":
                continue

            method = self.node.oget(s, 'method')

            kwargs = {"log": log, "node": self.node}
            try:
                manager = self.node.oget(s, 'manager')
                kwargs['manager'] = manager
            except Exception as e:
                kwargs['manager'] = name

            if method in ("ssh"):
                kwargs["username"] = self.node.oget(s, 'username')
                kwargs["key"] = self.node.oget(s, 'key')
                if kwargs["username"] is None or kwargs["key"] is None:
                    raise ex.Error("username and key are required for the ssh method")
            elif method in ("cli"):
                kwargs['cli'] = self.node.oget(s, "cli")
                kwargs['pwf'] = self.node.oget(s, "pwf")
                if kwargs["pwf"] is None:
                    raise ex.Error("pwf is required for the cli method")
            self.arrays.append(Hp3par(name, method, **kwargs))
            done.append(name)

    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", path="", log=None, node=None):
        self.name = name
        self.node = node
        self.manager = manager
        self.method = method
        self.username = username
        self.pwf = pwf
        self.cli = cli
        self.path = path
        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.path,
          'uuid' : self.uuid,
        }

        data = urlencode(values)
        req = Request(url, data, header)

        try:
            f = build_opener().open(req)
            response = f.read()
        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.Error("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.Error("3par command execution error")

        return out, err

    def cli_cmd(self, cmd, log=False):
        if which(self.cli) is None:
            raise ex.Error("%s executable not found" % self.cli)

        # HOME is needed to locate the ssl cert validation file
        os.environ["HOME"] = os.path.expanduser("~root")
        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.Error("3par connection error. array ssl cert is not trusted. open interactive session to trust it.")
            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.Error("3par command execution error")

        return out, err

    def get_uuid(self):
        if self.uuid is not None:
            return self.uuid
        try:
            self.uuid = self.node.conf_get("node", "uuid")
        except ex.OptNotFound:
            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.Error("unsupported method %s 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.Error("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.Error("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.Error("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 = list(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
            try:
                vv_data['LastSyncTime'] = self.s_to_datetime(vv_data['LastSyncTime'])
            except Exception:
                vv_data['LastSyncTime'] = None
            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())
0707010001f28f000081a40000000000000000000000016a100daf00002c5a000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/array/nexenta.py   import json
import base64

import core.exceptions as ex
from foreign.six.moves.urllib.request import Request, urlopen # pylint: disable=import-error
from foreign.six.moves.urllib.error import URLError # pylint: disable=import-error
from utilities.naming import factory, split_path
from utilities.string import base64encode
from core.node import Node

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, node=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 node:
            self.node = node
        else:
            self.node = Node()
        if log is not None:
            self.log = log
        else:
            self.log = self.node.log

    def init(self):
        if self.username is not None and self.password is not None:
            return
        s = "array#" + self.head
        try:
            stype = self.node.oget(s, "type")
        except Exception:
            raise ex.Error("no array configuration for head %s"%self.head)
        if stype != "nexenta":
            raise ex.Error("array %s type is not nexanta" % self.head)
        try:
            self.username = self.node.oget(s, "username")
        except Exception:
            raise ex.Error("no username information for head %s"%self.head)
        try:
            self.password = self.node.oget(s, "password")
        except Exception:
            raise ex.Error("no password information for head %s"%self.head)
        self.port = self.node.oget(s, "port")
        try:
            secname, namespace, _ = split_path(self.password)
            self.password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
        except Exception as exc:
            raise ex.Error("error decoding password: %s" % exc)
        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 = base64encode('%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.Error("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.Error(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.Error(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.Error(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.Error(data['error'])
        return data['result']

    def autosync_get_names(self):
        data = self.rest("autosync", "get_names", [''])
        if data['error'] is not None:
            raise ex.Error(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.Error(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.Error(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.Error(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.Error(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.Error(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.Error(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.Error(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.Error(data["error"])

    def folder_clone(self, src, dst):
        data = self.rest("folder", "clone", [src, dst])
        if data['error'] is not None:
            raise ex.Error(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.Error("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.Error("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.Error(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.Error(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.Error(data["error"])

    def snapshot_get_names(self):
        data = self.rest("snapshot", "get_names", [''])
        if data['error'] is not None:
            raise ex.Error(data["error"])
        return data['result']

    def folder_get_names(self):
        data = self.rest("folder", "get_names", [''])
        if data['error'] is not None:
            raise ex.Error(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.Error(data["error"])
        for zvol in data['result']:
            self.object_type_cache[zvol] = "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.Error("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.Error(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.Error(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"))

  0707010001f28c000081a40000000000000000000000016a100daf00000bdf000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/ibmsvc.py    from __future__ import print_function

import os
import sys

import core.exceptions as ex

from env import Env
from core.node import Node
from utilities.proc import justcall

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.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.Error("ssh command execution error")
    return out, err

class IbmSvcs(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "ibmsvc":
                continue

            try:
                username = self.node.oget(s, 'username')
                key = self.node.oget(s, 'key')
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            self.arrays.append(IbmSvc(name, username, key, node=self.node))

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class IbmSvc(object):
    def __init__(self, name, username, key, node=None):
        self.name = name
        self.node = node
        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())
 0707010001f282000081a40000000000000000000000016a100daf00000e28000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/array/centera.py   from __future__ import print_function

import os
import tempfile
from subprocess import *

from utilities.naming import factory, split_path
from env import Env
from core.node import Node

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.paths.pathbin

class Centeras(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "centera":
                continue

            try:
                server = self.node.oget(s, "server")
                username = self.node.oget(s, "username")
                password = self.node.oget(s, "password")
                jcass_dir = self.node.oget(s, "jcass_dir")
                java_bin = self.node.oget(s, "java_bin")
            except:
                print("error parsing section", s, file=sys.stderr)

            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue

            self.arrays.append(Centera(name, server=server, username=username, password=password, java_bin=java_bin, jcass_dir=jcass_dir, node=self.node))
            done.append(name)

    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, node=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=Env.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

0707010001f289000081a40000000000000000000000016a100daf000067cf000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/array/hds.py   from __future__ import print_function

import logging
import os
import sys
import json
from xml.etree.ElementTree import fromstring

import core.exceptions as ex
from utilities.converters import convert_size
from core.node import Node
from env import Env
from utilities.optparser import OptParser, Option
from utilities.naming import factory, split_path
from utilities.lazy import lazy
from utilities.storage import Storage
from utilities.proc import justcall, which

PROG = "om 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 the node or cluster configuration."),
    "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=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            try:
                name = self.node.oget(s, "name")
            except Exception:
                name = None
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "hds":
                continue
            try:
                bin = self.node.oget(s, 'bin')
                jre_path = self.node.oget(s, 'jre_path')
                url = self.node.oget(s, 'url')
                username = self.node.oget(s, 'username')
                password = self.node.oget(s, 'password')
            except Exception as exc:
                print("error parsing section %s: %s" % (s, exc), file=sys.stderr)
                continue

            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue

            self.arrays.append(Array(name, url, username, password, bin=bin, jre_path=jre_path, node=self.node))
            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, jre_path=None, node=None):
        self.keys = ['array', 'lu', 'arraygroup', 'port', 'pool']
        self.name = name
        self.node = node
        self.jre_path = jre_path
        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(Env.nodename+".array.sym."+self.name)
        self.journal = []

    def cmd(self, cmd, scoped=True, xml=True, log=False):
        if self.jre_path:
            os.environ["HDVM_CLI_JRE_PATH"] = self.jre_path

        if which(self.bin) is None:
            raise ex.Error("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))
            self.log_cmd(_l)
        out, err, ret = justcall(l)
        if log:
            self.log_result(out, err)
        if ret != 0:
            self.log.error(err)
            raise ex.Error(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.iter("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.iter("LogicalUnit"):
            lu = e_lu.attrib
            lu["Path"] = []
            for e_path in e_lu.iter("Path"):
                lu["Path"].append(e_path.attrib)
            for e_ldev in e_lu.iter("LDEV"):
                ldev = e_ldev.attrib
                for e_label in e_ldev.iter("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.iter("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.iter("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.iter("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.iter("HostStorageDomain"):
            d = elem.attrib
            d["WWN"] = []
            d["Path"] = []
            for subelem in elem.iter("WWN"):
                wwn = subelem.attrib
                wwn["WWN"] = wwn["WWN"].replace(".", "").lower()
                d["WWN"].append(wwn)
            for subelem in elem.iter("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.Error("--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.Error("--devnum is mandatory")
        if domain is None:
            raise ex.Error("--domain is mandatory")
        if portname is None:
            raise ex.Error("--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.Error(err)
        return {}

    def add_map(self, devnum=None, mappings=None, lun=None, **kwargs):
        if devnum is None:
            raise ex.Error("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        if mappings is None:
            raise ex.Error("--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.Error("--devnum is mandatory")
        if domain is None:
            raise ex.Error("--domain is mandatory")
        if portname is None:
            raise ex.Error("--portname is mandatory")
        if lun is None:
            raise ex.Error("--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.Error(err)
        data = self.parse(out)
        return data[0]["Path"][0]

    def log_cmd(self, cmd):
        self.journal.append([0, " ".join(cmd), {}])

    def log_result(self, out="", err=""):
        for line in out.strip().splitlines():
            self.journal.append([0, line, {}])
        for line in err.strip().splitlines():
            self.journal.append([1, line, {}])

    def add_disk(self, name=None, pool=None, size=None, lun=None, mappings=None, **kwargs):
        if pool is None:
            raise ex.Error("--pool is mandatory")
        if size == 0 or size is None:
            raise ex.Error("--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.Error(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.Error("--devnum is mandatory")
        devnum = self.to_devnum(devnum)
        if size == 0 or size is None:
            raise ex.Error("--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.Error(err)

    def del_disk(self, devnum=None, **kwargs):
        if devnum is None:
            raise ex.Error("--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.Error(err)
        self.del_diskinfo(devnum)

    def rename_disk(self, devnum=None, name=None, **kwargs):
        if devnum is None:
            raise ex.Error("--devnum is mandatory")
        if name is None:
            raise ex.Error("--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.Error(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.Error(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.Error(str(exc))
        if "error" in data:
            raise ex.Error(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.Error("array %s not found" % array_name)
    array.node = node
    node.logger.handlers[1].setLevel(logging.CRITICAL)
    if not hasattr(array, action):
        raise ex.Error("not implemented")
    try:
        ret = getattr(array, action)(**kwargs)
    except ex.Error as exc:
        ret = {
            "log": array.journal,
        }
        print(json.dumps(ret, indent=4))
        raise
    if ret is not None:
        ret["log"] = array.journal
        print(json.dumps(ret, indent=4))
    return ret

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)
    try:
        do_action(action, node=node, **kwargs)
        return 0
    except Exception:
        return 1

if __name__ == "__main__":
    try:
        main(sys.argv)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        sys.exit(1)
    except IOError as exc:
        if exc.errno == 32:
            # broken pipe
            sys.exit(1)
        else:
            raise


 0707010001f288000081a40000000000000000000000016a100daf0000c9e6000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/array/hcs.py   from __future__ import print_function

import sys
import json
import time

import core.exceptions as ex
from utilities.storage import Storage
from utilities.lazy import lazy
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from core.node import Node

ERRCODE_MAPPING_HOST_LUN_NOT_EXISTS = 1073804587
ERRCODE_MAPPING_HOST_LUN_EXISTS = 1073804588
ERRCODE_SESSION_TOO_MANY = 1077949067

MAX_COUNT = 16384
VIRTUAL_LDEV_ID_NONE = 65534
VIRTUAL_LDEV_ID_GAD = 65535
MODEL_ID = {
    "VSP G370": "886000",
    "VSP G700": "886000",
    "VSP G900": "886000",
    "VSP F370": "886000",
    "VSP F700": "886000",
    "VSP F900": "886000",
    "VSP G350": "882000",
    "VSP F350": "882000",
    "VSP G800": "836000",
    "VSP F800": "836000",
    "VSP G400": "834000",
    "VSP G600": "834000",
    "VSP F400": "834000",
    "VSP F600": "834000",
    "VSP G200": "832000",
    "VSP G1000": "800000",
    "VSP G1500": "800000",
    "VSP F1500": "800000",
    "Virtual Storage Platform": "700000",
    "HUS VM": "730000",
    "VSP 5100": "900000",
    "VSP 5100H": "900000",
    "VSP 5200": "900000",
    "VSP 5200H": "900000",
    "VSP 5500": "900000",
    "VSP 5500H": "900000",
    "VSP 5600": "900000",
    "VSP 5600H": "900000",
}
RETRYABLE_ERROR_MSG_IDS = [
    "KART00003-E",
    "KART00006-E",
    "KART30003-E",
    "KART30090-E",
    "KART30095-E",
    "KART30096-E",
    "KART30097-E",
    "KART40042-E",
    "KART40049-E",
    "KART40051-E",
    "KART40052-E",
    "KART30000-E",
    "KART30008-E",
    "KART30072-E",
    "KART30074-E",
    "KART30085-E", # fake: The user ID or password is incorrect.
]
RETRYABLE_LOCK_ERROR_MSG_IDS = [
    "KART30000-E",
    "KART30008-E",
    "KART40050-E",
    "KART40052-E",
    "KART40052-E",
]
RETRYABLE_STATUS = {
    401: "unauthorized",
    403: "forbidden",
    500: "server error",
    503: "service unavailable",
    504: "gateway timeout",
}
NON_RETRYABLE_STATUS = {
    400: "bad request",
    404: "not found",
    405: "method not allowed",
    406: "not acceptable",
    409: "conflict",
    411: "length required",
    412: "precondition failed",
    415: "unsupported media type",
    417: "expectation failed",
    502: "proxy error",
}

try:
    import requests
except ImportError:
    raise ex.InitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except AttributeError:
    pass

VERIFY = False

PROG = "om 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 node or cluster configuration."),
    "name": Option(
        "--name", default=None, action="store", dest="name",
        help="The object name"),
    "pool": Option(
        "--pool", default=None, action="store", dest="pool",
        help="The pool 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."),
    "compression": Option(
        "--compression", default=False, action="store_true", dest="compression",
        help="Toggle compression"),
    "dedup": Option(
        "--dedup", default=False, action="store_true", dest="dedup",
        help="Toggle deduplication"),
    "naa": Option(
        "--naa", default=None, action="store", dest="naa",
        help="The disk naa identifier"),
    "resource_group": Option(
        "--resource-group", default=None, type="int", dest="resource_group",
        help="Assign the ldevs to the specified resource group."),
    "start_ldev_id": Option(
        "--start-ldev-id", default=None, type="int", dest="start_ldev_id",
        help="Assign the ldevs an id in a range starting with --start-ldev-id"),
    "end_ldev_id": Option(
        "--end-ldev-id", default=None, type="int", dest="end_ldev_id",
        help="Assign the ldevs an id in a range ending with --end-ldev-id"),
    "lun": Option(
        "--lun", action="store", type=int, dest="lun",
        help="The logical unit number to assign to the extent on attach to a target. If not specified, a free lun is automatically assigned."),
    "id": Option(
        "--id", action="store", dest="id",
        help="An object id, as reported by a list action"),
    "target": Option(
        "--target", action="append", dest="target",
        help="The target object iqn"),
    "hba_id": Option(
        "--hba-id", action="store", dest="hba_id",
        help="The initiator port name"),
    "mappings": Option(
        "--mappings", action="append", dest="mappings",
        help="A <hba_id>:<tgt_id>,<tgt_id>,... mapping used in add map. Can be specified multiple times."),
})

GLOBAL_OPTS = [
    OPT.array,
]

DEPRECATED_ACTIONS = []

ACTIONS = {
    "Add actions": {
        "add_disk": {
            "msg": "Create and map a lun.",
            "options": [
                OPT.name,
                OPT.pool,
                OPT.size,
                OPT.target,
                OPT.mappings,
                OPT.lun,
                OPT.compression,
                OPT.dedup,
                OPT.start_ldev_id,
                OPT.end_ldev_id,
                OPT.resource_group,
            ],
        },
        "add_ldev": {
            "msg": "Create a ldev.",
            "options": [
                OPT.name,
                OPT.pool,
                OPT.size,
                OPT.compression,
                OPT.dedup,
                OPT.start_ldev_id,
                OPT.end_ldev_id,
                OPT.resource_group,
            ],
        },
        "map": {
            "msg": "Map a lun to the specified initiator:target links",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
                OPT.mappings,
                OPT.lun,
            ],
        },
    },
    "Delete actions": {
        "del_disk": {
            "msg": "Delete and unmap a ldev.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "del_ldev": {
            "msg": "Delete a ldev.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "unmap": {
            "msg": "Unmap the specified initiator:target links",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
                OPT.mappings,
            ],
        },
    },
    "Modify actions": {
        "rename_disk": {
            "msg": "Rename a ldev",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "resize_disk": {
            "msg": "Resize a ldev",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
                OPT.size,
            ],
        },
        "clear_reservation": {
            "msg": "Clear ldev reservation from the storage array.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "add_ldev_range_to_resource_group": {
            "msg": "Preassign a ldev range to the specified resource group.",
            "options": [
                OPT.start_ldev_id,
                OPT.end_ldev_id,
                OPT.resource_group,
            ],
        },
        "discard_zero_page": {
            "msg": "Reclaim freeable space of a DP volume.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "unassign_virtual_ldevid": {
            "msg": "Unassign the virtual ldev id.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
    },
    "List actions": {
        "list_mappings": {
            "msg": "List host ports a disk is mapped to.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "list_pools": {
            "msg": "List configured storage pools.",
        },
        "list_fc_port": {
            "msg": "List configured fibre channel ports",
        },
        "list_ldevs": {
            "msg": "List configured extents",
        },
        "list_host_groups": {
            "msg": "List configured host groups",
        },
        "list_resource_groups": {
            "msg": "List configured resource groups",
        },
        "list_supported_host_modes": {
            "msg": "List the hostmodes support by the storage array",
        },
        "list_virtual_storages": {
            "msg": "List the configured virtual storage machines",
        },
    },
    "Show actions": {
        "show_system": {
            "msg": "Show system information.",
        },
        "show_ldev": {
            "msg": "Show configured ldev.",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
            ],
        },
        "show_hostgroup": {
            "msg": "Show configured hostgroup.",
            "options": [
                OPT.id,
                OPT.hba_id,
            ],
        },
        "show_pool": {
            "msg": "Show configured storage pools.",
            "options": [
                OPT.name,
            ],
        },
        "show_virtual_storage": {
            "msg": "Show a virtual storage machine.",
            "options": [
                OPT.id,
            ],
        },
    },
    "Sessions": {
        "list_sessions": {
            "msg": "List the currently active sessions on the storage array",
        },
        "add_session": {
            "msg": "Open a new session on the storage array",
        },
        "del_session": {
            "msg": "Delete a session on the storage array",
            "options": [
                OPT.id,
            ],
        },
        "unlock": {
            "msg": "Unlock a resource group",
        },
    },
}

def apilock(func):
    def _func(self, *args, **kwargs):
        self.lock()
        try:
            ret = func(self, *args, **kwargs)
        finally:
            self._unlock()
        return ret
    return _func

def apiretry(func):
    def is_job_response(data):
        if not data:
            return False
        if "jobId" not in data:
            return False
        if "state" not in data:
            return False
        return True

    def is_retryable_error(data):
        return data.get("error", {}).get("messageId") in RETRYABLE_ERROR_MSG_IDS or \
               data.get("messageId") in RETRYABLE_ERROR_MSG_IDS

    def check_condition(self, data, condition, msg):
        try:
            cc = is_retryable_error(data)
        except Exception:
            cc = False
        try:
            sc = condition(data)
            if sc and msg:
                self.log.info(msg)
        except Exception:
            sc = False
        return sc or cc

    def _func(self, uri, retry=None, base="device", **kwargs):
        if base == "device":
            base_path = self.urlpath_device()
        else:
            base_path = self.urlpath_base()
        uri = self.api + base_path + uri
        try:
            if self.log and func.__name__ != "get" and "/sessions" not in uri:
                self.log.info("%s %s %s", func.__name__, uri, kwargs)
        except (KeyError, IndexError, AttributeError, TypeError):
            pass
        retry = retry or {}
        count = retry.get("count", self.retry)
        delay = retry.get("delay", self.delay)
        barrier = time.time() + count*delay
        msg = retry.get("message")
        condition = retry.get("condition", lambda: False)
        short_delay = 0.5
        while True:
            try:
                r = func(self, uri, **kwargs)
            except ex.Error as exc:
                raise ex.Error(str(exc))
            except Exception as exc:
                raise ex.Error("hcs api request error: %s" % str(exc))
            if r.status_code == 401:
                self.session_data = None
                if time.time() + short_delay < barrier:
                    time.sleep(short_delay)
                    continue
            if r.status_code in RETRYABLE_STATUS:
                desc = RETRYABLE_STATUS[r.status_code]
                if msg:
                    try:
                        data = r.json()
                    except Exception:
                        data = {}
                    #self.dump(func.__name__, uri, status=r.status_code, _result=data, **kwargs)
                self.log.info("  response status %d: %s (%s)" % (r.status_code, desc, uri), {"f_stream": False})
                if time.time() + delay < barrier:
                    time.sleep(delay)
                    continue
            if func.__name__ == "delete" and r.status_code == 404 and "/sessions" in uri:
                try:
                    data = r.json()
                except Exception:
                    data = {}
                return data
            if r.status_code in NON_RETRYABLE_STATUS:
                desc = NON_RETRYABLE_STATUS[r.status_code]
                try:
                    data = r.json()
                except Exception:
                    data = {}
                self.dump(func.__name__, uri, status=r.status_code, _result=data, **kwargs)
                raise ex.Error("response status %d: %s (%s)" % (r.status_code, desc, uri))
            if not r.text:
                return
            data = r.json()
            if not is_job_response(data) and check_condition(self, data, condition, msg):
                if time.time() + delay < barrier:
                    time.sleep(delay)
                    self.log.info("  retry sync request: %s (%s)" % (data, uri), {"f_stream": False})
                    continue
            if func.__name__ != "get":
                data = self.check_result(func.__name__, uri, result=data, **kwargs)
                if time.time() + short_delay < barrier and check_condition(self, data, condition, msg):
                    time.sleep(short_delay)
                    self.log.info("  retry async request: %s (%s)" % (data, uri), {"f_stream": False})
                    continue
                if data.get("state") == "Failed":
                    self.dump(func.__name__, uri, _result=data, **kwargs)
            try:
                if func.__name__ != "get" and "/sessions" not in uri and "jobId" not in data:
                    self.log.info("%s", data)
            except (KeyError, IndexError, AttributeError, TypeError):
                pass
            return data
        if r.status_code in RETRYABLE_STATUS:
            desc = RETRYABLE_STATUS[r.status_code]
        elif r.status_code in NON_RETRYABLE_STATUS:
            desc = NON_RETRYABLE_STATUS[r.status_code]
        else:
            desc = "unknown"
        raise ex.Error("response status %d: %s, api request retries exhausted (%s)" % (r.status_code, desc, uri))

    return _func

class Hcss(object):
    arrays = []


    def __init__(self, objects=None, node=None, log=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.timeout = 10
        if node:
            self.node = node
        else:
            self.node = Node()
        if log:
            self.log = log
        else:
            self.log = self.node.log
        done = []
        for s in self.node.conf_sections(cat="array"):
            oname = s.split("#", 1)[-1]
            try:
                name = self.node.oget(s, 'name')
            except Exception:
                name = None
            if not name:
                name = oname
            if oname in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "hcs":
                continue
            timeout = self.node.oget(s, "timeout")
            try:
                username = self.node.oget(s, "username")
                password = self.node.oget(s, "password")
                http_proxy = self.node.oget(s, "http_proxy")
                https_proxy = self.node.oget(s, "https_proxy")
                api = self.node.oget(s, "api")
                model = self.node.oget(s, "model")
                retry = self.node.oget(s, "retry")
                delay = self.node.oget(s, "delay")
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            o = Hcs(
                oname=oname,
                name=name,
                model=model,
                api=api,
                username=username,
                password=password,
                timeout=timeout,
                http_proxy=http_proxy,
                https_proxy=https_proxy,
                retry=retry,
                delay=delay,
                node=self.node,
                log=self.log,
            )
            self.arrays.append(o)
            done.append(oname)


    def __iter__(self):
        for array in self.arrays:
            yield(array)


    def get_hcs(self, name):
        for array in self.arrays:
            if array.oname == name:
                return array
        return None

class Hcs(object):

    def __init__(self, oname=None, name=None, model=None, api=None,
                 username=None, password=None, timeout=None,
                 http_proxy=None, https_proxy=None,
                 retry=30, delay=10,
                 node=None, log=None):
        self.oname = oname
        self.node = node
        self.log = log
        self.name = name
        self.model = model
        self.api = api.rstrip("/")
        self.username = username
        self.password = password
        self.http_proxy = http_proxy
        self.https_proxy = https_proxy
        self.auth = (username, password)
        self.timeout = timeout
        self.retry = retry
        self.delay = delay
        self.session_data = None
        self.locked = False
        self.keys = ["system",
                     "pools",
                     "fc_ports",
                     "ldevs"]
        self.session = requests.Session()
        if self.proxies:
            self.session.proxies = self.proxies
        self.naa_templates = {}


    @property
    def storage_id(self):
        pad = 6 - len(self.name)
        if pad <= 0:
            pad = ""
        else:
            pad = "0" * pad
        return MODEL_ID[self.model] + pad + self.name

    def urlpath_base(self):
        return "/ConfigurationManager/v1"

    def urlpath_device(self, device=None):
        return "/ConfigurationManager/v1/objects/storages/%s" % (device if device else self.storage_id)

    def headers(self, auth=True):
        data = {
            "Content-Type": "application/json",
            "Accept": "application/json",
        }
        if not auth:
            return data
        if not self.session_data:
            self.open_session()
        data["Authorization"] = "Session %s" % self.session_data["token"]
        return data

    @property
    def proxies(self):
        if not self.http_proxy and not self.https_proxy:
            return
        data = {}
        if self.http_proxy:
            data["http"] = self.http_proxy
        if self.https_proxy:
            data["https"] = self.https_proxy
        return data

    @apiretry
    def delete(self, uri, data=None, base="device", retry=None):
        headers = self.headers()
        if data:
            data = json.dumps(data)
        r = self.session.delete(uri, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r


    @apiretry
    def put(self, uri, data=None, base="device", retry=None):
        headers = self.headers()
        if data:
            data = json.dumps(data, indent=4)
        r = self.session.put(uri, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r

    @apiretry
    def post(self, uri, data=None, auth=True, base="device", retry=None):
        headers = self.headers(auth=auth)
        if data:
            data = json.dumps(data, indent=4)
        if auth:
            r = self.session.post(uri, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        else:
            r = requests.post(uri, data=data, timeout=self.timeout, verify=VERIFY, headers=headers, auth=self.auth, proxies=self.proxies)
        return r

    @apiretry
    def get(self, uri, params=None, base="device", retry=None):
        headers = self.headers()
        r = self.session.get(uri, params=params, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r


    def add_session(self, **kwargs):
        data = self._open_session()
        return data

    def open_session(self):
        self.session_data = self._open_session()
        self.log.info("session id %d" % self.session_data["sessionId"], {"f_stream": False})

    def _open_session(self):
        retries = 0
        while True:
            data = self.post("/sessions", auth=False)
            code = data.get("error", {}).get("code")
            if code == ERRCODE_SESSION_TOO_MANY and retries < self.timeout:
                retries += 1
                time.sleep(1)
                continue
            elif code:
                raise ex.Error("open_session error: %s => %s" % ((self.auth[0], "xxx"), data.get("error")))
            return data

    def close_session(self):
        if not self.session_data:
            return
        if self.locked:
            self._unlock()
        self.delete("/sessions/%d" % self.session_data["sessionId"])
        self.session_data = None


    def get_system(self):
        """
        {
            "storageDeviceId" : "88xxxxxxxx91",
            "model" : "VSP G350",
            "serialNumber" : xxxx91,
            "ctl1Ip" : "192.168.xx.xx",
            "ctl2Ip" : "192.168.xx.xx",
            "dkcMicroVersion" : "88-06-02/10",
            "communicationModes" : [ {
                "communicationMode" : "lanConnectionMode"
            } ],
            "isSecure" : true,
            "targetCtl" : "CTL1",
            "usesSvp" : false
        }
        """
        return self.get("/")


    def fmt_naa(self, ldev_id, rg_id):
        template = self.get_naa_template(rg_id)
        if template is None:
            return
        ldev_id = hex(int(ldev_id))[2:]
        ldev_id = (4 - len(ldev_id)) * "0" + ldev_id
        return template[:28] + ldev_id


    def get_ldevs(self):
        data = self.get("/ldevs", params={"ldevOption": "dpVolume", "count": MAX_COUNT})["data"]
        for i, d in enumerate(data):
            data[i]["naaId"] = self.fmt_naa(d["ldevId"], d["resourceGroupId"]) or ",".join([self.name, str(d["resourceGroupId"]), str(d["ldevId"])])
        return data


    def get_naa_template(self, rg_id):
        try:
            data = self.naa_templates[rg_id]
        except KeyError:
            try:
                data = self.get_any_mapped_ldev(rg_id)
            except IndexError:
                data = {}
            try:
                data = self.get_ldev(oid=data["ldevId"])["naaId"]
            except KeyError:
                data = None
            self.naa_templates[rg_id] = data
        return data


    def get_any_mapped_ldev(self, rg_id):
        params={
            "ldevOption": "luMapped",
            "resourceGroupId": rg_id,
            "count": 1,
        }
        return self.get("/ldevs", params=params)["data"][0]


    def get_resource_groups(self):
        data = self.get("/resource-groups")
        try:
            return data["data"]
        except KeyError:
            return


    def get_host_groups(self):
        data = self.get("/host-groups")
        try:
            return data["data"]
        except KeyError:
            return


    def get_virtual_storage(self, oid):
        data = self.get("/virtual-storages/%s" % oid)
        return data


    def get_virtual_storages(self):
        data = self.get("/virtual-storages")
        try:
            return data["data"]
        except KeyError:
            return


    def get_pool_by_id(self, oid=None):
        data = self.get("/pools", params={"poolId": oid})
        try:
            return data["data"][0]
        except KeyError:
            return


    def get_pools(self):
        return self.get("/pools")["data"]

    def get_pool_by_name(self, name=None):
        params = {
            "$query": "pool.storageDeviceId eq '%s' and pool.poolName eq '%s'" % (self.storage_id, name),
        }
        data = self.get("/views/pools", params=params, base="")
        try:
            return data["data"][0]["pool"]
        except (IndexError, KeyError):
            return

    def get_pool_id(self, name=None):
        return self.get_pool_by_name(name=name)["poolId"]

    def ldev_id_from_naa(self, naa):
        return int(naa[-8:], 16)

    def get_ldev(self, oid=None, name=None, naa=None):
        if oid is not None:
            try:
                oid = int(oid)
            except ValueError:
                oid = int(oid, 16)
            data = self.get("/ldevs/%d" % oid)
        elif naa:
            ldev_id = self.ldev_id_from_naa(naa)
            data = self.get_ldev(oid=ldev_id)
            if data and "naaId" in data and naa != data["naaId"]:
                raise ex.Error("expected naa %s for ldev id %s, found %s. use --id" % (naa, ldev_id, data["naaId"]))
            return data
        elif name:
            data = self.get("/views/ldevs", params={"$query": "ldev.storageDeviceId eq '%s' and ldev.label eq '%s'" % (self.storage_id, name)}, base="")
            if len(data["data"]) > 1:
                raise ex.Error("multiple ldev found for label %s: %s" % (name, ",".join([str(d["ldev"]["ldevId"]) for d in data["data"]])))
            try:
                return self.get_ldev(data["data"][0]["ldev"]["ldevId"])
            except (KeyError, IndexError):
                return
        else:
            raise ex.Error("oid, name or naa must be specified to get_ldev()")
        return data

    def get_fc_ports(self):
        data = self.get("/ports")["data"]
        return [d for d in data if d["portType"] == "FIBRE"]

    def del_mapping(self, mapping):
        data = self.unmap_ldev_from_host(
            host_group_id=mapping["hostGroupNumber"],
            port_id=mapping["portId"],
            lun_id=mapping["lun"]
        )
        return data


    def _del_ldev(self, ldev=None, **kwargs):
        try:
            ldev_id = (ldev["ldevId"])
        except KeyError:
            raise ex.Error("_del_ldev: invalid input ldev data")

        if ldev.get("poolId") is None:
            # already deleted
            return

        # dataReductionMode = disabled
        # dataReductionStatus = DISABLED
        force = ldev.get("dataReductionMode") != "disabled"

        d = {
            "isDataReductionDeleteForceExecute": force,
        }
        path = '/ldevs/%d' % ldev_id
        data = self.delete(path, data=d)
        return data


    def add_ldev(self, name=None, size=None, pool=None,
                 compression=True, dedup=True,
                 start_ldev_id=None, end_ldev_id=None,
                 resource_group=0, **kwargs):
        for key in ["name", "size", "pool"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        size = convert_size(size, _to="B") // 512
        if compression and dedup:
            data_reduction_mode = "compression_deduplication"
        elif compression and not dedup:
            data_reduction_mode = "compression"
        else:
            data_reduction_mode = "disabled"
        d = {
            "poolId": self.get_pool_id(pool),
            "blockCapacity": size,
            "dataReductionMode": data_reduction_mode,
            "isParallelExecutionEnabled": True,
        }
        if start_ldev_id is not None:
            d["startLdevId"] = start_ldev_id
        if end_ldev_id is not None:
            d["endLdevId"] = end_ldev_id
        path = "/ldevs"
        retry = {
            "condition": lambda x: x.get("error", {}).get("detailCode") == "30000E-2-2E11-2205",
            "message": "retry ldev add: resource group locked by another user",
        }
        data = self.post(path, data=d, retry=retry)
        ldev_id = int(data["affectedResources"][0].split("/")[-1])
        ldev = self.get_ldev(oid=ldev_id)
        self.set_label(ldev_id, name)

        virtual_ldev_id = ldev.get("virtualLdevId")
        _resource_group = ldev.get("resourceGroupId")
        if resource_group != _resource_group:
            self.unset_virtual_ldev_id(ldev)
            #self._unlock()
            self.set_ldev_resource_group(ldev_id, resource_group)
            #self.lock()
            self.set_virtual_ldev_id(ldev_id, ldev_id)
        elif virtual_ldev_id != ldev_id:
            self.unset_virtual_ldev_id(ldev)
            self.set_virtual_ldev_id(ldev_id, ldev_id)
        return ldev

    def lock(self):
        if self.locked:
            return
        data = {
            "parameters": {
                "waitTime": 30,
            }
        }
        retry = {
            "condition": lambda x: x.get("error", {}).get("messageId") in RETRYABLE_LOCK_ERROR_MSG_IDS
        }
        self.put("/%s/services/resource-group-service/actions/lock/invoke" % self.storage_id, data=data, base="", retry=retry)
        self.locked = True

    def unlock(self, **kwargs):
        return self._unlock(force=True)

    def _unlock(self, force=False):
        if not force and not self.locked:
            return
        retry = {
            "condition": lambda x: x.get("error", {}).get("messageId") in RETRYABLE_LOCK_ERROR_MSG_IDS
        }
        data = self.put("/%s/services/resource-group-service/actions/unlock/invoke" % self.storage_id, base="", retry=retry)
        self.locked = False
        return data

    def set_virtual_ldev_id(self, ldev_id, virtual_ldev_id):
        data = self.get("/ldevs/%d/actions/assign-virtual-ldevid" % ldev_id)
        data["parameters"]["virtualLdevId"] = virtual_ldev_id
        self.put("/ldevs/%d/actions/assign-virtual-ldevid/invoke" % ldev_id, data=data)

    def unassign_virtual_ldevid(self, id=None, name=None, naa=None, **kwargs):
        ldev = self.get_ldev(oid=id, name=name, naa=naa)
        self.unset_virtual_ldev_id(ldev)
        return self.get_ldev(oid=id, name=name, naa=naa)

    @apilock
    def unset_virtual_ldev_id(self, ldev):
        ldev_id = ldev["ldevId"]
        if ldev.get("virtualLdevId") == VIRTUAL_LDEV_ID_NONE:
            return
        data = self.get("/ldevs/%d/actions/unassign-virtual-ldevid" % ldev_id)
        if "errorSource" in data:
            # already unassigned
            return
        self.put("/ldevs/%d/actions/unassign-virtual-ldevid/invoke" % ldev_id, data=data)

    def add_ldev_range_to_resource_group(self, start_ldev_id=None, end_ldev_id=None, resource_group=0, **kwargs):
        if start_ldev_id is None or end_ldev_id is None:
            raise ex.Error("'start_ldev_id' and 'end_ldev_id' must be defined")
        data = {
            "parameters": {
                "startLdevId": start_ldev_id,
                "endLdevId": end_ldev_id,
            }
        }
        self.put("/resource-groups/%d/actions/add-resource/invoke" % resource_group, data=data)

    def set_ldev_resource_group(self, ldev_id, resource_group):
        data = {
            "parameters": {
                "ldevIds": [ldev_id],
            }
        }
        self.put("/resource-groups/%d/actions/add-resource/invoke" % resource_group, data=data)

    def rename_disk(self, id=None, name=None, naa=None, **kwargs):
        if id is None:
            ldev = self.get_ldev(naa=naa)
            id = ldev["ldevId"]
        self.set_label(id, name)

    def check_result(self, method, uri=None, headers=None, data=None, result=None, **kwargs):
        """
        {
            "jobId": 892,
            "self": "/ConfigurationManager/v1/objects/storages/882000452491/jobs/892",
            "userId": "restapi",
            "status": "Initializing",
            "state": "Queued",
            "createdTime": "2020-11-03T08:50:46Z",
            "updatedTime": "2020-11-03T08:50:46Z",
            "request": {
                "requestUrl": "/ConfigurationManager/v1/objects/storages/882000452491/ldevs",
                "requestMethod": "POST",
                "requestBody": "{\n    \"poolId\": 0,\n    \"blockCapacity\": 2097152,\n    \"dataReductionMode\": \"compression_deduplication\"\n}"
            }
        }

        {
            "jobId" : 892,
            "self" : "/ConfigurationManager/v1/objects/storages/882000452491/jobs/892",
            "userId" : "restapi",
            "status" : "Completed",
            "state" : "Succeeded",
            "createdTime" : "2020-11-03T08:50:46Z",
            "updatedTime" : "2020-11-03T08:50:49Z",
            "completedTime" : "2020-11-03T08:50:49Z",
            "request" : {
                "requestUrl" : "/ConfigurationManager/v1/objects/storages/882000452491/ldevs",
                "requestMethod" : "POST",
                "requestBody" : "{\n    \"poolId\": 0,\n    \"blockCapacity\": 2097152,\n    \"dataReductionMode\": \"compression_deduplication\"\n}"
            },
            "affectedResources" : [ "/ConfigurationManager/v1/objects/storages/882000452491/ldevs/3" ]
        }
        """
        #if "errorSource" in result:
        #    self.dump(method, uri, headers=headers, data=data, _result=result)
        if "jobId" in result:
            while True:
                r = self.get("/jobs/%d" % result["jobId"])
                if r is None:
                    return
                if r["status"] == "Completed":
                    return r
                time.sleep(2)
        return result

    def dump(self, method, uri=None, headers=None, data=None, status=None, _result=None, **kwargs):
        import traceback
        traceback.print_stack()
        buff = "%s %s\n" % (method, uri)
        if headers:
            buff += "headers:\n"
            buff += json.dumps(headers, indent=4)
            buff += "\n"
        if data:
            buff += "body:\n"
            try:
                buff += json.dumps(data, indent=4)
            except ValueError:
                buff += str(data)
            buff += "\n"
        if status:
            buff += "status: %s\n" % status
        if _result:
            buff += "result:\n"
            buff += json.dumps(_result, indent=4)
            buff += "\n"
        raise ex.Error(buff)

    @apilock
    def set_label(self, oid, label):
        d = {
            "label": label,
        }
        data = self.put("/ldevs/%d" % oid, data=d)
        return data

    def del_session(self, id=None, **kwargs):
        d = {
            "force": True
        }
        return self.delete("/sessions/%d" % int(id), data=d)

    def list_sessions(self, **kwargs):
        return self.get("/sessions")

    def list_supported_host_modes(self, **kwargs):
        return self.get("/supported-host-modes/instance")

    def list_mappings(self, id=None, naa=None, name=None, **kwargs):
        ldev_data = self.get_ldev(oid=id, naa=naa, name=name)
        if ldev_data is None:
            raise ex.Error("ldev not found")
        return self.get_mappings(ldev_data)

    def get_mappings(self, ldev_data):
        mappings = {}
        naa = ldev_data.get("naaId")
        for d in ldev_data.get("ports", []):
            tgt_id = self.port_wwn_by_id.get(d["portId"])
            hba_id = self.host_wwn_by_id(d["hostGroupNumber"], d["portId"])
            lun_id = d["lun"]
            mappings[hba_id+":"+tgt_id+":"+str(lun_id)] = {
                "mapping": d,
                "tgt_id": tgt_id,
                "hba_id": hba_id,
                "disk_id": naa,
            }
        return mappings

    def resize_disk(self, id=None, naa=None, name=None, size=None, **kwargs):
        if size is None:
            raise ex.Error("'size' key is mandatory")
        if name is None and id is None and naa is None:
            raise ex.Error("'name', 'naa' or 'id' must be specified")
        ldev = self.get_ldev(oid=id, naa=naa, name=name)
        if ldev is None:
            raise ex.Error("ldev not found")
        pool = self.get_pool_by_id(ldev["poolId"])
        if pool is None:
            raise ex.Error("pool not found")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="B") // 512
        else:
            current_size = int(ldev["blockCapacity"])
            size = convert_size(size, _to="B") // 512
            incr = size - current_size
        if incr <= 0:
            raise ex.Error("negative ldev expansion is not allowed")

        d = {
            "parameters": {
                "additionalBlockCapacity": incr,
            }
        }
        data = self.put("/ldevs/%d/actions/expand/invoke" % ldev["ldevId"], data=d)
        data = self.get_ldev(ldev["ldevId"])
        return data


    def unmap_ldev_from_host(self, host_group_id=None, port_id=None, lun_id=None):
        oid = ",".join([
            str(port_id),
            str(host_group_id),
            str(lun_id),
        ])
        retry = {
            "condition": lambda x: x.get("error", {}).get("detailCode") == "30000E-2-B958-0233",
            "message": "retry unmaping %s: LU is executing host I/O" % oid,
        }
        data = self.delete('/luns/%s' % oid, retry=retry)
        return data


    def map_ldev_to_host(self, ldev_id=None, host_group_id=None, port_id=None, lun_id=None):
        d = {
            "ldevId": ldev_id,
            "hostGroupNumber": host_group_id,
            "portId": port_id,
            "lun": lun_id,
        }
        data = self.post('/luns', data=d)
        return data


    def del_ldev(self, id=None, naa=None, name=None, **kwargs):
        if id is None and name is None and naa is None:
            raise ex.Error("'id', 'naa' or 'name' must be specified")
        data = self.get_ldev(oid=id, name=name)
        if data is None:
            return
        return self._del_ldev(data)

    #@apilock
    def del_disk(self, id=None, name=None, naa=None, **kwargs):
        if id is None and name is None and naa is None:
            raise ex.Error("'id', 'naa' or 'name' must be specified")
        data = self.get_ldev(oid=id, naa=naa, name=name)
        if data is None:
            return
        results = {}
        response = self._unmap_lun(data)
        results["unmap"] = response
        response = self._del_ldev(data)
        results["del_ldev"] = data
        return results


    def hbagroup_by_targetgroup(self, mappings):
        data = {}
        for mapping in mappings:
            hba, targets = mapping.split(":", 1)
            targets = ",".join(sorted(targets.split(",")))
            if targets not in data:
                data[targets] = set([hba])
            else:
                data[targets].add(hba)
        l = []
        for targets in data:
            t = targets.split(",")
            h = sorted(list(data[targets]))
            l.append((h, t))
        return l


    def translate_mappings(self, mappings):
        targets = set()
        for mapping in mappings:
            elements = mapping.split(":", 1)
            targets |= set((elements[-1]).split(","))
        targets = list(targets)
        return targets


    def split_mappings(self, mappings):
        data = []
        for mapping in mappings:
            elements = mapping.split(":", 1)
            for target in set((elements[-1]).split(",")):
                data.append((elements[0], target))
        return data


    def unmap_lun(self, id=None, name=None, naa=None, mappings=None, **kwargs):
        if not name and id is None and naa is None:
            raise ex.Error("'id' 'naa' or 'name' is mandatory")
        ldev_data = self.get_ldev(oid=id, naa=naa, name=name)
        if not ldev_data:
            raise ex.Error("no ldev found")
        return self._unmap_lun(ldev_data, mappings)


    def _unmap_lun(self, ldev_data, mappings=None):
        current_mappings = self.get_mappings(ldev_data)
        results = []
        mappings_done = set()
        if mappings:
            toremove = self.split_mappings(mappings)
        else:
            toremove = None
        for d in current_mappings.values():
            key = (d["hba_id"], d["tgt_id"])
            # {'portId': 'CL6-B', 'hostGroupNumber': 101, 'hostGroupName': 'ROS093SRDL3861_2', 'lun': 0}
            if key in mappings_done:
                continue
            if toremove is None or key in toremove:
                self.del_mapping(d["mapping"])
                mappings_done.add(key)
                results.append(d)
        return results

    def host_wwn_by_id(self, hostgroup_id, port_id):
        params = {
            "$query": "hostGroup.storageDeviceId eq %s and " \
                      "hostGroup.hostGroupNumber eq '%s' and " \
                      "hostGroup.portId eq '%s'" % (
                          self.storage_id,
                          hostgroup_id,
                          port_id
                      )
        }
        path = "/views/host-groups-host-wwns-wwns"
        data = self.get(path, params=params, base="")
        return data["data"][0]["wwn"]["wwn"]

    def show_hostgroup(self, id=None, hba_id=None, **kwargs):
        if id is not None:
            return [self.get_hostgroup_by_id(id)]
        elif hba_id:
            return self.get_hostgroups_by_hba(hba_id)
        else:
            raise ex.Error("'id' or 'hba_id' must be specified")

    def get_hostgroup_by_id(self, oid):
        path = "/host-groups/%s" % oid
        data = self.get(path)
        return data

    def get_hostgroups_by_hba(self, hba_id):
        params = {
            "$query": "hostGroup.storageDeviceId eq %s and wwn.wwn eq '%s'" % (self.storage_id, hba_id)
        }
        path = "/views/host-groups-host-wwns-wwns"
        data = self.get(path, params=params, base="")
        try:
            return [d.get("hostGroup") for d in data["data"]]
        except IndexError:
            return

    def find_hostgroups(self, hbagroup, targetgroup):
        hg_ids = set()
        data = []
        port_ids = [self.port_id_by_wwn.get(w) for w in targetgroup]
        for hba_id in hbagroup:
            _data = []
            for hg in self.get_hostgroups_by_hba(hba_id):
                if not hg:
                    continue
                if hg["hostGroupId"] in hg_ids:
                    continue
                if hg["portId"] not in port_ids:
                    continue
                hg_ids.add(hg["hostGroupId"])
                _data.append(hg)
            if not _data:
                raise ex.Error("hostgroup containing hba id %s not found" % hba_id)
            data += _data
        return data


    @apilock
    def map_lun(self, id=None, naa=None, name=None, targets=None, mappings=None, lun=None, ldev_data=None, **kwargs):
        if not name and id is None and naa is None:
            raise ex.Error("'id' 'naa' or 'name' is mandatory")
        if targets is None and mappings is None:
            raise ex.Error("'targets' or 'mappings' must be specified")

        if ldev_data is None:
            ldev_data = self.get_ldev(oid=id, naa=naa, name=name)
        if ldev_data is None:
            raise ex.Error("ldev not found")

        results = []
        for hbagroup, targetgroup in self.hbagroup_by_targetgroup(mappings):
            hostgroups = self.find_hostgroups(hbagroup, targetgroup)
            if not hostgroups:
                raise ex.Error("could not find a set of array hosts equivalent to: %s" % hbagroup)
            for hostgroup in hostgroups:
                result = self.map_ldev_to_host(ldev_id=ldev_data["ldevId"], host_group_id=hostgroup["hostGroupNumber"], port_id=hostgroup["portId"], lun_id=lun)
                results.append(result)
        return results


    #@apilock
    def add_disk(self, name=None, size=None, pool=None, targets=None,
                 mappings=None, compression=True, dedup=True, lun=None,
                 start_ldev_id=None, end_ldev_id=None, resource_group=0, **kwargs):
        for key in ["name", "size", "pool"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)

        # lun
        data = self.add_ldev(
            name=name, size=size, pool=pool,
            compression=compression, dedup=dedup,
            start_ldev_id=start_ldev_id, end_ldev_id=end_ldev_id,
            resource_group=resource_group,
        )

        # mappings
        if mappings:
            mappings = self.map_lun(id=data["ldevId"], mappings=mappings, targets=targets, lun=lun, ldev_data=data)

        data = self.get_ldev(data["ldevId"])

        if "naaId" not in data:
            print(json.dumps(data, indent=4))
            raise ex.Error("no WWN in data")

        # collector update
        warnings = []
        try:
            self.add_diskinfo(data, size, pool)
        except Exception as exc:
            warnings.append(str(exc))
        disk_id = data["naaId"]
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["ldevId"],
            "mappings": sorted(self.list_mappings(naa=disk_id).values(), key=lambda x: (x["hba_id"], x["tgt_id"], x["disk_id"])),
        }
        if warnings:
            results["warnings"] = warnings

        return results


    def show_system(self, **kwargs):
        return self.get_system()


    def show_pool(self, **kwargs):
        return self.get_pool_by_name(kwargs["name"])


    def list_pools(self, **kwargs):
        return self.get_pools()


    def list_virtual_storages(self, **kwargs):
        return self.get_virtual_storages()


    def list_host_groups(self, **kwargs):
        return self.get_host_groups()


    def list_resource_groups(self, **kwargs):
        return self.get_resource_groups()


    def discard_zero_page(self, id=None, name=None, naa=None, **kwargs):
        data = self.get_ldev(oid=id, name=name, naa=naa)
        return self.put("/ldevs/%s/actions/discard-zero-page/invoke" % data["ldevId"])

    @staticmethod
    def fmt_lun_path(port_id, hostgroup_id, lun_id):
        return ",".join([str(port_id), str(hostgroup_id), str(lun_id)])

    def clear_reservation(self, id=None, name=None, naa=None, **kwargs):
        data = self.get_ldev(oid=id, name=name, naa=naa)
        result = []
        for i, port in enumerate(data.get("ports", [])):
            result.append(self._clear_reservation(port["portId"], port["hostGroupNumber"], port["lun"]))
        return result

    def _clear_reservation(self, port_id, hostgroup_id, lun_id):
        return self.post("/luns/%s/actions/release-lu-host-reserve/invoke" % self.fmt_lun_path(port_id, hostgroup_id, lun_id))


    def show_virtual_storage(self, id=None, **kwargs):
        return self.get_virtual_storage(id)


    def show_ldev(self, id=None, name=None, naa=None, **kwargs):
        data = self.get_ldev(oid=id, name=name, naa=naa)
        for i, port in enumerate(data.get("ports", [])):
            data["ports"][i]["lun_data"] = self.get_lun(port["portId"], port["hostGroupNumber"], port["lun"])
        return data

    def get_lun(self, port_id, hostgroup_id, lun_id):
        return self.get("/luns/%s" % self.fmt_lun_path(port_id, hostgroup_id, lun_id))

    def list_ldevs(self, **kwargs):
        return self.get_ldevs()


    def list_fc_port(self, **kwargs):
        return self.get_fc_ports()

    @lazy
    def port_id_by_wwn(self):
        data = {}
        for d in self.get_fc_ports():
            data[d["wwn"]] = d["portId"]
        return data

    @lazy
    def port_wwn_by_id(self):
        data = {}
        for d in self.get_fc_ports():
            data[d["portId"]] = d["wwn"]
        return data

    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.Error(str(exc))
        if "error" in result:
            raise ex.Error(result["error"])
        return result


    def add_diskinfo(self, data, size=None, pool=None):
        if self.node is None:
            return
        try:
            result = self.node.collector_rest_post("/disks", {
                "disk_id": data["naaId"],
                "disk_devid": data["ldevId"],
                "disk_name": data["label"],
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": pool,
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(result["error"])
        return result


    # method aliases
    create_disk = add_disk
    unmap = unmap_lun
    map = map_lun


def do_action(action, array_name=None, node=None, **kwargs):
    o = Hcss()
    array = o.get_hcs(array_name)
    if array is None:
        raise ex.Error("array %s not found" % array_name)
    array.node = node
    if not hasattr(array, action):
        raise ex.Error("not implemented")
    try:
        result = getattr(array, action)(**kwargs)
    finally:
        array.close_session()
    if result is not None:
        try:
            print(json.dumps(result, indent=4))
        except TypeError:
            print(json.dumps({"error": "unserializable result: %s" % 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)


def debug_on():
    try:
        import httplib
    except ImportError:
        import http.client as httplib
    import logging
    httplib.HTTPConnection.debuglevel = 1
    logging.basicConfig()
    logging.getLogger().setLevel(logging.DEBUG)
    requests_log = logging.getLogger("requests.packages.urllib3")
    requests_log.setLevel(logging.DEBUG)
    requests_log.propagate = True

#debug_on()

if __name__ == "__main__":
    try:
        main(sys.argv)
        ret = 0
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


  0707010001f292000081a40000000000000000000000016a100daf00000d23000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/array/vioserver.py from __future__ import print_function

import os
import sys

import core.exceptions as ex
from env import Env
from core.node import Node
from utilities.proc import justcall

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.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.Error("ssh command execution error")
    return out, err

class VioServers(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = []
        self.filtering = len(objects) > 0
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "vioserver":
                continue
            try:
                username = self.node.oget(s, 'username')
                key = self.node.oget(s, 'key')
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            self.arrays.append(VioServer(name, username, key, node=self.node))
            done.append(name)

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class VioServer(object):
    def __init__(self, name, username, key, node=None):
        self.name = name
        self.node = node
        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())
 0707010001f286000081a40000000000000000000000016a100daf00009dc9000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/array/freenas.py   from __future__ import print_function

import sys
import json

import core.exceptions as ex
from foreign.six.moves.urllib.parse import quote_plus # pylint: disable=import-error
from utilities.storage import Storage
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from utilities.string import bdecode
from core.node import Node

try:
    import requests
except ImportError:
    raise ex.InitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except AttributeError:
    pass

VERIFY = False

PROG = "om 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 node or cluster configuration."),
    "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="inherit", action="store", dest="compression",
        choices=["off", "inherit", "lzjb", "lz4", "gzip", "gzip-9", "zle"],
        help="Toggle compression"),
    "sparse": Option(
        "--sparse", default=False, action="store_true", dest="sparse",
        help="Toggle zvol sparse mode."),
    "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="", 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"),
    "lun": Option(
        "--lun", action="store", type=int, dest="lun",
        help="The logical unit number to assign to the extent on attach to a target. If not specified, a free lun is automatically assigned."),
    "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": Option(
        "--target", action="append", dest="target",
        help="The target object iqn"),
    "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"),
    "authmethod": Option(
        "--auth-method", action="store", default="NONE", dest="authmethod",
        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": Option(
        "--initiatorgroup", action="append", dest="initiatorgroup",
        help="The initiator group 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,
                OPT.lun,
            ],
        },
        "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.sparse,
                OPT.dedup,
                OPT.mappings,
                OPT.lun,
            ],
        },
        "map_iscsi_zvol": {
            "msg": "Map an extent to the specified initiator:target links",
            "options": [
                OPT.name,
                OPT.target,
                OPT.mappings,
                OPT.lun,
            ],
        },
        "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.name,
                OPT.portal_id,
                OPT.target_id,
                OPT.target,
                OPT.initiatorgroup,
                OPT.initiatorgroup_id,
                OPT.authgroup_id,
                OPT.authmethod,
            ],
        },
    },
    "Delete actions": {
        "del_iscsi_file": {
            "msg": "Delete and unpresent a file-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.naa,
                OPT.volume,
            ],
        },
        "del_iscsi_zvol": {
            "msg": "Delete and unpresent a zvol-backed iscsi disk",
            "options": [
                OPT.name,
                OPT.naa,
                OPT.volume,
            ],
        },
        "unmap": {
            "msg": "Unmap the specified initiator:target links",
            "options": [
                OPT.mappings,
            ],
        },
        "unmap_iscsi_zvol": {
            "msg": "Unmap an extent from the specified initiator:target links",
            "options": [
                OPT.name,
                OPT.mappings,
            ],
        },
        "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,
                OPT.name,
            ],
        },
        "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_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=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "freenas":
                continue
            timeout = self.node.oget(s, "timeout")
            try:
                username = self.node.oget(s, "username")
                password = self.node.oget(s, "password")
                api = self.node.oget(s, "api")
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            self.arrays.append(Freenas(name, api, username, password, timeout, node=self.node))
            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, timeout, node=None):
        self.node = node
        self.name = name
        self.api = api.split("/api")[0]
        self.username = username
        self.password = password
        self.auth = (username, password)
        self.timeout = timeout
        self.keys = ['version',
                     'volumes',
                     'iscsi_targets',
                     'iscsi_targettoextents',
                     'iscsi_extents']

    def delete(self, uri, data=None, timeout=None, api="v2.0"):
        timeout = timeout or self.timeout
        ep = self.api + "/api/" + api + uri + "/"
        headers = {'Content-Type': 'application/json'}
        if data:
            data = json.dumps(data)
        try:
            r = requests.delete(ep, data=data, auth=self.auth, timeout=timeout, verify=VERIFY, headers=headers)
        except Exception as exc:
            raise ex.Error("DELETE %s %s => %s" % (ep, data, exc))
        content = bdecode(r.content)
        if r.status_code != 200:
            raise ex.Error("DELETE %s %s => %d: %s" % (ep, data, r.status_code, content))
        return content

    def put(self, uri, data=None, timeout=None, api="v2.0"):
        timeout = timeout or self.timeout
        ep = self.api + "/api/" + api + uri + "/"
        headers = {'Content-Type': 'application/json'}
        if data:
            data = json.dumps(data)
        try:
            r = requests.put(ep, data=data, auth=self.auth, timeout=timeout, verify=VERIFY, headers=headers)
        except Exception as exc:
            raise ex.Error("PUT %s %s => %s" % (ep, data, exc))
        content = bdecode(r.content)
        if r.status_code != 200:
            raise ex.Error("PUT %s %s => %d: %s" % (ep, data, r.status_code, content))
        return content

    def post(self, uri, data=None, timeout=None, api="v2.0"):
        timeout = timeout or self.timeout
        ep = self.api + "/api/" + api + uri + "/"
        headers = {'Content-Type': 'application/json'}
        if data:
            data = json.dumps(data)
        try:
            r = requests.post(ep, data=data, auth=self.auth, timeout=timeout, verify=VERIFY, headers=headers)
        except Exception as exc:
            raise ex.Error("POST %s %s => %s" % (ep, data, exc))
        content = bdecode(r.content)
        if r.status_code != 200:
            raise ex.Error("POST %s %s => %d: %s" % (ep, data, r.status_code, content))
        return content

    def get(self, uri, params=None, timeout=None, api="v2.0"):
        timeout = timeout or self.timeout
        ep = self.api + "/api/" + api + uri + "/"
        try:
            r = requests.get(ep, params=params, auth=self.auth, timeout=timeout, verify=VERIFY)
        except Exception as exc:
            raise ex.Error("GET %s %s => %s" % (ep, params, exc))
        content = bdecode(r.content)
        if r.status_code != 200:
            raise ex.Error("GET %s %s => %d: %s" % (ep, params, r.status_code, content))
        return content

    # OK
    def get_version(self):
        buff = self.get("/system/version")
        return buff

    # OK
    def get_volumes(self):
        buff = self.get("/pool/dataset", {"limit": 0})
        return buff

    def get_pools(self):
        buff = self.get("/storage/volume", {"limit": 0}, api="v1.0")
        return buff

    def get_iscsi_target_id(self, tgt_id):
        buff = self.get("/iscsi/target/id/%d" % tgt_id)
        return buff

    # OK
    def get_iscsi_targets(self):
        buff = self.get("/iscsi/target", {"limit": 0})
        return buff

    # OK
    def get_iscsi_targettoextents(self):
        buff = self.get("/iscsi/targetextent", {"limit": 0})
        return buff

    # OK
    def get_iscsi_extents(self):
        buff = self.get("/iscsi/extent", {"limit": 0})
        return buff

    # OK
    def get_iscsi_portal(self):
        buff = self.get("/iscsi/portal", {"limit": 0})
        return buff

    # OK
    def get_iscsi_targetgroup(self):
        buff = self.get("/iscsi/target", {"limit": 0})
        return buff

    def get_iscsi_targetgroup_id(self, tg_id):
        buff = self.get("/iscsi/target/id/%d" % tg_id)
        return buff

    def get_iscsi_authorizedinitiator(self):
        buff = self.get("/iscsi/initiator", {"limit": 0})
        return buff

    def get_iscsi_authorizedinitiator_id(self, initiator_id):
        buff = self.get("/iscsi/initiator/id/%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["name"] in target_names:
                l.append(target["id"])
        return l

    def get_iscsi_initiatorgroup_ids(self, initiator_names):
        buff = self.get_iscsi_authorizedinitiator()
        data = json.loads(buff)
        l = []
        for initiator in initiator_names:
            for item in data:
                if initiator in item["initiators"]:
                    l.append(item["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["name"]:
                return extent
            if naa and naa == extent["naa"]:
                return extent

    def del_iscsi_extent(self, extent_id):
        path = "/iscsi/extent/id/%d" % extent_id
        self.delete(path)

    def add_iscsi_zvol_extent(self, name=None, size=None, volume=None,
                              insecure_tpc=True, blocksize=512, sparse=False, compression="inherit", **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        data = self.add_zvol(name=name, size=size, volume=volume, sparse=sparse, compression=compression, **kwargs)
        d = {
            "type": "DISK",
            "name": name,
            "insecure_tpc": insecure_tpc,
            "blocksize": blocksize,
            "disk": "zvol/%s/%s" % (volume, name),
        }
        buff = self.post("/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.Error("'%s' key is mandatory" % key)
        size = convert_size(size, _to="B")
        d = {
            "type": "FILE",
            "name": name,
            "insecure_tpc": insecure_tpc,
            "blocksize": blocksize,
            "filesize": size,
            "path": "/mnt/%s/%s" % (volume, name),
        }
        buff = self.post("/iscsi/extent", d)
        data = json.loads(buff)
        return data

    def add_iscsi_targets_to_extent(self, extent_id=None, targets=None,
                                    lun=None, **kwargs):
        for key in ["extent_id", "targets"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        target_ids = self.get_iscsi_target_ids(targets)
        if lun is None:
            lun = self.get_aligned_lun(target_ids)
        data = []
        for target_id in target_ids:
            data.append(self.add_iscsi_target_to_extent(target_id, extent_id, lun=lun))
        return data

    def del_iscsi_targetextent(self, id):
        buff = self.delete("/iscsi/targetextent/id/%d" % id, data=True)
        data = json.loads(buff)
        return data

    def add_iscsi_target_to_extent(self, target_id, extent_id, lun=None):
        d = {
            "target": target_id,
            "extent": extent_id,
            "lunid": lun,
        }
        buff = self.post("/iscsi/targetextent", d)
        data = json.loads(buff)
        return data

    def del_iscsi_targetextent_of_extent(self, extent_id):
        d = {
            "extent": extent_id,
        }
        buff = self.get("/iscsi/targetextent", d)
        data = json.loads(buff)
        for d in data:
            self.del_iscsi_targetextent(d["id"])
        return data

    def del_zvol(self, name=None, volume=None, **kwargs):
        for key in ["name", "volume"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        data = self.get_zvol(volume, name)
        path = '/pool/dataset/id/%s' % quote_plus(data["id"])
        self.delete(path)

    def add_zvol(self, name=None, size=None, volume=None,
                 compression="inherit", dedup="off", sparse=False,
                 **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        size = convert_size(size, _to="B")
        d = {
            "name": "%s/%s" % (volume, name),
            "type": "VOLUME",
            "volsize": size,
            "sparse": sparse,
            "deduplication": dedup.upper(),
        }
        if compression != "inherit":
            d["compression"] = compression.upper()
        buff = self.post('/pool/dataset', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.Error(buff)

    def get_zvol(self, volume=None, name=None):
        params = {
            "name": "%s/%s" % (volume, name),
        }
        buff = self.get('/pool/dataset', params)
        try:
            return json.loads(buff)[0]
        except (IndexError, ValueError):
            raise ex.Error(buff)

    def get_aligned_lun(self, target_ids):
        tte_data = json.loads(self.get_iscsi_targettoextents())
        luns = [d["lunid"] for d in tte_data if d["target"] in target_ids]
        for lun in range(2^16):
            if luns.count(lun) == 0:
                return lun
        return

    # OK
    def list_mappings(self, name=None, naa=None, **kwargs):
        tte_data = json.loads(self.get_iscsi_targettoextents())
        #print("tte_data <%s>"%tte_data)
        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.Error("extent not found")
            extent_id = data["id"]
            tte_data = [d for d in tte_data if d["extent"] == extent_id]
        extent_data = {}
        for d in json.loads(self.get_iscsi_extents()):
            extent_data[d["id"]] = d
        #print("\nextent_data <%s>"%extent_data)
        target_data = {}
        for d in json.loads(self.get_iscsi_targets()):
            target_data[d["id"]] = d
        #print("\ntarget_data <%s>"%target_data)
        tg_by_target = {}
        for d in json.loads(self.get_iscsi_targetgroup()):
            tg_by_target[d["id"]] = d["groups"]
        #print("\ntg_by_target <%s>"%tg_by_target)
        ig_data = {}
        for d in json.loads(self.get_iscsi_authorizedinitiator()):
            ig_data[d["id"]] = d
        #print("\nig_data <%s>"%ig_data)
        mappings = {}
        #print("\nSTART LOOP")
        for d in tte_data:
            #print("d <%s>"%d)
            disk_id = extent_data[d["extent"]]["naa"].replace("0x", "")
            #print("disk_id <%s>"%disk_id)
            #for tg in tg_by_target.get(d["id"], []):
            for tg in tg_by_target.get(d["target"], []):
                #print("current_tg <%s>"%tg)
                ig_id = tg["initiator"]
                ig = ig_data[ig_id]
                #print("ig_id <%s>   ig <%s>"%(ig_id, ig))
                #for hba_id in ig["initiators"].split("\n"):
                for hba_id in ig["initiators"]:
                    tgt_id = target_data[d["target"]]["name"]
                    #tgt_id = "tgtid"
                    mappings[hba_id+":"+tgt_id+":"+disk_id] = {
                       "targetgroup": tg,
                       "extent": d,
                       "disk_id": disk_id,
                       "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.Error("'size' key is mandatory")
        if name is None and naa is None:
            raise ex.Error("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None:
            raise ex.Error("extent not found")
        volume = self.extent_volume(data)
        if volume is None:
            raise ex.Error("volume not found")
        zvol_data = self.get_zvol(volume=volume, name=data["name"])
        if zvol_data is None:
            raise ex.Error("zvol not found")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="B")
            current_size = int(zvol_data["volsize"]["parsed"])
            size = current_size + incr
        else:
            size = convert_size(size, _to="B")

        d = {
            "volsize": size,
        }
        buff = self.put('/pool/dataset/id/%s' % quote_plus(zvol_data["id"]), d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.Error(buff)

    def del_iscsi_initiatorgroup(self, id=None, **kwargs):
        content = self.get_iscsi_authorizedinitiator_id(id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.Error("initiator group not found")
        self._del_iscsi_initiatorgroup(ig_id=id, **kwargs)
        return data

    def _del_iscsi_initiatorgroup(self, ig_id=None, **kwargs):
        if ig_id is None:
            raise ex.Error("'id' in mandatory")
        self.delete('/iscsi/initiator/%d' % ig_id)

    def get_iscsi_targettoextent(self, id=None, **kwargs):
        if id is None:
            raise ex.Error("'id' in mandatory")
        content = self.get('/iscsi/targetextent/%d' % id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.Error("targettoextent not found")
        return data

    def add_iscsi_initiatorgroup(self, **kwargs):
        data = self._add_iscsi_initiatorgroup(**kwargs)
        return data

    def _add_iscsi_initiatorgroup(self, initiators=None, auth_network="", comment=None,
                                  **kwargs):
        for key in ["initiators"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        anet = list(auth_network.split(","))
        d = {
            "initiators": initiators,
            "auth_network": anet,
        }
        if comment:
            d["comment"] = comment

        buff = self.post('/iscsi/initiator/', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.Error(buff)

    # targetgroup
    def del_iscsi_targetgroup(self, id=None, **kwargs):
        content = self.get_iscsi_targetgroup_id(id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.Error("target group not found")
        self._del_iscsi_targetgroup(tg_id=id, **kwargs)
        return data

    def _del_iscsi_targetgroup(self, tg_id=None, **kwargs):
        if tg_id is None:
            raise ex.Error("'tg_id' is mandatory")
        self.delete('/iscsi/target/%d' % tg_id)

    def add_iscsi_targetgroup(self, **kwargs):
        if kwargs.get("portal_id") is None:
            kwargs["portal_id"] = 1
        for key in ["initiatorgroup", "target"]:
            idkey = key + "_id"
            val = kwargs.get(idkey)
            if val is None:
                ids = []
            else:
                ids = [val]

            names = kwargs.get(key)
            if names is not None:
                fn = "get_iscsi_%s_ids" % key
                ids += getattr(self, fn)(names)
                if not ids:
                    raise ex.Error("no '%s' ids found" % key)
                del kwargs[key]
            kwargs[idkey] = ids

        data = []
        for tid in kwargs["target_id"]:
            for igid in kwargs["initiatorgroup_id"]:
                _data = self._add_iscsi_targetgroup(
                    portal_id=kwargs.get("portal_id"),
                    initiatorgroup_id=igid,
                    target_id=tid,
                    authmethod=kwargs.get("authmethod"),
                    authgroup_id=kwargs.get("authgroup_id"),
                )
                data.append(_data)
        return data

    def _add_iscsi_targetgroup(self, portal_id=None, initiatorgroup_id=None,
                               target_id=None, authmethod="NONE",
                               authgroup_id=None, **kwargs):
        buff = self.get_iscsi_target_id(target_id)
        data = json.loads(buff)
        for g in data["groups"]:
            if portal_id == g["portal"] and initiatorgroup_id == g["initiator"]:
                return data
        d = {
            "initiator": initiatorgroup_id,
            "portal": portal_id,
            "authmethod": authmethod,
            "auth": authgroup_id,
        }
        data["groups"].append(d)
        del data["id"]
        buff = self.put('/iscsi/target/id/%d' % target_id, data)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.Error(buff)

    # target
    # OK
    def del_iscsi_target(self, id=None, name=None, **kwargs):
        if id is None and name:
            try:
                id = self.get_iscsi_target_ids([name])[0]
            except IndexError:
                return
        if id is None:
            raise ex.Error("'id' is mandatory")
        content = self.get_iscsi_target_id(id)
        try:
            data = json.loads(content)
        except ValueError:
            raise ex.Error("target not found")
        self._del_iscsi_target(id=id, **kwargs)
        return data

    # OK
    def _del_iscsi_target(self, id=None, **kwargs):
        if id is None:
            raise ex.Error("'id' is mandatory")
        self.delete('/iscsi/target/id/%d' % id)

    # OK
    def add_iscsi_target(self, **kwargs):
        data = self._add_iscsi_target(**kwargs)
        return data

    # OK
    def _add_iscsi_target(self, name=None, alias=None, **kwargs):
        for key in ["name"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        d = {
            "name": name,
        }
        if alias:
            d["alias"] = alias

        buff = self.post('/iscsi/target/', d)
        try:
            return json.loads(buff)
        except ValueError:
            raise ex.Error(buff)

    def add_iscsi_file(self, name=None, size=None, volume=None, targets=None,
                       mappings=None, insecure_tpc=True, blocksize=512, lun=None, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)

        if targets is None and mappings is None:
            raise ex.Error("'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 "name" in data:
                if isinstance(data["name"], list):
                    raise ex.Error("\n".join(data["name"]))
                raise ex.Error(data["name"])
            raise ex.Error(str(data))

        self.add_iscsi_targets_to_extent(extent_id=data["id"], targets=targets, lun=lun, **kwargs)
        disk_id = data["naa"].replace("0x", "")
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["id"],
            "mappings": sorted(self.list_mappings(naa=disk_id).values(), key=lambda x: (x["hba_id"], x["tgt_id"], x["disk_id"])),
        }
        return results

    def del_iscsi_file(self, name=None, naa=None, **kwargs):
        if name is None and naa is None:
            raise ex.Error("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None:
            return
        self.del_iscsi_targetextent_of_extent(data["id"])
        self.del_iscsi_extent(data["id"])
        return data

    def translate_mappings(self, mappings):
        targets = set()
        for mapping in mappings:
            elements = mapping.split(":iqn.")
            targets |= set(("iqn."+elements[-1]).split(","))
        targets = list(targets)
        return targets

    def split_mappings(self, mappings):
        data = []
        for mapping in mappings:
            elements = mapping.split(":iqn.")
            for target in set(("iqn."+elements[-1]).split(",")):
                data.append((elements[0], target))
        return data

    def unmap(self, mappings=None, **kwargs):
        for key in ["mappings"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        targets = self.split_mappings(mappings)
        current_mappings = self.list_mappings()
        results = []
        for mapping in self.split_mappings(mappings):
            for tg in current_mappings.values():
                if tg["tgt_id"] != mapping[1] or \
                   tg["hba_id"] != mapping[0]:
                    continue
                result = self.del_iscsi_targetgroup(id=tg["targetgroup"]["id"])
                results.append(result)
        return results

    def unmap_iscsi_zvol(self, name=None, mappings=None, **kwargs):
        for key in ["name", "mappings"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        targets = self.split_mappings(mappings)
        current_mappings = self.list_mappings()
        extent = self.get_iscsi_extent(name=name)
        if not extent:
            raise ex.Error("no extent found for disk : %s" % (name))
        results = []
        for mapping in self.split_mappings(mappings):
            mapping = ":".join(mapping)+":"+extent["naa"].replace("0x", "")
            tg = current_mappings.get(mapping)
            if not tg:
                continue
            result = self.del_iscsi_targetextent(tg["extent"]["id"])
            results.append(result)
        return results

    def map_iscsi_zvol(self, name=None, targets=None, mappings=None, lun=None, **kwargs):
        for key in ["name"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        if targets is None and mappings is None:
            raise ex.Error("'targets' or 'mappings' must be specified")

        if mappings is not None and targets is None:
            targets = self.translate_mappings(mappings)

        data = self.get_iscsi_extent(name=name)
        if data is None:
            raise ex.Error("zvol not found")
        results = self.add_iscsi_targets_to_extent(extent_id=data["id"], targets=targets, lun=lun, **kwargs)
        return results

    def add_iscsi_zvol(self, name=None, size=None, volume=None, targets=None,
                       mappings=None, insecure_tpc=True, blocksize=512, sparse=False, lun=None, **kwargs):
        for key in ["name", "size", "volume"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)

        # extent
        data = self.add_iscsi_zvol_extent(name=name, size=size, volume=volume, sparse=sparse, **kwargs)

        if "id" not in data:
            if "name" in data:
                if isinstance(data["name"], list):
                    raise ex.Error("\n".join(data["name"]))
                raise ex.Error(data["name"])
            raise ex.Error(str(data))

        # mappings
        if targets is not None or mappings is not None:
            if mappings is not None and targets is None:
                targets = self.translate_mappings(mappings)
            self.add_iscsi_targets_to_extent(extent_id=data["id"], targets=targets, lun=lun, **kwargs)

        # collector update
        warnings = []
        try:
            self.add_diskinfo(data, size, volume)
        except Exception as exc:
            warnings.append(str(exc))
        disk_id = data["naa"].replace("0x", "")
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["id"],
            "mappings": sorted(self.list_mappings(naa=disk_id).values(), key=lambda x: (x["hba_id"], x["tgt_id"], x["disk_id"])),
        }
        if warnings:
            results["warnings"] = warnings

        return results

    def del_iscsi_zvol(self, name=None, naa=None, volume=None, **kwargs):
        if name is None and naa is None:
            raise ex.Error("'name' or 'naa' must be specified")
        data = self.get_iscsi_extent(name=name, naa=naa)
        if data is None and volume is None:
            raise ex.Error("extent not found: need to specify the volume")
        if volume is None:
            try:
                volume = self.extent_volume(data)
            except ValueError:
                raise ex.Error("failed to identify zvol. may be a file ?")
        if data:
            self.del_iscsi_targetextent_of_extent(data["id"])
            self.del_iscsi_extent(data["id"])
        else:
            data = {}
        self.del_zvol(name=name, volume=volume)
        warnings = []
        try:
            self.del_diskinfo(data["naa"].replace("0x", ""))
        except Exception as exc:
            warnings.append(str(exc))
        if warnings:
            data["warnings"] = warnings
        return data

    def extent_volume(self, data):
        path = data["path"].split("/")
        volume = path[path.index("zvol")+1]
        return volume

    def list_pools(self, **kwargs):
        return json.loads(self.get_pools())

    def list_volume(self, **kwargs):
        return json.loads(self.get_volumes())

    def list_iscsi_target(self, **kwargs):
        return json.loads(self.get_iscsi_targets())

    def list_iscsi_targettoextent(self, **kwargs):
        return json.loads(self.get_iscsi_targettoextents())

    def list_iscsi_portal(self, **kwargs):
        return json.loads(self.get_iscsi_portal())

    def list_iscsi_extent(self, **kwargs):
        return json.loads(self.get_iscsi_extents())

    def list_iscsi_initiatorgroup(self, **kwargs):
        return json.loads(self.get_iscsi_authorizedinitiator())

    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.Error(str(exc))
        if "error" in result:
            raise ex.Error(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["naa"].replace("0x", ""),
                "disk_devid": data["id"],
                "disk_name": data["name"],
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": volume,
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(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.Error("array %s not found" % array_name)
    array.node = node
    if not hasattr(array, action):
        raise ex.Error("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)

def debug_on():
    try:
        import httplib
    except ImportError:
        import http.client as httplib
    import logging
    httplib.HTTPConnection.debuglevel = 1
    logging.basicConfig()
    logging.getLogger().setLevel(logging.DEBUG)
    requests_log = logging.getLogger("requests.packages.urllib3")
    requests_log.setLevel(logging.DEBUG)
    requests_log.propagate = True

#debug_on()

if __name__ == "__main__":
    try:
        main(sys.argv)
        ret = 0
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


   0707010001f291000081a40000000000000000000000016a100daf0000cc34000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/array/symmetrix.py from __future__ import print_function

import logging
import sys
import os
import json
import time

import core.exceptions as ex
from xml.etree.ElementTree import fromstring
from env import Env
from utilities.storage import Storage
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from core.node import Node
from utilities.proc import justcall, which
from utilities.lazy import lazy

PROG = "om 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 (sid)."),
    "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)"),
    "data": Option(
        "--data", action="store", dest="data",
        help="The workplan provided in json format."),
    "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_masking": {
            "msg": "Create masking objects from a workplan (IG, SG, MV).",
            "options": [
                OPT.data,
            ],
        },
        "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_srps": {
            "msg": "List storage resource groups.",
        },
        "list_directors": {
            "msg": "List directors.",
        },
        "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,
            ],
        },
    },
}

def set_sym_env():
    env = {
        "SYMCLI_WAIT_ON_DB": "1",
        "SYMCLI_WAIT_ON_GK": "1",
        "SYMCLI_CTL_ACCESS": "PARALLEL",
    }
    for key, val in env.items():
        if key in os.environ:
            continue
        os.environ[key] = val

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=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        if node:
            self.node = node
        else:
            self.node = Node()
        self.symms = []
        done = []
        for s in self.node.conf_sections(cat="array"):
            try:
                name = self.node.oget(s, 'name')
            except Exception:
                name = None
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and s not in self.objects:
                continue

            try:
                stype = self.node.oget(s, 'type')
            except:
                continue
            if stype != "symmetrix":
                continue

            symcli_path = self.node.oget(s, 'symcli_path')
            if symcli_path is None:
                symcli_path = self.find_symcli_path()
            if symcli_path is None:
                print("symcli path not found for array", s, file=sys.stderr)
                continue

            symcli_connect = self.node.oget(s, 'symcli_connect')
            if symcli_connect is not None:
                os.environ["SYMCLI_CONNECT"] = symcli_connect

            username = self.node.oget(s, 'username')
            password = self.node.oget(s, 'password')

            if password:
                try:
                    secname, namespace, _ = split_path(password)
                    password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
                except Exception as exc:
                    print("error decoding password: %s", exc, file=sys.stderr)

            symcfg = os.path.join(symcli_path, "symcfg")
            if which(symcfg) is None:
                raise ex.Error('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.iter('Symm_Info'):
                model = symm.find('model').text
                if model.startswith('VMAX'):
                    self.arrays.append(Vmax(name, symcli_path, symcli_connect, username, password, node=self.node))
                    done.append(name)
                elif model.startswith("PowerMax"):
                    self.arrays.append(PowerMax(name, symcli_path, symcli_connect, username, password, node=self.node))
                    done.append(name)
                #elif model.startswith("DMX-3"):
                #    self.arrays.append(Dmx3(name, symcli_path, symcli_connect, username, password, node=self.node))
                elif 'DMX' in model or '3000-M' in model:
                    self.arrays.append(Dmx(name, symcli_path, symcli_connect, username, password, node=self.node))
                    done.append(name)
                else:
                    print("unsupported sym model: %s" % model, file=sys.stderr)


    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 SymMixin(object):
    def __init__(self, sid, symcli_path, symcli_connect, username, password, node=None):
        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.node = node
        self.sid = sid
        self.symcli_path = symcli_path
        self.symcli_connect = symcli_connect
        self.username = username
        self.password = password
        self.log = logging.getLogger(Env.nodename+".array.sym."+self.sid)

        if 'SYMCLI_DB_FILE' in os.environ:
            dir = os.path.dirname(os.environ['SYMCLI_DB_FILE'])
            # flat format
            self.maskdb = os.path.join(dir, self.sid+'.bin')
            if not os.path.exists(self.maskdb):
                # emc grab format
                self.maskdb = os.path.join(dir, self.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 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, sid=None):
        self.set_environ()
        cmd += ['-sid', sid or 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, sid=None):
        cmd = ['/usr/symcli/bin/symdev'] + cmd
        return self.symcmd(cmd, xml=xml, log=log, sid=sid)

    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_remote_sid(self, rdfg):
        out, err, ret = self.symcfg(['-rdfg', rdfg, 'list'])
        tree = fromstring(out)
        for g in tree.iter('RdfGroup'):
            return g.find('remote_symid').text

    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_show_wwn(self, wwn):
        out, err, ret = self.symdev(['show', '-wwn', wwn])
        return self.parse_xml(out, key="Device")

    def wwn_to_dev(self, wwn):
        data = self.get_sym_dev_show_wwn(wwn)
        if not data:
            return
        return data[0].get("Dev_Info", {}).get("dev_name")

    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=None, exclude=None):
        if exclude is None:
            exclude = []
        if as_list is None:
            as_list = []
        tree = fromstring(buff)
        data = []
        def parse_elem(elem, as_list=None, exclude=None):
            if exclude is None:
                exclude = []
            if as_list is None:
                as_list = []
            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.iter(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_directors(self, **kwargs):
        print(json.dumps(self.get_directors(), indent=4))

    def get_directors(self, **kwargs):
        out = self.get_sym_dir_info()
        data = self.parse_xml(out, key="Director", as_list=["Port"])
        return data

    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 get_srps(self, **kwargs):
        out = self.get_sym_srp_info()
        data = self.parse_xml(out, key="SRP_Info")
        return data

    def list_sgs(self, **kwargs):
        print(json.dumps(self.get_sgs(), indent=4))

    def list_srps(self, **kwargs):
        print(json.dumps(self.get_srps(), 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):
        try:
            dev = self.resolve_dev(dev)
        except ex.Error:
            dev = None
        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):
        try:
            dev = self.resolve_dev(dev)
        except ex.Error:
            dev = None
        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, "-detail"])
        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
        initiator_count = 0
        for _data in data:
            initiator_count += len([1 for d in _data["Initiator_List"]["Initiator"] if "wwn" in d])
        data[0]["initiator_count"] = initiator_count
        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] = view_data["initiator_count"]
                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

    def add_ig(self, name, hba_ids=None, igs=None, consistent=True):
        if hba_ids is None:
            hba_ids = []
        if igs is None:
            igs = []
        cmd = ["-name", name, "-type", "initiator"]
        if consistent:
            cmd += ["-consistent_lun"]
        cmd += ["create"]
        result = []
        out, err, ret = self.symaccesscmd(cmd, xml=False, log=True)
        result.append({
            "cmd": ["symaccess"] + cmd,
            "ret": ret,
            "out": out,
            "err": err,
        })
        for ig in igs:
            cmd = ["-name", name, "-type", "initiator", "-ig", ig, "add"]
            out, err, ret = self.symaccesscmd(cmd, xml=False, log=True)
            result.append({
                "cmd": ["symaccess"] + cmd,
                "ret": ret,
                "out": out,
                "err": err,
            })
        for hba_id in hba_ids:
            cmd = ["-name", name, "-type", "initiator", "-wwn", hba_id, "add"]
            out, err, ret = self.symaccesscmd(cmd, xml=False, log=True)
            result.append({
                "cmd": ["symaccess"] + cmd,
                "ret": ret,
                "out": out,
                "err": err,
            })
        return result

    def add_igs(self, data):
        for i, ig in enumerate(data.get("ig", [])):
            name = ig.get("name")
            hba_ids = ig.get("hba_ids", [])
            igs = ig.get("ig", [])
            consistent = ig.get("consistent", True)
            result = self.add_ig(name, hba_ids, igs, consistent=consistent)
            data["ig"][i]["result"] = result
        return data

    def add_sg(self, name, srp, slo, sgs=None):
        if sgs is None:
            sgs = []
        cmd = ["create", name]
        if srp:
            cmd += ["-srp", srp]
        if slo:
            cmd += ["-slo", slo]
        result = []
        out, err, ret = self.symsg(cmd, xml=False, log=True)
        result.append({
            "cmd": ["symsg"] + cmd,
            "ret": ret,
            "out": out,
            "err": err,
        })
        for sg in sgs:
            cmd = ["-sg", name, "add", "sg", sg]
            out, err, ret = self.symsg(cmd, xml=False, log=True)
            if ret != 0 and "group is currently within device masking view" in err:
                # already done
                ret = 0
                out = err
                err = ""
            result.append({
                "cmd": ["symsg"] + cmd,
                "ret": ret,
                "out": out,
                "err": err,
            })
        return result

    def add_sgs(self, data):
        for i, sg in enumerate(data.get("sg", [])):
            name = sg.get("name")
            srp = sg.get("srp")
            slo = sg.get("slo")
            sgs = sg.get("sg", [])
            result = self.add_sg(name, srp, slo, sgs)
            data["sg"][i]["result"] = result
        return data

    def get_gks(self, sg):
        """
        <SymCLI_ML>
          <SG>
            <SG_Info>
              <name>SG_DL360S-20_GK</name>
              <symid>000297600015</symid>
              <update_time>Fri Mar 29 11:23:33 2019</update_time>
              ...
              <Num_of_GKS>0</Num_of_GKS>
        """
        cmd = ["show", sg]
        out, err, ret = self.symsg(cmd, xml=True)
        data = self.parse_xml(out, key="SG_Info")
        return int(data[0]["Num_of_GKS"])

    def add_gks(self, data):
        for i, gk_data in enumerate(data.get("gk", [])):
            result = self.add_gk(gk_data)
            data["gk"][i]["result"] = result
        return data

    def add_gk(self, data):
        sg = data.get("sg")
        tgt_gks = int(data.get("count", 6))
        try:
            cur_gks = self.get_gks(sg)
        except (IndexError, KeyError):
            return
        missing = tgt_gks - cur_gks
        if missing <= 0:
            return []
        cmd = ["create", "-gk", "-N", str(missing), "-sg", sg, "-noprompt"]
        out, err, ret = self.symdev(cmd, xml=False, log=True)
        result = [{
            "cmd": ["symdev"] + cmd,
            "ret": ret,
            "out": out,
            "err": err,
        }]
        return result


    def to_cyl(self, size):
        n = convert_size(size, _to="KB") // self.cylinder
        return n or 1

    def add_dev(self, data):
        size = data.get("size")
        name = data.get("name", "NONAME")
        sg = data.get("sg")
        cmd = ["show", sg]
        out, err, ret = self.symsg(cmd, xml=True, log=False)
        devs = self.parse_xml(out, key="Device", as_list=["Device"])
        if len(devs):
            return []
        out, err, ret = self.create_tdev(size, name, sg=sg)
        result = [{
            "cmd": ["symdev"] + cmd,
            "ret": ret,
            "out": out,
            "err": err,
        }]
        return result

    def create_tdev(self, size, name, sg=None, sid=None):
        cmd = ["create", "-tdev", "-N", "1", "-cap", str(self.to_cyl(size)), "-captype", "cyl"]
        if sg:
            cmd += ["-sg", sg,]
        cmd += ["-emulation", "FBA", "-device_name", name, "-noprompt", "-v"]
        out, err, ret = self.symdev(cmd, xml=False, log=True, sid=sid)
        return out, err, ret

    def parse_create_tdev(self, out):
        for line in out.splitlines():
            if "devices created are" not in line:
                continue
            try:
                return  line[line.index("[")+1:line.index("]")-1].split()
            except IndexError:
                pass
        return []

    def add_devs(self, data):
        for i, dev in enumerate(data.get("dev", [])):
            result = self.add_dev(dev)
            data["dev"][i]["result"] = result
        return data

    def list_pgs(self):
        out, err, ret = self.symaccesscmd(["list", "-type", "port"], xml=True)
        data = self.parse_xml(out, key="Port_Group", as_list=["Port_Group"])
        return [d["Group_Info"]["group_name"] for d in data]

    def pg_tgt_ids(self, name):
        out, err, ret = self.symaccesscmd(["show", name, "-type", "port"], xml=True)
        data = self.parse_xml(out, key="Director_Identification", as_list=["Director_Identification"])
        return [d["port_wwn"] for d in data if "port_wwn" in d]

    def find_pg(self, tgt_ids):
        for name in self.list_pgs():
            pg_tgt_ids = self.pg_tgt_ids(name)
            if set(pg_tgt_ids) == set(tgt_ids):
                return name

    def add_mvs(self, data):
        for i, mv in enumerate(data.get("mv", [])):
            pg = self.find_pg(mv["pg"])
            if not pg:
                data["mv"][i]["result"] = [{
                   "cmd": [],
                   "ret": 1,
                   "out": "",
                   "err": "can't create the '%s' masking view: no pg with port ids %s" % (mv["name"], mv["pg"]),
                }]
                continue
            cmd = ["create", "view", "-name", mv["name"], "-pg", pg]
            sgs = mv.get("sg", [])
            if sgs:
                cmd += ["-sg", ",".join(sgs)]
            igs = mv.get("ig", [])
            if igs:
                cmd += ["-ig", ",".join(igs)]
            out, err, ret = self.symaccesscmd(cmd, log=True, xml=False)
            data["mv"][i]["result"] = [{
               "cmd": ["symaccess"] + cmd,
               "ret": ret,
               "out": out,
               "err": err,
            }]
        return data

    def add_masking(self, data="null", **kwargs):
        data = json.loads(data)
        data = self.add_igs(data)
        data = self.add_sgs(data)
        data = self.add_gks(data)
        data = self.add_devs(data)
        data = self.add_mvs(data)
        return data

    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)

class Vmax(SymMixin):
    def __init__(self, sid, symcli_path, symcli_connect, username, password, node=None):
        SymMixin.__init__(self, sid, symcli_path, symcli_connect, username, password, node=node)
        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

    @lazy
    def get_symcli_version(self):
        symcli_bin = self.symcli_path + "/symcli"
        out, err, ret = justcall([symcli_bin, ])
        if ret != 0:
            return None
        import re
        m = re.search(r"\(SYMCLI\)\sVersion V(\d+)\.(\d+)", out)
        if m:
            return int(m.group(1)), int(m.group(2))
        return None

    def check_symcli_version(self, major, minor):
        version = self.get_symcli_version
        if version:
            v_major, v_minor = version
            return (v_major > major) or (v_major == major and v_minor > minor)
        return 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.Error("failed to write temp file: %s" % str(exc))
        return fpath

    def add_tdev(self, name=None, size=None, srdf=False, rdfg=None, **kwargs):
        if srdf and rdfg is None:
            raise ex.Error("--srdf is specified but --rdfg is not")

        if size is None:
            raise ex.Error("The '--size' parameter is mandatory")

        out, err, ret = self.create_tdev(size, name)
        if ret != 0:
            raise ex.Error(err)
        r1dev = self.parse_create_tdev(out)[0]

        if srdf and rdfg:
            rsid = self.get_sym_rdfg_remote_sid(rdfg)
            if not rsid:
                raise ex.Error("can't find remote sid of rdfg %s" % rdfg)
            out, err, ret = self.create_tdev(size, name, sid=rsid)
            if ret != 0:
                raise ex.Error(err)
            r2dev = self.parse_create_tdev(out)[0]
            self.createpair(r1dev+":"+r2dev, rdfg=rdfg, srdf_mode="sync", srdf_type="R1")

        data = self.get_sym_dev_wwn(r1dev)[0]

        return data

    def remote_dev_id(self, dev):
        data = self.get_dev_rdf(dev)
        return data.get("Remote").get("dev_name")

    def get_dev_rdf(self, dev):
        data = self.get_sym_dev_show(dev)
        if len(data) != 1:
            raise ex.Error("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.Error("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):
        dev = self.resolve_dev(dev)
        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.Error(out+err)

    def createpair(self, pair=None, rdfg=None, srdf_mode=None, srdf_type=None, invalidate=None, **kwargs):
        if pair is None:
            raise ex.Error("the --pair argument is mandatory")
        if srdf_type is None:
            raise ex.Error("the --srdf-type argument is mandatory")
        if srdf_mode is None:
            raise ex.Error("the --srdf-mode argument is mandatory")
        if pair.count(":") != 1:
            raise ex.Error("misformatted pair %s" % pair)
        dev, rdev = pair.split(":")
        try:
            rdf_data = self.get_dev_rdf(dev)
        except ex.Error:
            rdf_data = None
        if rdf_data is not None:
            raise ex.Error("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.Error(out+err)
        os.unlink(fpath)

    def deletepair(self, dev=None, **kwargs):
        dev = self.resolve_dev(dev)
        try:
            data = self.get_dev_rdf(dev)
        except ex.Error:
            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.Error(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.Error(out+err)
        os.unlink(fpath)
        return data

    def rename_disk(self, dev=None, name=None, **kwargs):
        dev = self.resolve_dev(dev)
        if dev is None:
            raise ex.Error("--dev is mandatory")
        if name is None:
            raise ex.Error("--name is mandatory")
        _cmd = "set dev %s -attribute device_name='%s'" % (dev, name)
        cmd = [_cmd]
        out, err, ret = self.symdev(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.Error(err)

    def resize_disk(self, dev=None, size=None, force=False, **kwargs):
        dev = self.resolve_dev(dev)
        if dev is None:
            raise ex.Error("The '--dev' parameter is mandatory")
        if size is None:
            raise ex.Error("The '--size' parameter is mandatory")
        dev_data = self.get_sym_dev_show(dev)
        if len(dev_data) != 1:
            raise ex.Error("device %s does not exist" % dev)
        dev_data = dev_data[0]
        current_size = int(dev_data["Capacity"]["cylinders"])
        if size.startswith("+"):
            incr = self.to_cyl(size.lstrip("+"))
            size = str(current_size + incr)
        else:
            size = str(self.to_cyl(size))
        if not force and int(size) < current_size:
            raise ex.Error("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
        deleted = False
        if rdf_data and not isinstance(self, PowerMax):
            self.deletepair(dev)
            deleted = True
        cmd = ["modify", dev, "-tdev", "-cap", str(size), "-captype", "cyl", "-noprompt"]
        if rdf_data and isinstance(self, PowerMax):
            cmd += ["-rdfg", rdf_data["Local"]["ra_group_num"]]
        out, err, ret = self.symdev(cmd, xml=False, log=True)
        if ret != 0:
            raise ex.Error(err)
        results = {
            "driver_data": {
                "pair_deleted": deleted,
            },
        }
        if rdf_data:
            results["driver_data"]["rdf"] = rdf_data
        return results

    def del_tdev(self, dev=None, **kwargs):
        dev = self.resolve_dev(dev)
        if dev is None:
            raise ex.Error("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.Error(err)
        self.del_diskinfo(data["wwn"])

    def resolve_dev(self, dev):
        if dev and len(dev) > 6:
            dev = self.wwn_to_dev(dev)
        if dev is None:
            raise ex.Error("dev not found")
        return dev

    def del_disk(self, dev=None, **kwargs):
        dev = self.resolve_dev(dev)
        try:
            rdf_data = self.get_dev_rdf(dev)
        except ex.Error as exc:
            self.log.info("rdf data: %s", exc)
            rdf_data = None
        data = self.get_sym_dev_show(dev)
        if len(data) != 1:
            raise ex.Error("dev %s does not exist" % dev)
        if data[0].get("Dev_Info", {}).get("snapvx_source") == "True":
            raise ex.Error("dev %s is a snapvx_source. can not delete" % dev)
        self.set_dev_ro(dev)
        self.del_map(dev)
        self.deletepair(dev)
        retry = 5
        do_free = self.check_symcli_version(9, 1)
        while retry > 0:
            if not do_free:
                self.free_tdev(dev)
            try:
                self.del_tdev(dev=dev, **kwargs)
                break
            except ex.Error as exc:
                if "A free of all allocations is required" in str(exc):
                    if retry == 1:
                        raise ex.Error("dev %s is still not free of all allocations after 5 tries" % dev)
                    # 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):
        dev = self.resolve_dev(dev)
        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.Error("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.Error("--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):
        dev = self.resolve_dev(dev)
        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.Error("no storage group found for the requested mappings")
        sgs = self.filter_sgs(sgs, srp=srp, slo=slo)
        if len(sgs) == 0:
            raise ex.Error("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.Error(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.Error(str(exc))
        if "error" in data:
            raise ex.Error(ret["error"])
        return ret


class Dmx(SymMixin):
    cylinder = 480 # KB

    def __init__(self, *args, **kwargs):
        SymMixin.__init__(self, *args, **kwargs)
        self.keys += ['sym_maskdb']

    def get_sym_maskdb(self):
        cmd = ['list', 'database']
        out, err, ret = self.symaccesscmd(cmd)
        return out

class Dmx3(Dmx):
    cylinder = 960 # KB

class PowerMax(Vmax):
    cylinder = 1920 # KB

def do_action(action, array_name=None, node=None, **kwargs):
    o = Arrays()
    array = o.get_array(array_name)
    if array is None:
        raise ex.Error("array %s not found" % array_name)
    if not hasattr(array, action):
        raise ex.Error("not implemented")
    array.node = node
    node.logger.handlers[1].setLevel(logging.CRITICAL)
    ret = getattr(array, action)(**kwargs)
    if ret is not None:
        print(json.dumps(ret, indent=4))
    return ret

def main(argv, node=None):
    set_sym_env()
    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)
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        sys.exit(1)

0707010001f285000081a40000000000000000000000016a100daf00001269000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/array/eva.py   from __future__ import print_function

import os
import sys

import core.exceptions as ex
from core.node import Node
from env import Env
from utilities.naming import factory, split_path
from utilities.proc 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(Env.paths.pathbin, "sssu")):
            sssubin = os.path.join(Env.paths.pathbin, "sssu")
        else:
            raise ex.Error("sssu command not found. set 'array#%s.bin' in the node or cluster configuration." % array)
    os.chdir(Env.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.Error("sssu command execution error")
    return out, err

class Evas(object):
    arrays = []

    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "eva":
                continue
            try:
                manager = self.node.oget(s, 'manager')
                username = self.node.oget(s, 'username')
                password = self.node.oget(s, 'password')
                sssubin = self.node.oget(s, 'bin')
            except Exception as exc:
                print("error parsing section %s: %s" % (s, exc), file=sys.stderr)
                pass
            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            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, node=None):
        self.name = name
        self.node = node
        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())
   0707010001f284000081a40000000000000000000000016a100daf0000133d000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/emcvnx.py    from __future__ import print_function

import os
from subprocess import *

import core.exceptions as ex
from core.node import Node
from env import Env
from utilities.naming import factory, split_path
from utilities.proc import justcall, which

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.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.Error('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.Error("naviseccli command execution error")

    return out, err

class EmcVnxs(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "emcvnx":
                continue

            try:
                method = self.node.oget(s, "method")
                scope = self.node.oget(s, "scope")
                spa = self.node.oget(s, "spa")
                spb = self.node.oget(s, "spb")
                username = self.node.oget(s, "username")
                password = self.node.oget(s, "password")
            except Exception as exc:
                print("error parsing section %s: %s" % (s, exc), file=sys.stderr)
                continue

            if method == "credentials":
                if username is None or password is None:
                    print("error parsing section %s: username and password are mandatory" % s, file=sys.stderr)
                    continue
                try:
                    secname, namespace, _ = split_path(password)
                    password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
                except Exception as exc:
                    print("error decoding password: %s", exc, file=sys.stderr)
                    continue

            self.arrays.append(EmcVnx(name, method, scope, spa, spb, username=username, password=password, node=self.node))
            done.append(name)

    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, node=None):
        self.name = name
        self.node = node
        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())
   0707010001f283000081a40000000000000000000000016a100daf000091c3000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/dorado.py    from __future__ import print_function

import sys
import json
import time

import core.exceptions as ex
from utilities.storage import Storage
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from core.node import Node

ERRCODE_MAPPING_HOST_LUN_NOT_EXISTS = 1073804587
ERRCODE_MAPPING_HOST_LUN_EXISTS = 1073804588
ERRCODE_SESSION_TOO_MANY = 1077949067

OBJTYPE_LUN = 11
OBJTYPE_HOSTGROUP = 14
OBJTYPE_HOST = 21
OBJTYPE_SNAPSHOT = 27
OBJTYPE_FC_PORT = 212
OBJTYPE_ETH_PORT = 213
OBJTYPE_REMOTEDISK = 224
OBJTYPE_SMARTQOSPOLICY = 230
OBJTYPE_ISCSILINK = 243
OBJTYPE_MAPPINGVIEW = 245
OBJTYPE_LUNGROUP = 256
OBJTYPE_PORTGROUP = 257
OBJTYPE_MAPPING = 12345
OBJTYPE_SNAPSHOT_CG = 57646
OBJTYPE_CLONE = 57702
OBJTYPE_CLONE_CG = 57703

INITIATOR_TYPE_ISCSI = 222
INITIATOR_TYPE_FC = 223

MAPPING_TYPE_UNMAPPED = 0
MAPPING_TYPE_HOST = 1
MAPPING_TYPE_HOSTGROUP = 2
MAPPING_TYPE_LUN = 3
MAPPING_TYPE_LUNGROUP = 4

OS_LINUX = 0
OS_WINDOWS = 1
OS_SOLARIS = 2
OS_HPUX = 3
OS_AIX = 4
OS_XENSERVER = 5
OS_DARWIN = 6
OS_VMWARE = 7
OS_LINUX_VIS = 8
OS_WINDOWSSERVER = 9
OS_ORACLEVM = 10
OS_VMS = 11
OS_OVMSERVERX86 = 12
OS_OVMSERVERSPARC = 13

HYPERMETRO_PATH_OPTIMIZED_NO = 0
HYPERMETRO_PATH_OPTIMIZED_YES = 1

try:
    import requests
except ImportError:
    raise ex.InitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except AttributeError:
    pass

VERIFY = False

PROG = "om 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 node or cluster configuration."),
    "name": Option(
        "--name", default=None, action="store", dest="name",
        help="The object name"),
    "storagepool": Option(
        "--storagepool", default=None, action="store", dest="storagepool",
        help="The storagepool 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"),
    "compression": Option(
        "--compression", default=False, action="store_true", dest="compression",
        help="Toggle compression"),
    "dedup": Option(
        "--dedup", default=False, action="store_true", dest="dedup",
        help="Toggle deduplication"),
    "naa": Option(
        "--naa", default=None, action="store", dest="naa",
        help="The disk naa identifier"),
    "hypermetrodomain": Option(
        "--hypermetrodomain", default=None, action="store", dest="hypermetrodomain",
        help="Create the LUN as HyperMetro pair, and use this domain."),
    "initiator": Option(
        "--initiator", action="append", dest="initiators",
        help="An initiator iqn. Can be specified multiple times."),
    "comment": Option(
        "--comment", action="store", dest="comment",
        help="Description for your reference"),
    "lun": Option(
        "--lun", action="store", type=int, dest="lun",
        help="The logical unit number to assign to the extent on attach to a target. If not specified, a free lun is automatically assigned."),
    "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": Option(
        "--target", action="append", dest="target",
        help="The target object iqn"),
    "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": Option(
        "--initiatorgroup", action="append", dest="initiatorgroup",
        help="The initiator group 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_disk": {
            "msg": "Create and map a lun.",
            "options": [
                OPT.name,
                OPT.storagepool,
                OPT.size,
                OPT.target,
                OPT.mappings,
                OPT.lun,
                OPT.compression,
                OPT.dedup,
                OPT.hypermetrodomain,
            ],
        },
        "add_lun": {
            "msg": "Create a lun.",
            "options": [
                OPT.name,
                OPT.storagepool,
                OPT.size,
                OPT.target,
                OPT.lun,
                OPT.compression,
                OPT.dedup,
            ],
        },
        "map": {
            "msg": "Map a lun to the specified initiator:target links",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
                OPT.mappings,
                OPT.lun,
            ],
        },
    },
    "Delete actions": {
        "del_disk": {
            "msg": "Delete and unmap a lun.",
            "options": [
                OPT.id,
                OPT.name,
                OPT.naa,
            ],
        },
        "unmap": {
            "msg": "Unmap the specified initiator:target links",
            "options": [
                OPT.id,
                OPT.naa,
                OPT.name,
                OPT.mappings,
            ],
        },
    },
    "Modify actions": {
        "resize_disk": {
            "msg": "Resize a lun",
            "options": [
                OPT.id,
                OPT.name,
                OPT.naa,
                OPT.size,
            ],
        },
    },
    "List actions": {
        "list_mappings": {
            "msg": "List host ports a disk is mapped to.",
            "options": [
                OPT.id,
                OPT.name,
                OPT.naa,
            ],
        },
        "list_storagepool": {
            "msg": "List configured storage pools.",
        },
        "list_portgroup": {
            "msg": "List configured port groups",
        },
        "list_fc_port": {
            "msg": "List configured fibre channel ports",
        },
        "list_eth_port": {
            "msg": "List configured ethernet ports",
        },
        "list_bond_port": {
            "msg": "List configured ethernet bonded ports",
        },
        "list_sas_port": {
            "msg": "List configured serial attached scsi ports",
        },
        "list_lun": {
            "msg": "List configured extents",
        },
        "list_hostgroup": {
            "msg": "List configured host groups",
        },
        "list_host": {
            "msg": "List configured host",
        },
        "list_host_link": {
            "msg": "List configured host links",
        },
    },
    "Show actions": {
        "show_system": {
            "msg": "Show system information.",
        },
        "show_lun": {
            "msg": "Show configured storage pools.",
            "options": [
                OPT.id,
                OPT.name,
                OPT.naa,
            ],
        },
        "show_storagepool": {
            "msg": "Show configured storage pools.",
            "options": [
                OPT.name,
            ],
        },
    },
}

class Dorados(object):
    arrays = []


    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.timeout = 10
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            try:
                name = self.node.oget(s, 'name')
            except Exception:
                name = None
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "dorado":
                continue
            timeout = self.node.oget(s, "timeout")
            try:
                username = self.node.oget(s, "username")
                password = self.node.oget(s, "password")
                api = self.node.oget(s, "api")
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            self.arrays.append(Dorado(name, api, username, password, timeout, node=self.node))
            done.append(name)


    def __iter__(self):
        for array in self.arrays:
            yield(array)


    def get_dorado(self, name):
        for array in self.arrays:
            if array.name == name:
                return array
        return None

class Dorado(object):

    def __init__(self, name, api, username, password, timeout, node=None):
        self.node = node
        self.name = name
        self.api = api.rstrip("/")
        self.username = username
        self.password = password
        self.auth = (username, password)
        self.timeout = timeout
        self.session = requests.Session()
        self.session_data = None
        self.keys = ["system",
                     "storagepools",
                     "fc_ports",
                     "luns"]


    def urlpath_device(self, device=None):
        return "/deviceManager/rest/%s" % (device if device else self.name)


    def headers(self, auth=True):
        data = {
            "Content-Type": "application/json",
        }
        if not auth:
            return data
        if not self.session_data:
            self.open_session()
        data["iBaseToken"] = self.session_data["iBaseToken"]
        return data


    def delete(self, uri, data=None):
        api = self.api + self.urlpath_device() + uri
        headers = self.headers()
        if data:
            data = json.dumps(data)
        r = self.session.delete(api, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r.json()


    def put(self, uri, data=None):
        api = self.api + self.urlpath_device() + uri
        headers = self.headers()
        if data:
            data = json.dumps(data)
        r = self.session.put(api, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r.json()


    def post(self, uri, data=None, auth=True):
        api = self.api + self.urlpath_device() + uri
        headers = self.headers(auth=auth)
        if data:
            data = json.dumps(data)
        r = self.session.post(api, data=data, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r.json()


    def get(self, uri, params=None):
        api = self.api + self.urlpath_device() + uri
        headers = self.headers()
        r = self.session.get(api, params=params, timeout=self.timeout, verify=VERIFY, headers=headers)
        return r.json()


    def open_session(self, timeout=30):
        d = {
            "username": self.auth[0],
            "password": self.auth[1],
            "scope": 0,
        }
        retries = 0
        while True:
            data = self.post("/sessions", data=d, auth=False)
            code = data.get("error", {}).get("code")
            if code == ERRCODE_SESSION_TOO_MANY and retries < timeout:
                retries += 1
                time.sleep(1)
                continue
            elif code:
                raise ex.Error("open_session error: %s => %s" % ((self.auth[0], "xxx"), data.get("error")))
            self.session_data = data["data"]
            return


    def close_session(self):
        if not self.session_data:
            return
        self.delete("/sessions")
        self.session_data = None


    def get_system(self):
        return self.get("/system/")["data"]


    def get_luns(self):
        return self.get("/lun")["data"]


    def get_hypermetrodomain(self, name):
        d = {
            "filter": "NAME::%s" % name,
        }
        data = self.get("/HyperMetroDomain", params=d)
        try:
            return data["data"][0]
        except (KeyError, IndexError):
            return


    def get_host_fc_links(self, oid):
        d = {
            "PARENTID": oid,
            "INITIATOR_TYPE": INITIATOR_TYPE_FC,
        }
        data = self.get("/host_link", params=d)
        return data["data"]


    def get_host(self):
        data = self.get("/host")
        try:
            return data["data"]
        except KeyError:
            return


    def get_hostgroup(self):
        data = self.get("/hostgroup")
        try:
            return data["data"]
        except KeyError:
            return


    def get_host_link(self):
        data = self.get("/host_link")
        try:
            return data["data"]
        except KeyError:
            return


    def get_fc_initiator(self, hba_id):
        data = self.get("/fc_initiator/%s" % hba_id)
        try:
            return data["data"]
        except KeyError:
            return


    def get_host_fc_initiators(self, host_id):
        d = {
            "filter": "PARENTID::%s" % host_id,
        }
        data = self.get("/fc_initiator", params=d)
        try:
            return data["data"]
        except KeyError:
            return


    def get_host_link_by_parentid(self, parentid):
        d = {
            "PARENTID": parentid,
        }
        data = self.get("/host_link", params=d)
        try:
            return data["data"]
        except KeyError:
            return


    def get_host_link_by_hba_id(self, hba_id, hba_type):
        d = {
            "INITIATOR_TYPE": hba_type,
            "INITIATOR_PORT_WWN": hba_id,
        }
        data = self.get("/host_link", params=d)
        try:
            return data["data"]
        except KeyError:
            return


    def get_storagepool_by_id(self, oid=None):
        data = self.get("/storagepool?filter=ID::%s" % oid)
        try:
            return data["data"][0]
        except KeyError:
            return


    def get_storagepool_by_name(self, name=None):
        data = self.get("/storagepool?filter=NAME::%s" % name)
        try:
            return data["data"][0]
        except KeyError:
            return


    def get_storagepool_id(self, name=None):
        return self.get_storagepool_by_name(name=name)["ID"]


    def get_lun(self, oid=None, name=None, naa=None):
        if oid:
            data = self.get("/lun?filter=ID::%s" % oid)
        elif name:
            data = self.get("/lun?filter=NAME::%s" % name)
        elif naa:
            data = self.get("/lun?filter=WWN::%s" % naa)
        else:
            raise ex.Error("oid, name or naa must be specified to get_lun()")
        try:
            return data["data"][0]
        except KeyError:
            return


    def get_lun_hosts(self, oid):
        d = {
            "ASSOCIATEOBJTYPE": OBJTYPE_LUN,
            "ASSOCIATEOBJID": oid,
        }
        data = self.get("/host/associate", params=d)
        try:
            return data["data"]
        except KeyError:
            return


    def get_lun_mappings(self, oid):
        d = {
            "ASSOCIATEOBJTYPE": OBJTYPE_LUN,
            "ASSOCIATEOBJID": oid,
        }
        data = self.get("/mapping/associate", params=d)
        return data["data"]


    def get_fc_port_associate(self, otype, oid):
        d = {
            "ASSOCIATEOBJTYPE": otype,
            "ASSOCIATEOBJID": oid,
        }
        data = self.get("/fc_port/associate", params=d)
        return data["data"]


    def get_hostgroup_hosts(self, oid):
        d = {
            "ASSOCIATEOBJTYPE": OBJTYPE_HOSTGROUP,
            "ASSOCIATEOBJID": oid,
        }
        data = self.get("/host/associate", params=d)
        return data["data"]


    def get_portgroup_fc_ports(self, oid):
        return self.get_fc_port_associate(OBJTYPE_PORTGROUP, oid)


    def get_storagepools(self):
        return self.get("/storagepool")["data"]


    def get_portgroups(self):
        return self.get("/portgroup")["data"]


    def get_fc_ports(self):
        return self.get("/fc_port")["data"]


    def get_eth_ports(self):
        return self.get("/eth_port")["data"]


    def get_bond_ports(self):
        return self.get("/bond_port")["data"]


    def get_sas_ports(self):
        return self.get("/sas_port")["data"]


    def del_mapping(self, mapping):
        path = '/mapping'
        d = {}
        for key in ("hostId", "hostGroupId", "lunId", "lunGroupId"):
            if mapping[key] != "":
                d[key] = int(mapping[key])
        data = self.delete(path, d)
        code = data.get("error", {}).get("code")
        if code and code != ERRCODE_MAPPING_HOST_LUN_NOT_EXISTS:
            raise ex.Error("delete error: %s %s %s" % (path, d, data.get("error")))
        return data["data"]


    def del_lun(self, oid):
        path = '/lun/%s' % oid
        data = self.delete(path)
        if data.get("error", {}).get("code"):
            raise ex.Error("delete error: %s %s" % (path, data.get("error")))
        return data["data"]


    def add_lun(self, name=None, size=None, storagepool=None,
                compression=True, dedup=True,
                **kwargs):
        for key in ["name", "size", "storagepool"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)
        size = convert_size(size, _to="B") // 512
        path = "/lun"
        d = {
            "NAME": name,
            "PARENTID": self.get_storagepool_id(storagepool),
            "CAPACITY": size,
            "MSGRETURNTYPE": 1, # sync
            "ALLOCTYPE": 1, # thin
            "ENABLECOMPRESSION": compression,
            "ENABLESMARTDEDUP": dedup,
        }
        data = self.post(path, d)
        if data.get("error", {}).get("code"):
            raise ex.Error("add lun error: %s => %s" % (d, data.get("error")))
        return data["data"]


    def list_mappings(self, id=None, name=None, naa=None, **kwargs):
        lun_data = self.get_lun(oid=id, name=name, naa=naa)
        if lun_data is None:
            raise ex.Error("lun not found")
        return self._list_mappings(lun_data["ID"], lun_data["WWN"])
        

    def get_mappings(self, lun_id):
        lun_mappings = self.get_lun_mappings(lun_id)
        mappings = {}
        for d in lun_mappings:
            if d["portGroupId"] == "":
                targets = self.list_fc_port()
            else:
                targets = self.get_portgroup_fc_ports(d["portGroupId"])
            targets = [t["WWN"] for t in targets]
            if d["hostGroupId"] == "":
                hbas = self.get_host_fc_initiators(d["hostId"])
            else:
                hbas = []
                for host in self.get_hostgroup_hosts(d["hostGroupId"]):
                    hbas += self.get_host_fc_initiators(host["ID"])
            hbas = [h["ID"] for h in hbas]
            for hba_id in hbas:
                for tgt_id in targets:
                    mappings[hba_id+":"+tgt_id+":"+lun_id] = {
                       "mapping": d,
                       "tgt_id": tgt_id,
                       "hba_id": hba_id,
                    }
        return mappings


    def _list_mappings(self, lun_id, disk_id):
        lun_mappings = self.get_lun_mappings(lun_id)
        mappings = {}
        for d in lun_mappings:
            if d["portGroupId"] == "":
                targets = self.list_fc_port()
            else:
                targets = self.get_portgroup_fc_ports(d["portGroupId"])
            targets = [t["WWN"] for t in targets]
            if d["hostGroupId"] == "":
                hbas = self.get_host_fc_initiators(d["hostId"])
            else:
                hbas = []
                for host in self.get_hostgroup_hosts(d["hostGroupId"]):
                    hbas += self.get_host_fc_initiators(host["ID"])
            hbas = [h["ID"] for h in hbas]
            for hba_id in hbas:
                for tgt_id in targets:
                    mappings[hba_id+":"+tgt_id+":"+disk_id] = {
                       "mapping": d,
                       "disk_id": disk_id,
                       "tgt_id": tgt_id,
                       "hba_id": hba_id,
                    }
        return mappings


    def resize_disk(self, id=None, name=None, naa=None, size=None, **kwargs):
        if size is None:
            raise ex.Error("'size' key is mandatory")
        if name is None and naa is None:
            raise ex.Error("'name' or 'naa' must be specified")
        lun_data = self.get_lun(oid=id, name=name, naa=naa)
        if lun_data is None:
            raise ex.Error("extent not found")
        storagepool = self.get_storagepool_by_id(lun_data["PARENTID"])
        if storagepool is None:
            raise ex.Error("storagepool not found")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="B") // 512
            current_size = int(lun_data["ALLOCCAPACITY"]) * 512
            size = current_size + incr
        else:
            size = convert_size(size, _to="B") // 512

        d = {
            "CAPACITY": size,
            "ID": lun_data["ID"],
        }
        data = self.put("/lun/expand", d)
        if data.get("error", {}).get("code"):
            raise ex.Error("expand_lun error: %s => %s" % ((lun_data["ID"], size), data.get("error")))
        return data["data"]


    def del_hostgroup(self, oid=None, **kwargs):
        if oid is None:
            raise ex.Error("'id' is mandatory")
        response = self.delete('/hostgroup/%s' % oid)
        if response.status_code != 200:
            raise ex.Error(str(response))


    def add_hostgroup(self, name=None, desc=None, hosts=None, **kwargs):
        if name is None:
            raise ex.Error("name is mandatory")
        d = {
            "NAME": name,
        }
        if desc:
            d["DESCRIPTION"] = desc

        data = self.post('/hostgroup', d)
        hostgroup = data["data"]
        for host in hosts:
            self.associate_hostgroup_host(hostgroup["ID"], host)
        return hostgroup


    def map_lun_to_host(self, lun_id, host_id, hostlun_id=None):
        d = {
            "lunId": lun_id,
            "hostId": host_id,
        }
        if hostlun_id:
            d["hostLunIdStart"] = hostlun_id
        data = self.post('/mapping', d)
        code = data.get("error", {}).get("code")
        if code and code != ERRCODE_MAPPING_HOST_LUN_EXISTS:
            raise ex.Error("map_lun_to_host error: %s => %s" % ((lun_id, host_id), data.get("error")))
        return data["data"]


    def pair_luns(self, local_id, remote_id, domain):
        domain_id = self.get_hypermetrodomain(domain)["ID"]
        return self.add_hypermetropair(local_id, remote_id, domain_id)


    def pause_lun_hypermetropair(self, oid):
        hypermetropair = self.lun_hypermetropair(oid)
        if not hypermetropair:
            return
        self.pause_hypermetropair(hypermetropair["ID"])
        return hypermetropair


    def pause_hypermetropair(self, oid):
        d = {
            "ID": oid,
        }
        data = self.put("/HyperMetroPair/disable_hcpair", d)
        if data.get("error", {}).get("code"):
            raise ex.Error("pause_hypermetropair error: %s => %s" % (oid, data.get("error")))
        return data["data"]


    def synchronize_hypermetropair(self, oid):
        d = {
            "ID": oid,
        }
        data = self.put("/HyperMetroPair/synchronize_hcpair", d)
        if data.get("error", {}).get("code"):
            raise ex.Error("synchronize_hypermetropair error: %s => %s" % (oid, data.get("error")))
        return data["data"]


    def synchronize_lun_hypermetropair(self, oid):
        hypermetropair = self.lun_hypermetropair(oid)
        if not hypermetropair:
            return
        self.synchronize_hypermetropair(hypermetropair["ID"])
        return hypermetropair


    def del_lun_hypermetropair(self, oid):
        hypermetropair = self.lun_hypermetropair(oid)
        if not hypermetropair:
            return
        self.del_hypermetropair(hypermetropair["ID"])
        return hypermetropair


    def lun_hypermetropair(self, oid):
        hypermetropair = self.get_hypermetropair("LOCALOBJID", oid)
        if not hypermetropair:
            hypermetropair = self.get_hypermetropair("REMOTEOBJID", oid)
        return hypermetropair


    def del_hypermetropair(self, oid):
        data = self.delete('/HyperMetroPair/%s' % oid)
        if data.get("error", {}).get("code"):
            raise ex.Error("del_hypermetropair error: %s => %s" % (oid, data.get("error")))
        return data["data"]


    def add_hypermetropair(self, local_id, remote_id, domain_id):
        d = {
            "DOMAINID": domain_id,
            "HCRESOURCETYPE": 1,
            "LOCALOBJID": local_id,
            "REMOTEOBJID": remote_id,
            "ISFIRSTSYNC": True,
        }
        data = self.post('/HyperMetroPair', d)
        if data.get("error", {}).get("code"):
            raise ex.Error("add_hypermetropair error: %s => %s" % ((local_id, remote_id, domain_id), data.get("error")))
        return data["data"]


    def get_hypermetropair(self, key, oid):
        d = {
            "filter": "%s::%s" % (key, oid),
        }
        data = self.get('/HyperMetroPair', params=d)
        if data.get("error", {}).get("code"):
            raise ex.Error("get_hypermetropair error: %s => %s" % ((key, oid), data.get("error")))
        try:
            return data["data"][0]
        except (KeyError, IndexError):
            return


    def associate_hostgroup_host(self, hostgroup_id, host_id):
        d = {
            "ID": hostgroup_id,
            "ASSOCIATEOBJTYPE": OBJTYPE_HOST,
            "ASSOCIATEOBJID": str(host_id),
        }
        data = self.post('/hostgroup/associate', d)
        if data.get("error", {}).get("code"):
            raise ex.Error("associate_hostgroup_host error: %s => %s" % ((hostgroup_id, host_id), data.get("error")))
        return data["data"]


    def del_disk(self, id=None, name=None, naa=None, **kwargs):
        if id is None and name is None and naa is None:
            raise ex.Error("'id', 'name' or 'naa' must be specified")
        data = self.get_lun(oid=id, name=name, naa=naa)
        if data is None:
            return
        results = {}
        response = self.pause_lun_hypermetropair(data["ID"])
        results["pause_hypermetropair"] = response
        response = self._unmap_lun(data["ID"])
        results["unmap"] = response
        response = self.del_lun_hypermetropair(data["ID"])
        results["del_hypermetropair"] = response
        response = self.del_lun(data["ID"])
        results["del_lun"] = data
        return results


    def hbagroup_by_targetgroup(self, mappings):
        data = {}
        for mapping in mappings:
            hba, targets = mapping.split(":", 1)
            targets = ",".join(sorted(targets.split(",")))
            if targets not in data:
                data[targets] = set([hba])
            else:
                data[targets].add(hba)
        l = []
        for targets in data:
            t = targets.split(",")
            h = sorted(list(data[targets]))
            l.append((h, t))
        return l


    def translate_mappings(self, mappings):
        targets = set()
        for mapping in mappings:
            elements = mapping.split(":", 1)
            targets |= set((elements[-1]).split(","))
        targets = list(targets)
        return targets


    def split_mappings(self, mappings):
        data = []
        for mapping in mappings:
            elements = mapping.split(":", 1)
            for target in set((elements[-1]).split(",")):
                data.append((elements[0], target))
        return data


    def unmap_lun(self, id=None, name=None, naa=None, mappings=None, **kwargs):
        if not name and not naa and not id:
            raise ex.Error("'id', 'name' or 'naa' is mandatory")
        lun_data = self.get_lun(oid=id, name=name, naa=naa)
        if not lun_data:
            raise ex.Error("no lun found")
        return self._unmap_lun(lun_data["ID"], mappings)


    def _unmap_lun(self, lun_id, mappings=None):
        current_mappings = self.get_mappings(lun_id)
        results = []
        mappings_done = set()
        if mappings:
            toremove = self.split_mappings(mappings)
        else:
            toremove = None
        for d in current_mappings.values():
            key = (d["hba_id"], d["tgt_id"])
            if d["mapping"]["ID"] in mappings_done:
                continue
            if toremove is None or key in toremove:
                self.del_mapping(d["mapping"])
                mappings_done.add(d["mapping"]["ID"])
                results.append(d)
        return results


    def find_hosts(self, hbagroup):
        hosts = {}
        hba_ids = set()
        for hba_id in hbagroup:
            hba = self.get_fc_initiator(hba_id)
            if not hba:
                raise ex.Error("fc initiator %s not found" % hba_id)
            host_id = hba["PARENTID"]
            host_name = hba["PARENTNAME"]
            host_hbas = self.get_host_fc_initiators(host_id)
            valid_host = True
            for host_hba in host_hbas:
                valid_host &= host_hba["ID"] in hbagroup
            if not valid_host:
                print("invalid host:", host_name)
                continue
            hosts[host_id] = host_name
            hba_ids |= set([h["ID"] for h in host_hbas])
        if hba_ids != set(hbagroup):
            return
        return hosts


    def map_lun(self, id=None, name=None, naa=None, targets=None, mappings=None, lun=None, lun_data=None, **kwargs):
        if not name and not naa and not id:
            raise ex.Error("'id', 'name' or 'naa' is mandatory")
        if targets is None and mappings is None:
            raise ex.Error("'targets' or 'mappings' must be specified")

        if lun_data is None:
            lun_data = self.get_lun(oid=id, name=name, naa=naa)
        if lun_data is None:
            raise ex.Error("lun not found")

        results = []
        for hbagroup, targetgroup in self.hbagroup_by_targetgroup(mappings):
            hosts = self.find_hosts(hbagroup)
            if hosts is None:
                raise ex.Error("could not find a set of array hosts equivalent to: %s" % hbagroup)
            for host_id in hosts:
                result = self.map_lun_to_host(lun_id=lun_data["ID"], host_id=host_id, hostlun_id=lun)
                results.append(result)
        return results


    def add_disk(self, name=None, size=None, storagepool=None, targets=None,
                 mappings=None, compression=True, dedup=True, lun=None,
                 hypermetrodomain=None, **kwargs):
        for key in ["name", "size", "storagepool"]:
            if locals()[key] is None:
                raise ex.Error("'%s' key is mandatory" % key)

        # lun
        data = self.add_lun(name=name, size=size, storagepool=storagepool, compression=compression, dedup=dedup)

        if "WWN" not in data:
            raise ex.Error("no WWN in data")

        # mappings
        if mappings:
            mappings = self.map_lun(name=name, mappings=mappings, targets=targets, lun=lun, lun_data=data)

        # collector update
        warnings = []
        try:
            self.add_diskinfo(data, size, storagepool)
        except Exception as exc:
            warnings.append(str(exc))
        disk_id = data["WWN"]
        results = {
            "driver_data": data,
            "disk_id": disk_id,
            "disk_devid": data["ID"],
            "mappings": sorted(self.list_mappings(naa=disk_id).values(), key=lambda x: (x["hba_id"], x["tgt_id"], x["disk_id"])),
        }
        if warnings:
            results["warnings"] = warnings

        return results


    def show_system(self, **kwargs):
        return self.get_system()


    def show_storagepool(self, **kwargs):
        return self.get_storagepool_by_name(kwargs["name"])


    def list_storagepool(self, **kwargs):
        return self.get_storagepools()


    def list_host(self, **kwargs):
        return self.get_host()


    def list_hostgroup(self, **kwargs):
        return self.get_hostgroup()


    def list_host_link(self, **kwargs):
        return self.get_host_link()


    def show_lun(self, **kwargs):
        return self.get_lun(oid=kwargs.get("id"), name=kwargs.get("name"), naa=kwargs.get("naa"))


    def list_lun(self, **kwargs):
        return self.get_luns()


    def list_portgroup(self, **kwargs):
        return self.get_portgroups()


    def list_fc_port(self, **kwargs):
        return self.get_fc_ports()


    def list_eth_port(self, **kwargs):
        return self.get_eth_ports()


    def list_bond_port(self, **kwargs):
        return self.get_bond_ports()


    def list_sas_port(self, **kwargs):
        return self.get_sas_ports()


    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.Error(str(exc))
        if "error" in result:
            raise ex.Error(result["error"])
        return result


    def add_diskinfo(self, data, size=None, storagepool=None):
        if self.node is None:
            return
        try:
            result = self.node.collector_rest_post("/disks", {
                "disk_id": data["WWN"],
                "disk_devid": data["ID"],
                "disk_name": data["NAME"],
                "disk_size": convert_size(size, _to="MB"),
                "disk_alloc": 0,
                "disk_arrayid": self.name,
                "disk_group": storagepool,
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(result["error"])
        return result


    # method aliases
    create_disk = add_disk
    unmap = unmap_lun
    map = map_lun


def do_action(action, array_name=None, node=None, **kwargs):
    o = Dorados()
    array = o.get_dorado(array_name)
    if array is None:
        raise ex.Error("array %s not found" % array_name)
    array.node = node
    if not hasattr(array, action):
        raise ex.Error("not implemented")
    result = getattr(array, action)(**kwargs)
    array.close_session()
    if result is not None:
        try:
            print(json.dumps(result, indent=4))
        except TypeError:
            print(json.dumps({"error": "unserializable result: %s" % 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)


def debug_on():
    try:
        import httplib
    except ImportError:
        import http.client as httplib
    import logging
    httplib.HTTPConnection.debuglevel = 1
    logging.basicConfig()
    logging.getLogger().setLevel(logging.DEBUG)
    requests_log = logging.getLogger("requests.packages.urllib3")
    requests_log.setLevel(logging.DEBUG)
    requests_log.propagate = True

#debug_on()

if __name__ == "__main__":
    try:
        main(sys.argv)
        ret = 0
    except ex.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


 0707010001f28e000081a40000000000000000000000016a100daf00000ccc000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/netapp.py    from __future__ import print_function

import os
from subprocess import *

from env import Env
from utilities.naming import factory, split_path
from core.node import Node
from utilities.proc import justcall

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.paths.pathbin

class Netapps(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "netapp":
                continue

            kwargs = {"node": self.node}

            for key in ("server", "username", "key"):
                try:
                    kwargs[key] = self.node.oget(s, key)
                except:
                    print("missing parameter: %s", s)
            if "server" not in kwargs or "username" not in kwargs or "key" not in kwargs:
                continue
            try:
                secname, namespace, _ = split_path(kwargs["password"])
                kwargs["password"] = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            self.arrays.append(Netapp(s, **kwargs))

    def __iter__(self):
        for array in self.arrays:
            yield(array)

class Netapp(object):
    def __init__(self, name, server=None, username=None, key=None, node=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

0707010001f290000081a40000000000000000000000016a100daf000087e3000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/array/pure.py  from __future__ import print_function

import os
import sys
import json
import logging
import time

import foreign.jwt as jwt

import core.exceptions as ex
from env import Env
from utilities.files import makedirs
from utilities.storage import Storage
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from core.node import Node

WWID_PREFIX = "624a9370"
RENEW_STATUS = 403
ITEMS_PER_PAGE = 100
MAX_PAGES = 1000
DAY_MS = 24*60*60*1000
REQUEST_TIMEOUT = 10

try:
    import requests
except ImportError:
    raise ex.InitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except AttributeError:
    pass

PROG = "om array"
OPT = Storage({
    "help": Option(
        "-h", "--help", action="store_true", dest="parm_help",
        help="show this help message and exit"),
    "now": Option(
        "--now", action="store_true", dest="now",
        help="Delete disk after flagging it destroyed (DANGER)"),
    "truncate": Option(
        "--truncate", action="store_true", dest="truncate",
        help="Allow truncating a resized volume (DANGER)"),
    "array": Option(
        "-a", "--array", action="store", dest="array_name",
        help="The name of the array, as defined in the node or cluster configuration."),
    "name": Option(
        "--name", action="store", dest="name",
        help="The object name"),
    "pod": Option(
        "--pod", action="store", dest="pod",
        help="The pod name"),
    "filter": Option(
        "--filter", action="store", dest="qfilter",
        help="The items filtering expression. ex: id='1' and serial='abc' and pod.name='pod1' and destroyed='false'."),
    "id": Option(
        "--id", action="store", dest="id",
        help="The item id"),
    "serial": Option(
        "--serial", action="store", dest="serial",
        help="The serial number"),
    "size": Option(
        "--size", action="store", dest="size",
        help="The disk size, expressed as a size expression like 1g, 100mib, ..."),
    "blocksize": Option(
        "--blocksize", type=int, action="store", dest="blocksize",
        help="The exported disk blocksize in B"),
    "wwn": Option(
        "--wwn", action="store", dest="wwn",
        help="The world wide port number identifier"),
    "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 --host and --hostgroup. 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"),
    "host": Option(
        "--host", action="store", dest="host",
        help="The host name"),
    "hostgroup": Option(
        "--hostgroup", action="store", dest="hostgroup",
        help="The host group name"),
    "volumegroup": Option(
        "--volumegroup", action="store", dest="volumegroup",
        help="A volume group name"),
    "volume-name": Option(
        "--volume-name", action="store", dest="volume_name",
        help="A volume name"),
    "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.mappings,
            ],
        },
        "add_map": {
            "msg": "Map a volume to an initiator group and target group",
            "options": [
                OPT.id,
                OPT.name,
                OPT.serial,
                OPT.mappings,
                OPT.host,
                OPT.hostgroup,
            ],
        },
        "del_disk": {
            "msg": "Delete a volume",
            "options": [
                OPT.now,
                OPT.id,
                OPT.name,
                OPT.serial,
            ],
        },
        "del_map": {
            "msg": "Unmap a volume from an initiator group and target group",
            "options": [
                OPT.id,
                OPT.name,
                OPT.serial,
                OPT.mappings,
                OPT.hostgroup,
            ],
        },
        "resize_disk": {
            "msg": "Resize a volume",
            "options": [
                OPT.id,
                OPT.name,
                OPT.serial,
                OPT.size,
                OPT.truncate,
            ],
        },
    },
    "Low-level actions": {
        "list_hosts": {
            "msg": "List hosts",
            "options": [
                OPT.filter,
            ],
        },
        "list_connections": {
            "msg": "List configured connections",
            "options": [
                OPT.filter,
            ],
        },
        "list_targets": {
            "msg": "List configured targets",
            "options": [
                OPT.target,
            ],
        },
        "list_hardware": {
            "msg": "List hardware",
            "options": [
                OPT.filter,
            ],
        },
        "list_arrays": {
            "msg": "List arrays",
            "options": [
                OPT.filter,
            ],
        },
        "list_hostgroups": {
            "msg": "List host-groups",
            "options": [
                OPT.filter,
            ],
        },
        "list_volumegroups": {
            "msg": "List configured volume-groups",
            "options": [
                OPT.filter,
                OPT.name,
                OPT.id,
            ],
        },
        "list_network_interfaces": {
            "msg": "List array network interfaces",
            "options": [
                OPT.filter,
                OPT.name,
            ],
        },
        "list_ports": {
            "msg": "List array target and replication ports",
            "options": [
                OPT.filter,
                OPT.name,
                OPT.wwn,
            ],
        },
        "list_pods": {
            "msg": "List configured pods",
            "options": [
                OPT.filter,
                OPT.name,
                OPT.id,
            ],
        },
        "list_volumes": {
            "msg": "List configured volumes",
            "options": [
                OPT.filter,
                OPT.pod,
                OPT.name,
                OPT.id,
                OPT.serial,
            ],
        },
    },
}

ts = int(time.time()*1000)
DAY_MS = 24*60*60*1000

class Arrays(object):
    arrays = []

    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            try:
                name = self.node.oget(s, "name")
            except Exception:
                name = None
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "pure":
                continue
            try:
                api = self.node.oget(s, 'api')
                client_id = self.node.oget(s, 'client_id')
                key_id = self.node.oget(s, 'key_id')
                username = self.node.oget(s, 'username')
                issuer = self.node.oget(s, 'issuer')
                insecure = self.node.oget(s, 'insecure')
                secret = self.node.oget(s, 'secret')
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            try:
                secname, namespace, _ = split_path(secret)
                sec = factory("sec")(secname, namespace=namespace, volatile=True)
                private_key = sec.decode_key("private_key")
            except Exception as exc:
                print("error reading private_key from %s: %s", secname, exc, file=sys.stderr)
                continue
            self.arrays.append(Array(name, api=api, username=username, client_id=client_id, key_id=key_id, issuer=issuer, insecure=insecure, private_key=private_key, node=self.node))
            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=None, client_id=None, key_id=None, issuer=None, insecure=True, private_key=None, node=None):
        self.node = node
        self.name = name
        self.api = api
        self.vapi = api + "/api/2.8"
        self.username = username
        self.client_id = client_id
        self.key_id = key_id
        self.issuer = issuer
        self.verify = not insecure
        self.private_key = private_key
        self.keys = [
            "arrays",
            "hardware",
            "pods",
            "volumes",
            "volumegroups",
            "ports",
        ]

        self.tg_portname = {}
        self.ig_portname = {}
        self.log = logging.getLogger(Env.nodename+".array.pure."+self.name)
        self.token = None

    def get_token(self):
        token = self.get_cached_token()
        if token:
            return token
        token = self.new_token()
        self.cache_token(token)
        return token

    def get_cached_token(self):
        if self.token:
            return self.token
        p = self.token_cache_file()
        try:
            with open(p, "r") as f:
                return f.read()
        except Exception:
            return None

    def expire_token(self):
        self.token = None
        p = self.token_cache_file()
        try:
            os.remove(p)
        except Exception:
            pass
       
    def cache_token(self, token):
        self.token = token
        p = self.token_cache_file()
        d = os.path.dirname(p)
        makedirs(d, 0o700)
        fd = os.open(p, os.O_CREAT|os.O_WRONLY, mode=0o600)
        try:
            os.write(fd, token.encode())
        finally:
            os.close(fd)

    def token_cache_file(self):
        return os.path.join(os.sep, "run", "opensvc", "array", self.name, "token")

    def new_token(self):
        key = self.private_key.encode()
        try:
            from cryptography.hazmat.primitives import serialization  # pylint: disable=import-error
            from cryptography.hazmat.backends import default_backend  # pylint: disable=import-error
            private_key = serialization.load_pem_private_key(key, password=None, backend=default_backend())
        except Exception as exc:
            raise ex.Error(exc)
        ts = int(time.time()*1000)
        jwt_headers = {
            "kid": self.key_id,
        }
        jwt_payload = {
            "aud": self.client_id,
            "sub": self.username,
            "iss": self.issuer or self.username,
            "iat": ts,
            "exp": ts+DAY_MS,
        }
        encoded_jwt = jwt.encode(jwt_payload, private_key, headers=jwt_headers, algorithm="RS256")
        encoded_jwt = encoded_jwt.decode()
        data = {
            "content-type": "application/json",
            "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
            "subject_token": encoded_jwt,
            "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
        }
        try:
            resp = requests.post(self.api+"/oauth2/1.0/token", data=data, verify=self.verify, timeout=REQUEST_TIMEOUT)
        except Exception as exc:
            raise ex.Error(exc)
        if resp.status_code != 200:
            raise ex.Error(resp.text)

        # {
        #   "access_token":"eyJ....eyJ....ln9...",
        #   "issued_token_type":"urn:ietf:params:oauth:token-type:access_token",
        #   "token_type":"Bearer",
        #   "expires_in":86399
        # }
        return resp.json()["access_token"]

    def delete(self, uri, params=None, data=None, renewed=False):
        if params is None:
            params = {}
        headers = self.headers()
        r = requests.delete(self.vapi+uri, headers=headers, params=params, json=data, verify=self.verify, timeout=REQUEST_TIMEOUT)
        if not renewed and r.status_code == RENEW_STATUS:
            self.expire_token()
            return self.delete(uri, params=params, data=data, renewed=True)
        if r.status_code != 200:
            raise ex.Error(r.text)

    def patch(self, uri, params=None, data=None, renewed=False):
        if params is None:
            params = {}
        headers = self.headers()
        r = requests.patch(self.vapi+uri, headers=headers, params=params, json=data, verify=self.verify, timeout=REQUEST_TIMEOUT)
        if not renewed and r.status_code == RENEW_STATUS:
            self.expire_token()
            return self.patch(uri, params=params, data=data, renewed=True)
        if r.status_code != 200:
            raise ex.Error(r.text)
        return r.json()

    def put(self, uri, params=None, data=None, renewed=False):
        if params is None:
            params = {}
        headers = self.headers()
        r = requests.put(self.vapi+uri, headers=headers, params=params, json=data, verify=self.verify, timeout=REQUEST_TIMEOUT)
        if not renewed and r.status_code == RENEW_STATUS:
            self.expire_token()
            return self.put(uri, params=params, data=data, renewed=True)
        if r.status_code != 200:
            raise ex.Error(r.text)
        return r.json()

    def post(self, uri, params=None, data=None, renewed=False):
        if params is None:
            params = {}
        headers = self.headers()
        r = requests.post(self.vapi+uri, headers=headers, params=params, json=data, verify=self.verify, timeout=REQUEST_TIMEOUT)
        if not renewed and r.status_code == RENEW_STATUS:
            self.expire_token()
            return self.post(uri, params=params, data=data, renewed=True)
        if r.status_code != 200:
            raise ex.Error(r.text)
        return r.json()


    def get(self, uri, params=None, renewed=False):
        params = params or {}
        params["limit"] = ITEMS_PER_PAGE
        headers = self.headers()
        r = requests.get(self.vapi+uri, headers=headers, params=params, verify=self.verify, timeout=REQUEST_TIMEOUT)
        if not renewed and r.status_code == RENEW_STATUS:
            self.expire_token()
            return self.get(uri, params=params, renewed=True)
        if r.status_code != 200:
            raise ex.Error(r.text)
        return r.json()

    def get_items(self, uri, params=None):
        params = params or {}
        items = []
        i = 1
        while i < MAX_PAGES:
            i += 1
            data = self.get(uri, params=params)
            items += data.get("items", [])
            token = data.get("continuation_token")
            more = data.get("more_items_remaining")
            if more:
                if "offset" in params:
                     params["offset"] += ITEMS_PER_PAGE
                else:
                     params["offset"] = ITEMS_PER_PAGE
            elif token:
                params["continuation_token"] = token
            else:
                break
        return items

    def headers(self):
        return {
            "Cache-Control": "no-cache",
            "Authorization": "Bearer " + self.get_token(),
            "Content-Type": "application/json",
            "Accept": "application/json",
        }

    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_hba_host(self, hba_id):
        #hba_id = self.convert_hba_id(hba_id)
        hosts = self.get_hosts(qfilter="wwns='%s'" % hba_id)
        if len(hosts) == 0:
            raise ex.Error("no host found for hba id %s" % hba_id)
        return hosts[0]

    def get_hosts(self, qfilter=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        params["filter"] = " and ".join(filters)
        return self.get_items("/hosts", params=params)

    def list_hosts(self, qfilter=None, **kwargs):
        data = self.get_hosts(qfilter=qfilter)
        print(json.dumps(data, indent=8))

    def get_hostgroups(self, qfilter=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        params["filter"] = " and ".join(filters)
        return self.get_items("/host-groups", params)

    def list_hostgroups(self, qfilter=None, **kwargs):
        data = self.get_hostgroups(qfilter=qfilter)
        print(json.dumps(data, indent=8))

    def get_connections(self, qfilter=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        params["filter"] = " and ".join(filters)
        return self.get_items("/connections", params)

    def list_connections(self, qfilter=None, **kwargs):
        data = self.get_connections(qfilter=qfilter)
        print(json.dumps(data, indent=8))

    def get_hardware(self, qfilter=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        params["filter"] = " and ".join(filters)
        return self.get_items("/hardware", params)

    def list_hardware(self, qfilter=None, **kwargs):
        data = self.get_hardware(qfilter=qfilter)
        print(json.dumps(data, indent=8))

    def get_arrays(self, qfilter=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        params["filter"] = " and ".join(filters)
        return self.get_items("/arrays", params)

    def list_arrays(self, qfilter=None, **kwargs):
        data = self.get_arrays(qfilter=qfilter)
        print(json.dumps(data, indent=8))

    def get_volumegroups(self, qfilter=None, id=None, name=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        if id is not None:
            filters += ["id='%s'" % id]
        if name is not None:
            filters += ["name='%s'" % name]
        params["filter"] = " and ".join(filters)
        return self.get_items("/volume-groups", params)

    def list_volumegroups(self, qfilter=None, id=None, name=None, **kwargs):
        data = self.get_volumegroups(qfilter=qfilter, id=id, name=name)
        print(json.dumps(data, indent=8))

    def get_network_interfaces(self, qfilter=None, name=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        if name is not None:
            filters += ["name='%s'" % name]
        params["filter"] = " and ".join(filters)
        return self.get_items("/network-interfaces", params)

    def list_network_interfaces(self, qfilter=None, name=None, **kwargs):
        data = self.get_network_interfaces(qfilter=qfilter, name=name)
        print(json.dumps(data, indent=8))

    def get_ports(self, qfilter=None, wwn=None, name=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        if wwn is not None:
            filters += ["wwn='%s'" % wwn]
        if name is not None:
            filters += ["name='%s'" % name]
        params["filter"] = " and ".join(filters)
        return self.get_items("/ports", params)

    def list_ports(self, qfilter=None, wwn=None, name=None, **kwargs):
        data = self.get_ports(qfilter=qfilter, wwn=wwn, name=name)
        print(json.dumps(data, indent=8))

    def get_pods(self, qfilter=None, id=None, name=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        if id is not None:
            filters += ["id='%s'" % id]
        if name is not None:
            filters += ["name='%s'" % name]
        params["filter"] = " and ".join(filters)
        return self.get_items("/pods", params)

    def list_pods(self, qfilter=None, id=None, name=None, **kwargs):
        data = self.get_pods(qfilter=qfilter, id=id, name=name)
        print(json.dumps(data, indent=8))

    def get_volumes(self, qfilter=None, id=None, name=None, serial=None, pod=None):
        params = {}
        filters = []
        if qfilter is not None:
            filters += [qfilter]
        if id is not None:
            filters += ["id='%s'" % id]
        if name is not None:
            filters += ["name='%s'" % name]
        if serial is not None:
            filters += ["serial='%s'" % serial]
        if pod is not None:
            filters += ["pod.name='%s'" % pod]
        params["filter"] = " and ".join(filters)
        return self.get_items("/volumes", params)

    def list_volumes(self, qfilter=None, id=None, name=None, serial=None, pod=None, **kwargs):
        data = self.get_volumes(qfilter=qfilter, id=id, name=name, serial=serial, pod=pod)
        print(json.dumps(data, indent=8))

    def add_disk(self, name=None, size=None, mappings=None, **kwargs):
        if name is None:
            raise ex.Error("--name is mandatory")
        if size == 0 or size is None:
            raise ex.Error("--size is mandatory")
        params = {
            "names": [name],
        }
        d = {
            "subtype": "regular",
            "provisioned": convert_size(size, _to="B"),
        }
        self.post("/volumes", data=d, params=params)
        if mappings:
            mappings_data = self.add_map(name=name, mappings=mappings)
        else:
            mappings_data = []
        driver_data = {}
        driver_data["volume"] = self.get_volumes(name=name)[0]
        driver_data["mappings"] = self.get_connections(qfilter="volume.id='%s'" % driver_data["volume"]["id"])
        results = {
            "driver_data": driver_data,
            "disk_id": WWID_PREFIX + driver_data["volume"]["serial"].lower(),
            "disk_devid": driver_data["volume"]["id"],
            "mappings": {},
        }
        targets = self.get_targets()
        for m in mappings_data:
            for hba_id in m["hba_ids"]:
                for tgt_id in targets:
                    results["mappings"][hba_id+":"+tgt_id] = {
                        "hba_id": hba_id,
                        "tgt_id": tgt_id,
                        "lun": m["data"][0]["lun"],
                    }
        self.push_diskinfo(results, name, size)
        return results

    def resize_disk(self, id=None, name=None, serial=None, size=None, truncate=False, **kwargs):
        if name and id:
            raise ex.Error("--name and --id are exclusive")
        if not name and not id and not serial:
            raise ex.Error("--name or --id is required")
        if size == 0 or size is None:
            raise ex.Error("--size is required")
        if size.startswith("+"):
            incr = convert_size(size.lstrip("+"), _to="B")
            data = self.get_volumes(id=id, name=name, serial=serial)
            current_size = int(data[0]["provisioned"])
            size = current_size + incr
        else:
            size = convert_size(size, _to="B")
        d = {
            "provisioned": size,
        }
        params = {}
        if truncate:
            params["truncate"] = 'true'
        if id:
            params["ids"] = ",".join([id])
        if name:
            params["names"] = ",".join([name])
        self.patch("/volumes", params=params, data=d)
        ret = self.get_volumes(id=id, name=name)
        return ret

    def get_volume_connections(self, id=None, name=None, serial=None, **kwargs):
        vols = self.get_volumes(id=id, name=name, serial=serial)
        if len(vols) == 0:
            return []
        params = {}
        params["filter"] = "volume.name='%s'" % vols[0]["name"]
        return self.get_items("/connections", params=params)

    def del_disk(self, id=None, name=None, serial=None, now=False, **kwargs):
        if name and id:
            raise ex.Error("--name and --id are exclusive")
        if not name and not id and not serial:
            raise ex.Error("--name or --id is required")
        data = self.get_volumes(name=name, id=id, serial=serial)
        if len(data) == 0:
            raise ex.Error("volume does not exist")

        disk_id = WWID_PREFIX + data[0]["serial"].lower()
        self.del_map(id=data[0]["id"])
        params = {}
        params["names"] = data[0]["name"]
        if not data[0]["destroyed"]:
            self.patch("/volumes", params=params, data={"destroyed": True})
        if now:
            self.delete("/volumes", params=params)
        self.del_diskinfo(disk_id)
        return data

    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_hostgroup(self, hba_id):
        params = {}
        hba_id = self.convert_hba_id(hba_id)
        params["filter"] = "wwns='%s'" % hba_id
        data = self.get_items("hosts", params=params)
        if len(data) == 0:
            raise ex.Error("no initiator found with wwn=%s" % hba_id)
        for d in data:
            if not d["is_local"]:
                continue
            if not d["host_group"]["name"]:
                continue
            return d["host_group"]["name"]
        raise ex.Error("initiator %s found in no hostgroup" % hba_id)

    def get_targets(self):
        l = []
        qfilter = "services='scsi-fc' and enabled='true'"
        for d in self.get_network_interfaces(qfilter=qfilter):
            l.append(d["fc"]["wwn"].replace(":", "").lower())
        return l

    def mappings_to_hostgroups(self, mappings):
        m = {}
        for mapping in mappings:
            elements = mapping.split(":")
            hba_id = elements[0]
            targets = elements[-1].split(",")
            hostgroup = self.get_hba_hostgroup(hba_id)
            for target in targets:
                qfilter = "fc.wwn='%s' and services='scsi-fc' and enabled='true'" % self.convert_hba_id(target)
                tg = self.get_network_interfaces(qfilter=qfilter)
                if not tg:
                    continue
                name = hostgroup["name"]
                if name in m:
                    m[name] += [hba_id]
                else:
                    m[name] = [hba_id]
        return m

    def mappings_to_hosts(self, mappings):
        m = {}
        for mapping in mappings:
            elements = mapping.split(":")
            hba_id = elements[0]
            targets = elements[-1].split(",")
            host = self.get_hba_host(hba_id)
            for target in targets:
                qfilter = "fc.wwn='%s' and services='scsi-fc' and enabled='true'" % self.convert_hba_id(target)
                tg = self.get_network_interfaces(qfilter=qfilter)
                if not tg:
                    continue
                name = host["name"]
                if name in m:
                    m[name] += [hba_id]
                else:
                    m[name] = [hba_id]
        return m

    def add_map(self, id=None, name=None, serial=None, mappings=None, host=None, hostgroup=None, lun=None, **kwargs):
        results = []
        if mappings is not None and hostgroup is None:
            for host, hba_ids in self.mappings_to_hosts(mappings).items():
                map_data = self._add_map(id=id, name=name, serial=serial, host=host, lun=lun, **kwargs)
                results += [{"hba_ids": hba_ids, "data": map_data}]
        elif host:
            map_data = self._add_map(id=id, name=name, serial=serial, host=host, lun=lun, **kwargs)
            results += [map_data]
        elif hostgroup:
            map_data = self._add_map(id=id, name=name, serial=serial, hostgroup=hostgroup, lun=lun, **kwargs)
            results += [map_data]
        else:
            raise ex.Error("one of --mappings --host --hostgroup is required")
        return results

    def _add_map(self, id=None, name=None, serial=None, host=None, hostgroup=None, lun=None, **kwargs):
        if not hostgroup and not host:
            raise ex.Error("--hostgroup or --host is required")
        vols = self.get_volumes(id=id, name=name, serial=serial)
        if len(vols) == 0:
            raise ex.Error("volume not found")
        vol = vols[0]
        data = {}
        params = {}
        params["volume_names"] = vol["name"]
        if hostgroup:
            params["host_group_names"] = hostgroup
        if host:
            params["host_names"] = host
        if lun is not None:
            data["lun"] = lun
        return self.post("/connections", data=data, params=params)["items"]

    def del_map(self, id=None, name=None, serial=None, mappings=None, host=None, hostgroup=None, **kwargs):
        results = []
        if mappings is not None and hostgroup is None:
            for host, hba_ids in self.mappings_to_hosts(mappings).items():
                self._del_map(id=id, name=name, serial=serial, host=host, **kwargs)
                results += [host]
        else:
            hostgroup_deleted = set()
            for c in self.get_volume_connections(id=id, name=name, serial=serial):
                if host:
                    if c["host"]["name"] != host:
                        continue
                    self._del_map(id=id, name=name, serial=serial, host=c["host"]["name"], **kwargs)
                    results += [c]
                elif hostgroup:
                    if c["host_group"]["name"] != hostgroup:
                        continue
                    if c["host_group"]["name"] in hostgroup_deleted:
                        results += [c]
                        continue
                    self._del_map(id=id, name=name, serial=serial, hostgroup=c["host_group"]["name"], **kwargs)
                    results += [c]
                    hostgroup_deleted.add(c["host_group"]["name"])
                elif c["host_group"]["name"]:
                    if c["host_group"]["name"] in hostgroup_deleted:
                        results += [c]
                        continue
                    self._del_map(id=id, name=name, serial=serial, hostgroup=c["host_group"]["name"], **kwargs)
                    results += [c]
                    hostgroup_deleted.add(c["host_group"]["name"])
                else:
                    self._del_map(id=id, name=name, serial=serial, host=c["host"]["name"], **kwargs)
                    results += [c]
        return results

    def _del_map(self, id=None, name=None, serial=None, host=None, hostgroup=None, **kwargs):
        vols = self.get_volumes(id=id, name=name, serial=serial)
        if len(vols) == 0:
            raise ex.Error("volume not found")
        vol = vols[0]
        params = {}
        params["volume_names"] = vol["name"]
        if hostgroup:
            params["host_group_names"] = hostgroup
        if host:
            params["host_names"] = host
        self.delete("/connections", params=params)

    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:
            self.log.error("failed to delete the disk object in the collector: %s", exc)
            return
        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": "",
            })
        except Exception as exc:
            raise ex.Error(str(exc))
        if "error" in data:
            raise ex.Error(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.Error("array %s not found" % array_name)
    if not hasattr(array, action):
        raise ex.Error("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.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


 0707010001f293000081a40000000000000000000000016a100daf00006a1e000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/array/xtremio.py   from __future__ import print_function

import sys
import json
import logging

import core.exceptions as ex
from env import Env
from utilities.storage import Storage
from utilities.naming import factory, split_path
from utilities.converters import convert_size
from utilities.optparser import OptParser, Option
from core.node import Node

try:
    import requests
except ImportError:
    raise ex.InitError("the requests module must be installed")

try:
    requests.packages.urllib3.disable_warnings()
except AttributeError:
    pass
verify = False

PROG = "om 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 the node or cluster configuration."),
    "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=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            try:
                name = self.node.oget(s, "name")
            except Exception:
                name = None
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "xtremio":
                continue
            try:
                api = self.node.oget(s, 'api')
                username = self.node.oget(s, 'username')
                password = self.node.oget(s, 'password')
            except:
                print("error parsing section", s, file=sys.stderr)
                continue
            try:
                secname, namespace, _ = split_path(password)
                password = factory("sec")(secname, namespace=namespace, volatile=True).decode_key("password")
            except Exception as exc:
                print("error decoding password: %s", exc, file=sys.stderr)
                continue
            self.arrays.append(Array(name, api, username, password, node=self.node))
            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, node=None):
        self.node = node
        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 = {}
        self.log = logging.getLogger(Env.nodename+".array.xtremio."+self.name)

    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:
            raise ex.Error(response.content)
        return 0

    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:
            raise ex.Error(response.content)
        return

    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.Error(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.Error("--name is mandatory")
        if size == 0 or size is None:
            raise ex.Error("--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"] = [val for val in 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.Error("--volume is mandatory")
        if volume == "":
            raise ex.Error("--volume can not be empty")
        if size == 0 or size is None:
            raise ex.Error("--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.Error("--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.Error("--volume is mandatory")
        if volume == "":
            raise ex.Error("volume can not be empty")
        data = self.get_volumes(volume=volume)
        if "content" not in data:
            raise ex.Error("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.Error("no initiator found with port-address=%s" % hba_id)
        if len(data["initiators"][0]["ig-id"]) == 0:
            raise ex.Error("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.Error("no target found with port-address=%s" % hba_id)
        if len(data["targets"][0]["tg-id"]) == 0:
            raise ex.Error("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.Error("--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.Error("--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.Error("--mapping is mandatory")
        if mapping == "":
            raise ex.Error("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.Error(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.Error(str(exc))
        if "error" in data:
            raise ex.Error(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.Error("array %s not found" % array_name)
    if not hasattr(array, action):
        raise ex.Error("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.Error as exc:
        print(exc, file=sys.stderr)
        ret = 1
    sys.exit(ret)


  0707010001f281000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/array/__init__.py  0707010001f28b000081a40000000000000000000000016a100daf00000da2000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/array/ibmds.py from __future__ import print_function

import sys
import os
from subprocess import *

import core.exceptions as ex
from env import Env
from core.node import Node

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.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.Error("dscli command execution error")
    return out, err

class IbmDss(object):
    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        self.filtering = len(objects) > 0
        self.arrays = []
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="array"):
            name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "ibmds":
                continue
            pwfile = os.path.join(Env.paths.pathvar, s+'.pwfile')
            if not os.path.exists(pwfile):
                raise ex.Error("%s does not exists. create it with 'dscli managepwfile ...'"%pwfile)

            try:
                username = self.node.oget(s, "username")
                hmc1 = self.node.oget(s, "hmc1")
                hmc2 = self.node.oget(s, "hmc2")
            except Exception as exc:
                print("error parsing section %s: %s" % (s, exc), file=sys.stderr)
                continue
            self.arrays.append(IbmDs(name, hmc1, hmc2, username, pwfile, node=self.node))
            done.append(name)

    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.Error("%s not defined in the node/cluster configuration, or is not usable" % array)

class IbmDs(object):
    def __init__(self, name, hmc1, hmc2, username, pwfile, node=None):
        self.name = name
        self.node = node
        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())
  0707010001f287000081a40000000000000000000000016a100daf000004ee000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/array/gce.py   from env import Env
from utilities.proc import justcall

class GceDiskss(object):
    arrays = []

    def __init__(self, objects=None):
        if objects is None:
            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 "+Env.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())
  0707010001f28d000081a40000000000000000000000016a100daf00002bc7000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/array/necism.py    import core.exceptions as ex
from utilities.proc import justcall, which

class NecMixin(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.Error('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.Error(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.Error(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.Error(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.Error(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.Error(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.Error(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 self.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.Error(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(NecMixin):
    def __init__(self, objects=None):
        if objects is None:
            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(NecMixin):
    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"))

 0707010001f31a000041ed0000000000000000000000106a102a9300000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/drivers/resource   0707010001f41f000041ed0000000000000000000000056a102a9300000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/resource/task  0707010001f421000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/task/docker   0707010001f422000081a40000000000000000000000016a100daf00000b2d000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/task/docker/__init__.py   from .. import \
    KEYWORDS as BASE_KEYWORDS, \
    BaseTask
from drivers.resource.container.docker import \
    KEYWORDS as DOCKER_KEYWORDS, \
    ContainerDocker
import core.exceptions as ex

from core.objects.svcdict import KEYS
from utilities.subsystems.docker import has_docker

DRIVER_GROUP = "task"
DRIVER_BASENAME = "docker"
DRIVER_BASENAME_ALIASES = ["oci"]
KEYWORDS = BASE_KEYWORDS + [kw for kw in DOCKER_KEYWORDS if kw["keyword"] != "start_timeout"]
DEPRECATED_KEYWORDS = {
    "task.docker.run_image": "image",
    "task.docker.run_command": "command",
    "task.docker.net": "netns",
    "task.oci.run_image": "image",
    "task.oci.run_command": "command",
    "task.oci.net": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "task.docker.image": "run_image",
    "task.docker.command": "run_command",
    "task.docker.netns": "net",
    "task.oci.image": "run_image",
    "task.oci.command": "run_command",
    "task.oci.netns": "net",
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    data = []
    if has_docker(["docker", "docker.io"]):
        data += [
            "task.docker",
            "task.docker.registry_creds",
        ]
    return data


class TaskDocker(ContainerDocker, BaseTask):
    def __init__(self, *args, **kwargs):
        kwargs["detach"] = False
        kwargs["type"] = "task.docker"
        ContainerDocker.__init__(self, *args, **kwargs)
        BaseTask.__init__(self, *args, **kwargs)
        self.start_timeout = self.timeout

    def on_add(self):
        if self.max_parallel > 1:
            print(self.container_name)
            from utilities.lazy import set_lazy, unset_lazy
            from os import getpid
            set_lazy(self, "container_name", "%s.%d" % (self.container_name, getpid()))
            set_lazy(self, "container_label_id", "%s.%d" % (self.container_label_id, getpid()))
            unset_lazy(self, "container_id")

    _info = ContainerDocker._info

    def start(self):
        BaseTask.start(self)

    def stop(self):
        BaseTask.stop(self)

    def _run_call(self):
        try:
            ContainerDocker.start(self)
            self.write_last_run(0)
        except ex.Error as exc:
            ret = 1
            if isinstance(exc, ex.ExecError):
                ret = exc.exitcode
            self.write_last_run(ret)
            raise
        finally:
            if self.rm:
                self.container_rm()

    def _status(self, *args, **kwargs):
        return BaseTask._status(self, *args, **kwargs)

    def post_provision_start(self):
        pass

    def pre_provision_stop(self):
        pass

   0707010001f423000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/task/host 0707010001f424000081a40000000000000000000000016a100daf000015c8000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/task/host/__init__.py import os

import core.exceptions as ex

from .. import BaseTask, KEYWORDS as BASE_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from drivers.resource.app import run_as_popen_kwargs
from utilities.lazy import lazy

DRIVER_GROUP = "task"
DRIVER_BASENAME = "host"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "command",
        "at": True, 
        "required": True,
        "text": "The command to execute on 'run' action and at scheduled interval. The default schedule for tasks is ``@0``.",
        "example": "/srv/{name}/data/scripts/backup.sh"
    },
    { 
        "keyword": "secrets_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "A whitespace separated list of ``<var>=<secret name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<secret name>/<key path>`` only or whole ``<var>=<secret name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "configs_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "limit_as",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_cpu",
        "convert": "duration",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_core",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_data",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_fsize",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_memlock",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nofile",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nproc",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_rss",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_stack",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_vmem",
        "convert": "size",
        "at": True,
        "text": ""
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class TaskHost(BaseTask):
    def __init__(self, *args, **kwargs):
        kwargs["type"] = "task.host"
        BaseTask.__init__(self, *args, **kwargs)

    @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))
        return data

    def _run_call(self):
        kwargs = {
            'timeout': self.timeout,
            'blocking': True,
        }
        kwargs.update(run_as_popen_kwargs(None, cwd=self.cwd, user=self.user, group=self.group, limits=self.limits, umask=self.umask))
        if self.configs_environment or self.secrets_environment:
            if "env" not in kwargs:
                kwargs["env"] = {}
            kwargs["env"].update(self.kind_environment_env("cfg", self.configs_environment))
            kwargs["env"].update(self.kind_environment_env("sec", self.secrets_environment))
        if self.environment:
            if "env" not in kwargs:
                kwargs["env"] = {}
            for mapping in self.environment:
                try:
                    var, val = mapping.split("=", 1)
                except Exception as exc:
                    self.log.info("ignored environment mapping %s: %s", mapping, exc)
                    continue
                var = var.upper()
                kwargs["env"][var] = val

        try:
            self.action_triggers("", "command", **kwargs)
            self.write_last_run(0)
        except ex.Error as exc:
            ret = 1
            if isinstance(exc, ex.ExecError):
                ret = exc.exitcode
            self.write_last_run(ret)
            if self.on_error:
                kwargs["blocking"] = False
                self.action_triggers("", "on_error", **kwargs)
            raise
0707010001f420000081a40000000000000000000000016a100daf0000339e000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/task/__init__.py  from __future__ import print_function

import json
import os
import time
import sys

import core.status
import core.exceptions as ex
import utilities.lock
import utilities.runfiles
from env import Env
from core.scheduler import SchedOpts
from utilities.lazy import lazy
from core.resource import Resource
from foreign.six.moves import input
from utilities.proc import lcall
from utilities.files import makedirs
from utilities.rfc3339 import RFC3339

KEYWORDS = [
    {
        "keyword": "timeout",
        "convert": "duration",
        "at": True,
        "text": "Wait for <duration> before declaring the task run action a failure. If no timeout is set, the agent waits indefinitely for the task command to exit.",
        "example": "5m"
    },
    {
        "keyword": "snooze",
        "at": True,
        "default": 0,
        "convert": "duration",
        "text": "Snooze the service before running the task, so if the command is known to cause a service status degradation the user can decide to snooze alarms for the duration set as value.",
        "example": "10m"
    },
    {
        "keyword": "log",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "Log the task outputs in the service log.",
    },
    {
        "keyword": "confirmation",
        "at": True,
        "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.",
    },
    {
        "keyword": "on_error",
        "at": True,
        "text": "A command to execute on :c-action:`run` action if :kw:`command` returned an error.",
        "example": "/srv/{name}/data/scripts/task_on_error.sh"
    },
    {
        "keyword": "max_parallel",
        "at": True,
        "default": 1,
        "convert": "integer",
        "text": "Allow running the task multiple times concurrently.",
    },
    {
        "keyword": "check",
        "candidates": [None, "last_run", "last_run_warn"],
        "at": True,
        "text": "If set to 'last_run', the last run retcode is used to report a task resource status. If set to 'last_run_warn', the last run error retcode is displayed as a resource warning. If not set (default), the status of a task is always n/a.",
        "example": "last_run"
    },
    {
        "keyword": "user",
        "at": True,
        "text": "The user to impersonate when running the task command. The default user is root.",
        "example": "admin"
    },
    {
        "keyword": "schedule",
        "default_keyword": "run_schedule",
        "at": True,
        "text": "Set the this task run schedule. See ``/usr/share/doc/opensvc/schedule`` for the schedule syntax reference.",
        "example": '["00:00-01:00@61 mon", "02:00-03:00@61 tue-sun"]'
    },
    {
        "keyword": "pre_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`run` action. Errors do not interrupt the action."
    },
    {
        "keyword": "post_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`run` action. Errors do not interrupt the action."
    },
    {
        "keyword": "blocking_pre_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute before the resource :c-action:`run` action. Errors interrupt the action."
    },
    {
        "keyword": "blocking_post_run",
        "generic": True,
        "at": True,
        "text": "A command or script to execute after the resource :c-action:`run` action. Errors interrupt the action."
    },
    {
        "prefixes": ["run"],
        "keyword": "_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 '{prefix}' action. A condition is expressed as ``<rid>(<state>,...)``. If states are omitted, ``up,stdby up`` is used as the default expected states."
    },
    {
        "keyword": "group",
        "at": True,
        "text": "If the binary is owned by the root user, run it as the specified group instead of root."
    },
    {
        "keyword": "cwd",
        "at": True,
        "text": "Change the working directory to the specified location instead of the default ``<pathtmp>``."
    },
    {
        "keyword": "umask",
        "at": True,
        "text": "The umask to set for the application process.",
        "example": "022"
    },
]


class BaseTask(Resource):
    default_optional = True
    def __init__(self,
                 type="task",
                 command=None,
                 user=None,
                 group=None,
                 cwd=None,
                 umask=None,
                 on_error=None,
                 timeout=0,
                 snooze=0,
                 confirmation=False,
                 log=True,
                 environment=None,
                 configs_environment=None,
                 secrets_environment=None,
                 check=None,
                 max_parallel=1,
                 **kwargs):
        super(BaseTask, self).__init__(type=type, **kwargs)
        self.command = command
        self.on_error = on_error
        self.user = user
        self.group = group
        self.umask = umask
        self.cwd = cwd
        self.snooze = snooze
        self.timeout = timeout
        self.confirmation = confirmation
        self.log_outputs = log
        self.environment = environment
        self.configs_environment = configs_environment
        self.secrets_environment = secrets_environment
        self.checker = check
        self.max_parallel = max_parallel

    def __str__(self):
        return "%s command=%s user=%s" % (
            super(BaseTask, self).__str__(),
            self.command,
            self.user
        )

    def _status_info(self):
        data = {}
        for k, v in self.read_last_run().items():
            data["last_run_"+k] = v
        return data

    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):
        self.remove_last_run()

    def boot(self):
        self.remove_last_run()

    def stopped(self):
        return False

    def set_stopped(self, action=None):
        pass

    def clear_stopped(self):
        pass

    def clear_stopped_if_not_scoped(self):
        pass

    def set_stopped_if_scoped(self):
        pass

    @lazy
    def running_d(self):
        return os.path.join(self.var_d, "run")

    @lazy
    def running_f(self):
        return os.path.join(self.running_d, str(os.getpid()))

    def set_running(self):
        makedirs(self.running_d)
        with open(self.running_f, "w") as f:
            f.write(Env.session_uuid)

    def unset_running(self):
        try:
            os.unlink(self.running_f)
        except FileNotFoundError:
            pass

    @lazy
    def last_run_retcode_f(self):
        # backward compat
        return os.path.join(self.var_d, "last_run_retcode")

    @lazy
    def last_run_f(self):
        return os.path.join(self.var_d, "last_run")

    def write_last_run(self, exitcode):
        data = {
            "exitcode": exitcode,
            "at": RFC3339().from_epoch(time.time()),
            "session_id": Env.session_uuid,
        }
        with open(self.last_run_f, "w") as f:
            return json.dump(data, f)

    def read_last_run_retcode(self):
        try:
            with open(self.last_run_retcode_f, "r") as f:
                return int(f.read())
        except Exception:
            return

    def read_last_run(self):
        data = {
            "exitcode": None,
            "at": "1970-01-01T01:00:00.000000+00:00",
            "session_id": "",
        }
        try:
            with open(self.last_run_f, "r") as f:
                data = json.load(f)
        except Exception:
            xc = self.read_last_run_retcode()
            if xc:
                data["exitcode"] = xc
                try:
                    data["at"] = RFC3339().from_epoch(os.path.getmtime(self.last_run_retcode_f))
                except Exception:
                    pass
        return data

    def remove_last_run(self):
        try:
            os.unlink(self.last_run_f)
        except Exception:
            pass
        try:
            os.unlink(self.last_run_retcode_f)
        except Exception:
            pass

    @staticmethod
    def alarm_handler(signum, frame):
        raise ex.Signal

    def lcall(self, *args, **kwargs):
        """
        Wrap lcall, setting the resource logger
        """
        if self.log_outputs:
            kwargs["logger"] = self.log
        else:
            kwargs["logger"] = None
        return lcall(*args, **kwargs)

    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
        if self.svc.options.confirm:
            self.log.info("confirmed by command line option")
            return

        print("This task run requires confirmation.\nPlease make sure you fully "
              "understand its role and effects before confirming the run.")
        print("Do you really want to run %s (yes/no) > " % self.rid, end="", flush=True)
 
        import select
        inputs, outputs, errors = select.select([sys.stdin], [], [], 30)
        if inputs:
            buff = sys.stdin.readline().strip()
        else:
            raise ex.Error("timeout waiting for confirmation")

        if buff == "yes":
            self.log.info("run confirmed")
        else:
            raise ex.Error("run aborted")

    def fast_scheduled(self):
        now = time.time()
        s = self.svc.sched.get_schedule(self.rid, "schedule")
        n, i = s.get_next(now, now)
        return i and i < 10

    def run(self):
        try:
            with utilities.lock.cmlock(lockfile=os.path.join(self.var_d, "run.lock"), timeout=1, intent="run"):
                n = utilities.runfiles.count(self.running_d, log=self.log, cleanup=True)
                if n >= self.max_parallel:
                    raise ex.Error("task is already running %d times" % n)
                self.set_running()
            try:
                self._run()
            finally:
                self.unset_running()
        except utilities.lock.LOCK_EXCEPTIONS:
            raise
        finally:
            if utilities.runfiles.count(self.running_d) == 0:
                self.svc.notify_done("run", rids=[self.rid])

    def _run(self):
        """
        Update status.json when starting to run a task.
    
        So the running resource list is updated asap in the daemon status
        data, and requesters can see the task they submitted is running.
        """
        if not self.svc.options.cron or not self.fast_scheduled():
            self.svc.print_status_data_eval()
        self.create_pg()
        if self.snooze:
            try:
                data = self.svc._snooze(self.snooze)
                self.log.info(data.get("info", ""))
            except Exception as exc:
                self.log.warning(exc)
        self._run_call()
        if self.snooze:
            try:
                data = self.svc._unsnooze()
                self.log.info(data.get("info", ""))
            except Exception as exc:
                self.log.warning(exc)


    def _run_call(self):
        pass

    def _status(self, verbose=False):
        n = utilities.runfiles.count(self.running_d, self.log)
        if n >= self.max_parallel:
            self.status_log("%d/%s max parallel runs reached" % (n, self.max_parallel), "info")
        if not self.checker:
            return core.status.NA
        elif self.checker == "last_run":
            try:
                self.check_requires("run")
            except (ex.Error, ex.ContinueAction):
                return core.status.NA
            ret = self.read_last_run().get("exitcode")
            if ret is None:
                return core.status.NA
            if ret:
                self.status_log("last run failed", "error")
                return core.status.DOWN
            return core.status.UP
        elif self.checker == "last_run_warn":
            ret = self.read_last_run().get("exitcode")
            if ret is not None and ret != 0:
                self.status_log("last run exitcode: %d" % ret, "warn")
        return core.status.NA

    def is_provisioned(self, refresh=False):
        return True

    def schedule_options(self):
        return {
            "run": SchedOpts(
                self.rid,
                fname="last_"+self.rid,
                schedule_option="no_schedule"
            )
        }
  0707010001f425000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/task/podman   0707010001f426000081a40000000000000000000000016a100daf00000a9c000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/task/podman/__init__.py   import core.exceptions as ex

from .. import \
    BaseTask, \
    KEYWORDS as BASE_KEYWORDS
from drivers.resource.container.podman import \
    ContainerPodman, \
    KEYWORDS as PODMAN_KEYWORDS
from core.objects.svcdict import KEYS

DRIVER_GROUP = "task"
DRIVER_BASENAME = "podman"
DRIVER_BASENAME_ALIASES = ["oci"]
KEYWORDS = BASE_KEYWORDS + [kw for kw in PODMAN_KEYWORDS if kw["keyword"] != "start_timeout"]
DEPRECATED_KEYWORDS = {
    "task.podman.run_image": "image",
    "task.podman.run_command": "command",
    "task.podman.net": "netns",
    "task.oci.run_image": "image",
    "task.oci.run_command": "command",
    "task.oci.net": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "task.podman.image": "run_image",
    "task.podman.command": "run_command",
    "task.podman.netns": "net",
    "task.oci.image": "run_image",
    "task.oci.command": "run_command",
    "task.oci.netns": "net",
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("podman"):
        data += [
            "task.podman",
            "task.podman.registry_creds",
        ]
    return data

class TaskPodman(ContainerPodman, BaseTask):
    def __init__(self, *args, **kwargs):
        kwargs["detach"] = False
        kwargs["type"] = "task.podman"
        ContainerPodman.__init__(self, *args, **kwargs)
        BaseTask.__init__(self, *args, **kwargs)
        self.start_timeout = self.timeout

    _info = ContainerPodman._info

    def on_add(self):
        if self.max_parallel > 1:
            print(self.container_name)
            from utilities.lazy import set_lazy, unset_lazy
            from os import getpid
            set_lazy(self, "container_name", "%s.%d" % (self.container_name, getpid()))
            set_lazy(self, "container_label_id", "%s.%d" % (self.container_label_id, getpid()))
            unset_lazy(self, "container_id")

    def start(self):
        BaseTask.start(self)

    def stop(self):
        BaseTask.stop(self)

    def _run_call(self):
        try:
            ContainerPodman.start(self)
            self.write_last_run(0)
        except ex.Error:
            self.write_last_run(1)
            raise
        finally:
            if self.rm:
                self.container_rm()

    def _status(self, *args, **kwargs):
        return BaseTask._status(self, *args, **kwargs)

    def post_provision_start(self):
        pass

    def pre_provision_stop(self):
        pass

0707010001f427000041ed0000000000000000000000036a102a9300000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/resource/vhost 0707010001f429000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/vhost/envoy   0707010001f42a000081a40000000000000000000000016a100daf000003ae000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/vhost/envoy/__init__.py   from core.resource import DataResource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "vhost"
DRIVER_BASENAME = "envoy"
KEYWORDS = [
    {
        "keyword": "domains",
        "convert": "list",
        "at": True,
        "text": "The list of http domains in this expose.",
        "default": "{name}",
        "example": "{name}"
    },
    {
        "keyword": "routes",
        "convert": "list",
        "at": True,
        "default": [],
        "text": "The list of route resource identifiers for this vhost."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class VhostEnvoy(DataResource):
    def __init__(self, **kwargs):
        super(VhostEnvoy, self).__init__(type="vhost.envoy", **kwargs)
        if self.options.domains:
            self.label = "envoy vhost %s" % ", ".join(self.options.domains)
        else:
            self.label = "envoy vhost"

  0707010001f428000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/vhost/__init__.py 0707010001f351000041ed0000000000000000000000176a102a9200000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/resource/disk  0707010001f38e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/zvol 0707010001f38f000081a40000000000000000000000016a100daf00001695000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/zvol/__init__.py import os
import time

import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from utilities.converters import convert_size
from env import Env
from utilities.lazy import lazy
from utilities.subsystems.zfs import dataset_exists, zpool_devs, zfs_getprop, zfs_setprop
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which
from utilities.string import bdecode

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "zvol"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "required": True,
        "at": True,
        "text": "The full name of the zfs volume in the ``<pool>/<name>`` form.",
        "example": "tank/zvol1"
    },
    {
        "keyword": "size",
        "provisioning": True,
        "convert": "size",
        "at": True,
        "text": "The size of the zfs volume to create.",
        "example": "1g"
    },
    {
        "keyword": "create_options",
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "at": True,
        "text": "The :cmd:`zfs create -V <name>` extra options.",
        "example": "-o dedup=on"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

STARTED_PROP = "opensvc:started"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("zfs"):
        return ["disk.zvol"]
    return []


class DiskZvol(BaseDisk):
    def __init__(self,
                 size=None,
                 create_options=None,
                 **kwargs):
        super(DiskZvol, self).__init__(type='disk.zvol', **kwargs)
        self.size = size
        self.create_options = create_options or []
        self.label = "zvol %s" % self.name
        if self.name:
            self.pool = self.name.split("/", 1)[0]
        else:
            self.pool = None

    def _info(self):
        data = [
          ["name", self.name],
          ["pool", self.pool],
          ["device", self.device],
        ]
        return data

    def has_it(self):
        return dataset_exists(self.name, "volume")

    def is_up(self):
        """
        Returns True if the zvol exists and the pool imported
        """
        if not self.has_it():
            return False
        return self.is_up_prop()

    def is_up_prop(self):
        return self.get_started_prop() == "true"

    def get_started_prop(self):
        return zfs_getprop(self.name, STARTED_PROP)

    def set_started_prop(self, val):
        self.log.info("zfs set %s=%s %s", STARTED_PROP, val, self.name)
        return zfs_setprop(self.name, STARTED_PROP, val)

    def do_start(self):
        if self.is_up():
            self.log.info("zvol %s is already up", self.name)
            return
        self.set_started_prop("true")

    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.sub_devs() & holders_devpaths
        if len(holders_handled_by_resources) > 0:
            raise ex.Error("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
        self.remove_dev_holders(self.device, tree)

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return
        self.remove_holders()
        self.set_started_prop("false")

    @lazy
    def device(self):
        return "/dev/%s/%s" % (self.pool, self.name.split("/", 1)[-1])

    def sub_devs(self):
        resources = [res for res in self.svc.get_resources("disk.zpool") \
                     if res.name == self.pool]
        if resources:
            return resources[0].sub_devs()
        return set(zpool_devs(self.pool, self.svc.node))

    def exposed_devs(self):
        print(self.device)
        if os.path.exists(self.device):
            return set([self.device])
        return set()


    def provisioned(self):
        return self.has_it()

    def unprovisioner(self):
        if not self.has_it():
            self.log.info("zvol %s already destroyed", self.name)
            return
        self.destroy()
        self.svc.node.unset_lazy("devtree")

    def destroy(self):
        def fn():
            ret, out, err = self._destroy()
            if ret == 0:
                return True
            if "busy" in err:
                return False
            raise ex.Error(err)
        self.wait_for_fn(fn, 10, 3, errmsg="waited too long for zvol destruction (busy)")

    def _destroy(self):
        cmd = [Env.syspaths.zfs, "destroy", "-f", self.name]
        return self.vcall(cmd)

    def provisioner(self):
        if self.has_it():
            self.log.info("zvol %s already exists", self.name)
            return
        cmd = [Env.syspaths.zfs, "create"]
        cmd += self.create_options
        cmd += ["-V", str(convert_size(self.size, _to="m"))+'M', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.can_rollback = True

        for i in range(3, 0, -1):
            if os.path.exists(self.device):
                break
            if i != 0:
                time.sleep(1)
        if i == 0:
            self.log.error("timed out waiting for %s to appear" % self.device)
            raise ex.Error

        self.svc.node.unset_lazy("devtree")

   0707010001f35e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/gandi    0707010001f35f000081a40000000000000000000000016a100daf00001b24000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/gandi/__init__.py    import grp
import os
import pwd
import stat

import core.status
import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.string import is_string

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "gandi"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "node",
        "at": True,
        "default_text": "The local node name.",
        "text": "The node name from the Gandi api point of view.",
    },
    {
        "keyword": "user",
        "at": True,
        "example": "root",
        "text": "The user that should be owner of the device. Either in numeric or symbolic form."
    },
    {
        "keyword": "group",
        "at": True,
        "example": "sys",
        "text": "The group that should be owner of the device. Either in numeric or symbolic form."
    },
    {
        "keyword": "perm",
        "at": True,
        "example": "600",
        "text": "The permissions the device should have. A string representing the octal permissions."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    try:
        from libcloud.compute.types import Provider
        from libcloud.compute.providers import get_driver
        return ["disk.gandi"]
    except ImportError:
        return []

class DiskGandi(BaseDisk):
    def __init__(self,
                 node=None,
                 cloud_id=None,
                 user="root",
                 group="root",
                 perm="660",
                 **kwargs):
        super(DiskGandi, self).__init__(type='disk.gandi', **kwargs)
        self.label = "gandi volume %s" % self.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))

    @lazy
    def cloud(self):
        try:
            cloud = self.svc.node.cloud_get(self.cloud_id)
        except ex.InitError as e:
            raise ex.Error(str(e))
        return 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.Error as e:
            raise ex.Error("can't find cloud node to list volumes (%s)"%str(e))

        disks = self.cloud.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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if s:
            return core.status.UP
        else:
            return core.status.DOWN

    def get_node(self):
        if self.node is not None:
            n = self.node
        else:
            n = Env.nodename
        try:
            nodes = self.cloud.driver.list_nodes()
        except Exception as e:
            raise ex.Error(str(e))
        for node in nodes:
            if node.name == n:
                return node
        raise ex.Error()

    def get_disk(self):
        disks = self.cloud.driver.ex_list_disks()
        _disk = None
        for disk in disks:
            if disk.name == self.name:
                _disk = disk
        if _disk is None:
            raise ex.Error()
        return _disk

    def do_start(self):
        try:
            node = self.get_node()
        except ex.Error as e:
            raise ex.Error("can't find cloud node to attach volume %s to (%s)"%(self.name, str(e)))

        try:
            disk = self.get_disk()
        except:
            raise ex.Error("volume %s not found in %s"%(self.name, self.cloud_id))

        try:
            status = self.is_up()
        except ex.Error 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)
        self.cloud.driver.ex_node_attach_disk(node, disk)
        self.can_rollback = True

    def do_stop(self):
        try:
            node = self.get_node()
        except ex.Error as e:
            raise ex.Error("can't find cloud node to detach volume %s from: %s"%(self.name, str(e)))

        try:
            disk = self.get_disk()
        except:
            raise ex.Error("volume %s not found in %s"%(self.name, self.cloud_id))

        try:
            status = self.is_up()
        except ex.Error 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)
        self.cloud.driver.ex_node_detach_disk(node, disk)

    def shutdown(self):
        pass

0707010001f364000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/ldom 0707010001f365000081a40000000000000000000000016a100daf000010cc000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/ldom/__init__.py import re

from subprocess import *

import core.exceptions as ex
import core.status
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from .. import BaseDisk, BASE_KEYWORDS

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "ldom"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "container_id",
        "required": True,
        "at": True,
        "text": "The id of the container whose configuration to extract the disk mapping from."
    },
]
DEPRECATED_SECTIONS = {
    "vmdg": ["disk", "ldom"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    if which("ldm"):
        return ["disk.ldom"]
    return []


class DiskLdom(BaseDisk):
    def __init__(self, container_id=None, **kwargs):
        super(DiskLdom, self).__init__(type='disk.ldom', **kwargs)
        self.label = "vmdg %s" % self.name
        self.container_id = container_id

    @lazy
    def container(self):
        try:
            res = self.svc.resources_by_id[self.container_id]
        except KeyError:
            raise ex.Error("%s.container_id points to an invalid section" % self.container_id)
        if res.type != "container.ldom":
            raise ex.Error("%s.container_id points to a non-ldom container" % self.container_id)
        return res

    def has_it(self):
        return True

    def is_up(self):
        return True

    def _status(self, verbose=False):
        return core.status.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.Error
        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.container.name]
        p = Popen(cmd, stdout=PIPE, stderr=PIPE, close_fds=True)
        buff = p.communicate()
        if p.returncode != 0:
            raise ex.Error
        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
0707010001f360000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/disk/gce  0707010001f361000081a40000000000000000000000016a100daf00002737000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/disk/gce/__init__.py  import json

import core.status
from .. import BaseDisk, BASE_KEYWORDS
from utilities.converters import convert_size
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.subsystems.gce import GceMixin

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "gce"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "names",
        "convert": "list",
        "at": True,
        "required": True,
        "text": "Set the gce disk names",
        "example": "svc1-disk1"
    },
    {
        "keyword": "gce_zone",
        "at": True,
        "required": True,
        "text": "Set the gce zone",
        "example": "europe-west1-b"
    },
    {
        "keyword": "description",
        "provisioning": True,
        "at": True,
        "text": "An optional, textual description for the disks being created.",
        "example": "foo"
    },
    {
        "keyword": "image",
        "provisioning": True,
        "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"
    },
    {
        "keyword": "image_project",
        "provisioning": True,
        "at": True,
        "text": "The project against which all image references will be resolved.",
        "example": "myprj"
    },
    {
        "keyword": "size",
        "provisioning": True,
        "at": True,
        "convert": "size",
        "text": "A size expression for the disk allocation.",
        "example": "20g"
    },
    {
        "keyword": "source_snapshot",
        "provisioning": True,
        "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"
    },
    {
        "keyword": "disk_type",
        "provisioning": True,
        "at": True,
        "text": "Specifies the type of disk to create. To get a list of available disk types, run :cmd:`gcloud compute disk-types list`. The default disk type is ``pd-standard``.",
        "example": "pd-standard"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    if which("gcloud"):
        return ["disk.gce"]
    return []


class DiskGce(BaseDisk, GceMixin):
    def __init__(self, names=None, gce_zone=None, description=None, image=None, image_project=None, size=None, source_snapshot=None, disk_type=None, **kwargs):
        BaseDisk.__init__(self, type="disk.gce", **kwargs)
        self.names = names or set()
        self.gce_zone = gce_zone
        self.description = description
        self.image = image
        self.image_project = image_project
        self.size = size
        self.source_snapshot = source_snapshot
        self.disk_type = disk_type
        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", Env.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 core.status.WARN
        l = self.up_count()
        n = len(l)
        unattached = sorted(list(set(self.names) - set(l)))
        if n == len(self.names):
            return core.status.UP
        elif n == 0:
            return core.status.DOWN
        else:
            self.status_log("unattached: "+", ".join(unattached))
            return core.status.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 != Env.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",
          Env.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",
          Env.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)
        if self.svc.namespace:
            return ".".join([self.svc.namespace.lower(), self.svc.name, self.rid.replace("#", "."), str(index)])
        else:
            return ".".join([self.svc.name, 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])


    def provisioner(self):
        for name in self.names:
            self._provisioner(name)
        self.log.info("provisioned")
        self.get_disks(refresh=True)
        self.start()
        self.svc.node.unset_lazy("devtree")

    def _provisioner(self, name):
        disk_names = self.get_disk_names()
        if name in disk_names:
            self.log.info("gce disk name %s already provisioned" % name)
            return

        size = str(convert_size(self.size, _to="MB"))+'MB'

        cmd = ["gcloud", "compute", "disks", "create", "-q",
               name,
               "--size", size,
               "--zone", self.gce_zone]

        if self.description:
            cmd += ["--description", self.description]
        if self.image:
            cmd += ["--image", self.image]
        if self.source_snapshot:
            cmd += ["--source-snapshot", self.source_snapshot]
        if self.image_project:
            cmd += ["--image-project", self.image_project]
        if self.disk_type:
            cmd += ["--type", self.disk_type]

        self.vcall(cmd)

    def unprovisioner(self):
        self.stop()
        for name in self.names:
            self._unprovisioner(name)
        self.log.info("unprovisioned")
        self.svc.node.unset_lazy("devtree")

    def _unprovisioner(self, name):
        disk_names = self.get_disk_names()
        if name not in disk_names:
            self.log.info("gce disk name %s already unprovisioned" % name)
            return

        cmd = ["gcloud", "compute", "disks", "delete", "-q", name,
               "--zone", self.gce_zone]

        self.vcall(cmd)


 0707010001f38c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/zpool    0707010001f38d000081a40000000000000000000000016a100daf00003973000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/zpool/__init__.py    import json
import glob
import os
import time

import core.exceptions as ex
import core.status
from .. import BaseDisk, BASE_KEYWORDS
from core.capabilities import capabilities
from core.objects.svcdict import KEYS
from env import Env
from utilities.lazy import lazy
from utilities.lock import cmlock
from utilities.proc import justcall, drop_option, call_log
from utilities.subsystems.zfs import zpool_devs, zpool_getprop, zpool_setprop

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "zpool"
DRIVER_BASENAME_ALIASES = ["pool"]
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "multihost",
        "convert": "tristate",
        "at": True,
        "text": "If set to ``true``, sets zfs option ``multihost=on`` on start if not already set. This requires all nodes to be booted with a /etc/hostid installed, preferrably generated by the zgenhostid command."
    },
    {
        "keyword": "create_options",
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "at": True,
        "text": "The zpool create extra options.",
        "example": "-O dedup=on"
    },
    {
        "keyword": "vdev",
        "default": [],
        "convert": "list",
        "at": True,
        "provisioning": True,
        "text": "The vdev list, including optional parity keywords, as would be passed to zpool create."
    },
    {
        "keyword": "name",
        "required": True,
        "at": True,
        "text": "The name of the zfs pool"
    },
    {
        "keyword": "zone",
        "at": True,
        "text": "The zone name the zpool refers to. If set, the zpool is activated in the zone context."
    },
]
DEPRECATED_KEYWORDS = {
    "disk.pool.poolname": "name",
    "disk.zpool.poolname": "name",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "disk.pool.name": "poolname",
    "disk.zpool.name": "poolname",
}
DEPRECATED_SECTIONS = {
    "disk.pool": ["disk", "zpool"],
    "pool": ["disk", "zpool"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("zpool"):
        return ["disk.zpool"]
    return []


class DiskZpool(BaseDisk):
    """
    Zfs pool resource driver.
    """
    def __init__(self,
                 multihost=None,
                 vdev=None,
                 create_options=None,
                 zone=None,
                 **kwargs):
        super(DiskZpool, self).__init__(type='disk.zpool', **kwargs)
        self.multihost = multihost
        self.vdev = vdev or []
        self.create_options = create_options or []
        self.label = 'zpool ' + self.name if self.name else "<undefined>"
        if zone is not None:
            self.tags.add("zone")
            self.tags.add(zone)

    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
        """
        self.update_sub_devs_cache()

    def has_it(self):
        """Returns True if the pool is present
        """
        if "node.x.zpool" not in capabilities:
            raise ex.Error("zpool command not found")
        if not self.name:
            raise ex.Error("zpool name undefined")
        _, _, ret = justcall(['zpool', 'list', self.name])
        if ret == 0 :
            return True
        return False

    def zpool_health(self, status):
        for line in status.splitlines():
            line = line.strip()
            if not line.strip().startswith("state:"):
                continue
            return line.split(":", 1)[-1].strip()
        return "unknown"

    def zpool_errors(self, status):
        errors = []
        for line in status.splitlines():
            if not line.strip().startswith("errors:"):
                continue
            entry = line.split(":", 1)[-1].strip()
            if entry == "No known data errors":
                continue
            errors.append(entry)
        return errors

    @lazy
    def zpool_status(self):
        if not self.name:
            raise ex.Error("zpool name undefined")
        cmd = ["zpool", "status", "-v", self.name]
        out, err, ret = justcall(cmd)
        return out+err

    def _status(self, verbose=False):
        errors = self.zpool_errors(self.zpool_status)
        if errors:
            for error in errors:
                if "pool I/O is currently suspended" in error:
                    self.status_log("pool I/O is currently suspended")
                    return core.status.WARN
                self.status_log(error)
        if self.is_up():
            return core.status.UP
        else:
            return core.status.DOWN

    def is_up(self):
        """Returns True if the pool is present and activated
        """
        state = self.zpool_health(self.zpool_status)
        if not self.has_it():
            return False
        if state == "ONLINE":
            return True
        elif state == "SUSPENDED":
            self.status_log(state.lower())
            return True
        elif state == "DEGRADED":
            self.status_log(state.lower())
            return True
        return False

    @lazy
    def zpool_cache(self):
        return os.path.join(Env.paths.pathvar, 'zpool.cache')

    def import_pool_no_cachefile(self):
        cmd = ['zpool', 'import', '-f', '-o', 'cachefile='+self.zpool_cache,
               self.name]
        out, err, ret = justcall(cmd)
        return cmd, ret, out, err

    def import_pool_cachefile(self):
        devzp = os.path.join(self.var_d, 'dev', 'dsk')
        if not os.path.isdir(devzp):
            return [], 1, "", ""
        cmd = ['zpool', 'import', '-f', '-o', 'cachefile='+self.zpool_cache,
               '-d', devzp, self.name]
        out, err, ret = justcall(cmd)
        return cmd, ret, out, err

    def import_pool(self, verbose=True, retries=10):
        """
        Import the pool.
        1/ try using a dev list cache, which is fastest
        2/ fallback without dev list cache

        Parallel import can fail on Solaris 11.4, with a "no such
        pool available" error. Retry in this case, if we confirm the
        pool exists.
        """
        for i in range(retries):
            cmd, ret, out, err = self._import_pool()
            if "no such pool available" in err:
                self.log.info("retry import %d/%d, pool '%s' reported unavailable", i+1, retries, self.name)
                time.sleep(2)
                continue
            break
        if verbose:
            self.log.info("%s", " ".join(cmd))
            if out:
                self.log.info("%s", out)
            if err:
                if ret:
                    self.log.error("%s [retcode %d]", err, ret)
                else:
                    self.log.warning("%s [retcode %d]", err, ret)
        if ret == 0:
            self.can_rollback = True
        return ret

    def _import_pool(self, verbose=True):
        cmd, ret, out, err = self.import_pool_cachefile()
        if ret == 0:
            return cmd, ret, out, err
        if verbose:
            self.log.info("import fallback without dev cache")
        cmd, ret, out, err = self.import_pool_no_cachefile()
        return cmd, ret, out, err

    def do_start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.name)
            return 0
        self.zgenhostid()
        ret = self.import_pool()
        if ret != 0:
            raise ex.Error("failed to import pool")
        self.can_rollback = True
        self.set_multihost()
        self.unset_lazy("zpool_status")

    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, err_to_warn=True)
        if ret != 0:
            cmd = ["zpool", "export", "-f", self.name]
            ret, out, err = self.vcall(cmd)
        self.unset_lazy("zpool_status")
        if ret != 0:
            raise ex.Error

    def exposed_devs(self):
        return set([self.name])

    def sub_devs(self):
        if self.is_up():
            self.log.debug("resource up ... update sub devs cache")
            dl = self.update_sub_devs_cache()
            return dl
        if 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()
        dl = self.load_sub_devs_cache()
        dl = self.remap_cached_sub_devs_controller(dl)
        return dl

    def update_sub_devs_cache(self):
        if not self.has_it():
            return set()
        dl = self._sub_devs()
        self.write_sub_devs_cache(dl)
        return dl

    def write_sub_devs_cache(self, dl):
        with cmlock(timeout=5, delay=1, lockfile=self.sub_devs_name+".lock", intent="write"):
            with open(self.sub_devs_name, 'w') as f:
                f.write(json.dumps(list(dl)))

    def load_sub_devs_cache(self):
        with cmlock(timeout=5, delay=1, lockfile=self.sub_devs_name+".lock", intent="load"):
            with open(self.sub_devs_name, 'r') as f:
                try:
                    return set(json.load(f))
                except:
                    raise ex.Error("corrupted sub devs cache file %s"%self.sub_devs_name)

    def remap_cached_sub_devs_controller(self, dl):
        if Env.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

    def _sub_devs(self):
        """
        Search zpool vdevs from the output of "zpool status poolname" if
        imported.
        """
        return set(zpool_devs(self.name, self.svc.node))

    def zgenhostid(self):
        if self.multihost and not os.path.exists("/etc/hostid"):
            try:
                justcall(["zgenhostid"])
            except Exception:
                self.log.warning("/etc/hostid does not exist and zgenhostid is not installed")

    def set_multihost(self):
        if self.multihost is None:
            # don't care
            return
        current = zpool_getprop(self.name, "multihost")
        if current == "":
            # not multihost capable (pre-0.7) or not imported
            return
        ret = 0
        if self.multihost is True:
            if current == "off":
                ret = zpool_setprop(self.name, "multihost", "on", log=self.log)
            else:
                self.log.info("multihost is already on")
        elif self.multihost is False:
            if current == "on":
                ret = zpool_setprop(self.name, "multihost", "off", log=self.log)
            else:
                self.log.info("multihost is already off")
        if ret != 0:
            raise ex.Error


    def provisioned(self):
        return self.has_it()

    def unprovisioner(self):
        if not self.is_up():
            ret = self.import_pool(verbose=False, retries=1)
            if ret != 0:
                self.log.info("already unprovisioned")
                return

        cmd = ["zpool", "destroy", "-f", self.name]
        self.log.info(" ".join(cmd))

        def destroy():
            out, err, ret = justcall(cmd)
            if "pool is busy" in err:
                return False
            if ret != 0:
                raise ex.Error
            call_log(out, self.log, "info")
            call_log(err, self.log, "error")
            return True

        self.wait_for_fn(destroy, 5, 1, "failed to destroy the pool after 5 tries at 1 second interval: pool is busy")
        self.svc.node.unset_lazy("devtree")

    def pre_unprovision_stop(self):
        # a pool must be imported for destroy
        pass

    def provisioner(self):
        if self.is_provisioned():
            self.log.info("already provisioned")
            return
        name = self.name
        multihost = self.multihost
        create_options = self.create_options
        vdev = self.vdev or self.oget("vdev") # reference

        args = create_options
        args += [name]
        args += vdev
        args = drop_option("-m", args, drop_value=True)
        args = drop_option("-o", args, drop_value="cachefile")
        args = drop_option("-o", args, drop_value="multihost")
        args = [
            "-m", "legacy",
            "-o", "cachefile="+self.zpool_cache,
        ] + args
        if self.multihost and Env.sysname == "Linux":
            args = ["-o", "multihost=on"] + args
            self.zgenhostid()
        cmd = ["zpool", "create"] + args
        ret, _, _ = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.can_rollback = True
        self.svc.node.unset_lazy("devtree")

 0707010001f388000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vxdg 0707010001f389000081a40000000000000000000000016a100daf000028b2000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vxdg/__init__.py import fnmatch
import glob
import os
import re

from collections import namedtuple
from stat import *

import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall, qcall, which

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vxdg"
DRIVER_BASENAME_ALIASES = ["veritas"]
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the volume group"
    },
    {
        "keyword": "pvs",
        "required": True,
        "text": "The list of paths to the physical volumes of the volume group.",
        "provisioning": True
    },
]
DEPRECATED_SECTIONS = {
    "disk.veritas": ["disk", "vxdg"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("vxdg"):
        return ["disk.vxdg"]
    return []


class DiskVxdg(BaseDisk):
    """
    Veritas Volume group resource
    """
    def __init__(self, pvs=None, **kwargs):
        super(DiskVxdg, self).__init__(type='disk.vxdg', **kwargs)
        self.label = "vxdg %s" % self.name
        self.sub_devs_cache = set()
        self.pvs = pvs or None

    def vxprint(self):
        cmd = ["vxprint", "-g", self.name]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        data = {}
        headers = list()
        for line in out.splitlines():
            words = line.split()
            if len(words) < 7:
                continue
            if words[0] == "TY":
                headers = list(words)
                continue
            line = namedtuple("line", headers)._make(words)
            data[(getattr(line, "TY"), getattr(line, "NAME"))] = line
        return data

    def has_it(self):
        """
        Return True if the vg is present
        """
        if not which("vxdg"):
            raise ex.Error("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 defects(self):
        try:
            data = self.vxprint()
        except ex.Error:
            # dg does not exist
            return []
        errs = ["%s:%s:%s" % (key[0], key[1], val.STATE) for key, val in data.items() if val.STATE not in ("-", "ACTIVE")]
        errs += ["%s:%s:%s" % (key[0], key[1], val.KSTATE) for key, val in data.items() if val.KSTATE not in ("-", "ENABLED")]
        return sorted(errs)

    def _status(self, **kwargs):
        for defect in self.defects():
             self.status_log(defect, "warn")
        return super(DiskVxdg, self)._status(**kwargs)

    def has_vxvol_resources(self):
        for res in self.svc.get_resources("disk.vxvol"):
            if res.vg == self.name:
                return True
        return False

    def do_startvol(self):
        if self.has_vxvol_resources():
            return 0
        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 vxdisk_list(self):
        if not which("vxdisk"):
            return {}
        cmd = ["vxdisk", "list"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        data = {}
        headers = list()
        for line in out.splitlines():
            words = line.split(None, 4)
            if len(words) < 5:
                continue
            if words[0] == "DEVICE":
                headers = list(words)
                continue
            dev = namedtuple("dev", headers)._make(words)
            if getattr(dev, "GROUP") != self.name and getattr(dev, "GROUP") != "(%s)"%self.name:
                continue
            data[getattr(dev, "DEVICE")] = dev
        return data

    def sub_devs(self):
        """
        Return the set of devices used by the dg.
        """
        if hasattr(self, "sub_devs_cache") and len(self.sub_devs_cache) > 0:
            return self.sub_devs_cache

        devs = ["/dev/vx/dsk/"+dev for dev in self.vxdisk_list()]
        if Env.sysname == "SunOS":
            for idx, dev in enumerate(devs):
                if re.match('^.*s[0-9]$', dev) is None:
                    devs[idx] += "s2"

        self.log.debug("found devs %s held by vg %s" % (devs, self.name))
        self.sub_devs_cache = devs

        return devs


    def provisioned(self):
        return self.prov_has_it()

    def prov_has_it(self):
        cmd = ["vxdisk", "list"]
        out, err, ret = justcall(cmd)
        words  = out.split()
        if "(%s)" % self.name in words or \
           " %s " % self.name in words:
            return True
        return False

    def unprovisioner(self):
        #if self.has_it():
        #    self.destroy_vg()
        self.unsetup()

    def unsetup(self):
        cmd = ["vxdisk", "list"]
        out, err, ret = justcall(cmd)
        for line in out.splitlines():
            words = line.split()
            if "(%s)" % self.name in words:
                cmd = ["/opt/VRTS/bin/vxdiskunsetup", "-f", words[0]]
                ret, out, err = self.vcall(cmd)
                if ret != 0:
                    raise ex.Error

    def destroy_vg(self):
        cmd = ["vxdg", "destroy", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.svc.node.unset_lazy("devtree")

    def has_pv(self, pv):
        cmd = ["vxdisk", "list", pv]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        for line in out.splitlines():
            if line.startswith("group:"):
                if "name=%s "%self.name in line:
                    self.log.info("pv %s is already a member of vg %s", pv, self.name)
                    return True
                elif "name= " in line:
                    # pv already initialized but not in a dg
                    return True
                else:
                    vg = line.split("name=", 1)[0].split()[0]
                    raise ex.Error("pv %s in use by vg %s" % (pv, vg))
            if line.startswith("flags:") and "invalid" in line:
                return False
        return False

    @lazy
    def vxdisks(self):
        """
        Parse vxdisk list output.

        Example:

        # vxdisk list
        DEVICE          TYPE            DISK         GROUP        STATUS
        aluadisk_0   auto:cdsdisk    aluadisk_0   osvcdg       online
        aluadisk_1   auto:cdsdisk    aluadisk_1   osvcdg       online
        aluadisk_2   auto:none       -            -            online invalid

        """
        cmd = ["vxdisk", "list"]
        out, err, ret = justcall(cmd)
        data = []
        for line in out.splitlines():
            words = line.split()
            if len(words) < 1:
                continue
            if words[0] == "DEVICE":
                continue
            data.append(words[0])
        return data

    def vxname_glob(self, pattern):
        """
        Return the list of vxdisks matching the name globing pattern.
        """
        return [name for name in self.vxdisks if fnmatch.fnmatch(name, pattern)]

    def sysname_glob(self, pattern):
        """
        Return the list of vxdisks matching the system devpath globing pattern.
        """
        data = []
        for devpath in glob.glob(pattern):
            dev = self.svc.node.devtree.get_dev_by_devpath(devpath)
            if dev is None:
                continue
            data.append(dev.alias)
        return data

    def provisioner(self):
        if not self.pvs:
            # lazy reference
            self.pvs = self.oget("pvs")

        if self.pvs is None:
            # lazy reference not resolvable
            raise ex.Error("%s.pvs value is not valid" % self.rid)

        self.pvs = self.pvs.split()
        l = []
        for pv in self.pvs:
            if os.sep not in pv:
                _l = self.vxname_glob(pv)
            else:
                _l = self.sysname_glob(pv)
            if _l:
                self.log.info("expand %s to %s" % (pv, ', '.join(_l)))
            l += _l
        self.pvs = l

        if len(self.pvs) == 0:
            raise ex.Error("no pvs specified")

        for pv in self.pvs:
            if self.has_pv(pv):
                continue
            cmd = ["/opt/VRTS/bin/vxdisksetup", "-i", pv]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error

        if self.prov_has_it():
            self.log.info("vg %s already exists")
            return

        cmd = ["vxdg", "init", self.name] + self.pvs
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

        self.can_rollback = True
        self.svc.node.unset_lazy("devtree")
  0707010001f362000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/hpvm 0707010001f363000081a40000000000000000000000016a100daf000012cd000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/hpvm/__init__.py import os

from subprocess import *

import core.exceptions as ex
import core.status
from core.objects.svcdict import KEYS
from ..vg.hpux import DiskVg
from .. import BASE_KEYWORDS

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "hpvm"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "container_name",
        "at": True,
        "required": True,
        "text": "The name of the container to map the disks in its configuration file to the resource."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    if which("/opt/hpvm/bin/hpvmdevmgmt"):
        return "disk.hpvm"
    return []


class DiskHpvm(DiskVg):
    def __init__(self, container_name=None, **kwargs):
        kwargs["name"] = "vmdg_%s" % container_name
        super(DiskHpvm, self).__init__(**kwargs)
        self.label = "vmdg %s" % self.name
        self.container_name = container_name

    def has_it(self):
        return True

    def is_up(self):
        return True

    def _status(self, verbose=False):
        return core.status.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(["app", "sync", "task", "disk.scsireserv"]))
        if s['overall'].status != core.status.UP:
            self.do_mksf()
            self.do_share()

    def presync(self):
        s = self.svc.group_status(excluded_groups=set(["app", "sync", "task", "disk.scsireserv"]))
        if self.svc.options.force or s['overall'].status == core.status.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.Error
        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.Error
                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.Error

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

        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
   0707010001f374000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/disk/raw  0707010001f376000081a40000000000000000000000016a100daf00000245000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/disk/raw/hpux.py  from . import BaseDiskRaw, BASE_RAW_KEYWORDS
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "raw"
KEYWORDS = BASE_RAW_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class DiskRaw(BaseDiskRaw):
    def __init__(self, **kwargs):
        super(DiskRaw, self).__init__(**kwargs)

    def verify_dev(self, dev):
        cmd = ["diskinfo", dev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        return True
   0707010001f377000081a40000000000000000000000016a100daf00002498000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/disk/raw/linux.py import os

import core.exceptions as ex
import utilities.lock
import utilities.devices

from . import BaseDiskRaw, BASE_RAW_KEYWORDS
from env import Env
from utilities.cache import cache
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which
from utilities.devices.linux import loop_to_file

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "raw"
KEYWORDS = BASE_RAW_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = ["disk.raw"]
    cmd = ['modinfo', 'raw']
    out, err, ret = justcall(cmd)
    if ret == 0 and which("raw"):
        data.append("disk.raw.cdev")
    return data

class DiskRaw(BaseDiskRaw):
    def __init__(self, **kwargs):
        super(DiskRaw, self).__init__(**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.
        """
        if not self.has_capability("disk.raw.cdev"):
            return
        cmd = ["raw", "-qa"]
        out, err, ret = justcall(cmd)
        if ret == 0:
            # no need to load (already loaded or compiled-in)
            return
        cmd = [Env.syspaths.lsmod]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error
        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.Error

    @cache("raw.list")
    def get_raws(self):
        if not self.has_capability("disk.raw.cdev"):
            return {}
        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.Error
        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.Error
        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):
        lockfile = os.path.join(Env.paths.pathlock, 'startvgraw')
        lockfd = None
        try:
            lockfd = utilities.lock.lock(timeout=timeout, delay=delay, lockfile=lockfile)
        except utilities.lock.LockTimeout:
            self.log.error("timed out waiting for lock")
            raise ex.Error
        except utilities.lock.LockNoLockFile:
            self.log.error("lock_nowait: set the 'lockfile' param")
            raise ex.Error
        except utilities.lock.LockCreateError:
            self.log.error("can not create lock file %s"%lockfile)
            raise ex.Error
        except utilities.lock.LockAcquire as e:
            self.log.warning("another action is currently running (pid=%s)"%e.pid)
            raise ex.Error
        except ex.Signal:
            self.log.error("interrupted by signal")
            raise ex.Error
        except:
            self.save_exc()
            raise ex.Error("unexpected locking error")
        self.lockfd = lockfd

    def unlock(self):
        utilities.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.Error
                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.Error
                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 utilities.devices.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

    def verify_dev(self, dev):
        if dev.startswith("/dev/loop"):
            fpath = loop_to_file(dev)
            return fpath is not None
        return True
0707010001f375000081a40000000000000000000000016a100daf00003a95000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/disk/raw/__init__.py  import glob
import grp
import os
import pwd
import re
import stat

import core.exceptions as ex
import core.status
from .. import BaseDisk, BASE_KEYWORDS
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import which
from utilities.string import is_string, is_glob

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "raw"
BASE_RAW_KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "devs",
        "convert": "set",
        "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"
    },
    {
        "keyword": "zone",
        "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"
    },
    {
        "keyword": "create_char_devices",
        "at": True,
        "default": True,
        "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"
    },
    {
        "keyword": "user",
        "at": True,
        "example": "root",
        "text": "The user that should be owner of the device. Either in numeric or symbolic form."
    },
    {
        "keyword": "group",
        "at": True,
        "example": "sys",
        "text": "The group that should be owner of the device. Either in numeric or symbolic form."
    },
    {
        "keyword": "perm",
        "at": True,
        "example": "600",
        "text": "The permissions the device should have. A string representing the octal permissions."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=BASE_RAW_KEYWORDS,
)


class BaseDiskRaw(BaseDisk):
    def __init__(self,
                 devs=None,
                 user=None,
                 group=None,
                 perm=None,
                 create_char_devices=True,
                 zone=None,
                 **kwargs):
        super(BaseDiskRaw, self).__init__(name="raw", type='disk.raw', **kwargs)
        self.label = "raw"
        self.user = user
        self.group = group
        self.perm = perm
        self.zone = zone
        self.create_char_devices = create_char_devices
        if zone:
            self.tags.add("zone")
            self.tags.add(zone)
            devs = self.mangle_zone_devs(devs)
        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 mangle_zone_devs(self, devs):
        if devs is None:
            return
        return set([dev.replace(":", ":<%s>" % self.zone) for dev in devs])

    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.oget('devs')
        zone = self.oget('zone')
        if zone:
            self.original_devs = self.mangle_zone_devs(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(r"<(\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 r.type == "container.zone":
                    # zone
                    container_root = r.zonepath
                elif hasattr(r, "get_rootfs"):
                    # lxc
                    container_root = r.get_rootfs()
                else:
                    return path
                path = re.sub(r"<\w+>", container_root, path)
                break
        return path

    def validate_devs(self):
        if not self.original_devs:
            self.reload_config()
        self.devs = set()
        self.devs_not_found = set()
        self.dst_devs_not_found = set()
        if self.original_devs is None:
            self.original_devs = self.oget("devs")
        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
            else:
                if not is_glob(dev) and not os.path.exists(dev) or not self.verify_dev(dev):
                    self.devs_not_found.add(dev)
                    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.name+".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.Error("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.Error("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(sorted(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",
                "task"
            ]))
            msg = "%s not found" % ', '.join(sorted(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()
        if len(self.original_devs) == 0:
            return core.status.NA
        if self.is_up():
            return core.status.UP
        else:
            return core.status.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.Error("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.Error("%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.Error

    def do_start_blocks(self):
        if which("mknod") is None:
            raise ex.Error("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.Error(str(e))
        else:
            self.log.info("%s already unlinked" % dst)

    def sub_devs(self):
        return self.devs

    def provisioned(self):
        return True

   0707010001f378000081a40000000000000000000000016a100daf0000046b000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/disk/raw/sunos.py import os
import re

from . import BaseDiskRaw, BASE_RAW_KEYWORDS
from core.objects.svcdict import KEYS

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "raw"
KEYWORDS = BASE_RAW_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class DiskRaw(BaseDiskRaw):
    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

 0707010001f359000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/disk 0707010001f35b000081a40000000000000000000000016a100daf00000f37000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/disk/disk/linux.py    import glob
import os
import time

import core.exceptions as ex
import core.status
import utilities.devices.linux

from . import DiskDisk as BaseDiskDisk, KEYWORDS
from utilities.lazy import lazy
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import which

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "disk"

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class DiskDisk(BaseDiskDisk):
    @lazy
    def devpath(self):
        self.unset_lazy("disk_id")
        wwid = str(self.disk_id).lower().replace("0x", "")
        try:
            return glob.glob("/dev/disk/by-id/dm-uuid-mpath-[36]%s" % wwid)[0]
        except Exception as exc:
            return

    @lazy
    def anypath(self):
        wwid = str(self.disk_id).lower().replace("0x", "")
        path = "/dev/disk/by-id/wwn-0x%s" % wwid
        return path

    def sub_devs(self):
        if self.devpath:
            return set([self.devpath])
        else:
            return set()

    def _status(self, verbose=False):
        if self.disk_id is None:
            return core.status.NA
        if not self.devpath or not os.path.exists(self.devpath):
            if self.devpath:
                self.status_log("%s does not exist" % self.devpath, "warn")
            return core.status.DOWN
        return core.status.NA

    def exposed_devs(self):
        self.unset_lazy("devpath")
        try:
            dev = os.path.realpath(self.devpath)
            return set([dev])
        except Exception as exc:
            pass
        return set()

    def unconfigure(self):
        self.log.info("unconfigure disk %s", self.disk_id)
        try:
            mpath = list(self.exposed_devs())[0] # /dev/dm-<minor>
        except IndexError:
            mpath = None
        if mpath and mpath.startswith("/dev/dm-"):
            paths = utilities.devices.linux.dev_to_paths(mpath)
            utilities.devices.linux.multipath_flush(mpath, log=self.log)
            for path in paths:
                utilities.devices.linux.dev_delete(path, log=self.log)
        self.svc.node.unset_lazy("devtree")

    def configure(self, force=False):
        self.unset_lazy("disk_id")
        self.unset_lazy("anypath")
        self.unset_lazy("devpath")
        if not force and self.exposed_devs():
            self.log.info("disk already configured: exposed devs %s", self.exposed_devs())
            return
        self.log.info("configure disk %s", self.disk_id)
        if not self.disk_id:
            raise ex.Error("disk_id is not set. should be at this point")
        try:
            with utilities.lock.cmlock(lockfile=self.lockfile, timeout=20):
                self.svc.node._scanscsi(log=self.log)
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.Error("lock acquire: %s" % str(exc))
        time.sleep(2)
        self.wait_anypath()
        self.svc.node.unset_lazy("devtree")
        if self.devpath and which(Env.syspaths.multipath):
            dev = os.path.realpath(self.devpath)
            cmd = [Env.syspaths.multipath, "-v1", dev]
            ret, out, err = self.vcall(cmd)
        self.wait_devpath()

    def wait_anypath(self):
        for retry in range(30):
            self.unset_lazy("anypath")
            if self.anypath and os.path.exists(self.anypath):
                self.log.info("%s now exists", self.anypath)
                return
            time.sleep(1)
        raise ex.Error("time out waiting for %s to appear" % self.anypath)

    def wait_devpath(self):
        for retry in range(30):
            self.unset_lazy("devpath")
            if self.devpath and os.path.exists(self.devpath):
                self.log.info("%s now exists", self.devpath)
                return
            time.sleep(1)
        raise ex.Error("time out waiting for %s to appear" % self.devpath)

 0707010001f35a000081a40000000000000000000000016a100daf00001537000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/disk/__init__.py import json
import time
import os

import core.exceptions as ex

from .. import BASE_KEYWORDS
from env import Env
from utilities.lazy import lazy
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.render.color import format_str_flat_json

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "disk"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "size",
        "provisioning": True,
        "at": True,
        "convert": "size",
        "text": "A size expression for the disk allocation.",
        "example": "20g"
    },
    {
        "keyword": "pool",
        "provisioning": True,
        "at": True,
        "text": "The name of the pool this volume was allocated from.",
    },
    {
        "keyword": "name",
        "provisioning": True,
        "at": True,
        "text": "The name of the disk.",
    },
    {
        "keyword": "disk_id",
        "at": True,
        "text": "The wwn of the disk.",
        "example": "6589cfc00000097484f0728d8b2118a6"
    },
    {
        "keyword": "array",
        "at": True,
        "provisioning": True,
        "text": "The array to provision the disk from.",
        "example": "xtremio-prod1"
    },
    {
        "keyword": "diskgroup",
        "at": True,
        "provisioning": True,
        "text": "The array disk group to provision the disk from.",
        "example": "default"
    },
    {
        "keyword": "slo",
        "at": True,
        "provisioning": True,
        "text": "The provisioned disk service level objective. This keyword is honored on arrays supporting this (ex: EMC VMAX)",
        "example": "Optimized"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class DiskDisk(Resource):
    """
    SAN Disk resource
    """

    def __init__(self, name=None, size=None, pool=None, disk_id=None, array=None, diskgroup=None, slo=None, **kwargs):
        super(DiskDisk, self).__init__(type="disk.disk", **kwargs)
        self.name = name
        self.size = size
        self.poolname = pool
        self.disk_id = disk_id
        self.array = array
        self.diskgroup = diskgroup
        self.slo = slo

    def __str__(self):
        return "%s disk disk_id=%s" % (
            super(DiskDisk, self).__str__(),
            self.disk_id
        )

    def on_add(self):
        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],
        ]

    @lazy
    def lockfile(self):
        return os.path.join(Env.paths.pathvar, "disk.disk.lock")

    def configure(self, force=False):
        # OS specific
        pass

    def unconfigure(self):
        # OS specific
        pass

    def provisioned(self):
        if self.disk_id:
            return True
        return False

    def provisioner(self):
        if self.disk_id:
            self.log.info("skip disk creation: the disk_id keyword is already set")
            self.configure()
        else:
            self.create_disk()
            self.configure(force=True)

    def create_disk(self):
        pool = self.svc.node.get_pool(self.poolname)
        pool.log = self.log
        if self.shared:
            disk_id_kw = "disk_id"
            result = pool.create_disk(self.name, size=self.size, nodes=self.svc.nodes)
        else:
            disk_id_kw = "disk_id@" + Env.nodename
            name = self.name + pool.sep() + Env.nodename
            result = pool.create_disk(name, size=self.size, nodes=[Env.nodename])
        if not result:
            raise ex.Error("invalid create disk result: %s" % result)
        for line in format_str_flat_json(result).splitlines():
            self.log.info(line)
        changes = []
        if "disk_ids" in result:
            for node, disk_id in result["disk_ids"].items():
                changes.append("%s.disk_id@%s=%s" % (self.rid, node, disk_id))
        elif "disk_id" in result:
            disk_id = result["disk_id"]
            changes.append("%s.%s=%s" % (self.rid, disk_id_kw, disk_id))
        else:
            raise ex.Error("no disk id found in result")
        self.log.info("changes: %s", changes)
        self.svc.set_multi(changes, validation=False)
        self.log.info("changes applied")
        self.disk_id = self.oget("disk_id")
        self.log.info("disk %s provisioned" % result["disk_id"])

    def provisioner_shared_non_leader(self):
        self.configure()

    def unprovisioner_shared_non_leader(self):
        self.unconfigure()

    def unprovisioner(self):
        if not self.disk_id:
            self.log.info("skip unprovision: 'disk_id' is not set")
            return
        self.unconfigure()
        pool = self.svc.node.get_pool(self.poolname)
        pool.log = self.log
        if self.shared:
            disk_id_kw = "disk_id"
            name = self.name
        else:
            disk_id_kw = "disk_id@" + Env.nodename
            name = self.name + pool.sep() + Env.nodename
        result = pool.delete_disk(name=name, disk_id=self.disk_id)
        for line in format_str_flat_json(result).splitlines():
            self.log.info(line)
        self.svc.set_multi(["%s.%s=%s" % (self.rid, disk_id_kw, "")], validation=False)
        self.log.info("unprovisioned")

 0707010001f353000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/advfs    0707010001f354000081a40000000000000000000000016a100daf00000eac000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/advfs/__init__.py    import json
import os

import core.exceptions as ex
import core.status

from .. import BaseDisk, BASE_KEYWORDS
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "advfs"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the volume group"
    },
    {
        "keyword": "options",
        "default": "",
        "at": True,
        "convert": "shlex",
        "provisioning": True,
        "text": "The vgcreate options to use upon vg provisioning."
    },
    {
        "keyword": "pvs",
        "required": True,
        "convert": "list",
        "text": "The list of paths to the physical volumes of the volume group.",
        "provisioning": True
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if not os.path.exists("/etc/fdmns"):
        return data
    if not which("showfdmn"):
        return data
    data.append("disk.advfs")
    return data


class DiskAdvfs(BaseDisk):
    def __init__(self, options=None, pvs=None, **kwargs):
        super(DiskAdvfs, self).__init__(type='disk.advfs', **kwargs)
        self.label = "fdmn %s" % self.name
        self.sub_devs_cache = set()
        self.options = options or []
        self.pvs = pvs or []

    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(["app", "sync", "task", "disk.scsireserv"]))
            if s['overall'].status == core.status.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.Error

    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
0707010001f370000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/md   0707010001f371000081a40000000000000000000000016a100daf00004c32000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/disk/md/__init__.py   import json
import os

import core.exceptions as ex
import core.status
import utilities.devices.linux
from os.path import exists as path_exists
from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.cache import cache
from utilities.converters import convert_size
from utilities.fcache import fcache
from utilities.lazy import lazy
from utilities.proc import justcall, which

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "md"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "uuid",
        "at": True,
        "text": "The md uuid to use with mdadm assemble commands"
    },
    {
        "keyword": "name",
        "at": True,
        "text": "The name of the md device. This name must have 32 characters max. Beware not to set a name already in use by another md resource.",
        "default_text": "<namespace>.<name>.disk.<rindex>",
    },
    {
        "keyword": "devs",
        "at": True,
        "default": [],
        "convert": "list",
        "provisioning": True,
        "example": "/dev/rbd0 /dev/rbd1",
        "text": "The md member devices to use with mdadm create command"
    },
    {
        "keyword": "level",
        "at": True,
        "provisioning": True,
        "example": "raid1",
        "text": "The md raid level to use with mdadm create command (see mdadm man for values)"
    },
    {
        "keyword": "layout",
        "at": True,
        "provisioning": True,
        "text": "The md raid layout to use with mdadm create command (see mdadm man for values)"
    },
    {
        "keyword": "chunk",
        "at": True,
        "provisioning": True,
        "example": "128k",
        "text": "The md chunk size to use with mdadm create command. Values are converted to kb and rounded to 4."
    },
    {
        "keyword": "spares",
        "at": True,
        "provisioning": True,
        "convert": "integer",
        "example": "0",
        "default": 0,
        "text": "The md number of spare devices to use with mdadm create command"
    },
    {
        "keyword": "bitmap",
        "at": True,
        "provisioning": True,
        "example": "internal",
        "text": "'none' disables the write-intent bitmap, 'internal' writes the bitmap on the md legs, a file path may write the bitmap to a file (deprecated upstream)."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mdadm"):
        return ["disk.md"]
    return []

def justcall_mdadm_detail(*args, **kwargs):
    return justcall(*args, **kwargs)


def justcall_mdadm_scan(*args, **kwargs):
    return justcall(*args, **kwargs)


def justcall_md_create(*args, **kwargs):
    return justcall(*args, **kwargs)


class DiskMd(BaseDisk):
    startup_timeout = 10

    def __init__(self,
                 name=None,
                 uuid=None,
                 level=None,
                 devs=None,
                 spares=None,
                 chunk=None,
                 layout=None,
                 bitmap=None,
                 **kwargs):
        self.uuid = uuid
        self.level = level
        self.devs = devs or []
        self.spares = spares
        self.chunk = chunk
        self.layout = layout
        self.bitmap = bitmap
        self.mdadm = "/sbin/mdadm"
        super(DiskMd, self).__init__(name=name, 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.cd[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 provisioned(self):
        if which("mdadm") is None:
            return
        return self.has_it()

    def provisioner(self):
        self._create_md()
        self.can_rollback = True
        self._set_real_uuid()
        self._set_svc_rid_uuid()
        self.svc.node.unset_lazy("devtree")

    def unprovisioner(self):
        if self.uuid == "" or self.uuid is None:
            return
        for dev in self.sub_devs():
            self.vcall([self.mdadm, "--brief", "--zero-superblock", dev])
        self._unset_svc_rid_uuid()
        self.svc.node.unset_lazy("devtree")
        self.clear_cache("mdadm.scan.v")

    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):
        self.auto_assemble_disable()

    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", "task", "disk.scsireserv"]))
        if self.svc.options.force or s['avail'].status == core.status.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):
        devpath = self.devpath()
        if path_exists(devpath):
            return devpath
        out, err, ret = self.mdadm_scan_v()
        devname = self.devname()
        for line in out.splitlines():
            if self._mdadm_scan_match(line, uuid=self.uuid, devname=devname):
                devname = line.split()[1]
                if path_exists(devname):
                    return devname
        raise ex.Error("unable to find a devpath for md")

    def devname(self):
        if self.name:
            return "/dev/md/"+self.name
        elif self.svc.namespace:
            return "/dev/md/"+self.svc.namespace.lower()+"."+self.svc.name.split(".")[0]+"."+self.rid.replace("#", ".")
        else:
            return "/dev/md/"+self.svc.name.split(".")[0]+"."+self.rid.replace("#", ".")

    def posix_devpath(self):
        # mdadm starts refusing "/dev/by-id/md-uuid-*" names as non-POSIX circa v4.3 (ubuntu 2404)
        d = self.devpath()
        return os.path.realpath(d)

    def devpath(self):
        return "/dev/disk/by-id/md-uuid-"+str(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.devname(), "-u", self.uuid]
        ret, out, err = self.vcall(cmd, warn_to_info=True)
        self.clear_cache("mdadm.scan.v")
        if ret == 2:
            self.log.info("no changes were made to the array")
        elif ret != 0:
            raise ex.Error
        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)
        self.clear_cache("mdadm.scan.v")
        if ret != 0:
            raise ex.Error

    def detail(self, devname=None):
        try:
            devpath = devname or self.md_devpath()
        except ex.Error as e:
            return "State : " + str(e)
        if not path_exists(devpath):
            return "State : devpath does not exist"
        out, err, ret = justcall_mdadm_detail(argv=[self.mdadm, '--detail', devpath])
        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.Error(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 _mdadm_scan_match(self, output, uuid=None, devname=None):
        words = output.split()
        if uuid and "UUID=" + uuid in words:
            return True
        elif devname and devname in words:
            return True
        else:
            return False

    def has_it(self):
        if self.uuid == "" or self.uuid is None:
            return False
        return self._mdadm_scan_match(self.mdadm_scan_v()[0], uuid=self.uuid)

    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)
        if len(states) == 0:
            return False
        false_states = [
            "Not Started",
            "devpath does not exist",
            "unable to find a devpath for md",
            "unknown"
        ]
        for state in false_states:
            if state in states:
                self.log.debug("status eval'ed down because: %s", buff)
                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):
        invalid_devname_message = self._invalid_devname()
        if invalid_devname_message:
            self.status_log(invalid_devname_message)
        if self.uuid is None:
            return core.status.NA
        self.auto_assemble_disabled()
        s = super(DiskMd, self)._status(verbose=verbose)
        if s == core.status.DOWN:
             self.down_state_alerts()
        return s

    def do_start(self):
        if self.uuid is None:
            raise ex.Error("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:
                self.devs = self.oget("devs")
                devs = self.devs
                if devs is None:
                    return set()
                return set([os.path.realpath(dev) for dev in devs])
            except ex.OptNotFound:
                return set()
        try:
            devpath = self.md_devpath()
        except ex.Error as e:
            return self.sub_devs_inactive()
        if path_exists(devpath):
            return self.sub_devs_active()
        else:
            return self.sub_devs_inactive()

    @cache("mdadm.scan.v")
    def mdadm_scan_v(self):
        return justcall_mdadm_scan(argv=[self.mdadm, "-E", "--scan", "-v"])

    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 self._mdadm_scan_match(line, uuid=self.uuid):
                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(utilities.devices.linux.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.Error 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()
        added = 0
        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.posix_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)
                self.clear_cache("mdadm.scan.v")
                if ret != 0:
                    raise ex.Error("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))

    def _set_svc_rid_uuid(self):
        if self.shared:
            self.log.info("set %s.uuid = %s", self.rid, self.uuid)
            self.svc._set(self.rid, "uuid", self.uuid)
        else:
            self.log.info("set %s.uuid@%s = %s", self.rid, Env.nodename, self.uuid)
            self.svc._set(self.rid, "uuid@" + Env.nodename, self.uuid)

    def _unset_svc_rid_uuid(self):
        if self.shared:
            self.log.info("reset %s.uuid", self.rid)
            self.svc._set(self.rid, "uuid", "")
        else:
            self.log.info("reset %s.uuid@%s", self.rid, Env.nodename)
            self.svc._set(self.rid, "uuid@" + Env.nodename, "")

    def _create_md(self):
        if which("mdadm") is None:
            raise ex.Error("mdadm is not installed")
        argv = self._md_create_argv()
        self.log.info(" ".join(argv))
        out, err, return_code = justcall_md_create(argv=argv, input=b'no\n')
        self.clear_cache("mdadm.scan.v")
        self.log.info(out)
        if return_code != 0:
            raise ex.Error(err)
        if len(out) > 0:
            self.log.info(out)
        if len(err) > 0:
            self.log.error(err)

    def _md_create_argv(self):
        level = self.level
        devs = self.devs or self.oget("devs")
        spares = self.spares
        chunk = self.chunk
        layout = self.layout
        number_devs = len(devs) - (spares or 0)
        if number_devs < 1:
            raise ex.Error("at least 1 device must be set in the 'devs' provisioning")
        invalid_devname_message = self._invalid_devname()
        if invalid_devname_message:
            raise ex.Error(invalid_devname_message)
        name = self.devname()
        argv = [self.mdadm, '--create', name, '--force', '--run', '--quiet',
               '--metadata=default']
        argv += ['-n', str(number_devs)]
        if level:
            argv += ["-l", level]
        if spares:
            argv += ["-x", str(spares)]
        if chunk:
            argv += ["-c", str(convert_size(chunk, _to="k", _round=4))]
        if layout:
            argv += ["-p", layout]
        if self.bitmap in ("none", "internal"):
            argv += ["--bitmap="+self.bitmap]
        argv += devs
        return argv

    def _set_real_uuid(self):
        buff = self.detail(devname=self.devname())
        for line in buff.split("\n"):
            line = line.strip()
            if line.startswith("UUID :"):
                self.uuid = line.split(" : ")[-1]
                return
        raise ex.Error("unable to determine md uuid")

    def _invalid_devname(self):
        md_name = os.path.basename(self.devname())
        if len(md_name) >= 32:
            return "device md name is too long, 32 chars max (name is %s)" % md_name
  0707010001f357000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/crypt    0707010001f358000081a40000000000000000000000016a100daf0000252f000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/crypt/__init__.py    import os
import re

import core.exceptions as ex
import utilities.devices.linux

from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which
from utilities.lazy import lazy
from utilities.naming import factory


DRIVER_GROUP = "disk"
DRIVER_BASENAME = "crypt"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "text": "The basename of the exposed device.",
        "default_text": "The basename of the underlying device, suffixed with '-crypt'.",
        "example": "{fqdn}-crypt"
    },
    {
        "keyword": "dev",
        "at": True,
        "required": True,
        "text": "The fullpath of the underlying block device.",
        "example": "/dev/{fqdn}/lv1"
    },
    {
        "keyword": "manage_passphrase",
        "at": True,
        "convert": "boolean",
        "text": "By default, on provision the driver allocates a new random passphrase (256 printable chars), and forgets it on unprovision. If set to false, require a passphrase to be already present in the sec object to provision, and don't remove it on unprovision.",
        "provisioning": True,
        "default": True,
    },
    {
        "keyword": "secret",
        "at": True,
        "text": "The name of the sec object hosting the crypt secrets. The sec object must be in the same namespace than the object defining the disk.crypt resource.",
        "default": "{name}",
    },
    {
        "keyword": "label",
        "at": True,
        "text": "The label to set in the cryptsetup metadata writen on dev. A label helps admin understand the role of a device.",
        "default": "{fqdn}",
        "provisioning": True,
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("cryptsetup"):
        return ["disk.crypt"]
    return []

def gen_passphrase():
    import string
    import random
    characters = string.ascii_letters + string.digits + '!"#$%&()*+,-./:;<=>?@[]^_`{|}~'
    return "".join(random.choice(characters) for i in range(256))

class DiskCrypt(BaseDisk):
    def __init__(self,
                 name=None,
                 dev=None,
                 secret=None,
                 label=None,
                 manage_passphrase=True,
                 **kwargs):
        super(DiskCrypt, self).__init__(type='disk.crypt', **kwargs)
        self.dev = dev
        self.name = name
        self.secret = secret
        self.fmtlabel = label
        self.manage_passphrase = manage_passphrase

    @lazy
    def label(self):  # pylint: disable=method-hidden
        return "crypt %s" % self.exposed_dev()

    def _info(self):
        name = self.get_name()
        dev = self.get_dev()
        data = [
          ["name", name],
          ["dev", dev],
          ["secret", self.secret],
          ["label", self.fmtlabel],
          ["manage_passphrase", self.manage_passphrase],
        ]
        return data

    def sec(self):
        if not self.secret:
            return
        return factory("sec")(self.secret, namespace=self.svc.namespace, node=self.svc.node)

    def passphrase_key(self):
        key = self.rid.replace("#", "_")
        key += "_crypt_passphrase"
        return key

    def forget_passphrase(self):
        sec = self.sec()
        key = self.passphrase_key()
        if not self.manage_passphrase:
            self.log.info("leave key %s in %s", key, sec.path)
            return
        self.log.info("remove key %s in %s", key, sec.path)
        sec.remove_key(key)

    def passphrase_strict(self):
        sec = self.sec()
        key = self.passphrase_key()
        if not sec.exists():
            raise ex.Error("%s does not exist" % sec.path)
        if not sec.has_key(key):
            raise ex.Error("%s has no %s key" % (sec.path, key))
        return sec.decode_key(key)

    def passphrase_new(self):
        sec = self.sec()
        key = self.passphrase_key()
        new_pp = gen_passphrase()
        sec.add_key(key, new_pp)
        return new_pp

    def has_it(self):
        dev = self.get_dev()
        if dev is None:
            return False
        cmd = ["cryptsetup", "isLuks", dev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        return True

    def is_up(self):
        if not self.has_it():
            return False
        return os.path.exists(self.exposed_dev())

    def do_start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.label)
            return 0
        self.activate()
        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.Error("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
        self.remove_dev_holders(self.exposed_dev(), tree)

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return
        self.remove_holders()
        utilities.devices.linux.udevadm_settle()
        self.deactivate()

    def sub_devs(self):
        dev = self.get_dev()
        if dev is None:
            return set([])
        return set([dev])

    def exposed_dev(self):
        name = self.get_name()
        if name is None:
            return
        return "/dev/mapper/%s" % name

    def exposed_devs(self):
        dev = self.exposed_dev()
        if dev is None:
            return set()
        return set([dev])

    def get_dev(self):
        return self.oget("dev")

    def get_name(self):
        if self.name:
            return self.name
        dev = self.get_dev()
        if dev is None:
            return
        return os.path.basename(dev) + "-crypt"

    def verify_passphrase(self):
        if self.svc.options.force:
            return
        try:
            pp = self.passphrase_strict()
        except ex.Error as exc:
            raise ex.Error("abort crypt deactivate, so you can backup the device that we won't be able to activate again: %s. restore the key or use --force to skip this safeguard" % str(exc))

    def deactivate(self):
        if not which('cryptsetup'):
            self.log.debug("cryptsetup command not found")
            return

        dev = self.exposed_dev()
        if dev is None:
            return
        self.verify_passphrase()
        cmd = ["cryptsetup", "luksClose", dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def activate(self):
        if not which('cryptsetup'):
            self.log.debug("cryptsetup command not found")
            return

        dev = self.get_dev()
        if dev is None:
            raise ex.Error("abort luksOpen: no dev")
        name = self.get_name()
        if name is None:
            raise ex.Error("abort luksOpen: no name")
        pp = self.passphrase_strict()
        cmd = ["cryptsetup", "luksOpen", dev, name, "-"]
        self.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd, input=pp)
        if out:
            self.log.info(out)
        if err:
            self.log.error(err)
        if ret != 0:
            raise ex.Error()

    def unprovisioner(self):
        if not which('cryptsetup'):
            self.log.debug("skip crypt unprovision: cryptsetup command not found")
            return

        dev = self.get_dev()
        if dev is not None:
            cmd = ["cryptsetup", "luksErase", "--batch-mode", dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error
            self.svc.node.unset_lazy("devtree")
        self.forget_passphrase()

    def provisioned(self):
        return self.has_it()

    def provisioner(self):
        if not which('cryptsetup'):
            raise ex.Error("cryptsetup command not found")

        name = self.get_name()
        if name is None:
            raise ex.Error("skip crypt provisioning: no name")

        dev = self.get_dev()
        if dev is None:
            raise ex.Error("skip crypt provisioning: no dev")

        if self.manage_passphrase:
            pp = self.passphrase_new()
        else:
            pp = self.passphrase_strict()

        cmd = [
            "cryptsetup", "luksFormat",
            "--hash", "sha512",
            "--key-size", "512",
            "--cipher", "aes-xts-plain64",
            "--type", "luks2",
            "--batch-mode",
        ]
        if self.fmtlabel:
            cmd += ["--label", self.fmtlabel]
        cmd += [
            dev,
            "-"
        ]
        self.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd, input=pp)
        if out:
            self.log.info(out)
        if err:
            self.log.error(err)
        if ret != 0:
            raise ex.Error()

        self.svc.node.unset_lazy("devtree")

 0707010001f35c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/drbd 0707010001f35d000081a40000000000000000000000016a100daf000078dd000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/drbd/__init__.py import os
import re
import time

import core.exceptions as ex
import core.status
import daemon.handler
from core.comm import DEFAULT_DAEMON_TIMEOUT
from .. import BASE_KEYWORDS
from env import Env
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.cache import cache
from utilities.lazy import lazy
from utilities.proc import justcall, call_log, which
from utilities.render.listener import fmt_listener

RE_MINOR = r"^\s*device\s*/dev/drbd([0-9]+).*;"
RE_PORT = r"^\s*address.*:([0-9]+).*;"
RE_NODE_ID = r"^\s*node-id\s+([0-9]+)\s*;"
MAX_NODES = 32
MAX_DRBD = 512
MIN_PORT = 7289
MAX_PORT = 7489
DRIVER_GROUP = "disk"
DRIVER_BASENAME = "drbd"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "res",
        "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 :c-res:`sync#i0` "
                "resource will take care of replicating this file to remote "
                "nodes."
    },
    {
        "keyword": "disk",
        "at": True,
        "required": True,
        "provisioning": True,
        "text": "The path of the device to provision the drbd on."
    },
    {
        "keyword": "max_peers",
        "provisioning": True,
        "convert": "integer",
        "default": 0,
        "default_text": "(nodes_count*2)-1",
        "text": "The integer value to use in create-md --max-peers. The driver ensures the value is not lesser than the number of instances."
    },
    {
        "keyword": "addr",
        "at": True,
        "required": False,
        "provisioning": True,
        "text": "The addr to use to connect a peer. Use scoping to define "
                "each non-default address.",
        "default_text": "The ipaddr resolved for the nodename.",
    },
    {
        "keyword": "port",
        "at": True,
        "required": False,
        "provisioning": True,
        "text": "The port to use to connect a peer. The default",
        "default_text": "A port free on all nodes, allocated by the agent.",
    },
    {
        "keyword": "network",
        "required": False,
        "provisioning": True,
        "text": "The name of the backend network to use for drbd trafic. Set this keyword if some node names are resolved to NATed addresses."
    },
]
DEPRECATED_SECTIONS = {
    "drbd": ["disk", "drbd"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
)


def driver_capabilities(node=None):
    data = []
    if which("drbdadm"):
        data.append("disk.drbd")
        out, err, ret = justcall(["drbdadm"])
        if "Version: 9" in out:
            out, err, ret = justcall(["modinfo", "drbd"])
            if ret == 0:
                for line in out.splitlines():
                    details = line.split()
                    if details[0] == 'version:' and details[1].startswith('9.'):
                        data.append("disk.drbd.mesh")
    return data


class PostConfigHandler(daemon.handler.BaseHandler):
    """
    Write a resource configuration file. Used by the provisioner to
    replicate the new configuration.
    """
    routes = (
        ("POST", "config"),
    )
    prototype = [
        {
            "name": "res",
            "desc": "The drbd resource name.",
            "required": True,
            "format": "string",
        },
        {
            "name": "data",
            "desc": "The drbd resource configuration file content.",
            "required": True,
            "format": "string",
        },
    ]
    access = {}

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        cf = "/etc/drbd.d/%s.res" % options.res
        with open(cf, "w") as f:
            f.write(options.data)


class GetConfigHandler(daemon.handler.BaseHandler):
    """
    Read a resource configuration file. Used by the provisioner to
    fetch an existing drbd resource configuration, for example
    when a new service instance provisions.
    """
    routes = (
        ("GET", "config"),
    )
    prototype = [
        {
            "name": "res",
            "desc": "The drbd resource name.",
            "required": True,
            "format": "string",
        },
    ]
    access = {}

    def action(self, nodename, thr=None, **kwargs):
        options = self.parse_options(kwargs)
        cf = "/etc/drbd.d/%s.res" % options.res
        if not os.path.exists(cf):
            raise ex.HTTP(404, "resource configuration file does not exist")
        with open(cf, "r") as f:
            buff = f.read()
        return {"data": buff}


class AllocationsHandler(daemon.handler.BaseHandler):
    """
    Return the supported authentication methods.
    """
    routes = (
        ("GET", "allocations"),
    )
    prototype = []
    access = {}

    def action(self, nodename, thr=None, **kwargs):
        if "node.x.drbdadm" not in capabilities:
            raise ex.Error("this node is not drbd capable")
        out, err, ret = justcall(["drbdadm", "dump"])
        if ret:
            raise ex.HTTP(500, err)
        minors = set()
        ports = set()
        for line in out.splitlines():
            m = re.match(RE_MINOR, line)
            if m is not None:
                minors.add(int(m.group(1)))
            m = re.match(RE_PORT, line)
            if m is not None:
                ports.add(int(m.group(1)))
        return {
            "minors": sorted(list(minors)),
            "ports": sorted(list(ports)),
        }


DRIVER_HANDLERS = [
    AllocationsHandler,
    GetConfigHandler,
    PostConfigHandler,
]


class DiskDrbd(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, res=None, disk=None, network=None, **kwargs):
        super(DiskDrbd, self).__init__(type="disk.drbd", **kwargs)
        self.res = res
        self.disk = disk
        self.network = network
        self.label = "drbd %s" % res
        self.drbdadm = None
        self.rollback_even_if_standby = True
        self.can_rollback_role = False
        self.can_rollback_connection = False

    def __str__(self):
        return "%s resource=%s" % (super(DiskDrbd, self).__str__(), self.res)

    def files_to_sync(self):
        if os.path.exists(self.cf):
            return [self.cf]
        return []

    def drbdadm_cmd(self, cmd):
        if self.drbdadm is None:
            if "node.x.drbdadm" in capabilities:
                self.drbdadm = "drbdadm"
            else:
                raise ex.Error("drbdadm command not found")
        return [self.drbdadm] + cmd.split() + [self.res]

    @cache("drbdadm.dump.xml")
    def dump_xml(self):
        return justcall(["drbdadm", "dump-xml"])[0]

    def res_xml(self):
        from xml.etree.ElementTree import fromstring
        try:
            tree = fromstring(self.dump_xml())
        except Exception:
            return
        for res in tree.iter("resource"):
            if res.attrib["name"] != self.res:
                continue
            return res

    def exposed_devs(self):
        devps = set()
        res = self.res_xml()
        if res is None:
            return set()
        for host in res.iter("host"):
            if host.attrib["name"] != Env.nodename:
                continue
            d = host.find("device")
            if d is not None:
                devps |= set([d.text])
                continue
            for volume in res.iter("volume"):
                d = volume.find("device")
                if d is None:
                    continue
                devps |= set([d.text])
        return devps

    def sub_devs(self):
        devps = set()
        res = self.res_xml()
        if res is None:
            return set()
        for host in res.iter("host"):
            if host.attrib["name"] != Env.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 state_changing_action(self, cmd, timeout=10):
        """
        State changing action can be denied by a peer node during
        commits. This method implement a retry loop waiting for
        the action to be not-denied.
        """
        self.log.info(" ".join(cmd))
        for i in range(timeout):
            out, err, ret = justcall(cmd)
            if ret == 11:
                # cluster-wide drbd state change in-progress
                time.sleep(1)
                continue
            elif ret != 0:
                call_log(buff=err, log=self.log, level="error")
                raise ex.Error()
            call_log(buff=out, log=self.log, level="info")
            return out, err, ret
        raise ex.Error("timeout waiting for action non-denied by peer")

    def drbdadm_adjust(self):
        cmd = self.drbdadm_cmd("adjust")
        self.vcall(cmd)

    def drbdadm_down_force(self):
        self.drbdadm_adjust()
        cmd = self.drbdadm_cmd("disconnect")
        self.state_changing_action(cmd)
        cmd = self.drbdadm_cmd("detach --force")
        self.state_changing_action(cmd)
        cmd = self.drbdadm_cmd("down")
        self.state_changing_action(cmd)
        self.svc.node.unset_lazy("devtree")

    def drbdadm_down(self):
        cmd = self.drbdadm_cmd("down")
        self.state_changing_action(cmd)
        self.svc.node.unset_lazy("devtree")

    def drbdadm_up(self):
        cmd = self.drbdadm_cmd("up")
        self.state_changing_action(cmd)
        self.wait_for_kwown_dstate()
        self.can_rollback_connection = True
        self.can_rollback = True

    def get_cstate(self):
        self.prereq()
        out, err, ret = justcall(self.drbdadm_cmd("cstate"))
        if ret != 0:
            if "Device minor not allocated" in err or ret == 10:
                return "Unattached"
            else:
                raise ex.Error
        return out.split("\n")[0].strip()

    def prereq(self):
        if not os.path.exists("/proc/drbd"):
            ret, out, err = self.vcall(["modprobe", "drbd"])
            if ret != 0:
                raise ex.Error

    def start_connection(self):
        """
        see drbd_conn_state from https://github.com/LINBIT/drbd-headers/blob/master/linux/drbd.h
        """
        cstate = self.get_cstate()
        transiant_to_connect = ["Unconnected", "Timeout", "BrokenPipe", "NetworkFailure", "ProtocolError", "TearDown"]
        if cstate in ["Connecting", "Connected"]:
            self.log.info("drbd resource %s is already %s", self.res, cstate)
        elif cstate in transiant_to_connect:
            self.log.info("drbd resource %s cstate is %s: wait for Connecting or Connected", self.res, cstate)
            self.wait_for_cstate(["Connected", "Connecting"])
        elif cstate == "StandAlone":
            self.drbdadm_down()
            self.drbdadm_up()
        elif cstate == "Disconnecting":
            self.log.info("drbd resource %s cstate is %s: wait for StandAlone, to restart connection", self.res, cstate)
            self.wait_for_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.log.info("cstate before connect: %s", cstate)
            self.drbdadm_up()

    def get_role(self):
        out, err, ret = justcall(self.drbdadm_cmd("role"))
        if ret != 0:
            raise ex.Error(err)
        out = out.strip()
        if out in ("Primary", "Secondary"):
            # drbd9
            return out
        try:
            loc, rem = out.split("\n")[0].split("/")
        except (IndexError, ValueError, AttributeError):
            raise ex.Error(out)
        return loc

    def start_role(self, role, extra_args=None):
        cur_role = self.get_role()
        if cur_role != role:
            if extra_args:
                cmd = self.drbdadm_cmd("%s %s" % (role.lower(), ' '.join(extra_args)))
            else:
                cmd = self.drbdadm_cmd(role.lower())
            self.state_changing_action(cmd)
            self.can_rollback_role = True
            self.can_rollback = True
        else:
            self.log.info("drbd resource %s is already %s", self.res, role)

    def startstandby(self):
        self.start_connection()
        role = self.get_role()
        if role == "Primary":
            return
        self.start_role("Secondary")

    def stopstandby(self):
        if not os.path.exists(self.cf):
            self.log.info("skip: resource not configured")
            return
        if not self.res_defined():
            self.log.info("skip: resource not defined (for this host)")
            return
        self.go_secondary()

    def go_secondary(self):
        self.start_connection()
        role = self.get_role()
        if role == "Secondary":
            return
        self.start_role("Secondary")

    def drbdadm_connect(self, discard_my_data=False):
        cmd1 = ["drbdadm", "--"]
        cmd2 = ["connect", self.res]
        if discard_my_data:
            cmd2 = ["--discard-my-data"] + cmd2
        ret, out, err = self.vcall(cmd1 + cmd2)
        if ret != 0:
            raise ex.Error

    def drbdadm_disconnect(self):
        cmd = ["drbdadm", "disconnect", self.res]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def start(self):
        if not os.path.exists(self.cf):
            self.log.info("skip: resource not configured")
            return
        if not self.res_defined():
            self.log.info("skip: resource not defined (for this host)")
            return
        self.start_connection()
        self.start_role("Primary")

    def stop(self):
        if not os.path.exists(self.cf):
            self.log.info("skip: resource not configured")
            return
        if not self.res_defined():
            self.log.info("skip: resource not defined (for this host)")
            return
        if self.is_standby and not self.svc.options.force:
            self.stopstandby()
        else:
            self.drbdadm_down()

    def shutdown(self):
        if not os.path.exists(self.cf):
            self.log.info("skip: resource not configured")
            return
        if not self.res_defined():
            self.log.info("skip: resource not defined (for this host)")
            return
        self.drbdadm_down()

    def rollback(self):
        if not self.can_rollback:
            return
        if self.is_standby:
            if not self.can_rollback_role:
                return
            self.go_secondary()
        else:
            if not self.can_rollback_connection:
                return
            self.drbdadm_down()

    def get_dstate(self):
        ret, out, err = self.call(self.drbdadm_cmd("dstate"))
        if ret != 0:
            raise ex.Error
        return out.splitlines()

    def wait_for_kwown_dstate(self):
        def check():
            for dstate in self.get_dstate():
                if dstate == "Diskless/DUnknown":
                    return False
            return True
        self.wait_for_fn(check, 5, 1, errmsg="waited too long for a known remote dstate")

    def wait_for_cstate(self, cstates):
        def check():
            cstate = self.get_cstate()
            return cstate in cstates

        self.wait_for_fn(check, 5, 1, errmsg="waited too long for cstate in %s" % cstates)

    def dstate_uptodate(self, dstates):
        for dstate in dstates:
            if dstate != "UpToDate/UpToDate":
                return False
        return True

    def dstate_bootstraping(self, dstates):
        if len(dstates) != 1:
            return False
        return dstates[0] == "Inconsistent/DUnknown"

    def _status(self, verbose=False):
        try:
            role = self.get_role()
        except Exception as e:
            self.status_log(str(e))
            return core.status.DOWN
        self.status_log(str(role), "info")
        try:
            dstates = self.get_dstate()
        except ex.Error:
            self.status_log("drbdadm dstate %s failed" % self.res)
            return core.status.WARN
        if self.dstate_uptodate(dstates):
            pass
        else:
            status = None
            for idx, dstate in enumerate(dstates):
                dstatelist = dstate.split("/")
                dstateset = set(dstatelist)
                dstatelocal = dstatelist[0]
                if set(["UpToDate"]) == dstateset:
                    pass
                elif dstatelocal in ["Diskless", "DUnknown", "Unconfigured"]:
                    status = core.status.DOWN
                else:
                    self.status_log("unexpected drbd resource %s/%d state: %s" % (self.res, idx, dstate))
                # warnings
                if set(["Diskless", "DUnknown", "Unconfigured"]) & dstateset:
                    self.status_log("unexpected drbd resource %s/%d state: %s" % (self.res, idx, dstate))
            if status is not None:
                return status
        if role == "Primary":
            return core.status.UP
        elif role == "Secondary" and self.is_standby:
            return core.status.STDBY_UP
        else:
            return core.status.WARN

    def pre_provision_stop(self):
        """
        Skip normal stop before a unprovision.
        """
        pass

    def post_provision_start(self):
        """
        Skip normal start after a provision.
        """
        pass

    def provisioner(self):
        if self.svc.options.leader:
            self.write_config()
        elif not os.path.exists(self.cf) or not self.node_in_config():
            self.write_config_from_peer()
        self.create_md()
        self.drbdadm_down()
        self.drbdadm_up()
        if self.svc.options.leader:
            self.start_role("Primary", extra_args=["--force"])
            cstate = self.get_cstate()
        else:
            self.drbdadm_disconnect()
            self.drbdadm_connect()
        self.svc.node.unset_lazy("devtree")

    def unprovisioner(self):
        if not os.path.exists(self.cf):
            self.log.info("skip: resource not configured")
            return
        if self.res_defined():
            self.drbdadm_down_force()
            self.wipe_md()
        else:
            self.log.info("skip: resource not defined (for this host)")
        self.del_config()
        self.svc.node.unset_lazy("devtree")

    def res_defined(self):
        cmd = ["drbdadm", "--", "status", self.res]
        out, err, ret = justcall(cmd)
        if "not defined" in err:
            return False
        return True

    def has_md(self):
        cmd = ["drbdadm", "--", "--force", "dump-md", self.res]
        ret, out, err = self.call(cmd, errlog=False, outlog=False)
        if "No valid meta data found" in err:
            return False
        return True

    def max_peers(self):
        """
        Return the value to use in create-md --max-peers
        """
        v = self.oget("max_peers")
        n_nodes = len(self.svc.nodes)
        min_v = n_nodes - 1
        if min_v < 1:
            min_v = 1
        max_v = MAX_NODES - 1
        if v == 0:
            v = (n_nodes * 2) - 1
        if v < min_v:
            v = min_v
        if v > max_v:
            v = max_v
        return v

    def create_md(self):
        if self.has_md():
            self.log.info("resource %s already has metadata" % self.res)
            return
        cmd = ["drbdadm", "create-md", "--force", "--max-peers", str(self.max_peers()), self.res]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    def wipe_md(self):
        if not self.has_md():
            self.log.info("resource %s already has no metadata" % self.res)
            return
        cmd = ["drbdadm", "--", "--force", "wipe-md", self.res]
        self.log.info(" ".join(cmd))
        out, err, ret = justcall(cmd, input=b'yes\n')
        if ret == 20:
            # sub dev not found. no need to fail, as the sub dev is surely
            # flagged for unprovision too, which will wipe metadata.
            # this situation happens on unprovision on a stopped instance,
            # when drbd is stacked over another (stopped) disk resource.
            return
        if ret != 0:
            raise ex.Error(err)

    def res_create(self):
        if self.res_exists():
            self.log.info("resource %s already exists" % self.res)
            return
        cmd = ["drbdsetup", "new-resource", self.res]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    def res_delete(self):
        if not self.res_exists():
            self.log.info("resource %s already deleted" % self.res)
            return
        cmd = ["drbdsetup", "del-resource", self.res]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    def res_exists(self):
        cmd = ["drbdsetup", "show", self.res]
        ret, out, err = self.call(cmd)
        if ret != 20:
            return True
        return False

    def del_config(self):
        if not os.path.exists(self.cf):
            self.log.info("%s already deleted", self.res)
            return
        self.log.info("delete %s", self.cf)
        os.unlink(self.cf)

    def allocate_drbd(self):
        for idx in range(MAX_DRBD):
            if idx in self.allocations["minors"]:
                continue
            return "/dev/drbd%d" % idx
        raise ex.Error("no free minor")

    def allocate_port(self):
        for idx in range(MIN_PORT, MAX_PORT):
            if idx in self.allocations["ports"]:
                continue
            return idx
        raise ex.Error("no free minor")

    @lazy
    def cf(self):
        return "/etc/drbd.d/%s.res" % self.res

    def fetch_config(self):
        for node in self.svc.nodes:
            if node == Env.nodename:
                continue
            data = self.svc.daemon_get(
                {
                    "action": "/drivers/resource/disk/drbd/config",
                    "options": {"res": self.res}
                },
                node=node,
            )
            buff = data.get("nodes", {}).get(node, {}).get("data")
            if buff:
                return buff
        raise ex.Error("couldn't fetch the resource config from any peer")

    def write_config_from_peer(self):
        buff = self.fetch_config()
        need_replicate = False
        if not self.node_in_config(buff):
            buff = self.add_node_to_config(buff)
            need_replicate = True
        with open(self.cf, "w") as f:
            f.write(buff)
        if need_replicate:
            self.replicate_config(buff)

    def write_config(self):
        if os.path.exists(self.cf):
            self.log.info("%s already exists", self.cf)
            return
        lock_name = "drivers.resources.disk.drbd.allocate"
        lock_id = self.svc.node._daemon_lock(lock_name, timeout=120, on_error="raise")
        self.log.info("lock acquire: name=%s id=%s", lock_name, lock_id)
        try:
            self._write_config()
        finally:
            self.svc.node._daemon_unlock(lock_name, lock_id, timeout=120)
            self.log.info("lock released: name=%s id=%s", lock_name, lock_id)

    def _write_config(self):
        device = self.allocate_drbd()
        port = self.allocate_port()
        buff = self.format_config(device, port)
        with open(self.cf, "w") as filep:
            filep.write(buff)
        self.log.info("%s created", self.cf)
        self.unset_lazy("allocations")
        self.clear_cache("drbdadm.dump.xml")
        self.replicate_config(buff)

    def replicate_config(self, buff):
        data = self.svc.daemon_post(
            {
                "action": "/drivers/resource/disk/drbd/config",
                "options": {
                    "res": self.res,
                    "data": buff,
                },
            },
            node=[n for n in self.svc.nodes if n != Env.nodename],
            timeout=DEFAULT_DAEMON_TIMEOUT
        )
        if data.get("status", 1):
            raise ex.Error("failed to replicate config on nodes: %s" % data)

    @lazy
    def allocations(self):
        data = self.daemon_get_allocations()
        minors = set()
        ports = set()
        try:
            items = data["nodes"].items()
        except KeyError:
            raise ex.Error("unable to get current allocations: %s" % data.get("error"))
        for node, ndata in items:
            try:
                minors |= set(ndata["minors"])
                ports |= set(ndata["ports"])
            except KeyError:
                raise ex.Error("node %s invalid allocations report: %s" % (node, ndata.get("error", "")))
        data = {
            "minors": sorted(list(minors)),
            "ports": sorted(list(ports)),
        }
        return data

    def daemon_get_allocations(self):
        return self.svc.daemon_get(
            {"action": "/drivers/resource/disk/drbd/allocations"},
            node=self.svc.node.cluster_nodes,
        )

    @staticmethod
    def format_on(node, device, disk, addr, port, node_id=None):
        fmt_on =        "    on %s {\n%s    }\n"         # pep8: disable=E222
        fmt_on_device = "        device    %s;\n"        # pep8: disable=E222
        fmt_on_disk =   "        disk      %s;\n"        # pep8: disable=E222
        fmt_on_meta =   "        meta-disk internal;\n"  # pep8: disable=E222
        fmt_on_addr =   "        address   %s;\n"        # pep8: disable=E222
        fmt_on_nid =    "        node-id   %d;\n"        # pep8: disable=E222
        buff_content = fmt_on_device % device
        buff_content += fmt_on_disk % disk
        buff_content += fmt_on_meta
        if isinstance(port, str):
            int_port = int(port)
        elif isinstance(port, int):
            int_port = port
        else:
            raise ex.Error("expected string or int for port, got type %s %s" % (type(port), port))
        listener = fmt_listener(addr, int_port)
        if listener.startswith("["):
            listener = "ipv6 " + listener
        buff_content += fmt_on_addr % listener
        if node_id is not None:
            buff_content += fmt_on_nid % node_id
        buff_on = fmt_on % (node, buff_content)
        return buff_on

    def format_config(self, device, freeport):
        if self.has_capability("disk.drbd.mesh"):
            return self.format_config_v9(device, freeport)
        else:
            return self.format_config_v8(device, freeport)

    def get_node_addr(self, node):
        if self.network:
            return self.get_node_addr_with_network(node)
        else:
            return self.get_node_addr_with_getaddrinfo(node)

    def get_node_addr_with_network(self, node):
        from utilities.net.ipaddress import ip_network
        ndata = self.svc.node.networks_data()[self.network]
        subnet = ip_network(ndata["config"]["subnets"][node])
        return str(subnet[1])

    def get_node_addr_with_getaddrinfo(self, node):
        import socket
        addrinfo = socket.getaddrinfo(node, None)[0]
        return addrinfo[4][0]

    def format_config_v9(self, device, freeport):
        fmt_res =       "resource %s {\n%s%s}\n"
        buff_on = ""
        for node_id, node in enumerate(self.svc.ordered_nodes):
            disk = self.oget("disk", impersonate=node)
            addr = self.oget("addr", impersonate=node) or self.get_node_addr(node)
            port = self.oget("port", impersonate=node) or freeport
            buff_on += self.format_on(node, device, disk, addr, port, node_id=node_id)
        buff_mesh = "    connection-mesh {\n        hosts %s;\n    }\n" % " ".join(self.svc.ordered_nodes)
        buff = fmt_res % (self.res, buff_on, buff_mesh)
        return buff

    def format_config_v8(self, device, freeport):
        fmt_res =       "resource %s {\n%s}\n"
        buff_on = ""
        for node in self.svc.ordered_nodes:
            disk = self.oget("disk", impersonate=node)
            addr = self.oget("addr", impersonate=node) or self.get_node_addr(node)
            port = self.oget("port", impersonate=node) or freeport
            buff_on += self.format_on(node, device, disk, addr, port)
        buff = fmt_res % (self.res, buff_on)
        return buff

    def read_first_port(self, buff):
        for line in buff.splitlines():
            m = re.match(RE_PORT, line)
            if not m:
                continue
            return int(m.group(1))
        raise ex.Error("can not find the port in the current configuration")

    def read_first_device(self, buff):
        for line in buff.splitlines():
            m = re.match(RE_MINOR, line)
            if not m:
                continue
            return "/dev/drbd" + m.group(1)
        raise ex.Error("can not find the device in the current configuration")

    def read_next_node_id(self, buff):
        node_ids = []
        for line in buff.splitlines():
            m = re.match(RE_NODE_ID, line)
            if not m:
                continue
            node_ids.append(int(m.group(1)))
        for i in range(MAX_NODES):
            if i not in node_ids:
                return i
        raise ex.Error("can not find the device in the current configuration")

    def add_node_to_config(self, buff, node=None):
        node = node or Env.nodename
        disk = self.oget("disk")
        addr = self.oget("addr") or self.get_node_addr(node)
        port = self.oget("port") or self.read_first_port(buff)
        device = self.read_first_device(buff)
        node_id = self.read_next_node_id(buff)
        on_section = self.format_on(node, device, disk, addr, port, node_id)
        idx = buff.rindex("}")
        buff = "%s%s%s" % (buff[:idx], on_section, buff[idx:])
        buff = buff.replace("hosts ", "hosts %s " % node)
        return buff

    def node_in_config(self, buff=None, node=None):
        node = node or Env.nodename
        if buff is None:
            with open(self.cf, "r") as f:
                buff = f.read()
        pattern = r"^\s*on\s+%s\s*{" % node
        for line in buff.splitlines():
            if re.match(pattern, line):
                return True
        return False
   0707010001f352000081a40000000000000000000000016a100daf00000bcf000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/disk/__init__.py  """
Base disk resource driver module.
"""

import os

import core.status
from core.resource import Resource

KW_PRKEY = {
    "keyword": "prkey",
    "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."
}
KW_PROMOTE_RW = {
    "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."
}
KW_NO_PREEMPT_ABORT = {
    "keyword": "no_preempt_abort",
    "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, :kw:`no_preempt_abort` can be activated on a per-resource basis."
}
KW_SCSIRESERV = {
    "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, :kw:`scsireserv` can be activated on a per-resource basis."
}

BASE_KEYWORDS = [
    KW_PRKEY,
    KW_PROMOTE_RW,
    KW_NO_PREEMPT_ABORT,
    KW_SCSIRESERV,
]

class BaseDisk(Resource):
    """
    Base disk resource driver, derived for LVM, Veritas, ZFS, ...
    """

    def __init__(self, name=None, **kwargs):
        super(BaseDisk, self).__init__(**kwargs)
        self.name = name

    def __str__(self):
        return "%s name=%s" % (super(BaseDisk, self).__str__(), 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 core.status.UP
        else:
            return core.status.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
 0707010001f366000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop 0707010001f36a000081a40000000000000000000000016a100daf00000d3b000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop/linux.py    import os
import time

import core.exceptions as ex
import core.status
import utilities.devices.linux

from . import BaseDiskLoop
from env import Env
from utilities.cache import clear_cache
from utilities.lock import cmlock
from utilities.mounts.linux import Mounts
from utilities.files import getmount

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "loop"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("losetup"):
        return ["disk.loop"]
    return []

class DiskLoop(BaseDiskLoop):
    def is_up(self):
        """
        Returns True if the loop group is present and activated
        """
        self.loop = utilities.devices.linux.file_to_loop(self.loopfile)
        if len(self.loop) == 0:
            return False
        return True

    def is_volatile(self):
        mnt = getmount(self.loopfile)
        mount = Mounts().mount("tmpfs", mnt)
        if mount and mount.type == "tmpfs":
            return True
        return False

    def auto_provision(self):
        """
        If the loop file is hosted on a volatile fs, auto provision on start.
        """
        if not self.is_volatile():
            return
        self.provisioner()

    def auto_unprovision(self):
        """
        If the loop file is hosted on a volatile fs, auto unprovision on stop.
        Free the memory asap.
        """
        if not self.is_volatile():
            return
        self.unprovisioner()

    def start(self):
        if self.is_up():
            self.log.info("%s is already up" % self.label)
            return
        if not os.path.exists(self.loopfile):
            self.auto_provision()
        lockfile = os.path.join(Env.paths.pathlock, "disk.loop")
        try:
            with cmlock(timeout=30, delay=1, lockfile=lockfile):
                cmd = [Env.syspaths.losetup, '-f', self.loopfile]
                ret, out, err = self.vcall(cmd)
                clear_cache("losetup.json")
        except Exception as exc:
            raise ex.Error(str(exc))
        if ret != 0:
            raise ex.Error
        self.loop = utilities.devices.linux.file_to_loop(self.loopfile)
        if len(self.loop) == 0:
            raise ex.Error("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 = [Env.syspaths.losetup, '-d', loop]
            ret, out, err = self.vcall(cmd)
            clear_cache("losetup.json")
            if ret != 0:
                raise ex.Error
        if os.path.exists(self.loopfile):
            self.auto_unprovision()

    def _status(self, verbose=False):
        r = self.svc.resource_handling_file(self.loopfile)
        if self.is_provisioned() and not os.path.exists(self.loopfile):
            if r is None or (r and r.status() in (core.status.UP, core.status.STDBY_UP)):
                self.status_log("%s does not exist" % self.loopfile)
        if self.is_up():
            return core.status.UP
        else:
            return core.status.DOWN

    def exposed_devs(self):
        self.loop = utilities.devices.linux.file_to_loop(self.loopfile)
        return set(self.loop)
 0707010001f369000081a40000000000000000000000016a100daf000009d7000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop/freebsd.py  import os

import core.exceptions as ex
import core.status
from utilities.proc import call, which
from . import BaseDiskLoop

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "loop"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mdconfig"):
        return ["disk.loop"]
    return []


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 DiskLoop(BaseDiskLoop):
    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.Error
        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.Error

    def _status(self, verbose=False):
        r = self.svc.resource_handling_file(self.loopfile)
        if self.is_provisioned() and not os.path.exists(self.loopfile):
            if r is None or (r and r.status() in (core.status.UP, core.status.STDBY_UP)):
                self.status_log("%s does not exist" % self.loopfile)
        if self.is_up():
            return core.status.UP
        else:
            return core.status.DOWN
 0707010001f368000081a40000000000000000000000016a100daf00000768000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop/darwin.py   import os

import core.exceptions as ex
import core.status
import utilities.devices.darwin

from . import BaseDiskLoop

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "loop"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("hdiutil"):
        return ["disk.loop"]
    return []


class DiskLoop(BaseDiskLoop):
    def is_up(self):
        """Returns True if the loop group is present and activated
        """
        self.loop = utilities.devices.darwin.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.Error
        self.loop = utilities.devices.darwin.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.Error

    def _status(self, verbose=False):
        r = self.svc.resource_handling_file(self.loopfile)
        if self.is_provisioned() and not os.path.exists(self.loopfile):
            if r is None or (r and r.status() in (core.status.UP, core.status.STDBY_UP)):
                self.status_log("%s does not exist" % self.loopfile)
        if self.is_up():
            return core.status.UP
        else:
            return core.status.DOWN
0707010001f36b000081a40000000000000000000000016a100daf00000912000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop/sunos.py    import os
import time

import core.exceptions as ex
import core.status
import utilities.devices.sunos

from . import BaseDiskLoop
from utilities.lock import cmlock
from env import Env

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "loop"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("lofiadm"):
        return ["disk.loop"]
    return []


class DiskLoop(BaseDiskLoop):
    def is_up(self):
        """Returns True if the loop group is present and activated
        """
        self.loop = utilities.devices.sunos.file_to_loop(self.loopfile)
        if len(self.loop) == 0:
            return False
        return True

    def start(self):
        lockfile = os.path.join(Env.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 = ['lofiadm', '-a', self.loopfile]
                ret, out, err = self.vcall(cmd)
        except Exception as exc:
            raise ex.Error(str(exc))
        if ret != 0:
            raise ex.Error
        self.loop = utilities.devices.sunos.file_to_loop(self.loopfile)
        if len(self.loop) == 0:
            raise ex.Error("loop device did not appear or disappeared")
        time.sleep(1)
        self.log.info("%s now loops to %s" % (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 = ['lofiadm', '-d', loop]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error

    def _status(self, verbose=False):
        r = self.svc.resource_handling_file(self.loopfile)
        if self.is_provisioned() and not os.path.exists(self.loopfile):
            if r is None or (r and r.status() in (core.status.UP, core.status.STDBY_UP)):
                self.status_log("%s does not exist" % self.loopfile)
        if self.is_up():
            return core.status.UP
        else:
            return core.status.DOWN

    def exposed_devs(self):
        self.loop = utilities.devices.sunos.file_to_loop(self.loopfile)
        return set(self.loop)
  0707010001f367000081a40000000000000000000000016a100daf00000d38000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/loop/__init__.py import os
from os.path import isdir

import core.exceptions as ex
from utilities.converters import convert_size
from .. import BASE_KEYWORDS
from core.resource import Resource
from core.objects.svcdict import KEYS

KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "size",
        "at": True,
        "required": True,
        "default": "100m",
        "convert": "size",
        "text": "The size of the loop file to provision.",
        "provisioning": True
    },
    {
        "keyword": "file",
        "protoname": "loopfile",
        "at": True,
        "required": True,
        "text": "The loopback device backing file full absolute path."
    },
]
DEPRECATED_SECTIONS = {
    "loop": ["disk", "loop"],
}

KEYS.register_driver(
    "disk",
    "loop",
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
)


class BaseDiskLoop(Resource):
    """
    Base loopback device resource
    """

    def __init__(self, loopfile=None, size=None, **kwargs):
        super(BaseDiskLoop, self).__init__(type="disk.loop", **kwargs)
        self.loopfile = loopfile
        self.size = size
        self.label = "loop %s" % loopfile

    def _info(self):
        return [["file", self.loopfile]]

    def __str__(self):
        return "%s loopfile=%s" % (super(BaseDiskLoop, self).__str__(), self.loopfile)

    def chown(self):
        if os.name != 'posix':
            return
        self.log.info("chown 0:0 %s", self.loopfile)
        os.chown(self.loopfile, 0, 0)

    def chmod(self):
        if os.name != 'posix':
            return
        self.log.info("chmod 600 %s", self.loopfile)
        os.chmod(self.loopfile, 0o0600)

    def provisioned(self):
        try:
            return os.path.exists(self.loopfile)
        except Exception:
            return False

    def unprovisioner(self):
        try:
            self.loopfile
        except Exception as e:
            raise ex.Error(str(e))

        if not self.provisioned():
            return

        if isdir(self.loopfile):
            raise ex.Error("unprovision loop file is not allowed on directory: %s" % self.loopfile)

        self.log.info("unlink %s" % self.loopfile)
        try:
            os.unlink(self.loopfile)
        except Exception as e:
            raise ex.Error("unlink %s: %s"% (self.loopfile, str(e)))
        self.svc.node.unset_lazy("devtree")

    def provisioner(self):
        self._check_loopfile_path()
        d = os.path.dirname(self.loopfile)
        try:
            if not os.path.exists(d):
                self.log.info("create directory %s"%d)
                os.makedirs(d)
            with open(self.loopfile, 'w') as f:
                self.log.info("create file %s, size %s"%(self.loopfile, self.size))
                f.seek(convert_size(self.size, _to='b', _round=512)-1)
                f.write('\0')
            self.chown()
            self.chmod()
        except Exception as e:
            raise ex.Error("failed to create %s: %s"% (self.loopfile, str(e)))
        self.svc.node.unset_lazy("devtree")

    def _check_loopfile_path(self):
        path = self.loopfile
        if not isinstance(path, str):
            raise ex.Error("Resource loop file path must be absolute: found %s" % path)
        if not path.startswith(os.sep):
            raise ex.Error("Resource loop file path must be absolute: found %s" % path)
0707010001f372000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/rados    0707010001f373000081a40000000000000000000000016a100daf000027d5000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/rados/__init__.py    import json
import os

import core.status
import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from utilities.converters import convert_size
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "rados"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "client_id",
        "text": "Client id to use for authentication with the rados servers."
    },
    {
        "keyword": "keyring",
        "text": "keyring to look for the client id secret for authentication with the rados servers."
    },
    {
        "keyword": "lock",
        "candidates": ["exclusive", "shared", "None"],
        "text": "Locking mode for the rados images"
    },
    {
        "keyword": "lock_shared_tag",
        "depends": [('lock', ['shared'])],
        "text": "The tag to use upon rados image locking in shared mode"
    },
    {
        "keyword": "image_format",
        "provisioning": True,
        "default": "2",
        "text": "The rados image format"
    },
    {
        "keyword": "size",
        "convert": "size",
        "provisioning": True,
        "text": "The block device size in size expression format."
    },
    {
        "keyword": "images",
        "convert": "list",
        "required": True,
        "text": "The rados image names handled by this vg resource. whitespace separated."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("rbd"):
        return ["disk.rados"]
    return []


class DiskRados(BaseDisk):
    def __init__(self,
                 type="disk.rados",
                 images=None,
                 client_id=None,
                 keyring=None,
                 lock_shared_tag=None,
                 lock=None,
                 image_format=None,
                 size=None,
                 **kwargs):
        super(DiskRados, self).__init__(type=type, **kwargs)
        self.images = images or set()
        self.keyring = keyring
        if not client_id.startswith("client."):
            client_id = "client."+client_id
        self.client_id = client_id
        self.image_format = image_format
        self.size = size
        self.lock = lock
        self.lock_shared_tag = lock_shared_tag
        self.label = self.fmt_label()
        self.modprobe_done = False

    def on_add(self):
        if self.lock:
            kwargs = {
                "rid": self.rid + "lock",
                "lock": self.lock,
                "lock_shared_tag": self.lock_shared_tag,
            }
            r = DiskRadoslock(**kwargs)
            self.svc += r

    def validate_image_fmt(self):
        l = []
        for image in self.images:
            if "/" not in image:
                l.append(image)
        if len(l):
            raise ex.Error("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 = [Env.syspaths.lsmod]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.Error("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.Error("failed to load rbd device driver")
        self.modprobe_done = True

    def showmapped(self, refresh=False):
        if not refresh:
            try:
                return getattr(self, "mapped_data")
            except AttributeError:
                pass
        self.modprobe()
        cmd = ["rbd", "showmapped", "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("rbd showmapped failed: "+err)
        try:
            _data = json.loads(out)
        except Exception as e:
            raise ex.Error(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 core.status.WARN
        l = self.up_count()
        n = len(l)
        unmapped = sorted(list(set(self.images) - set(l)))
        if n == len(self.images):
            return core.status.UP
        elif n == 0:
            return core.status.DOWN
        else:
            self.status_log("unmapped: "+", ".join(unmapped))
            return core.status.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.Error("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.Error("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 DiskRadoslock(DiskRados):
    def __init__(self, lock=None, lock_shared_tag=None, **kwargs):
        super(DiskRadoslock, self).__init__(type="disk.radoslock", **kwargs)
        self.lock = lock
        self.lock_shared_tag = lock_shared_tag
        self.label = self.fmt_label()
        self.unlocked = []

    def fmt_label(self):
        return "%s lock on %s" % (
            self.lock,
            super(DiskRadoslock, self).fmt_label()
        )

    def locklist(self, image):
        cmd = self.rbd_rcmd()+["lock", "list", image, "--format", "json"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("rbd lock list failed")
        data = {}
        try:
            data = json.loads(out)
        except Exception as e:
            raise ex.Error(str(e))
        return data


    def has_lock(self, image):
        data = self.locklist(image)
        if Env.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 core.status.UP
        elif n == 0:
            return core.status.DOWN
        else:
            self.status_log("unlocked: "+", ".join(self.unlocked))
            return core.status.DOWN

    def do_stop_one(self, image):
        data = self.locklist(image)
        if Env.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, Env.nodename, data[Env.nodename]["locker"]]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error("failed to unlock %s"%self.devname(image))
            data = self.locklist(image)

    def do_start_one(self, image):
        data = self.locklist(image)
        if Env.nodename in data:
            self.log.info(image+" is already locked")
            return
        cmd = self.rbd_rcmd()+["lock", "add", image, Env.nodename]
        if self.lock_shared_tag:
            cmd += ["--shared", self.lock_shared_tag]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error("failed to lock %s"%self.devname(image))

    def provisioner(self):
        for image in self.images:
            self.provisioner_one(image)
        self.log.info("provisioned")
        self.start()
        self.svc.node.unset_lazy("devtree")

    def provisioner_one(self, image):
        if self.exists(image):
            self.log.info("%s already provisioned"%image)
            return
        size = convert_size(self.size, _to="m")
        cmd = self.rbd_rcmd() + ['create', '--size', str(size), image]
        if self.image_format:
            cmd += ["--image-format", str(self.image_format)]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.svc.node.unset_lazy("devtree")

   0707010001f36c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/lv   0707010001f36e000081a40000000000000000000000016a100daf00000538000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/disk/lv/hpux.py   import core.exceptions as ex

from . import BaseDiskLv
from utilities.converters import convert_size
from utilities.proc import justcall, which

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "lv"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("vgdisplay") and which("lvcreate"):
        return ["disk.lv"]
    return []

class DiskLv(BaseDiskLv):
    def provisioner(self):
        if not which('vgdisplay'):
            self.log.error("vgdisplay command not found")
            raise ex.Error

        if not which('lvcreate'):
            self.log.error("lvcreate command not found")
            raise ex.Error

        if self.vg is None:
            raise ex.Error("skip lv provisioning: vg is not set")

        if self.size is None:
            raise ex.Error("skip lv provisioning: size is not set")

        size = convert_size(self.size, _to="m")
        vg = self.vg

        cmd = ['vgdisplay', vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.log.error("volume group %s does not exist" % vg)
            raise ex.Error

        # create the logical volume
        cmd = ['lvcreate', '-n', self.name, '-L', str(size)+'M', vg]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

        self.svc.node.unset_lazy("devtree")

0707010001f36f000081a40000000000000000000000016a100daf000013c3000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/disk/lv/linux.py  import os
import signal
import time

from subprocess import *

import core.exceptions as ex

from . import BaseDiskLv
from utilities.converters import convert_size
from env import Env
from utilities.proc import justcall, which
from utilities.string import bdecode

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "lv"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("lvchange"):
        return ["disk.lv"]
    return []

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 DiskLv(BaseDiskLv):
    def get_dev(self):
        """
        Return the device path in the /dev/<vg>/<lv> format
        """
        return "/dev/" + self.fullname

    def activate(self, dev):
        if not which('lvchange'):
            self.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.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def unprovisioner(self):
        if self.vg is None:
            self.log.debug("skip lv unprovision: no vg option")
            return

        if not which('lvdisplay'):
            self.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.log.debug("skip lv unprovision: %s is not a lv" % dev)
            return

        if not which('lvremove'):
            self.log.error("lvcreate command not found")
            raise ex.Error

        if which('wipefs') and os.path.exists(dev):
            self.vcall(["wipefs", "-a", dev])

        cmd = ["lvremove", "-f", dev]
        ret, out, err = self.vcall(cmd)
        self.clear_cache("lvs.attr")
        if ret != 0:
            raise ex.Error
        self.svc.node.unset_lazy("devtree")

    def provisioned(self):
        dev = self.get_dev()
        cmd = ["lvdisplay", dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        return False

    def provisioner(self):
        if not which('vgdisplay'):
            raise ex.Error("vgdisplay command not found")

        if not which('lvcreate'):
            raise ex.Error("lvcreate command not found")

        if not which('lvdisplay'):
            raise ex.Error("lvdisplay command not found")

        if self.vg is None:
            raise ex.Error("skip lv provisioning: vg is not set")

        dev = self.get_dev()

        try:
            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]
        except Exception as e:
            self.log.info("skip lv provisioning: %s" % str(e))
            return

        cmd = ['vgdisplay', self.vg]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("volume group %s does not exist" % self.vg)

        lvname = os.path.basename(dev)

        # create the logical volume
        cmd = ['lvcreate', '-n', lvname] + size_parm + self.create_options + [self.vg]
        _cmd = "yes | " + " ".join(cmd)
        self.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)
        self.clear_cache("lvs.attr")
        if p2.returncode != 0:
            raise ex.Error(err)
        self.can_rollback = True
        if len(out) > 0:
            for line in out.splitlines():
                self.log.info(line)
        if len(err) > 0:
            for line in err.splitlines():
                if line.startswith("WARNING:"):
                    self.log.warning(line.replace("WARNING: ", ""))
                else:
                    self.log.error(err)

        # /dev/mapper/$vg-$lv and /dev/$vg/$lv creation is delayed ... refresh
        try:
            cmd = [Env.syspaths.dmsetup, 'mknodes']
            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
            p.communicate()
        except:
            # best effort
            pass
        mapname = "%s-%s" % (self.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.log.error("timed out waiting for %s to appear"%dev)
            raise ex.Error

        self.svc.node.unset_lazy("devtree")

    def boot(self):
        self.do_stop()
 0707010001f36d000081a40000000000000000000000016a100daf000013ee000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/disk/lv/__init__.py   import os
import re

import core.exceptions as ex
import utilities.devices.linux

from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which
from utilities.subsystems.lvm.linux import get_lvs_attr


DRIVER_GROUP = "disk"
DRIVER_BASENAME = "lv"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "required": True,
        "at": True,
        "text": "The name of the logical volume.",
        "example": "lv1"
    },
    {
        "keyword": "vg",
        "at": True,
        "text": "The name of the volume group hosting the logical volume.",
        "example": "vg1"
    },
    {
        "keyword": "size",
        "convert": "size",
        "at": True,
        "provisioning": True,
        "text": "The size of the logical volume to provision. A size expression or <n>%{FREE|PVS|VG}.",
        "example": "10m"
    },
    {
        "keyword": "create_options",
        "convert": "shlex",
        "default": [],
        "at": True,
        "provisioning": True,
        "text": "Additional options to pass to the logical volume create command (:cmd:`lvcreate` or :cmd:`vxassist`, depending on the driver). Size and name are alread set.",
        "example": "--contiguous y"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class BaseDiskLv(BaseDisk):
    def __init__(self,
                 vg=None,
                 size=None,
                 create_options=None,
                 **kwargs):
        super(BaseDiskLv, self).__init__(type='disk.lv', **kwargs)
        self.fullname = "%s/%s" % (vg, self.name)
        self.label = "lv %s" % self.fullname
        self.vg = vg
        self.size = size
        self.create_options = create_options or []
        self.refresh_provisioned_on_provision = True
        self.refresh_provisioned_on_unprovision = True

    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):
        data = get_lvs_attr()
        lv_attr = data.get(self.vg, {}).get(self.name)
        return lv_attr

    def activate_lv(self):
        cmd = ['lvchange', '-a', 'y', self.fullname]
        ret, out, err = self.vcall(cmd)
        self.clear_cache("lvs.attr")
        if ret != 0:
            raise ex.Error

    def deactivate_lv(self):
        cmd = ['lvchange', '-a', 'n', self.fullname]
        ret, out, err = self.vcall(cmd, err_to_info=True)
        self.clear_cache("lvs.attr")
        if ret != 0:
            raise ex.Error

    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.Error("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()
        utilities.devices.linux.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):
        lvp = "/dev/%s" % self.fullname
        return set([lvp])

  0707010001f355000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/amazon   0707010001f356000081a40000000000000000000000016a100daf00002951000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/amazon/__init__.py   import os
import glob
import time

import core.status
import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from core.objects.svcdict import KEYS
from utilities.subsystems.amazon import AmazonMixin

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "amazon"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "volumes",
        "convert": "list",
        "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"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if not which("ec2"):
        return data
    if os.path.exists("/sys/hypervisor/uuid"):
        with open("/sys/hypervisor/uuid", "r") as f:
           uuid = f.read().lower()
        if not uuid.startswith("ec2"):
           return data
    if os.path.exists("/sys/devices/virtual/dmi/id/product_uuid"):
        with open("/sys/devices/virtual/dmi/id/product_uuid", "r") as f:
           uuid = f.read().lower()
        if not uuid.startswith("ec2"):
           return data
    data.append("ip.amazon")
    return data


class DiskAmazon(BaseDisk, AmazonMixin):
    def __init__(self,
                 volumes=None,
                 client_id=None,
                 keyring=None,
                 **kwargs):

        BaseDisk.__init__(self, type="disk.amazon", **kwargs)

        self.volumes = volumes or set()
        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.Error("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.Error("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.Error("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.Error("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.Error("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.Error("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 core.status.WARN
        l = self.up_count()
        n = len(l)
        unmapped = sorted(list(set(self.volumes) - set(l)))
        if n == len(self.volumes):
            return core.status.UP
        elif n == 0:
            return core.status.DOWN
        else:
            self.status_log("unattached: "+", ".join(unmapped))
            return core.status.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.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

    def provisioner(self):
        for volume in self.volumes:
            volumes_done = self._provisioner(volume)
        volumes = ' '.join(volumes_done)
        self.svc.set_multi(["%s.volumes=%s" % (self.rid, volumes)])
        self.volumes = volumes_done
        self.log.info("provisioned")
        self.start()
        self.svc.node.unset_lazy("devtree")

    def _provisioner(self, volume):
        volumes_done = []
        if not volume.startswith("<") and not volume.endswith(">"):
            self.log.info("volume %s already provisioned" % volume)
            volumes_done.append(volume)
            return

        s = volume.strip("<>")
        v = s.split(",")
        kwargs = {}
        for e in v:
            try:
                key, val = e.split("=")
            except:
                raise ex.Error("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.get_instance_data()
            availability_zone = node["Placement"]["AvailabilityZone"]
            cmd += ["--availability-zone", availability_zone]
        data = self.aws(cmd)
        self.wait_avail(data["VolumeId"])
        volumes_done.append(data["VolumeId"])
        self.svc.node.unset_lazy("devtree")
        return volumes_done


   0707010001f379000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv   0707010001f37e000081a40000000000000000000000016a100daf0000178a000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/osf1.py   import os
from subprocess import *

import core.exceptions as ex
from utilities.proc import which
from . import BaseDiskScsireserv

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "scsireserv"


class DiskScsireserv(BaseDiskScsireserv):
    def __init__(self, **kwargs):
        super(DiskScsireserv, self).__init__(**kwargs)
        self.prtype = "wero"
        self.disk_id = {}
        self.itn = {}

    def get_devs(self):
        if len(self.devs) > 0:
            return
        for dev in self.peer_resource.sub_devs():
            dev = dev.replace('/disk/', '/rdisk/')
            self.devs[dev] = [dev]

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

    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.Error 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.Error 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
  0707010001f37b000081a40000000000000000000000016a100daf00000117000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/freebsd.py    import utilities.devices.linux

from .sg import DiskScsireservSg

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "scsireserv"

class DiskScsireserv(DiskScsireservSg):
    def mangle_devs(self, devs):
        return dict((dev, utilities.devices.linux.dev_to_paths(dev)) for dev in devs)
 0707010001f37d000081a40000000000000000000000016a100daf0000035c000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/linux.py  import core.status
import utilities.devices.linux

from .sg import DiskScsireservSg, driver_capabilities

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "scsireserv"
assert driver_capabilities  # ensure driver scan its capabilities


class DiskScsireserv(DiskScsireservSg):
    def mangle_devs(self, devs):
        return dict((dev, utilities.devices.linux.dev_to_paths(dev)) for dev in devs)

    def _status(self, verbose=False):
        ret = super(DiskScsireserv, self)._status(verbose=verbose)
        if ret != core.status.UP:
            return ret
        self.get_devs()
        for dev, paths in self.devs.items():
            for path in paths:
                if utilities.devices.linux.dev_is_ro(path):
                    self.status_log("resv held on ro dev %s in %s" % (path, dev), "info")
                    return core.status.NA
        return ret
0707010001f37c000081a40000000000000000000000016a100daf000011fe000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/hpux.py   import os
from subprocess import *

import core.exceptions as ex
from utilities.proc import which
from . import BaseDiskScsireserv

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "scsireserv"


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 DiskScsireserv(BaseDiskScsireserv):
    def __init__(self, **kwargs):
        super(DiskScsireserv, self).__init__(**kwargs)
        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.Error
        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)
        p.communicate()
        ret = p.returncode
        if ret != 0:
            self.log.error("can not set 'leg_mpath_enable' value")
            raise ex.Error

    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
  0707010001f37a000081a40000000000000000000000016a100daf000027b7000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/__init__.py   import os
import time

import core.exceptions as ex
import core.status
from core.resource import Resource


class BaseDiskScsireserv(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 = {}
        self.prtype = '5'
        self.hostid = None
        self.peer_resource = peer_resource
        self.prkey = prkey
        super(BaseDiskScsireserv, self).__init__(rid=rid+"pr", type="disk.scsireserv", **kwargs)
        self.sort_key = rid


    def mangle_devs(self, devs):
        """
        Can be overidden by child class to apply a mangling the peer
        resource devices
        """
        return dict((dev, [dev]) for dev in devs)


    def set_label(self):
        self.get_devs()
        if len(self.devs) == 0:
            self.label = 'preserv 0 scsi disk'
        elif len(', '.join(sorted(self.devs))) > 248:
            self.label = 'preserv '+', '.join(sorted(self.devs))[0:248]
            self.label += " ..."
        else:
            self.label = ', '.join(sorted(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.Error(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.MissImpl


    def disk_registered(self, disk):
        raise ex.MissImpl


    def disk_register(self, disk):
        raise ex.MissImpl


    def disk_unregister(self, disk):
        raise ex.MissImpl


    def get_reservation_key(self, disk):
        raise ex.MissImpl
        return


    def disk_reserved(self, disk):
        raise ex.MissImpl


    def disk_release(self, disk):
        raise ex.MissImpl


    def disk_reserve(self, disk):
        raise ex.MissImpl


    def _disk_preempt_reservation(self, disk, oldkey):
        raise ex.MissImpl


    def disk_preempt_reservation(self, disk, oldkey):
        return self._disk_preempt_reservation(disk, oldkey)

    def get_devs(self):
        if len(self.devs) > 0:
            return
        peer_sub_devs = self.peer_resource.sub_devs()
        self.devs = self.mangle_devs(peer_sub_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.ScsiPrNotsupported 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()
        self.ack_all_unit_attention()
        r = 0
        for d in self.devs:
            try:
                r += self.disk_register(d)
            except ex.ScsiPrNotsupported as exc:
                self.log.warning(str(exc))
                continue
        return r


    def unregister(self):
        self.log.debug("starting unregister. prkey %s"%self.hostid)
        self.get_devs()
        self.ack_all_unit_attention()
        r = 0
        for d in self.devs:
            try:
                if not self.disk_registered(d):
                    continue
                r += self.disk_unregister(d)
            except ex.ScsiPrNotsupported as exc:
                self.log.warning(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 safety(self):
        if self.svc.options.force or os.environ.get("OSVC_ACTION_ORIGIN") == "daemon":
            return
        self.log.debug("starting safety. prkey %s"%self.hostid)
        self.get_devs()
        self.ack_all_unit_attention()
        for d in self.devs:
            try:
                key = self.get_reservation_key(d) # pylint: disable=assignment-from-none
            except ex.ScsiPrNotsupported as exc:
                self.log.warning(str(exc))
                continue
            if key is None or key == self.hostid:
                continue
            self.log.error("%s is already reserved. use --force to override this safety net", d)
            raise ex.Error


    def reserve(self):
        self.log.debug("starting reserve. prkey %s"%self.hostid)
        self.get_devs()
        self.ack_all_unit_attention()
        r = 0
        for d in self.devs:
            try:
                key = self.get_reservation_key(d) # pylint: disable=assignment-from-none
                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.ScsiPrNotsupported as exc:
                self.log.warning(str(exc))
                continue
        return r


    def release(self):
        self.log.debug("starting release. prkey %s"%self.hostid)
        self.get_devs()
        self.ack_all_unit_attention()
        r = 0
        for d in self.devs:
            try:
                if not self.disk_reserved(d):
                    continue
                r += self.disk_release(d)
            except ex.ScsiPrNotsupported as exc:
                self.log.warning(str(exc))
                continue
        return r


    def clear(self):
        self.log.debug("starting clear. prkey %s"%self.hostid)
        self.get_devs()
        self.ack_all_unit_attention()
        r = 0
        for d in self.devs:
            try:
                if self.disk_reserved(d):
                    r += getattr(self, "disk_clear_reservation")(d)
                elif self.disk_registered(d):
                    r += self.disk_unregister(d)
            except ex.ScsiPrNotsupported as exc:
                self.log.warning(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 core.status.WARN
        r = core.status.Status("n/a")
        for d in self.devs:
            try:
                key = self.get_reservation_key(d) # pylint: disable=assignment-from-none
                if key is None:
                    self.log.debug("disk %s is not reserved" % d)
                    r += core.status.DOWN
                elif key != self.hostid:
                    self.log.debug("disk %s is reserved by another host whose key is %s" % (d, key))
                    r += core.status.DOWN
                else:
                    self.log.debug("disk %s is correctly reserved" % d)
                    r += core.status.UP
            except ex.ScsiPrNotsupported as exc:
                self.status_log("%s: pr not supported" % d)
            except ex.Error as exc:
                self.status_log(str(exc))
        return r.status


    def scsireserv(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return 0
        self.safety()
        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 check_all_paths_registered(self):
        pass


    def _status(self, verbose=False):
        self.set_label()
        try:
            self.get_hostid()
        except Exception as e:
            self.status_log(str(e))
            return core.status.WARN
        if not self.scsireserv_supported():
            self.status_log("scsi reservation is not supported")
            return core.status.NA
        try:
            self.check_all_paths_registered()
        except ex.Signal as exc:
            self.status_log(str(exc))
        except ex.Error as exc:
            self.status_log(str(exc))
            return core.status.WARN
        return self.checkreserv()


    def start(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        if hasattr(self.peer_resource, "rescan_sub_devs"):
            self.peer_resource.rescan_sub_devs()
        if self._status() == core.status.UP:
            self.log.info("already started")
            return
        self.can_rollback = True
        if self.scsireserv() != 0:
            raise ex.Error

    def post_provision_start(self):
        def start_and_check():
            self.start()
            time.sleep(5)
            if self._status() in (core.status.UP, core.status.NA):
                self.log.info("registration and reservation stability check passed")
                return True
            return False
        retries = 5
        self.wait_for_fn(
            lambda: start_and_check(),
            retries, 1,
            errmsg="not registered after %d registrations attempts" % retries
        )

    def stop(self):
        self.get_hostid()
        if not self.scsireserv_supported():
            return
        if self.scsirelease() != 0:
            raise ex.Error


    def boot(self):
        self.stop()


    def is_provisioned(self, refresh=False):
        return True
 0707010001f380000081a40000000000000000000000016a100daf000000e5000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sunos.py  from .sg import DiskScsireservSg, driver_capabilities

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "scsireserv"
assert driver_capabilities  # ensure driver can scan its capabilities


class DiskScsireserv(DiskScsireservSg):
    pass
   0707010001f37f000081a40000000000000000000000016a100daf00003d00000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/disk/scsireserv/sg.py import os
import time
import re

import core.exceptions as ex
from core.capabilities import capabilities
from env import Env
from utilities.proc import justcall, which
from . import BaseDiskScsireserv

# maximum number of unit attention to read from a device during ack_unit_attention()
ACK_UNIT_ATTENTION_RETRY_MAX = 10

# delay to wait before retry ack unit attention on a device that reports unit attention
ACK_UNIT_ATTENTION_RETRY_DELAY = 0.1


def mpathpersist_enabled_in_conf(output):
    for conf_line in output.splitlines():
        if re.search(r'^\s*reservation_key\s+("|)?file\1\s*$', conf_line) is not None:
            return True
    return False


def parse_version(buff):
    for line in buff.splitlines():
        try:
            return [int(v) for v in line.split()[1].strip("v").split(".")]
        except Exception:
            continue
    return [0, 0, 0]

# noinspection PyUnusedLocal
def driver_capabilities(node=None):
    data = []
    if which("sg_persist"):
        data.append("disk.scsireserv")
        data.append("disk.scsireserv.sg_persist")
    if which("mpathpersist"):
        _, err, ret = justcall(["multipath", "-h"])
        version = parse_version(err)
        if version > [0, 7, 8]:
            def multipath_get_conf():
                conf_output, _, exit_code = justcall(["multipathd", "show", "config"])
                if exit_code == 0:
                    return conf_output
                # fallback to config file if multipathd is not running yet
                conf_output, _, exit_code = justcall(["multipath", "-t"])
                if exit_code == 0:
                    return conf_output
                return ""

            if mpathpersist_enabled_in_conf(multipath_get_conf()):
                data.append("disk.scsireserv")
                data.append("disk.scsireserv.mpathpersist")
    return data


class DiskScsireservSg(BaseDiskScsireserv):
    def scsireserv_supported(self):
        if not self.has_capability("disk.scsireserv"):
            self.status_log("sg_persist or mpathpersist must be installed to use scsi-3 reservations")
            return False
        return True

    @staticmethod
    def set_read_only(val):
        if Env.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
        if self.use_mpathpersist(d):
            return 0
        i = ACK_UNIT_ATTENTION_RETRY_MAX
        self.set_read_only(0)
        while i > 0:
            i -= 1
            cmd = ["sg_persist", "-n", "-r", d]
            out, err, ret = justcall(cmd)
            if "unsupported service action" in err:
                raise ex.ScsiPrNotsupported("disk %s does not support persistent reservation" % d)
            if "Not ready" in err:
                # huawei dorado hypermetropair paused member set that.
                raise ex.ScsiPrNotsupported("disk %s Not Ready" % 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(ACK_UNIT_ATTENTION_RETRY_DELAY)
                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 get_mpath_registrations(self, path, paths):
        ret, out, err = self.read_mpath_registrations(path)
        n_expected = self.get_expected_registration_count(path, paths)
        n_registered = out.count(self.hostid)
        return n_expected, n_registered

    def read_mpath_registrations(self, disk):
        if not os.path.exists(disk):
            return 1, "", ""
        self.set_read_only(1)
        cmd = ["mpathpersist", "-i", "-k", disk]
        ret, out, err = self.call(cmd)
        return ret, out, err

    def get_path_registrations(self, path, paths):
        self.ack_unit_attention(path)
        ret, out, err = self.read_path_registrations(path)
        if ret != 0:
            return 0, 0
        n_expected = self.get_expected_registration_count(path, paths)
        n_registered = out.count(self.hostid)
        return n_expected, n_registered

    def get_expected_registration_count(self, path, paths):
        if Env.sysname != "Linux":
            return len(paths)
        try:
            from utilities.diskinfo import DiskInfo
            if DiskInfo(deferred=True).disk_vendor(path).strip() != "3PARdata":
                return len(paths)
            if DiskInfo(deferred=True).disk_model(path).strip() != "VV":
                return len(paths)
        except Exception:
            return len(paths)

        # Here we known the mpath is served by a 3PARdata array.
        # This array show one registration per I_L nexus, instead of the
        # standard per I_T_L nexus. So the expected registration count is
        # the number of unique I (hostid) of the paths of L. 

        hostids = set()
        # example:
        #  ["/dev/sdy", "/dev/sdaf", "/dev/sdk", "/dev/sdr"]
        for p in paths:
            sysdev = os.path.realpath("/sys/block/%s/device"%os.path.basename(p))
            # => /sys/block/sdy/device  (symlink to ../../../3:0:0:172/)

            t = os.path.basename(sysdev)
            # => "3:0:0:172"

            hostid = t.split(":")[0]
            # => "3"

            hostids.add(hostid)
        # set("3", "4")
        return len(hostids)

    def read_path_registrations(self, disk):
        if not os.path.exists(disk):
            return 1, "", ""
        self.set_read_only(1)
        cmd = ["sg_persist", "-n", "-k", disk]
        ret, out, err = self.call(cmd)
        return ret, out, err

    def read_registrations(self):
        n_expected = 0
        n_registered = 0
        for mpath, paths in self.devs.items():
            n_paths = len(paths)
            if self.use_mpathpersist(mpath):
                i, j = self.get_mpath_registrations(mpath, paths)
                n_expected += i
                n_registered += j
            else:
                for path in paths:
                    i, j = self.get_path_registrations(path, paths)
                    if not i and not j:
                        continue
                    n_expected += i
                    n_registered += j
                    break
        return n_expected, n_registered

    def check_all_paths_registered(self):
        n_paths, n_registered = self.read_registrations()
        if n_registered == n_paths:
            return
        if n_registered == 0:
            return
        if n_registered > n_paths:
            raise ex.Signal("%d/%d paths registered" % (n_registered, n_paths))
        raise ex.Error("%d/%d paths registered" % (n_registered, n_paths))

    def disk_registered(self, disk):
        ret, out, err = self.read_path_registrations(disk)
        if ret != 0:
            self.log.error("failed to read registrations for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def use_mpathpersist(self, disk):
        if not self.has_capability("disk.scsireserv.mpathpersist"):
            return False
        if [disk] != self.devs.get(disk, []):
            return True
        return False

    def disk_register(self, disk):
        if self.use_mpathpersist(disk):
            return self.mpath_register(disk)
        else:
            ret = 0
            for path in self.devs[disk]:
                ret += self.path_register(path)
            return ret

    def mpath_register(self, disk):
        self.set_read_only(0)
        cmd = ["mpathpersist", "--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 path_register(self, disk):
        self.set_read_only(0)
        # Need ack unit attention from possible previous register-ignore on other paths
        # example:
        # Persistent reservation out:
        #  Fixed format, current; Sense key: Unit Attention
        #  Additional sense: Registrations preempted
        #  PR out (Register and ignore existing key): Unit attention
        self.ack_unit_attention(disk)
        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):
        if self.use_mpathpersist(disk):
            return self.mpath_unregister(disk)
        else:
            ret = 0
            for path in self.devs[disk]:
                ret += self.path_unregister(path)
            return ret

    def mpath_unregister(self, disk):
        self.set_read_only(0)
        cmd = ["mpathpersist", "--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 path_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

    @staticmethod
    def dev_to_mpath_dev(devpath):
        if "node.x.multipath" not in capabilities:
            return devpath
        cmd = [Env.syspaths.multipath, "-l", "-v1", devpath]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        _devpath = "/dev/mapper/" + out.strip()
        if not out:
            raise ex.Error()
        if not os.path.exists(_devpath):
            raise ex.Error("%s does not exist" % _devpath)
        return _devpath

    def get_reservation_key(self, disk):
        try:
            return self._get_reservation_key(disk)
        except ex.Error:
            disk = self.dev_to_mpath_dev(disk)
            return self._get_reservation_key(disk)

    def _get_reservation_key(self, disk):
        self.set_read_only(1)
        if self.use_mpathpersist(disk):
            cmd = ["mpathpersist", "-i", "-r", disk]
        else:
            cmd = ["sg_persist", "-n", "-r", disk]
        ret, out, err = self.call(cmd, errlog=None)
        if ret != 0:
            raise ex.Error("failed to list reservation for disk %s" % disk)
        if "Key=" in out:
            # sg_persist format
            for w in out.split():
                if "Key=" in w:
                    return w.split("=")[1]
        elif "Key = " in out:
            # mpathpersist format
            return out.split("Key = ")[-1].split()[0]
        else:
            return None
        raise Exception()

    def disk_reserved(self, disk):
        try:
            return self._disk_reserved(disk)
        except ex.Error:
            disk = self.dev_to_mpath_dev(disk)
            return self._disk_reserved(disk)

    def _disk_reserved(self, disk):
        self.set_read_only(1)
        if self.use_mpathpersist(disk):
            cmd = ["mpathpersist", "-i", "-r", disk]
        else:
            cmd = ["sg_persist", "-n", "-r", disk]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.Error("failed to read reservation for disk %s" % disk)
        if self.hostid in out:
            return True
        return False

    def disk_release(self, disk):
        if self.use_mpathpersist(disk):
            return self.mpath_release(disk)
        else:
            return self.path_release(disk)

    def mpath_release(self, disk):
        self.set_read_only(0)
        cmd = ["mpathpersist", "--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 path_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):
        if self.use_mpathpersist(disk):
            return self.mpath_clear_reservation(disk)
        else:
            ret = self.path_clear_reservation(disk)
            if ret == 24:
                self.log.warning("clear %s failed, will try clear on sub devs" % disk)
                for path in self.devs[disk]:
                    sub_ret = self.path_clear_reservation(path)
                    if sub_ret != 0:
                        self.log.warning("clear %s sub device %s failed" % (disk, path))
                        continue
                    return sub_ret
            return ret

    def mpath_clear_reservation(self, disk):
        cmd = ["mpathpersist", "--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 path_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):
        if self.use_mpathpersist(disk):
            return self.mpath_reserve(disk)
        else:
            return self.path_reserve(disk)

    def mpath_reserve(self, disk):
        self.set_read_only(0)
        cmd = ["mpathpersist", "--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 path_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):
        from utilities.diskinfo import DiskInfo
        if self.no_preempt_abort or DiskInfo(deferred=True).disk_vendor(disk).strip() in ["VMware"]:
            preempt_opt = "--preempt"
        else:
            preempt_opt = "--preempt-abort"
        self.set_read_only(0)
        if self.use_mpathpersist(disk):
            cmd = ["mpathpersist", "--out", preempt_opt, "--param-sark=" + oldkey, "--param-rk=" + self.hostid,
                   "--prout-type=" + self.prtype, disk]
        else:
            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
0707010001f38a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vxvol    0707010001f38b000081a40000000000000000000000016a100daf00001a56000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vxvol/__init__.py    import re
import os
from collections import namedtuple

import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.cache import cache
from utilities.converters import convert_size
from utilities.proc import justcall, which

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vxvol"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the logical volume group"
    },
    {
        "keyword": "vg",
        "at": True,
        "text": "The name of the volume group hosting the logical volume.",
        "example": "vg1"
    },
    {
        "keyword": "create_options",
        "convert": "shlex",
        "default": [],
        "at": True,
        "provisioning": True,
        "text": "Additional options to pass to the logical volume create command (:cmd:`lvcreate` or :cmd:`vxassist`, depending on the driver). Size and name are alread set.",
        "example": "--contiguous y"
    },
    {
        "keyword": "size",
        "convert": "size",
        "provisioning": True,
        "text": "The logical volume size, in size expression format."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("vxvol"):
        return ["disk.vxvol"]
    return []


class DiskVxvol(BaseDisk):
    def __init__(self, vg=None, create_options=None, size=None, **kwargs):
        super(DiskVxvol, self).__init__(type='disk.vxvol', **kwargs)
        self.fullname = "%s/%s" % (vg, self.name)
        self.label = "vxvol %s" % self.fullname
        self.vg = vg
        self.devpath  = "/dev/vx/dsk/%s/%s" % (self.vg, self.name)
        self.create_options = create_options
        self.size = size

    def _info(self):
        data = [
          ["name", self.name],
          ["vg", self.vg],
        ]
        return data

    def vxprint(self):
        cmd = ["vxprint", "-g", self.vg, "-v"]
        out, err, ret = justcall(cmd)
        if ret == 11:
            # no lv
            return {}
        if ret != 0:
            raise ex.Error(err)
        data = {}
        headers = list()
        for line in out.splitlines():
            words = line.split()
            if len(words) < 7:
                continue
            if words[0] == "TY":
                headers = list(words)
                continue
            lv = namedtuple("lv", headers)._make(words)
            data[getattr(lv, "NAME")] = lv
        return data

    def has_it(self):
        return self.name in self.vxprint()

    def is_up(self):
        """
        Returns True if the logical volume is present and activated
        """
        data = self.vxprint()
        return self.name in data and data[self.name].STATE == "ACTIVE"

    def activate_lv(self):
        cmd = ['vxvol', '-g', self.vg, 'start', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def deactivate_lv(self):
        cmd = ['vxvol', '-g', self.vg, 'stop', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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)
        if dev is None:
            self.log.error("the device %s is not in the devtree", devpath)
            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.sub_devs() & holders_devpaths
        if len(holders_handled_by_resources) > 0:
            raise ex.Error("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
        self.remove_dev_holders(self.devpath, tree)

    def do_stop(self):
        if not self.is_up():
            self.log.info("%s is already down" % self.label)
            return
        self.remove_holders()
        self.deactivate_lv()

    def lv_devices(self):
        """
        Return the set of sub devices.
        """
        devs = set()
        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()
        if os.path.exists(self.devpath):
            devs.add(self.devpath)
        return devs

    def pre_unprovision_stop(self):
        # leave the vxvol active for wipe
        pass

    def unprovisioner(self):
        if not which('vxassist'):
            raise ex.Error("vxassist command not found")

        if not self.has_it():
            self.log.info("skip vxvol unprovision: %s already unprovisioned", self.fullname)
            return

        if which('wipefs') and os.path.exists(self.devpath) and self.is_up():
            self.vcall(["wipefs", "-a", self.devpath])

        cmd = ["vxassist", "-g", self.vg, "remove", "volume", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.svc.node.unset_lazy("devtree")

    def provisioner(self):
        if not which('vxassist'):
            raise ex.Error("vxassist command not found")

        if self.has_it():
            self.log.info("skip vxvol provision: %s already exists" % self.fullname)
            return

        if not self.size:
            raise ex.Error("a size is required")

        size_parm = str(self.size).upper()
        size_parm = [str(convert_size(size_parm, _to="m"))+'M']
        create_options = self.create_options or self.oget("create_options")

        # strip dev dir in case the alloc vxassist parameter was formatted using sub_devs
        # lazy references
        for idx, option in enumerate(create_options):
            create_options[idx] = option.replace("/dev/vx/dsk/", "")

        # create the logical volume
        cmd = ['vxassist', '-g', self.vg, "make", self.name] + size_parm + create_options
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        self.can_rollback = True
        self.svc.node.unset_lazy("devtree")

  0707010001f383000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vg   0707010001f386000081a40000000000000000000000016a100daf00003a1f000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vg/hpux.py   import glob
import os

from stat import *

import core.exceptions as ex
import utilities.lock

from .. import BaseDisk, BASE_KEYWORDS
from subprocess import *
from utilities.lazy import lazy
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import qcall

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vg"
DRIVER_BASENAME_ALIASES = ["lvm"]
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the volume group"
    },
    {
        "keyword": "options",
        "default": [],
        "at": True,
        "convert": "shlex",
        "provisioning": True,
        "text": "The vgcreate options to use upon vg provisioning."
    },
    {
        "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."
    },
    {
        "keyword": "pvs",
        "required": True,
        "convert": "list",
        "default": [],
        "text": "The list of paths to the physical volumes of the volume group.",
        "provisioning": True
    },
]
DEPRECATED_KEYWORDS = {
    "disk.lvm.vgname": "name",
    "disk.vg.vgname": "name",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "disk.lvm.name": "vgname",
    "disk.vg.name": "vgname",
}
DEPRECATED_SECTIONS = {
    "vg": ["disk", "vg"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("vgdisplay"):
        return ["disk.vg"]
    return []


class DiskVg(BaseDisk):
    def __init__(self, dsf=True, options=None, pvs=None, **kwargs):
        super(DiskVg, self).__init__(type='disk.vg', **kwargs)
        self.label = "vg %s " % self.name
        self.dsf = dsf
        self.options = options or []
        self.raw_pvs = pvs or []

    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.Error
        return out.split()[0]

    def write_mksf(self):
        cmd = ['ioscan', '-F', '-m', 'dsf']
        (ret, buff, err) = self.call(cmd)
        if ret != 0:
            raise ex.Error
        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.Error
                    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], Env.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.Error

    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.Error
        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.Error:
                # 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.Error

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

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

    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.Error
        cmd = ['vgchange', '-a', 'y', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

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

        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):
        lockfile = os.path.join(Env.paths.pathlock, 'vgimport')
        lockfd = None
        try:
            lockfd = utilities.lock.lock(timeout=timeout, delay=delay, lockfile=lockfile)
        except utilities.lock.LockTimeout:
            self.log.error("timed out waiting for lock (%s)"%lockfile)
            raise ex.Error
        except utilities.lock.LockNoLockFile:
            self.log.error("lock_nowait: set the 'lockfile' param")
            raise ex.Error
        except utilities.lock.LockCreateError:
            self.log.error("can not create lock file %s"%lockfile)
            raise ex.Error
        except utilities.lock.LockAcquire as e:
            self.log.warning("another action is currently running (pid=%s)"%e.pid)
            raise ex.Error
        except ex.Signal:
            self.log.error("interrupted by signal")
            raise ex.Error
        except:
            self.save_exc()
            raise ex.Error("unexpected locking error")
        self.lockfd = lockfd

    def unlock(self):
        utilities.lock.unlock(self.lockfd)

    @lazy
    def pvs(self):
        if not self.raw_pvs:
            # update lazy reference
            self.raw_pvs = self.oget("pvs")
        if not self.raw_pvs:
            raise ex.Error("pvs provisioning keyword is not set")
        l = []
        for pv in self.raw_pvs:
            l += glob.glob(pv)
        if not l:
            raise ex.Error("pvs provisioning keyword expands to an empty list")
        return l

    def provisioner(self):
        if self.has_it():
            self.log.info("already provisioned")
            return

        err = False
        for i, pv in enumerate(self.pvs):
            if not os.path.exists(pv):
                self.log.error("pv %s does not exist"%pv)
                err |= True
            mode = os.stat(pv)[ST_MODE]
            if S_ISBLK(mode):
                continue
            else:
                self.log.error("pv %s is not a block device nor a loop file"%pv)
                err |= True
        if err:
            raise ex.Error

        for pv in self.pvs:
            pv = pv.replace('/disk/', '/rdisk/')
            cmd = ['pvcreate', '-f', pv]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error

        pvs = []
        for pv in self.pvs:
            pvs.append(pv.replace('/rdisk/', '/disk/'))
        cmd = ['vgcreate']
        if len(self.options) > 0:
            cmd += self.options
        cmd += [self.name] + pvs
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

        self.log.info("provisioned")
        self.svc.node.unset_lazy("devtree")
 0707010001f385000081a40000000000000000000000016a100daf0000255c000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vg/aix.py    import json
import os
import shutil

from subprocess import *

import core.exceptions as ex

from .. import BaseDisk, BASE_KEYWORDS
from core.objects.svcdict import KEYS

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vg"
DRIVER_BASENAME_ALIASES = ["lvm"]
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the volume group"
    },
    {
        "keyword": "options",
        "default": "",
        "at": True,
        "convert": "shlex",
        "provisioning": True,
        "text": "The vgcreate options to use upon vg provisioning."
    },
    {
        "keyword": "pvs",
        "required": True,
        "convert": "list",
        "text": "The list of paths to the physical volumes of the volume group.",
        "provisioning": True
    },
]
DEPRECATED_KEYWORDS = {
    "disk.lvm.vgname": "name",
    "disk.vg.vgname": "name",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "disk.lvm.name": "vgname",
    "disk.vg.name": "vgname",
}
DEPRECATED_SECTIONS = {
    "vg": ["disk", "vg"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    if which("lsvg"):
        return ["disk.vg"]
    return []

# ajouter un dump regulier de la config des vg (pour ne pas manquer les extensions de vol)

class DiskVg(BaseDisk):
    def __init__(self, options=None, pvs=None, **kwargs):
        super(DiskVg, self).__init__(type='disk.vg', **kwargs)
        self.options = options or []
        self.pvs = pvs or []
        self.label = "vg %s" % self.name

    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.Error("%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.Error("%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.Error("Missing hdisks for pvids %s to be able to import vg" % ','.join(missing))
        elif len(missing) == 1:
            raise ex.Error("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.Error
        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.Error

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

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

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

        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

0707010001f384000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vg/__init__.py   0707010001f387000081a40000000000000000000000016a100daf0000356a000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vg/linux.py  import glob
import os
import re

from stat import *

import core.exceptions as ex
import utilities.devices.linux

from .. import BaseDisk, BASE_KEYWORDS
from core.objects.svcdict import KEYS
from env import Env
from utilities.cache import cache
from utilities.lazy import lazy
from utilities.proc import justcall
from utilities.subsystems.lvm.linux import get_lvs_attr

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vg"
DRIVER_BASENAME_ALIASES = ["lvm"]
KEYWORDS = [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the volume group"
    },
    {
        "keyword": "options",
        "default": [],
        "convert": "shlex",
        "at": True,
        "provisioning": True,
        "text": "The vgcreate options to use upon vg provisioning."
    },
    {
        "keyword": "pvs",
        "convert": "list",
        "default": [],
        "text": "The list of paths to the physical volumes of the volume group.",
        "provisioning": True
    },
] + BASE_KEYWORDS
DEPRECATED_KEYWORDS = {
    "disk.lvm.vgname": "name",
    "disk.vg.vgname": "name",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "disk.lvm.name": "vgname",
    "disk.vg.name": "vgname",
}
DEPRECATED_SECTIONS = {
    "vg": ["disk", "vg"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

VGSCANS = 0

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("vgdisplay"):
        return ["disk.vg"]
    return []


class DiskVg(BaseDisk):
    def __init__(self, pvs=None, options=None, **kwargs):
        super(DiskVg, self).__init__(type='disk.vg', **kwargs)
        self.label = "vg %s" % self.name
        self.pvs = pvs or []
        self.options = options or []
        self.tag = Env.nodename
        self.refresh_provisioned_on_provision = True
        self.refresh_provisioned_on_unprovision = True

    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 = 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.tags")
    def get_tags(self):
        cmd = [Env.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 pvscan(self):
        cmd = ["pvscan", "--cache"]
        ret, out, err = self.vcall(cmd, warn_to_info=True)
        if 'Not using lvmetad because config setting use_lvmetad=0' in err:
            cmd = ["pvscan"]
            ret, out, err = self.vcall(cmd, warn_to_info=True)
        self.clear_cache("vg.lvs")
        self.clear_cache("lvs.attr")
        self.clear_cache("vg.tags")

    def list_tags(self, tags=None):
        if tags is None:
            tags = []
        if not self.test_vgs():
            self.pvscan()
        data = self.get_tags()
        if self.name not in data:
            raise ex.Error("vg %s not found" % self.name)
        return data[self.name]

    def remove_tags(self, tags=None):
        if tags is None:
            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.Error
        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("lvs.attr")
        self.clear_cache("vg.tags")
        if ret != 0:
            raise ex.Error

    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("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("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.Error("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):
        need_deactivate = self.is_up()
        if need_deactivate:
            self.remove_holders()
            utilities.devices.linux.udevadm_settle()
            self.deactivate_vg()
        try:
            need_remove_tag = self.tag in self.get_tags()[self.name]
        except KeyError:
            need_remove_tag = False
        if need_remove_tag:
            self.remove_tag(self.tag)
        if not need_deactivate and not need_remove_tag:
            self.log.info("vg %s is already down", self.name)

    @cache("vg.lvs")
    def vg_lvs(self):
        cmd = [Env.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 = [Env.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 rescan_sub_devs(self):
        self.vgscan_once()

    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

    def boot(self):
        self.do_stop()



    def provisioned(self):
        # don't trust cache for that
        self.clear_cache("vg.lvs")
        self.clear_cache("lvs.attr")
        self.clear_cache("vg.tags")
        self.clear_cache("vg.pvs")
        self.vgscan()
        return self.has_it()

    def provisioner_shared_non_leader(self):
        self.pvscan()

    def unprovisioner(self):
        if not self.has_it():
            return
        cmd = ['vgremove', '-ff', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        self.clear_cache("vg.lvs")
        self.clear_cache("lvs.attr")
        self.clear_cache("vg.tags")
        self.clear_cache("vg.pvs")
        self.svc.node.unset_lazy("devtree")

    def vgscan_once(self):
        global VGSCANS
        if VGSCANS > 0:
            return
        self.vgscan()

    def vgscan(self):
        global VGSCANS
        cmd = [Env.syspaths.vgscan, "--cache"]
        justcall(cmd)
        VGSCANS += 1

    def has_pv(self, pv):
        cmd = [Env.syspaths.pvscan, "--cache", pv]
        justcall(cmd)
        cmd = [Env.syspaths.pvs, "-o", "vg_name", "--noheadings", pv]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return False
        out = out.strip()
        if out == self.name:
            self.log.info("pv %s is already a member of vg %s", pv, self.name)
            return True
        if out != "":
            raise ex.Error("pv %s in use by vg %s" % (pv, out))
        return True

    def provisioner(self):
        self.pvs = self.pvs or self.oget("pvs")
        if not self.pvs:
            # lazy reference not resolvable
            raise ex.Error("%s.pvs value is not valid" % self.rid)

        l = []
        for pv in self.pvs:
            _l = glob.glob(pv)
            self.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.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 = [Env.syspaths.losetup, '-j', pv]
                out, err, ret = justcall(cmd)
                if ret != 0 or not out.startswith('/dev/loop'):
                    self.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.log.error("pv %s is not a block device nor a loop file"%pv)
                pv_err |= True
        if pv_err:
            raise ex.Error

        for pv in self.pvs:
            if self.has_pv(pv):
                continue
            cmd = ['pvcreate', '-f', pv]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                raise ex.Error

        if len(self.pvs) == 0:
            raise ex.Error("no pvs specified")

        if self.has_it():
            self.log.info("vg %s already exists", self.name)
            return

        cmd = ["vgcreate"] + self.options + [self.name] + self.pvs
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

        self.can_rollback = True
        self.clear_cache("vg.lvs")
        self.clear_cache("lvs.attr")
        self.clear_cache("vg.tags")
        self.clear_cache("vg.pvs")
        self.svc.node.unset_lazy("devtree")
  0707010001f381000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vdisk    0707010001f382000081a40000000000000000000000016a100daf000009cd000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/disk/vdisk/__init__.py    """ Module providing device path remapping for libvirt VMs
"""
import core.status
import core.exceptions as ex

from .. import BASE_KEYWORDS
from env import Env
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.lazy import lazy

DRIVER_GROUP = "disk"
DRIVER_BASENAME = "vdisk"
KEYWORDS = BASE_KEYWORDS + [
    {
        "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."
    },
]
DEPRECATED_SECTIONS = {
    "vdisk": ["disk", "vdisk"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
)


class DiskVdisk(Resource):
    def __init__(self, name=None, path=None, **kwargs):
        super(DiskVdisk, self).__init__(type="disk.vdisk", **kwargs)
        self.name = name
        self.label = "vdisk %s" % self.name
        self.devpath = path

    def __str__(self):
        return "%s name=%s" % (
            super(DiskVdisk, self).__str__(),
            self.name
        )

    @lazy
    def devpaths(self):
        data = {}
        for n in self.svc.nodes | self.svc.drpnodes:
            data[n] = self.oget("path", impersonate=n)
        return data

    def sub_devs(self):
        return self.devpaths.keys()

    def remap(self):
        path = self.devpaths[Env.nodename]
        paths = set(self.devpaths.values()) - set(self.devpaths[Env.nodename])
        from xml.etree.ElementTree import ElementTree
        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.Error
        for dev in tree.iter('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 core.status.NA

   0707010001f3c5000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/hashpolicy    0707010001f3c7000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy  0707010001f3c8000081a40000000000000000000000016a100daf000006a5000000e600010003ffffffffffffffff0000004d00000000root/usr/share/opensvc/opensvc/drivers/resource/hashpolicy/envoy/__init__.py  from core.resource import DataResource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "hashpolicy"
DRIVER_BASENAME = "envoy"
KEYWORDS = [
    {
        "keyword": "cookie_name",
        "text": "The name of the cookie that will be used to obtain the hash key. If the cookie is not present and ttl below is not set, no hash will be produced.",
    },
    {
        "keyword": "cookie_path",
        "text": "The name of the path for the cookie. If no path is specified here, no path will be set for the cookie.",
    },
    {
        "keyword": "cookie_ttl",
        "convert": "duration",
        "text": "If specified, a cookie with the TTL will be generated if the cookie is not present. If the TTL is present and zero, the generated cookie will be a session cookie.",
    },
    {
        "keyword": "header_header_name",
        "text": "The name of the request header that will be used to obtain the hash key. If the request header is not present, no hash will be produced.",
    },
    {
        "keyword": "connection_source_ip",
        "text": "Hash on source IP address.",
    },
    {
        "keyword": "terminal",
        "convert": "boolean",
        "text": "Shortcircuits the hash computing. This field provides a fallback style of configuration: if a terminal policy doesn't work, fallback to rest of the policy list. It saves time when the terminal policy works.",
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class HashpolicyEnvoy(DataResource):
    def __init__(self, **kwargs):
        super(HashpolicyEnvoy, self).__init__(type="hashpolicy.envoy", **kwargs)
        self.label = "envoy hash policy"

   0707010001f3c6000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/hashpolicy/__init__.py    0707010001f326000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/certificate   0707010001f328000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/certificate/tls   0707010001f329000081a40000000000000000000000016a100daf0000073f000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/drivers/resource/certificate/tls/__init__.py   from core.resource import DataResource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "certificate"
DRIVER_BASENAME = "tls"
KEYWORDS = [
    {
        "keyword": "certificate_secret",
        "at": True,
        "text": "The name of the secret object name hosting the certificate files. The secret must have the certificate_chain and server_key keys set. This setting makes the certificate served to envoy via the secret discovery service, which allows its live rotation."
    },
    {
        "keyword": "validation_secret",
        "at": True,
        "text": "The name of the secret object name hosting the certificate autority files for certificate_secret validation. The secret must have the trusted_ca and verify_certificate_hash keys set. This setting makes the validation data served to envoy via the secret discovery service, which allows certificates live rotation."
    },
    {
        "keyword": "certificate_chain_filename",
        "at": True,
        "text": "Local filesystem data source of the TLS certificate chain."
    },
    {
        "keyword": "private_key_filename",
        "at": True,
        "text": "Local filesystem data source of the TLS private key."
    },
    {
        "keyword": "certificate_chain_inline_string",
        "at": True,
        "text": "String inlined data source of the TLS certificate chain."
    },
    {
        "keyword": "private_key_inline_string",
        "at": True,
        "text": "String inlined filesystem data source of the TLS private key. A reference to a secret for example."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class CertificateTls(DataResource):
    def __init__(self, **kwargs):
        super(CertificateTls, self).__init__(type="certificate.tls", **kwargs)
        self.label = "tls certificate"
 0707010001f327000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/certificate/__init__.py   0707010001f3e5000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/resource/route 0707010001f3e7000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/route/envoy   0707010001f3e8000081a40000000000000000000000016a100daf000010c0000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/route/envoy/__init__.py   from core.resource import DataResource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "route"
DRIVER_BASENAME = "envoy"
KEYWORDS = [
    {
        "keyword": "match_path",
        "at": True,
        "text": "If specified, the route is an exact path rule meaning that the path must exactly match the :path header once the query string is removed. Precisely one of prefix, path, regex must be set.",
    },
    {
        "keyword": "match_regex",
        "at": True,
        "text": " If specified, the route is a regular expression rule meaning that the regex must match the :path header once the query string is removed. The entire path (without the query string) must match the regex. The rule will not match if only a subsequence of the :path header matches the regex.",
        "example": "/b[io]t",
    },
    {
        "keyword": "match_prefix",
        "at": True,
        "text": " If specified, the route is a prefix rule meaning that the prefix must match the beginning of the :path header. Precisely one of prefix, path, regex must be set.",
    },
    {
        "keyword": "match_case_sensitive",
        "at": True,
        "text": "Indicates that prefix/path matching should be case sensitive. The default is ``true``.",
    },
    {
        "keyword": "route_prefix_rewrite",
        "at": True,
        "text": "The string replacing the url path prefix if matching.",
    },
    {
        "keyword": "route_host_rewrite",
        "at": True,
        "text": "Indicates that during forwarding, the host header will be swapped with this value.",
    },
    {
        "keyword": "route_cluster_header",
        "at": True,
        "text": "If the route is not a redirect (host_redirect and/or path_redirect is not specified), one of cluster, cluster_header, or weighted_clusters must be specified. When cluster_header is specified, Envoy will determine the cluster to route to by reading the value of the HTTP header named by cluster_header from the request headers. If the header is not found or the referenced cluster does not exist, Envoy will return a 404 response.",
    },
    {
        "keyword": "route_timeout",
        "at": True,
        "text": "Specifies the timeout for the route. If not specified, the default is 15s. Note that this timeout includes all retries.",
    },
    {
        "keyword": "redirect_host_redirect",
        "at": True,
        "text": "The host portion of the URL will be swapped with this value.",
    },
    {
        "keyword": "redirect_prefix_rewrite",
        "at": True,
        "text": "Indicates that during redirection, the matched prefix (or path) should be swapped with this value. This option allows redirect URLs be dynamically created based on the request.",
    },
    {
        "keyword": "redirect_path_redirect",
        "at": True,
        "text": "Indicates that the route is a redirect rule. If there is a match, a 301 redirect response will be sent which swaps the path portion of the URL with this value. host_redirect can also be specified along with this option.",
    },
    {
        "keyword": "redirect_response_code",
        "at": True,
        "text": "The HTTP status code to use in the redirect response. The default response code is MOVED_PERMANENTLY (301).",
    },
    {
        "keyword": "redirect_https_redirect",
        "convert": "boolean",
        "default": False,
        "at": True,
        "text": "The scheme portion of the URL will be swapped with 'https'.",
    },
    {
        "keyword": "redirect_strip_query",
        "convert": "boolean",
        "default": False,
        "at": True,
        "text": "Indicates that during redirection, the query portion of the URL will be removed. Default value is ``false``.",
    },
    {
        "keyword": "hash_policies",
        "convert": "list",
        "at": True,
        "default": [],
        "default_text": "",
        "text": "The list of hash policy resource ids for the route. Honored if lb_policy is set to ring_hash or maglev.",
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class RouteEnvoy(DataResource):
    def __init__(self, **kwargs):
        super(RouteEnvoy, self).__init__(type="route.envoy", **kwargs)
        self.label = "envoy route"
0707010001f3e6000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/route/__init__.py 0707010001f390000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/expose    0707010001f391000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/expose/__init__.py    0707010001f392000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/expose/envoy  0707010001f393000081a40000000000000000000000016a100daf00000d41000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/expose/envoy/__init__.py  from core.resource import DataResource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "expose"
DRIVER_BASENAME = "envoy"
KEYWORDS = [
    {
        "keyword": "cluster_data",
        "convert": "json",
        "at": True,
        "text": "The envoy protocol compliant data in json format used to bootstrap the Cluster config messages. Parts of this structure, like endpoints, are amended to reflect the actual cluster state."
    },
    {
        "keyword": "filter_config_data",
        "convert": "json",
        "at": True,
        "text": "The envoy protocol compliant data in json format used to bootstrap the Listener filter config messages. Parts of this structure, like routes, are amended by more specific keywords."
    },
    {
        "keyword": "port",
        "convert": "integer",
        "at": True,
        "required": True,
        "text": "The port number of the endpoint."
    },
    {
        "keyword": "protocol",
        "candidates": ["tcp", "udp"],
        "default": "tcp",
        "at": True,
        "text": "The protocol of the endpoint."
    },
    {
        "keyword": "listener_addr",
        "default_text": "The main proxy ip address.",
        "at": True,
        "text": "The public ip address to expose from."
    },
    {
        "keyword": "listener_port",
        "convert": "integer",
        "default_text": "The expose <port>.",
        "at": True,
        "text": "The public port number to expose from. The special value 0 is interpreted as a request for auto-allocation."
    },
    {
        "keyword": "sni",
        "at": True,
        "convert": "list",
        "text": "The SNI server names to match on the proxy to select this service endpoints. The socket server must support TLS."
    },
    {
        "keyword": "lb_policy",
        "default": "round robin",
        "candidates": ["round robin", "least_request", "ring_hash", "random", "original_dst_lb", "maglev"],
        "at": True,
        "text": "The name of the envoy cluster load balancing policy."
    },
    {
        "keyword": "gateway",
        "at": True,
        "text": "The name of the ingress gateway that should handle this expose."
    },
    {
        "keyword": "vhosts",
        "convert": "list",
        "at": True,
        "text": "The list of vhost resource identifiers for this expose."
    },
    {
        "keyword": "listener_certificates",
        "convert": "list",
        "at": True,
        "text": "The TLS certificates used by the listener."
    },
    {
        "keyword": "cluster_certificates",
        "convert": "list",
        "at": True,
        "text": "The TLS certificates used to communicate with cluster endpoints."
    },
    {
        "keyword": "cluster_private_key_filename",
        "at": True,
        "text": "Local filesystem data source of the TLS private key used to communicate with cluster endpoints."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class ExposeEnvoy(DataResource):
    def __init__(self, **kwargs):
        super(ExposeEnvoy, self).__init__(type="expose.envoy", **kwargs)
        self.label = "envoy expose %s/%s via %s:%d" % (
            self.options.port,
            self.options.protocol,
            self.options.listener_addr if self.options.listener_addr else "0.0.0.0",
            self.options.listener_port
        ) 

   0707010001f31c000041ed0000000000000000000000056a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/resource/app   0707010001f31e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/app/forking   0707010001f320000081a40000000000000000000000016a100daf0000145e000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/app/forking/__init__.py   """
The module defining the app.forking resource class.
"""

from .. import App, KEYWORDS as BASE_KEYWORDS
from core.objects.svcdict import KEYS

DRIVER_GROUP = "app"
DRIVER_BASENAME = "forking"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "script",
        "at": True,
        "required": False,
        "text": "Full path to the app launcher script. Or its basename if the file is hosted in the ``<pathetc>/<namespace>/<kind>/<name>.d/`` path. This script must accept as arg0 the activated actions word: ``start`` for start, ``stop`` for stop, ``status`` for check, ``info`` for info."
    },
    {
        "keyword": "status_log",
        "at": True,
        "required": False,
        "default": False,
        "convert": "boolean",
        "text": "Redirect the checker script stdout to the resource status info-log, and stderr to warn-log. The default is ``false``, for it is common the checker scripts outputs are not tuned for opensvc."
    },
    {
        "keyword": "check_timeout",
        "convert": "duration",
        "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 service instance check when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "info_timeout",
        "convert": "duration",
        "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 service instance info when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "start",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> start` on start action. ``false`` do nothing on start action. ``<shlex expression>`` execute the command on start.",
    },
    {
        "keyword": "stop",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> stop` on stop action. ``false`` do nothing on stop action. ``<shlex expression>`` execute the command on stop action.",
    },
    {
        "keyword": "check",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> status` on status evaluation. ``false`` do nothing on status evaluation. ``<shlex expression>`` execute the command on status evaluation.",
    },
    {
        "keyword": "info",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> info` on info action. ``false`` do nothing on info action. ``<shlex expression>`` execute the command on info action.",
    },
    {
        "keyword": "limit_as",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_cpu",
        "convert": "duration",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_core",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_data",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_fsize",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_memlock",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nofile",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nproc",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_rss",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_stack",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_vmem",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "user",
        "at": True,
        "text": "If the binary is owned by the root user, run it as the specified user instead of root."
    },
    {
        "keyword": "group",
        "at": True,
        "text": "If the binary is owned by the root user, run it as the specified group instead of root."
    },
    {
        "keyword": "cwd",
        "at": True,
        "text": "Change the working directory to the specified location instead of the default ``<pathtmp>``."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from env import Env
    data = []
    if Env.sysname == "Windows":
        return data
    data.append("app.forking")
    return data

class AppForking(App):
    """
    The forking App resource driver class.
    """

    def __init__(self, **kwargs):
        super(AppForking, self).__init__(type="app.forking", **kwargs)
  0707010001f324000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/app/winservice    0707010001f325000081a40000000000000000000000016a100daf00000e93000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/app/winservice/__init__.py    """
The module defining the app.simple resource class.
"""

try:
    import win32serviceutil
except ImportError:
    pass

import core.exceptions as ex

from .. import App, KEYWORDS as BASE_KEYWORDS, StatusNA, StatusWARN
from core.objects.svcdict import KEYS


DEFAULT_TIMEOUT = 300

SERVICE_STOPPED = 1
SERVICE_START_PENDING = 2
SERVICE_STOP_PENDING = 3
SERVICE_RUNNING = 4
SERVICE_CONTINUE_PENDING = 5
SERVICE_PAUSE_PENDING = 6
SERVICE_PAUSED = 7

STATUS_STR = {
    1: "STOPPED",
    2: "START_PENDING",
    3: "STOP_PENDING",
    4: "RUNNING",
    5: "CONTINUE_PENDING",
    6: "PAUSE_PENDING",
    7: "PAUSED",
}

DRIVER_GROUP = "app"
DRIVER_BASENAME = "winservice"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "name",
        "at": True,
        "text": "The name of the Windows service."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from env import Env
    data = []
    if Env.sysname != "Windows":
        return data
    data.append("app.winservice")
    return data


class AppWinservice(App):
    """
    The app.winservice resource driver class.
    """

    def __init__(self, name=None, **kwargs):
        self.name = name
        super(AppWinservice, self).__init__(type="app.winservice", **kwargs)

    def get_state(self):
        """
        typedef struct _SERVICE_STATUS {
          DWORD dwServiceType;
          DWORD dwCurrentState;
          DWORD dwControlsAccepted;
          DWORD dwWin32ExitCode;
          DWORD dwServiceSpecificExitCode;
          DWORD dwCheckPoint;
          DWORD dwWaitHint;
        } SERVICE_STATUS, *LPSERVICE_STATUS;
        """
        _, state, _, _, _, _, _ = win32serviceutil.QueryServiceStatus(self.name)
        return state

    def get_timeout(self, action):
        timeout = super(AppWinservice, self).get_timeout(action)
        if timeout is None:
            return DEFAULT_TIMEOUT
        return timeout

    def wait_for_state(self, timeout, target, transition):
        def fn():
            state = self.get_state()
            if state == target:
                return True
            if state != transition:
                raise ex.Error("unexpected state: %s" % STATUS_STR[state])
            return False
        self.wait_for_fn(fn, timeout, 1)

    def _check(self):
        if self.name is None:
            raise StatusNA
        state = self.get_state()
        if state == SERVICE_RUNNING:
            return 0
        if state == SERVICE_STOPPED:
            return 1
        self.status_log(STATUS_STR[state])
        raise StatusWARN

    def stop(self):
        if self.name is None:
            return
        try:
            if self.is_up() == 1:
                self.log.info("already down")
                return
        except StatusNA:
            self.log.info("skip, no name set")
            return
        except StatusWARN:
            pass
        self.log.info("stop winservice %s", self.name)
        win32serviceutil.StopService(self.name)
        timeout = self.get_timeout("stop")
        self.wait_for_state(timeout, SERVICE_STOPPED, SERVICE_STOP_PENDING)

    def start(self):
        if self.name is None:
            return
        try:
            if self.is_up() == 0:
                self.log.info("already up")
                return
        except StatusNA:
            self.log.info("skip, no name set")
            return
        except StatusWARN:
            pass
        self.log.info("start winservice %s", self.name)
        win32serviceutil.StartService(self.name)
        timeout = self.get_timeout("start")
        self.wait_for_state(timeout, SERVICE_RUNNING, SERVICE_START_PENDING)
 0707010001f321000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/app/simple    0707010001f323000081a40000000000000000000000016a100daf0000044d000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/app/simple/sunos.py   """
The module defining the app.simple resource class.
"""

from . import AppSimple as ParentAppSimple
from utilities.proc import justcall

DRIVER_GROUP = "app"
DRIVER_BASENAME = "simple"

class AppSimple(ParentAppSimple):
    """
    The simple App resource driver class.
    """
    def get_running(self, with_children=False):
        cmd = ["pgrep", "-f", " ".join(self.get_cmd("start", validate=False))]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return []
        match = []
        for pid in out.split():
            cmd = ["pargs", "-e", pid]
            out, _, _ = justcall(cmd)
            words = out.split()
            if "OPENSVC_SVC_ID="+self.svc.id not in words:
                continue
            if "OPENSVC_RID="+self.rid not in words:
                continue
            match.append(pid)
        if with_children:
            return match
        # exclude child processes
        cmd += ["-P", ",".join(match)]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return match
        return list(set(match) - set(out.split()))

   0707010001f322000081a40000000000000000000000016a100daf00002557000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/app/simple/__init__.py    """
The module defining the app.simple resource class.
"""

import os
import subprocess
import time

import core.status
from .. import App as BaseApp, KEYWORDS as BASE_KEYWORDS, StatusNA
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall, get_updated_preexec_fn

DRIVER_GROUP = "app"
DRIVER_BASENAME = "simple"
KEYWORDS = BASE_KEYWORDS + [
    {
        "keyword": "kill",
        "candidates": ["parent", "tree"],
        "default": "parent",
        "at": True,
        "text": "Select a process kill strategy to use on resource stop. ``parent`` kill only the parent process forked by the agent. ``tree`` also kill its children."
    },
    {
        "keyword": "script",
        "at": True,
        "required": False,
        "text": "Full path to the app launcher script. Or its basename if the file is hosted in the ``<pathetc>/<namespace>/<kind>/<name>.d/`` path. This script must accept as arg0 the activated actions word: ``start`` for start, ``stop`` for stop, ``status`` for check, ``info`` for info."
    },
    {
        "keyword": "status_log",
        "at": True,
        "required": False,
        "default": False,
        "convert": "boolean",
        "text": "Redirect the checker script stdout to the resource status info-log, and stderr to warn-log. The default is ``false``, for it is common the checker scripts outputs are not tuned for opensvc."
    },
    {
        "keyword": "check_timeout",
        "convert": "duration",
        "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 service instance check when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "info_timeout",
        "convert": "duration",
        "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 service instance info when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "start",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> start` on start action. ``false`` do nothing on start action. ``<shlex expression>`` execute the command on start.",
    },
    {
        "keyword": "stop",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> stop` on stop action. ``false`` do nothing on stop action. ``<shlex expression>`` execute the command on stop action.",
    },
    {
        "keyword": "check",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> status` on status evaluation. ``false`` do nothing on status evaluation. ``<shlex expression>`` execute the command on status evaluation.",
    },
    {
        "keyword": "info",
        "at": True,
        "default": False,
        "text": "``true`` execute :cmd:`<script> info` on info action. ``false`` do nothing on info action. ``<shlex expression>`` execute the command on info action.",
    },
    {
        "keyword": "limit_as",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_cpu",
        "convert": "duration",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_core",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_data",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_fsize",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_memlock",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nofile",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_nproc",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_rss",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_stack",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "limit_vmem",
        "convert": "size",
        "at": True,
        "text": ""
    },
    {
        "keyword": "user",
        "at": True,
        "text": "If the binary is owned by the root user, run it as the specified user instead of root."
    },
    {
        "keyword": "group",
        "at": True,
        "text": "If the binary is owned by the root user, run it as the specified group instead of root."
    },
    {
        "keyword": "cwd",
        "at": True,
        "text": "Change the working directory to the specified location instead of the default ``<pathtmp>``."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = []
    if Env.sysname == "Windows":
        return data
    data.append("app.simple")
    return data


class AppSimple(BaseApp):
    """
    The simple App resource driver class.
    """

    def __init__(self, kill="parent", **kwargs):
        self.kill = kill
        super(AppSimple, self).__init__(type="app.simple", **kwargs)

    def _check_simple(self):
        match = self.get_running()
        count = len(match)
        if count == 0:
            return core.status.DOWN
        elif count > 1:
            self.status_log("more than one process runs (pid %s)" % ", ".join(match))
            return core.status.UP
        else:
            return core.status.UP

    def ps_pids_e(self, pids):
        """
        When a lot of services and/or a lot of app.simple resources run the
        same program, caching effective.
        """
        cmd = [Env.syspaths.ps, "-p", pids, "e"]
        out, _, _ = justcall(cmd)
        return out

    def get_matching(self, cmd):
        cmd = cmd.replace("(", "\\(").replace(")", "\\)")
        out, err, ret = justcall(["pgrep", "-f", cmd])
        if ret != 0:
            return []
        pids = ",".join(out.split())
        if not pids:
            return []
        return self.ps_pids_e(pids).splitlines()

    def get_running(self, with_children=False):
        cmd = self.get_cmd("start", validate=False)
        if isinstance(cmd, list):
            cmd_s = " ".join(cmd)
        else:
            cmd_s = cmd
        lines = self.get_matching(cmd_s)
        match = []
        for line in lines:
            words = line.split()
            if not words or words[0] == "PID":
                continue
            if "OPENSVC_SVC_ID="+self.svc.id not in words:
                continue
            if "OPENSVC_RID="+self.rid not in words:
                continue
            match.append(words[0])
        if with_children or not match:
            return match
        # exclude child processes
        cmd = ["pgrep", "-P", ",".join(match)]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return match
        return list(set(match) - set(out.split()))

    def _check(self):
        try:
            ret = super(AppSimple, self)._check(verbose=False)
            return ret
        except StatusNA:
            return self._check_simple()

    def stop(self):
        super(AppSimple, self).stop()
        match = self.get_running(with_children=self.kill=="tree")
        if not match:
            self.log.info("already stopped")
            return
        cmd = ["kill"] + [str(pid) for pid in match]
        ret, _, _ = self.vcall(cmd)
        self.wait_for_fn(lambda: not self.get_running(), 5, 1, errmsg="Waited too long for process %s to disappear"%(",".join([str(pid) for pid in match])))
        return ret

    def _run_cmd_dedicated_log(self, action, cmd):
        """
        Poll stdout and stderr to log as soon as new lines are available.
        """
        for lim, val in self.limits.items():
            self.log.info("set limit %s = %s", lim, val)

        if hasattr(os, "devnull"):
            devnull = os.open(os.devnull, os.O_RDWR)
        else:
            devnull = os.open("/dev/null", os.O_RDWR)

        kwargs = {
            'stdout': devnull,
            'stderr': devnull,
            'close_fds': os.name != "nt",
        }
        try:
            kwargs.update(self.common_popen_kwargs(cmd))
        except ValueError as exc:
            self.log.error("%s", exc)
            return 1
        kwargs["preexec_fn"] = get_updated_preexec_fn(kwargs.get("preexec_fn"))
        user = kwargs.get("env").get("LOGNAME")
        if isinstance(cmd, list):
            cmd_s = ' '.join(cmd)
        else:
            cmd_s = cmd
        self.log.info("exec '%s' as user %s", cmd_s, user)
        try:
            proc = subprocess.Popen(cmd, **kwargs)
            if proc.returncode is not None:
                return proc.returncode
            time.sleep(0.2)
            proc.poll()
            if proc.returncode is not None:
                return proc.returncode
            return 0
        except Exception as exc:
            self.log.error("%s", exc)
            return 1

 0707010001f31d000081a40000000000000000000000016a100daf00006a3c000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/app/__init__.py   """
The module defining the App resource class.
"""
from datetime import datetime
import os
import stat
import shlex

import core.exceptions as ex
import utilities.lock
import core.status
import foreign.six as six

from utilities.lazy import lazy
from utilities.converters import convert_boolean
from env import Env
from core.resource import Resource
from core.capabilities import capabilities
from utilities.proc import which, lcall
from utilities.string import is_string

KEYWORDS = [
    {
        "keyword": "timeout",
        "convert": "duration",
        "at": True,
        "text": "Wait for <duration> before declaring the app launcher action a failure. Can be overridden 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 service instance action when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "start_timeout",
        "convert": "duration",
        "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 service instance start when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "stop_timeout",
        "convert": "duration",
        "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 service instance stop when an app launcher did not return.",
        "example": "180"
    },
    {
        "keyword": "secrets_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "A whitespace separated list of ``<var>=<secret name>/<key path>``. A shell expression splitter is applied, so double quotes can be around ``<secret name>/<key path>`` only or whole ``<var>=<secret name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "configs_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression splitter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression splitter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "retcodes",
        "at": True,
        "default": "0:up 1:down",
        "text": "The whitespace separated list of ``<retcode>:<status name>``. All undefined retcodes are mapped to the 'warn' status.",
        "example": "0:up 1:down 3:n/a"
    },
    {
        "keyword": "umask",
        "at": True,
        "text": "The umask to set for the application process.",
        "example": "022"
    },
]

def run_as_popen_kwargs(fpath, limits=None, user=None, group=None, cwd=None, umask=None):
    """
    Setup the Popen keyword args to execute <fpath> with the
    privileges demoted to those of the owner of <fpath>.
    """
    if limits is None:
        limits = {}
    if Env.sysname == "Windows":
        return {}

    import pwd
    if cwd is None:
        cwd = Env.paths.pathtmp

    pwd_user = None
    user_name = "unknown"

    if fpath:
        fpath = os.path.realpath(fpath)

        try:
            fstat = os.stat(fpath)
        except Exception as exc:
            raise ex.Error(str(exc))
        user_uid = fstat[stat.ST_UID]
        user_gid = fstat[stat.ST_GID]
        try:
            pwd_user = pwd.getpwuid(user_uid)
            user_name = pwd_user.pw_name
        except KeyError:
            pwd_user = None
            user_name = "unknown"
    elif user is None:
        user = "root"

    if user_name in ("root", "unknown") and user is not None:
        try:
            user_uid = int(user)
        except ValueError:
            try:
                pwd_user = pwd.getpwnam(user)
            except KeyError:
                raise ex.Error("user %s does not exist" % user)
            user_uid = pwd_user.pw_uid
            user_gid = pwd_user.pw_gid
            user_name = user
        else:
            try:
                pwd_user = pwd.getpwuid(user_uid)
            except KeyError:
                raise ex.Error("user %d does not exist" % user_uid)
            user_name = pwd_user.pw_name
            user_gid = pwd_user.pw_gid

        if group is not None:
            import grp
            try:
                user_gid = int(group)
            except ValueError:
                try:
                    grp_group = grp.getgrnam(group)
                except KeyError:
                    raise ex.Error("group %s does not exist" % group)
                user_gid = grp_group.gr_gid
            else:
                try:
                    grp_group = grp.getgrgid(user_gid)
                except KeyError:
                    raise ex.Error("group %d does not exist" % user_gid)

    try:
        pw_record = pwd.getpwnam(user_name)
        user_home_dir = pw_record.pw_dir
    except KeyError:
        user_home_dir = Env.paths.pathtmp

    try:
        umask = int(umask, 8)
    except (TypeError, ValueError):
        umask = None

    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, umask), 'cwd': cwd, 'env': env}

def preexec(user_uid, user_gid, limits, umask):
    def result():
        if Env.sysname != "Windows":
            os.setsid()
        if umask:
            os.umask(umask)
        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>.
    """
    try:
        os.setresgid(user_gid, user_gid, user_gid)
        os.setresuid(user_uid, user_uid, user_uid)
    except AttributeError:
        # os.setresgid not implemented in this python interpreter.
        # use the next best thing.
        os.setregid(user_gid, user_gid)
        os.setreuid(user_uid, 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,
                 script=None,
                 start=None,
                 stop=None,
                 check=None,
                 info=None,
                 timeout=None,
                 start_timeout=None,
                 stop_timeout=None,
                 check_timeout=None,
                 info_timeout=None,
                 status_log=False,
                 user=None,
                 group=None,
                 cwd=None,
                 environment=None,
                 configs_environment=None,
                 secrets_environment=None,
                 retcodes=None,
                 umask=None,
                 **kwargs):

        Resource.__init__(self, **kwargs)
        self.script = script
        self.start_seq = start
        self.stop_seq = stop
        self.check_seq = check
        self.info_seq = info
        self.timeout = timeout
        self.user = user
        self.group = group
        self.cwd = cwd
        self.umask = umask
        self.status_log_flag = status_log
        self.start_timeout = start_timeout
        self.stop_timeout = stop_timeout
        self.check_timeout = check_timeout
        self.info_timeout = info_timeout
        self.label = self.type.split(".")[-1]
        self.environment = environment
        self.configs_environment = configs_environment
        self.secrets_environment = secrets_environment
        self.retcodes = retcodes
        if script:
            self.label += ": " + os.path.basename(script)
        elif start:
            self.label += ": " + os.path.basename(start.split()[0])
        self.lockfd = None
        try:
            # compat
            self.sort_key = ("app#%d" % int(self.start_seq), self.rid)
        except (TypeError, ValueError):
            self.sort_key = (self.rid, self.rid)

    @lazy
    def retcodes_map(self):
        data = {}
        for element in self.retcodes.split():
            try:
                retcode, status = element.split(":")
                value = core.status.status_value(status)
                if value is not None:
                    data[int(retcode)] = value
            except Exception:
                pass
        return data

    @lazy
    def lockfile(self):
        """
        Lazy init for the resource lock file path property.
        """
        lockfile = os.path.join(self.var_d, "lock")
        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:
            fpath = os.path.realpath(cmd[0])
            self.status_log("%s is not executable" % fpath)
            self.set_executable(fpath)

    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>/<name>.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]):
            os.path.realpath(cmd[0])
            return cmd
        raise ex.Error("%s does not exist" % cmd[0])

    def is_up(self):
        """
        Return 0 if the app resource is up.
        """
        if self.pg_frozen():
            raise StatusNA
        return self._check()

    def _check(self, verbose=True):
        if self.check_seq is False:
            if verbose:
                self.status_log("check is not set", "info")
            raise StatusNA
        try:
            cmd = self.get_cmd("check", "status")
        except ex.AbortAction:
            raise StatusNA
        except ex.Error 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.
        """
        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.AbortAction, 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():
            try:
                idx = line.find(':')
            except ValueError:
                continue
            elements = line.split(":", 1)
            keyvals.append([elements[0].strip(), elements[1].strip()])
        return keyvals

    def replace_volname(self, buff, **kwargs):
        if os.sep not in buff:
            return buff, None
        return Resource.replace_volname(self, buff, **kwargs)

    def get_cmd(self, action, script_arg=None, validate=True):
        key = action + "_seq"
        val = getattr(self, key)
        if val in (None, False):
            raise ex.AbortAction()
        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.AbortAction()
                cmd = [self.script, script_arg if script_arg else action]
            except ex.AbortAction:
                raise
            except:
                if six.PY2:
                    cmd = map(lambda s: s.decode('utf8'), shlex.split(val.encode('utf8')))
                else:
                    cmd = shlex.split(val)
                if "|" in cmd or "||" in cmd or "&&" in cmd or any([True for w in cmd if w.endswith(";")]):
                    val, vol = self.replace_volname(val, strict=False, errors="ignore")
                    return val
        cmd[0], vol = self.replace_volname(cmd[0], strict=False, errors="ignore")
        if validate:
            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.AbortAction:
            return

        try:
            status = self.is_up()
        except:
            status = core.status.DOWN

        if status == core.status.UP:
            self.log.info("%s is already started", self.label)
            return

        ret = self.run("start", cmd)
        if ret != 0:
            raise ex.Error("exit code %d" % ret)

        def iu():
            try:
                r = self.is_up()
            except StatusNA:
                r = core.status.NA
            except:
                r = core.status.DOWN
            if r == core.status.NA:
                self.log.info("stop waiting for resource status up (currently n/a)")
            return r in (core.status.UP, core.status.NA)

        if self.start_timeout is not None:
            self.log.info("wait for resource status up")
            self.wait_for_fn(iu, self.start_timeout, 1)
        self.can_rollback = True

    def stop(self):
        """
        Stop the resource.
        """
        try:
            cmd = self.get_cmd("stop")
        except ex.AbortAction:
            return
        except ex.Error as exc:
            if "does not exist" in str(exc):
                return
            raise

        status = self.status()
        if status == core.status.DOWN:
            self.log.info("%s is already stopped", self.label)
            return

        try:
            ret = self.run("stop", cmd)
            if ret != 0:
                raise ex.Error("exit code %d" % ret)
        except Exception as exc:
            if self.svc.command_is_scoped():
                raise
            self.log.warning(exc)

    def unlock(self):
        """
        Release the app action lock.
        """
        if not self.lockfd:
            return
        self.log.debug("release app lock")
        utilities.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)
        lockfd = None
        try:
            lockfd = utilities.lock.lock(
                timeout=timeout,
                delay=delay,
                lockfile=self.lockfile,
                intent=action
            )
        except utilities.lock.LockTimeout as exc:
            raise ex.Error("timed out waiting for lock %s: %s" % (details, str(exc)))
        except utilities.lock.LockNoLockFile:
            raise ex.Error("lock_nowait: set the 'lockfile' param %s" % details)
        except utilities.lock.LockCreateError:
            raise ex.Error("can not create lock file %s" % details)
        except utilities.lock.LockAcquire as exc:
            raise ex.Error("another action is currently running %s: %s" % (details, str(exc)))
        except ex.Signal:
            self.log.info("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.Error("unexpected locking error %s: %s" % (details, str(exc)))
        finally:
            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",
            "task"
        ]))
        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 core.status.NA

        try:
            ret = self.is_up()
        except StatusWARN:
            return core.status.WARN
        except StatusNA:
            return core.status.NA
        except ex.Error 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 core.status.NA

        if ret in self.retcodes_map:
            return self.retcodes_map[ret]
        else:
            self.status_log("check reports errors (%d)" % ret)
            return core.status.WARN

    def set_executable(self, fpath):
        """
        Switch the script file execution bit to on.
        """
        if not os.path.exists(fpath):
            return
        self.vcall(['chmod', '+x', fpath])

    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, timeout=1, delay=0.2)
        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 ex.Error as exc:
            self.log.error(exc)
            return 1
        except:
            self.svc.save_exc()
            return 1

    def netns_formatter(self, cmd):
        if Env.sysname != "Linux":
            return cmd
        if "node.x.ip" not in capabilities:
            return cmd
        resources = [res for res in self.svc.get_resources("ip.cni") if res.container_rid is None]
        if not resources:
            return cmd
        return ["/sbin/ip", "netns", "exec", resources[0].nspid] + cmd

    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:
            if action == "start":
                cmd = self.netns_formatter(cmd)
            return self._run_cmd_dedicated_log(action, cmd)
        else:
            try:
                kwargs = self.common_popen_kwargs(cmd)
            except ValueError as exc:
                if return_out:
                    return "error: %s" % str(exc)
                else:
                    self.log.error("%s", exc)
                    return 1
            if return_out:
                ret, out, err = self.call(cmd, **kwargs)
                if ret != 0:
                    return "Error: info not implemented in launcher"
                return out
            else:
                kwargs["outlog"] = False
                kwargs["errlog"] = False
                ret, out, err = self.call(cmd, **kwargs)
                if self.status_log_flag:
                    out = out.strip()
                    for line in out.splitlines():
                        self.status_log(line, "info")
                    err = err.strip()
                    for line in err.splitlines():
                        self.status_log(line, "warn")
                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))
        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 common_popen_kwargs(self, cmd):
        kwargs = {
            'stdin': self.svc.node.devnull,
        }
        if isinstance(cmd, list):
            kwargs.update(run_as_popen_kwargs(cmd[0], self.limits, self.user, self.group, self.cwd, self.umask))
        else:
            kwargs.update(run_as_popen_kwargs(None, self.limits, self.user, self.group, self.cwd, self.umask))
            kwargs["shell"] = True
        if "env" in kwargs:
            kwargs["env"]["OPENSVC_RID"] = self.rid
            kwargs["env"]["OPENSVC_NAME"] = self.svc.name
            kwargs["env"]["OPENSVC_KIND"] = self.svc.kind
            kwargs["env"]["OPENSVC_ID"] = self.svc.name
            kwargs["env"]["OPENSVC_SVCNAME"] = self.svc.name # deprecated
            kwargs["env"]["OPENSVC_SVC_ID"] = self.svc.id # deprecated
            if self.svc.namespace:
                kwargs["env"]["OPENSVC_NAMESPACE"] = self.svc.namespace
        if self.configs_environment or self.secrets_environment:
            if "env" not in kwargs:
                kwargs["env"] = {}
            kwargs["env"].update(self.kind_environment_env("cfg", self.configs_environment))
            kwargs["env"].update(self.kind_environment_env("sec", self.secrets_environment))
        if self.environment:
            kwargs["env"].update(self.direct_environment_env(self.environment))

        return kwargs

    def _run_cmd_dedicated_log(self, action, cmd):
        """
        Poll stdout and stderr to log as soon as new lines are available.
        """
        now = datetime.now()
        for lim, val in self.limits.items():
            self.log.info("set limit %s = %s", lim, val)
        kwargs = {
            'timeout': self.get_timeout(action),
            'logger': self.log,
        }
        try:
            kwargs.update(self.common_popen_kwargs(cmd))
        except ValueError as exc:
            self.log.error("%s", exc)
            return 1
        user = kwargs.get("env").get("LOGNAME")
        if isinstance(cmd, list):
            cmd_s = ' '.join(cmd)
        else:
            cmd_s = cmd
        self.log.info('exec %s as user %s', cmd_s, user)
        try:
            ret = lcall(cmd, **kwargs)
        except (KeyboardInterrupt, ex.Signal):
            _len = datetime.now() - now
            self.log.info('%s interrupted after %s - ret %d',
                           action, _len, 1)
            return 0
        _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

    def pre_provision_start(self):
        pass
0707010001f3c9000041ed00000000000000000000000b6a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/resource/ip    0707010001f3e1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/ip/rule   0707010001f3e2000081ed0000000000000000000000016a100daf0000113e000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/ip/rule/__init__.py   import os

import core.exceptions as ex
import core.status
import utilities.ifconfig

from env import Env
from core.objects.svcdict import KEYS
from core.resource import Resource
from utilities.lazy import lazy
from utilities.proc import justcall, which

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "rule"
KEYWORDS = [
    {
        "keyword": "netns",
        "at": True,
        "required": True,
        "text": "The resource id of the container to plumb the ip into.",
        "example": "container#0"
    },
    {
        "keyword": "spec",
        "at": True,
        "required": True,
        "convert": "shlex",
        "text": "The rule specification, passed to the ip rule command, with the appropriate network namespace set.",
    },
]
KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if Env.sysname == "Linux" and which("ip"):
        return ["ip.rule"]
    return []

class IpRule(Resource):
    def __init__(self,
                 netns=None,
                 spec=None,
                 **kwargs):
        super(IpRule, self).__init__(type="ip.rule", **kwargs)
        self.container_rid = str(netns)
        self.spec = spec
        self.tags = self.tags | set(["docker"])
        self.tags.add(self.container_rid)
        self.label = " ".join(spec)

    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)

    def start_rule(self):
        if self.netns is None:
            raise ex.Error("unable to find the network namespace")
        try:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "rule", "add"] + self.spec
            ret, out, err = self.vcall(cmd)
        except ex.Error:
            pass
        if ret != 0:
            raise ex.Error

    def stop_rule(self):
        if self.netns is None:
            self.log.info("skip: unable to find the network namespace")
            return
        try:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "rule", "del"] + self.spec
            ret, out, err = self.vcall(cmd)
        except ex.Error:
            pass
        if ret != 0:
            raise ex.Error

    def is_up(self):
        try:
            self.netns
        except ex.Error:
            return False
        if self.netns is None:
            return False
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "rule", "list"] + self.spec
        out, err, ret = justcall(cmd)
        return ret == 0 and out.strip() != ""

    def _status(self, verbose=False):
        self.unset_lazy("netns")
        if self.is_up():
            return core.status.UP
        return core.status.DOWN

    def start(self):
        if self.is_up():
            self.log.info("rule is already up")
            return
        self.start_rule()

    def stop(self):
        if not self.is_up():
            self.log.info("rule is already down")
            return
        self.stop_rule()

    @lazy
    def container(self):
        if self.container_rid not in self.svc.resources_by_id:
            raise ex.Error("rid %s not found" % self.container_rid)
        return self.svc.resources_by_id[self.container_rid]

    @lazy
    def netns(self):
        if self.container.type in ("container.docker", "container.podman"):
            path = self.sandboxkey()
            if os.path.exists(path):
                return path
            # compat with older netns location
            path = path.replace("/services/", "/svc/")
            if os.path.exists(path):
                return path
            return
        elif self.container.type in ("container.lxd", "container.lxc"):
            return self.container.cni_netns()
        raise ex.Error("unsupported container type: %s" % self.container.type)

    def sandboxkey(self):
        sandboxkey = self.container.container_sandboxkey()
        if sandboxkey is None:
            raise ex.Error("failed to get sandboxkey")
        sandboxkey = str(sandboxkey).strip()
        if "'" in sandboxkey:
            sandboxkey = sandboxkey.replace("'","")
        if sandboxkey == "":
            raise ex.Error("sandboxkey is empty")
        return sandboxkey

  0707010001f3dd000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/ip/netns  0707010001f3de000081a40000000000000000000000016a100daf000062eb000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/ip/netns/__init__.py  import os

import core.exceptions as ex
import core.status
import utilities.ifconfig

from drivers.resource.ip.host.linux import IpHost
from drivers.resource.ip import KW_IPNAME, KW_IPDEV, KW_NETMASK, KW_GATEWAY, COMMON_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall, which
from utilities.net.converters import to_cidr

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "netns"
DRIVER_BASENAME_ALIASES = ["docker"]
KEYWORDS = [
    KW_IPNAME,
    KW_IPDEV,
    KW_NETMASK,
    KW_GATEWAY,
    {
        "keyword": "netns",
        "at": True,
        "required": True,
        "text": "The resource id of the container to plumb the ip into.",
        "example": "container#0"
    },
    {
        "keyword": "vlan_tag",
        "depends": [("mode", "ovs")],
        "at": True,
        "required": False,
        "text": "The VLAN tag the switch port will relay.",
        "example": "44 45"
    },
    {
        "keyword": "vlan_mode",
        "candidates": ["access", "native-tagged", "native-untagged"],
        "depends": [("mode", "ovs")],
        "at": True,
        "required": False,
        "default": "native-untagged",
        "text": "The VLAN port mode.",
        "example": "access"
    },
    {
        "keyword": "mode",
        "at": True,
        "required": False,
        "candidates": ["bridge", "dedicated", "macvlan", "ipvlan-l2", "ipvlan-l3", "ovs"],
        "text": "The ip link mode. If ipdev is set to a bridge interface the mode defaults to bridge, else defaults to macvlan. ipvlan requires a 4.2+ kernel.",
        "example": "ipvlan-l3"
    },
    {
        "keyword": "nsdev",
        "at": True,
        "required": False,
        "text": "If specified, use this interface name in the netns. If not specified the first free ``eth<n>`` is chosen."
    },
    {
        "keyword": "macaddr",
        "at": True,
        "required": False,
        "text": "If specified, use this mac address in the netns."
    },
    {
        "keyword": "del_net_route",
        "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."
    },
] + COMMON_KEYWORDS
DEPRECATED_KEYWORDS = {
    "ip.docker.container_rid": "netns",
    "ip.netns.container_rid": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "ip.docker.netns": "container_rid",
    "ip.netns.netns": "container_rid",
}
DEPRECATED_SECTIONS = {
    "ip.docker": ["ip", "netns"],
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_sections=DEPRECATED_SECTIONS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if Env.sysname == "Linux" and which("ip"):
        return ["ip.netns"]
    return []

class IpNetns(IpHost):
    def __init__(self,
                 mode=None,
                 network=None,
                 del_net_route=False,
                 netns=None,
                 nsdev=None,
                 macaddr=None,
                 vlan_tag=None,
                 vlan_mode=None,
                 **kwargs):
        super(IpNetns, self).__init__(type="ip.netns", **kwargs)
        self.mode = mode if mode else "bridge"
        self.network = network
        self.nsdev = nsdev
        self.macaddr = macaddr
        self.del_net_route = del_net_route
        self.container_rid = str(netns)
        self.vlan_tag = vlan_tag
        self.vlan_mode = vlan_mode
        self.tags = self.tags | set(["docker"])
        self.tags.add(self.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)
        self.set_label()

    def set_macaddr(self):
        """
        Set the intf mac addr
        """
        if not self.macaddr:
            return
        try:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", self.final_guest_dev, "address", self.macaddr]
            ret, out, err = self.vcall(cmd)
        except ex.Error:
             pass
        if ret != 0:
            return ret, out, err

    def set_label(self):
        """
        Set the resource label property.
        """
        try:
            self.get_mask()
        except ex.Error:
            pass
        self.label = "netns %s %s/%s %s@%s" % (
            self.mode,
            self.ipname,
            to_cidr(self.netmask),
            self.ipdev,
            self.container_rid,
        )

    def has_nsdev(self):
        if not self.nsdev:
            # let start configure a new nsdev
            return False
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip" , "link", "ls", "dev", self.nsdev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        return False

    def nsdev_ips(self):
        if not self.nsdev:
            # let stop unconfigure the auto-allocated and resource-dedicated nsdev
            return []
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip" , "addr", "ls", "dev", self.nsdev]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        ips = []
        for line in out.splitlines():
            l = line.strip().split()
            if len(l) > 1 and l[0].startswith("inet") and not l[1].startswith("fe80:"):
                ips.append(l[1])
        return ips

    def activate_netns_ipv6(self):
        if ":" in self.addr:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "sysctl" , "-w", "net.ipv6.conf."+self.final_guest_dev+".disable_ipv6=0"]
            self.vcall(cmd)
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "sysctl" , "-w", "net.ipv6.conf."+self.final_guest_dev+".ndisc_notify=1"]
            self.vcall(cmd)

    @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.
        """
        if self.netns is None:
            raise ex.Error("could not determine netns")
        with open("/proc/net/dev", "r") as filep:
            local_devs = [line.split(":", 1)[0] for line in filep.readlines() if ":" in line]

        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "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("@")]
            try:
                used.append(int(idx))
            except ValueError:
                # user named interface. ex: eth-metier
                continue
        idx = 0
        nspid = self.get_nspid()
        while True:
            guest_dev = "eth%d" % idx
            local_dev = "v%spl%s" % (guest_dev, nspid)
            if idx not in used and local_dev not in local_devs:
                return guest_dev
            idx += 1

    @lazy
    def final_guest_dev(self):
        if self.nsdev:
            return self.nsdev
        else:
            return self.guest_dev

    @lazy
    def container(self):
        if self.container_rid not in self.svc.resources_by_id:
            raise ex.Error("rid %s not found" % self.container_rid)
        return self.svc.resources_by_id[self.container_rid]

    def container_id(self, refresh=False):
        if self.container.type in ("container.lxd", "container.lxc"):
            return self.container.name
        elif self.container.type in ("container.docker", "container.podman"):
            return self.container.lib.get_container_id(self.container, refresh=refresh)
        else:
            raise ex.Error("unsupported container %s type: %s" % (
                self.container.rid,
                self.container.type,
            ))

    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.Error as e:
            return
        if nspid is None:
            return
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = utilities.ifconfig.Ifconfig(ip_out=out)
        return ifconfig

    def abort_start(self):
        return super(IpNetns, self).abort_start()

    def is_up(self):
        if not self.container.is_up():
            return False
        ifconfig = self.get_docker_ifconfig()
        if ifconfig is None:
            return False
        return super(IpNetns, self)._is_up(ifconfig)

    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):
        return False

    def _status(self, verbose=False):
        self.unset_lazy("netns")
        if self.container_running_elsewhere():
            self.status_log("%s is hosted by another host" % self.container_rid, "info")
            return core.status.NA
        ret = super(IpNetns, self)._status()
        return ret

    def startip_cmd(self):
        self.unset_lazy("netns")
        if "dedicated" in self.tags or self.mode == "dedicated":
            self.log.info("dedicated mode")
            return self.startip_cmd_dedicated()
        else:
            return self.startip_cmd_shared()

    def startip_cmd_shared(self):
        if self.mode is None:
            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()
        elif self.mode == "bridge":
            self.log.info("bridge mode")
            return self.startip_cmd_shared_bridge()
        elif self.mode == "macvlan":
            self.log.info("macvlan mode")
            return self.startip_cmd_shared_macvlan()
        elif self.mode == "ipvlan-l2":
            self.log.info("ipvlan-l2 mode")
            return self.startip_cmd_shared_ipvlan("l2")
        elif self.mode == "ipvlan-l3":
            self.log.info("ipvlan-l3 mode")
            return self.startip_cmd_shared_ipvlan("l3")
        elif self.mode == "ovs":
            self.log.info("ovs mode")
            return self.startip_cmd_shared_ovs()

    def startip_cmd_dedicated(self):
        # assign interface to the nspid
        nspid = self.get_nspid()
        if nspid is None:
            raise ex.Error("could not determine nspid")

        if not self.has_nsdev():
            cmd = [Env.syspaths.ip, "link", "set", self.ipdev, "netns", nspid, "name", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # plumb the ip
            self.activate_netns_ipv6()
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "add", "%s/%s" % (self.addr, to_cidr(self.netmask)), "dev", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # activate
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", self.final_guest_dev, "up"]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        # add default route
        if self.gateway:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "replace", "default", "via", self.gateway, "dev", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        if ":" not in self.addr:
            # announce
            cmd = [Env.syspaths.nsenter, "--net="+self.netns] + Env.python_cmd + [os.path.join(Env.paths.pathlib, "utilities", "arp.py"), self.final_guest_dev, self.addr]
            self.log.info(" ".join(cmd))
            out, err, ret = justcall(cmd)

        return 0, "", ""

    def stopip_cmd_shared_ovs(self):
        nspid = self.get_nspid()
        tmp_local_dev = "v%spl%s" % (self.guest_dev, nspid)

        cmd = ["ovs-vsctl", "del-port", self.ipdev, tmp_local_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err
        return ret, out, err
 
    def startip_cmd_shared_ovs(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()

        if not which("ovs-vsctl"):
            raise Exception("ovs-vsctl must be installed")

        if not self.has_nsdev():
            # create peer devs
            cmd = [Env.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

            cmd = ["ovs-vsctl", "--may-exist", "add-port", self.ipdev, tmp_local_dev, "vlan_mode=%s" % self.vlan_mode]
            if self.vlan_tag is not None:
                cmd += ["tag=%s" % self.vlan_tag]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            cmd = [Env.syspaths.ip, "link", "set", tmp_local_dev, "up"]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # assign the interface to the container namespace
            cmd = [Env.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 = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", tmp_guest_dev, "name", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        # plumb ip
        self.activate_netns_ipv6()
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "add", self.addr+"/"+to_cidr(self.netmask), "dev", self.final_guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        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()

        if not self.has_nsdev():
            # create peer devs
            cmd = [Env.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 = [Env.syspaths.ip, "link", "set", tmp_local_dev, "master", self.ipdev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err
            cmd = [Env.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 = [Env.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 = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", tmp_guest_dev, "name", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # set the mac addr
            self.set_macaddr()

        # plumb ip
        self.activate_netns_ipv6()
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "add", self.addr+"/"+to_cidr(self.netmask), "dev", self.final_guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        return 0, "", ""

    def startip_cmd_shared_ipvlan(self, mode):
        nspid = self.get_nspid()

        tmp_guest_dev = "ph%s%s" % (nspid, self.guest_dev)

        if not self.has_nsdev():
            mtu = self.ip_get_mtu()

            # create a macvlan interface
            cmd = [Env.syspaths.ip, "link", "add", "link", self.ipdev, "dev", tmp_guest_dev, "mtu", mtu, "type", "ipvlan", "mode", mode]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # activate the parent dev
            cmd = [Env.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 = [Env.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 = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", tmp_guest_dev, "name", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        # plumb the ip
        self.activate_netns_ipv6()
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "add", "%s/%s" % (self.addr, to_cidr(self.netmask)), "dev", self.final_guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        return 0, "", ""

    def startip_cmd_shared_macvlan(self):
        nspid = self.get_nspid()

        tmp_guest_dev = "ph%s%s" % (nspid, self.guest_dev)

        if not self.has_nsdev():
            mtu = self.ip_get_mtu()

            # create a macvlan interface
            cmd = [Env.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 = [Env.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 = [Env.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 = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", tmp_guest_dev, "name", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

            # set the mac addr
            self.set_macaddr()

        # plumb the ip
        self.activate_netns_ipv6()
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "add", "%s/%s" % (self.addr, to_cidr(self.netmask)), "dev", self.final_guest_dev]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        # setup default route
        self.ip_setup_route()

        return 0, "", ""

    def ip_get_mtu(self):
        # get mtu
        cmd = [Env.syspaths.ip, "link", "show", self.ipdev]
        ret, out, err = self.call(cmd)
        if ret != 0:
            raise ex.Error("failed to get %s mtu: %s" % (self.ipdev, err))
        mtu = out.split()[4]
        return mtu

    def ip_setup_route(self):
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "set", self.final_guest_dev, "up"]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            return ret, out, err

        if ":" in self.addr:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "-6", "route", "list", "default"]
        else:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "list", "default"]
        ret, out, err = self.call(cmd, errlog=False)
        if out.startswith("default via"):
            pass
        elif out.startswith("default dev") and not self.gateway:
            pass
        elif self.gateway:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "replace", "default", "via", self.gateway]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err
        else:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "replace", "default", "dev", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        if self.del_net_route and self.network:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "del", self.network+"/"+to_cidr(self.netmask), "dev", self.final_guest_dev]
            ret, out, err = self.vcall(cmd)
            if ret != 0:
                return ret, out, err

        if ":" not in self.addr:
            # announce
            cmd = [Env.syspaths.nsenter, "--net="+self.netns] + Env.python_cmd + [os.path.join(Env.paths.pathlib, "utilities", "arp.py"), self.final_guest_dev, self.addr]
            self.log.info(" ".join(cmd))
            out, err, ret = justcall(cmd)

    def get_nspid(self):
        if self.container.type in ("container.docker", "container.podman"):
            return self.get_nspid_docker()
        elif self.container.type in ("container.lxd", "container.lxc"):
            return self.get_nspid_lxc()

    def get_nspid_lxc(self):
        return str(self.container.get_pid())

    def get_nspid_docker(self):
        nspid = self.container.container_pid()
        if nspid is None:
            raise ex.Error("failed to get nspid")
        nspid = str(nspid).strip()
        if "'" in nspid:
            nspid = nspid.replace("'","")
        if nspid == "0":
            raise ex.Error("nspid is 0")
        return nspid

    @lazy
    def netns(self):
        if self.container.type in ("container.docker", "container.podman"):
            path = self.sandboxkey()
            if os.path.exists(path):
                return path
            # compat with older netns location
            path = path.replace("/services/", "/svc/")
            if os.path.exists(path):
                return path
            return
        elif self.container.type in ("container.lxd", "container.lxc"):
            return self.container.cni_netns()
        raise ex.Error("unsupported container type: %s" % self.container.type)

    def sandboxkey(self):
        sandboxkey = self.container.container_sandboxkey()
        if sandboxkey is None:
            raise ex.Error("failed to get sandboxkey")
        sandboxkey = str(sandboxkey).strip()
        if "'" in sandboxkey:
            sandboxkey = sandboxkey.replace("'","")
        if sandboxkey == "":
            raise ex.Error("sandboxkey is empty")
        return sandboxkey

    def stopip_cmd(self):
        intf = self.get_docker_interface()
        if intf is None:
            raise ex.ContinueAction("can't find on which interface %s is plumbed in container %s" % (self.addr, self.container_id()))
        if self.netmask is None:
            raise ex.ContinueAction("netmask is not set")
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr", "del", self.addr+"/"+to_cidr(self.netmask), "dev", intf]
        ret, out, err = self.vcall(cmd)
        ips = self.nsdev_ips()
        if ips is not None and len(ips) == 0:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "link", "del", "dev", intf]
            ret, out, err = self.vcall(cmd)

            if self.mode == "ovs":
                self.log.info("ovs mode")
                ret, out, err = self.stopip_cmd_shared_ovs()
        else:
            self.log.info("preserve nsdev %s, still in use by %s", intf, " ".join(ips))

        self.unset_lazy("netns")
        return ret, out, err

 0707010001f3cd000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/ip/cni    0707010001f3ce000081a40000000000000000000000016a100daf000042e7000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/ip/cni/__init__.py    """
A CNI driver following the specs available at
https://github.com/containernetworking/cni/blob/master/SPEC.md
"""
import os
import json

from subprocess import Popen, PIPE

import core.exceptions as ex
import core.status
import utilities.ifconfig
import utilities.lock

from drivers.resource.ip import \
    KW_WAIT_DNS, \
    KW_DNS_NAME_SUFFIX, \
    KW_PROVISIONER, \
    KW_DNS_UPDATE, \
    KW_CHECK_CARRIER, \
    KW_ALIAS, \
    KW_EXPOSE
from drivers.resource.ip.host.linux import IpHost
from env import Env
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall, which
from utilities.net.converters import to_cidr
from utilities.render.color import format_str_flat_json
from utilities.string import bencode, bdecode

CNI_VERSION = "0.3.0"
PORTMAP_CONF = {
    "cniVersion": CNI_VERSION,
    "name": "osvc-portmap",
    "type": "portmap",
    "snat": True,
    "capabilities": {
        "portMappings": True
    },
#    "externalSetMarkChain": "OSVC-MARK-MASQ"
}

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "cni"
KEYWORDS = [
    {
        "keyword": "ipname",
        "required": False,
        "at": True,
        "text": "Not used by the cni driver."
    },
    {
        "keyword": "network",
        "at": True,
        "required": False,
        "default": "default",
        "text": "The name of the CNI network to plug into. The default network is created using the host-local bridge plugin if no existing configuration already exists.",
        "example": "my-weave-net",
    },
    {
        "keyword": "netns",
        "at": True,
        "required": False,
        "text": "The resource id of the container to plug into the CNI network.",
        "example": "container#0"
    },
    {
        "keyword": "ipdev",
        "default": "eth12",
        "at": True,
        "required": False,
        "text": "The interface name in the container namespace."
    },
    KW_WAIT_DNS,
    KW_DNS_NAME_SUFFIX,
    KW_PROVISIONER,
    KW_DNS_UPDATE,
    KW_CHECK_CARRIER,
    KW_ALIAS,
    KW_EXPOSE,
]
DEPRECATED_KEYWORDS = {
    "ip.cni.container_rid": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "ip.cni.netns": "container_rid",
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
)

def cni_plugins(node):
    path = node.oget("cni", "plugins")
    if os.path.exists(os.path.join(path, "bridge")):
        return path
    altpath = os.path.join(os.sep, "usr", "lib", "cni")
    if os.path.exists(os.path.join(altpath, "bridge")):
        return altpath
    altpath = os.path.join(os.sep, "usr", "libexec", "cni")
    if os.path.exists(os.path.join(altpath, "bridge")):
        return altpath
    return path

def driver_capabilities(node=None):
    if os.path.exists(cni_plugins(node)):
        return ["ip.cni"]
    return []


class NoIpAddrAvail(ex.OsvcException):
    pass

class DupAlloc(ex.OsvcException):
    pass

class IpCni(IpHost):
    def __init__(self,
                 network=None,
                 netns=None,
                 **kwargs):
        super(IpCni, self).__init__(type="ip.cni", **kwargs)
        self.network = network
        self.container_rid = netns
        if self.container_rid:
            self.tags = self.tags | set(["docker"])
            self.tags.add(self.container_rid)

    def set_label(self):
        pass

    @lazy
    def label(self): # pylint: disable=method-hidden
        intf = self.get_ipdev()
        label = "cni "
        label += self.network if self.network else ""
        if intf and len(intf.ipaddr) > 0:
            label += " %s/%s" % (intf.ipaddr[0], to_cidr(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 = super(IpCni, self)._status_info()
        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):
        if self.netns is None:
            return

        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = utilities.ifconfig.Ifconfig(ip_out=out)
        return ifconfig

    def _get_ifconfig(self):
        try:
            nspid = self.nspid
        except ex.Error as e:
            return
        if nspid is None:
            return

        cmd = [Env.syspaths.ip, "netns", "exec", self.nspid, "ip", "addr"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return

        ifconfig = utilities.ifconfig.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):
        self.svc.node.network_create_config(self.network)
        try:
            with open(self.cni_conf, "r") as ofile:
                return json.load(ofile)
        except ValueError:
            raise ex.Error("invalid json in cni configuration file %s" % self.cni_conf)

    @lazy
    def cni_plugins(self):
        return cni_plugins(node=self.svc.node)

    @lazy
    def cni_config(self):
        return self.svc.node.oget("cni", "config")

    @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):
        return self.svc.id

    @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 netns(self):
        if self.container is None:
            return
        return self.container.cni_netns()

    @lazy
    def containerid(self):
        return self.container.cni_containerid()

    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)]
        if not which(cmd[0]):
            raise ex.Error("%s not found" % cmd[0])
        self.log_cmd(_env, data, cmd)
        env = {}
        env.update(Env.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.Error("%s (retcode %d)" % (err, proc.returncode))
        if "code" in data:
            msg = data.get("msg", "")
            if "no IP addresses available" in msg:
                raise NoIpAddrAvail(msg)
            elif "duplicate allocation" in msg:
                raise DupAlloc(msg)
            raise ex.Error("%s (retcode %d)" % (msg, proc.returncode))
        for line in format_str_flat_json(data).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 = [Env.syspaths.ip, "netns", "add", self.nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error(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 = [Env.syspaths.ip, "netns", "del", self.nspid]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    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.Error("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 "hostPort" 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 expose.get("type") not in (None, "cni"):
                continue
            if "port" not in expose or not expose["port"]:
                continue
            if "protocol" not in expose or not expose["port"]:
                continue
            if "host_port" not in expose or not expose["host_port"]:
                expose["host_port"] = expose["port"]
            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.Error("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.containerid is None:
                raise ex.Error("container %s is down" % self.container_rid)
            _env["CNI_CONTAINERID"] = str(self.containerid)
            _env["CNI_NETNS"] = self.netns
        else:
            _env["CNI_CONTAINERID"] = self.svc.id
            _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
            try:
                result = self.cni_cmd(_env, data)
            except DupAlloc as exc:
                self.log.info("%s => retry", exc)
                self._del_cni()
                result = self.cni_cmd(_env, data)
            except NoIpAddrAvail as exc:
                self.log.info("%s => clean allocation and retry", exc)
                self._del_cni()
                self.cleanup_var_cni()
                result = self.cni_cmd(_env, data)

        if self.expose and result:
            data = {}
            data.update(PORTMAP_CONF)
            data["prevResult"] = result
            data["runtimeConfig"] = self.runtime_config()
            if data["runtimeConfig"]:
                result = self.cni_cmd(_env, data)

    def cleanup_var_cni(self):
        import glob
        pattern = "/var/lib/cni/networks/%s/*.*.*.*" % self.cni_data["name"]
        for path in glob.glob(pattern):
            try:
                with open(path, "r") as f:
                    buff = f.read()
                lines = buff.split(os.linesep)
                pid = int(lines[0])
                if not os.path.exists("/proc/%d" % pid):
                    self.log.info("free %s allocation for dead pid %d", os.path.basename(path), pid)
                    os.unlink(path)
            except Exception as exc:
                continue

    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
        self._del_cni()
        self.cleanup_var_cni_ip()

    def _del_cni(self):
        _env = {
            "CNI_COMMAND": "DEL",
            "CNI_IFNAME": self.ipdev,
            "CNI_PATH": self.cni_plugins,
        }
        if self.container_rid:
            if self.containerid is None:
                self.log.info("container %s is already down" % self.container_rid)
                return
            _env["CNI_CONTAINERID"] = str(self.containerid)
            _env["CNI_NETNS"] = self.netns
        else:
            _env["CNI_CONTAINERID"] = str(self.nspid)
            _env["CNI_NETNS"] = self.nspidfile

        if self.expose:
            data = {}
            data.update(PORTMAP_CONF)
            data["runtimeConfig"] = self.runtime_config()
            if data["runtimeConfig"]:
                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 cleanup_var_cni_ip(self):
        intf = self.get_ipdev()
        if intf and len(intf.ipaddr) > 0:
            ipaddr = intf.ipaddr[0]
        else:
            ipaddr = None
        var_f = "/var/lib/cni/networks/%s/%s" % (self.network, ipaddr)
        if os.path.exists(var_f):
            self.log.info("rm %s", var_f)
            os.unlink(var_f)

    @lazy
    def lockfile(self):
        return os.path.join(Env.paths.pathvar, "cni.lock")

    def start(self):
        self.unset_lazy("containerid")
        self.unset_lazy("netns")
        try:
            with utilities.lock.cmlock(lockfile=self.lockfile, timeout=20):
                self.add_netns()
                self.add_cni()
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.Error("cni lock acquire: %s" % str(exc))
        self.wait_dns_records()

    def stop(self):
        self.del_cni()
        self.del_netns()
        self.unset_lazy("containerid")
        self.unset_lazy("netns")

    def _status(self, verbose=False):
        try:
            self.cni_data
        except ex.Error 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 core.status.DOWN
        elif not _has_netns and not _has_ipdev:
            return core.status.DOWN
        elif _has_netns and _has_ipdev:
            return core.status.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 core.status.DOWN

    def is_up(self):
        if not self.has_netns():
            return False
        if not self.has_ipdev():
            return False
        return True

    def is_provisioned(self, refresh=False):
        return

 0707010001f3ca000081a40000000000000000000000016a100daf00007c37000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/ip/__init__.py    """
Generic ip resource driver.
"""
from __future__ import unicode_literals

import os
import re
import time

import core.status
import utilities.net.ipaddress
import utilities.lock
import core.exceptions as ex
import utilities.ifconfig

from core.objects.svcdict import KEYS
from core.resource import Resource
from env import Env
from utilities.converters import convert_duration, print_duration
from utilities.lazy import lazy
from utilities.loop_delay import delay
from utilities.net.converters import to_cidr
from utilities.net.getaddr import getaddr

KW_IPNAME = {
    "keyword": "ipname",
    "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."
}
KW_IPDEV = {
    "keyword": "ipdev",
    "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. If the value is expressed as '<intf>:<alias>, the stacked interface index is forced to <alias> instead of the lowest free integer. If the value is expressed as <name>@<intf>, a macvtap interface named <name> is created and attached to <intf>."
}
KW_NETMASK = {
    "keyword": "netmask",
    "at": True,
    "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"
}
KW_GATEWAY = {
    "keyword": "gateway",
    "at": True,
    "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
}
KW_DNS_NAME_SUFFIX = {
    "keyword": "dns_name_suffix",
    "at": True,
    "text": "Add the value as a suffix to the DNS record name. The record created is thus formatted as ``<name>-<dns_name_suffix>.<app>.<managed zone>``."
}
KW_PROVISIONER = {
    "keyword": "provisioner",
    "provisioning": True,
    "candidates": ("collector", None),
    "at": True,
    "example": "collector",
    "text": "The IPAM driver to use to provision the ip.",
}
KW_NETWORK = {
    "keyword": "network",
    "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 :kw:`del_net_route` is set to ``true``.",
}
KW_DNS_UPDATE = {
    "keyword": "dns_update",
    "at": True,
    "default": False,
    "convert": "boolean",
    "candidates": [True, False],
    "text": "Setting this parameter triggers a DNS update. The record created is formatted as ``<name>.<app>.<managed zone>``, unless dns_record_name is specified."
}
KW_WAIT_DNS = {
    "section": "ip",
    "keyword": "wait_dns",
    "at": True,
    "convert": "duration",
    "default": 0,
    "example": "10s",
    "text": "Wait for the cluster DNS records associated to the resource to appear after a resource start and before the next resource can be started. This can be used for apps or containers that require the ip or ip name to be resolvable to provision or execute properly."
}
KW_ZONE = {
    "keyword": "zone",
    "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"
}
KW_CHECK_CARRIER = {
    "keyword": "check_carrier",
    "at": True,
    "required": False,
    "default": True,
    "convert": "boolean",
    "text": "Activate the link carrier check. Set to false if ipdev is a backend "
            "bridge or switch",
}
KW_ALIAS = {
    "keyword": "alias",
    "at": True,
    "required": False,
    "default": True,
    "convert": "boolean",
    "text": "Use ip aliasing. Modern ip stack support multiple ip/mask per interface, so :kw:`alias` should be set to false when possible.",
}
KW_EXPOSE = {
    "keyword": "expose",
    "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"
}

COMMON_KEYWORDS = [
    KW_WAIT_DNS,
    KW_DNS_NAME_SUFFIX,
    KW_PROVISIONER,
    KW_NETWORK,
    KW_DNS_UPDATE,
    KW_CHECK_CARRIER,
    KW_ALIAS,
    KW_EXPOSE,
]

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"
KEYWORDS = [
    KW_IPNAME,
    KW_IPDEV,
    KW_NETMASK,
    KW_GATEWAY,
] + COMMON_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class Ip(Resource):
    """
    Base ip resource driver.
    """

    def __init__(self,
                 ipdev=None,
                 ipname=None,
                 netmask=None,
                 gateway=None,
                 type="ip",
                 expose=None,
                 check_carrier=True,
                 alias=True,
                 wait_dns=0,
                 provisioner=None,
                 **kwargs):
        super(Ip, self).__init__(type=type, **kwargs)
        self.forced_stacked_dev = None
        self.base_ipdev = None
        self.ipdev = ipdev
        self.alias = alias
        if ipdev:
            if ":" in ipdev:
                self.ipdev = ipdev.split(":", 1)[0]
                self.forced_stacked_dev = ipdev
            elif "@" in ipdev:
                self.ipdev, self.base_ipdev = ipdev.split("@", 1)
                self.alias = False
        self.ipname = ipname
        self.netmask = netmask
        self.gateway = gateway
        self.lockfd = None
        self.stacked_dev = None
        self.addr = None
        self.expose = expose
        self.check_carrier = check_carrier
        self.wait_dns = wait_dns
        self.kw_provisioner = provisioner

    def on_add(self):
        self.set_label()

    def wait_dns_records(self):
        if not self.wait_dns:
            return

        # refresh the ipaddr advertized in status.json
        self.status_info()
        self.write_status_last()
        self.svc.print_status_data_eval()

        left = self.wait_dns
        time_max = self._current_time() + left
        self.log.info("wait address propagation to peers (wait_dns=%s)", print_duration(left))
        path = ".monitor.nodes.'%s'.services.status.'%s'.resources.'%s'.info.ipaddr~[0-9]" % (Env.nodename, self.svc.path, self.rid)
        try:
            result = self.svc.node._wait(path=path, duration=left)
        except KeyboardInterrupt:
            raise ex.Error("dns resolution not ready after %s (ip not in local dataset)" % print_duration(self.wait_dns))
        left = time_max - self._current_time()
        self.log.info("wait cluster sync (time left is %s)", print_duration(left))
        while left > 0:
            result = self.svc.node.daemon_get({
                "action": "sync",
                "options": {
                    "timeout": left
                },
            }, timeout=left+10)
            if result["status"] == 0:
                return
            wait_dns_records_delay_func(0.3)  # avoid fast-looping the listener
            left = time_max - self._current_time()
        raise ex.Error("dns resolution not ready after %s (cluster sync timeout)" % print_duration(self.wait_dns))

    @lazy
    def dns_name_suffix(self):
        try:
            dns_name_suffix = self.conf_get("dns_name_suffix").strip("'\"$#")
        except ex.OptNotFound:
            dns_name_suffix = None
        return dns_name_suffix

    def set_label(self):
        """
        Set the resource label property.
        """
        try:
             self.get_mask()
        except ex.Error:
             pass
        try:
            self.getaddr()
            addr = self.addr
        except ex.Error:
            addr = self.ipname
        self.label = "%s/%s %s" % (addr, self.netmask, self.ipdev)
        if self.ipname != addr:
            self.label += " " + self.ipname

    def _status_info(self):
        """
        Contribute resource key/val pairs to the resource info.
        """
        data = {}
        try:
            self.getaddr()
        except ex.Error:
            pass

        try:
            data["ipaddr"] = self.addr
        except:
            pass

        if self.ipdev:
            data["ipdev"] = self.ipdev
        if self.gateway:
            data["gateway"] = self.gateway
        if self.netmask is not None:
            data["netmask"] = to_cidr(self.netmask)
        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.Error:
            pass
        data = [
            ["ipaddr", self.addr],
            ["ipname", self.ipname],
            ["ipdev", self.ipdev],
            ["gateway", str(self.gateway)],
        ]
        if self.netmask is not None:
            data.append(["netmask", str(to_cidr(self.netmask))])
        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.Error("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.Error("could not resolve name %s: %s" % (self.ipname, str(exc)))

    def __str__(self):
        return "%s ipdev=%s ipname=%s" % (super(Ip, self).__str__(), \
                                         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_NETMASK'] = str(self.netmask)
        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', 'netmask']:
            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 core.status.DOWN
            else:
                return core.status.WARN
        ifconfig = self.get_ifconfig()
        intf = ifconfig.interface(self.ipdev)
        mode = getattr(self, "mode") if hasattr(self, "mode") else None
        if intf is None and self.base_ipdev is None and "dedicated" not in self.tags and mode != "dedicated":
            self.status_log("interface %s not found" % self.ipdev)
            return core.status.DOWN
        try:
            if self.is_up() and self.has_carrier(intf) is not False:
                return core.status.UP
            else:
                return core.status.DOWN
        except ex.NotSupported:
            self.status_log("not supported", "info")
            return core.status.NA
        except ex.Error as exc:
            self.status_log(str(exc), "error")
            return core.status.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 or self.ipdev in ("lo", "lo0"):
            return
        self.log.info("send gratuitous arp to announce %s is at %s", self.addr, self.ipdev)
        from utilities.arp import send_arp
        send_arp(self.ipdev, self.addr)

    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 add_link(self):
        pass

    def del_link(self):
        pass

    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()
        return self._is_up(ifconfig)

    def has_carrier(self, intf):
        if intf is None:
            return
        if not self.check_carrier:
            return
        mode = getattr(self, "mode") if hasattr(self, "mode") else None
        if "dedicated" in self.tags or mode == "dedicated":
            return
        if intf.flag_no_carrier:
            self.status_log("no carrier")
            return False
        return True

    def _is_up(self, ifconfig):
        intf = ifconfig.has_param("ipaddr", self.addr)
        if intf is not None:
            if isinstance(intf.ipaddr, list):
                idx = intf.ipaddr.index(self.addr)
                current_mask = to_cidr(intf.mask[idx])
            else:
                current_mask = to_cidr(intf.mask)
            if self.netmask is None:
                self.status_log("netmask is not set nor guessable")
            elif current_mask != to_cidr(self.netmask):
                self.status_log("current netmask %s, expected %s" %
                                (current_mask, to_cidr(self.netmask)))
            ref_dev = intf.name.split(":")[0]
            if self.type == "ip" and ref_dev != self.ipdev:
                self.status_log("current dev %s, expected %s" %
                                (ref_dev, self.ipdev))
            return True
        intf = ifconfig.has_param("ip6addr", self.addr)
        if intf is not None:
            if isinstance(intf.ip6addr, list):
                idx = intf.ip6addr.index(self.addr)
                current_mask = to_cidr(intf.ip6mask[idx])
            else:
                current_mask = to_cidr(intf.ip6mask)
            if current_mask != to_cidr(self.netmask):
                self.status_log("current netmask %s, expected %s" %
                                (current_mask, to_cidr(self.netmask)))
            ref_dev = intf.name.split(":")[0]
            if self.type == "ip" and ref_dev != self.ipdev:
                self.status_log("current dev %s, expected %s" %
                                (ref_dev, self.ipdev))
            return True
        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)
        self.add_link()
        ifconfig = self.get_ifconfig()
        intf = ifconfig.interface(self.ipdev)
        if self.has_carrier(intf) is False and not self.svc.options.force:
            self.log.error("interface %s no-carrier.", self.ipdev)
            raise ex.IpDevDown(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.
        """
        timeout = convert_duration(self.svc.options.waitlock)
        if timeout is None or timeout < 0:
            timeout = 120
        delay = 1
        lockfd = None
        action = "startip"
        lockfile = os.path.join(Env.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 = utilities.lock.lock(timeout=timeout, delay=delay, lockfile=lockfile, intent="startip")
        except utilities.lock.LockTimeout as exc:
            raise ex.Error("timed out waiting for lock %s: %s" % (details, str(exc)))
        except utilities.lock.LockNoLockFile:
            raise ex.Error("lock_nowait: set the 'lockfile' param %s" % details)
        except utilities.lock.LockCreateError:
            raise ex.Error("can not create lock file %s" % details)
        except utilities.lock.LockAcquire as exc:
            raise ex.Error("another action is currently running %s: %s" % (details, str(exc)))
        except ex.Signal:
            raise ex.Error("interrupted by signal %s" % details)
        except Exception as exc:
            self.save_exc()
            raise ex.Error("unexpected locking error %s: %s" % (details, str(exc)))

        if lockfd is not None:
            self.lockfd = lockfd

    def unlock(self):
        """
        Release the startip lock.
        """
        utilities.lock.unlock(self.lockfd)

    @staticmethod
    def get_ifconfig():
        """
        Wrapper around the os specific rcIfconfig module's ifconfig function.
        Return a parsed ifconfig dataset.
        """
        return utilities.ifconfig.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.Error
        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:
            try:
                self.arp_announce()
            except AttributeError:
                self.log.info("arp announce not supported")

        try:
            self.dns_update()
        except ex.Error as exc:
            self.log.error(str(exc))
        self.wait_dns_records()

    def get_mask(self, ifconfig=None):
        if ifconfig is None:
            ifconfig = self.get_ifconfig()
        if self.netmask is None:
            intf = ifconfig.interface(self.ipdev)
            if intf is None:
                raise ex.Error("netmask parameter is mandatory with 'noalias' tag")
            self.netmask = intf.mask
        if not self.netmask:
            if "noaction" not in self.tags:
                self.netmask = None
                raise ex.Error("No netmask set on parent interface %s" % self.ipdev)
        if isinstance(self.netmask, list):
            try:
                self.netmask = self.netmask[0]
            except IndexError:
                self.netmask = None

    def start_locked(self):
        """
        The start codepath fragment protected by the startip lock.
        """
        self.get_stack_dev()
        if self.stacked_dev is None:
            raise ex.Error("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.NotSupported:
            self.log.info("start ip not supported")
            ret = 0
            arp_announce = False

        if ret != 0:
            raise ex.Error("failed")

        return arp_announce

    def get_stack_dev(self):
        ifconfig = self.get_ifconfig()
        self.get_mask(ifconfig)
        if 'noalias' in self.tags:
            self.stacked_dev = self.ipdev
        elif self.forced_stacked_dev:
            self.stacked_dev = "" + self.forced_stacked_dev
        else:
            self.stacked_dev = ifconfig.get_stacked_dev(self.ipdev,
                                                        self.addr,
                                                        self.log)

    def dns_update(self):
        """
        Post a dns update request to the collector.
        """
        if self.svc.node.collector_env.dbopensvc is None:
            return

        if self.ipname is None:
            self.log.debug("skip dns update: ipname is not set")
            return

        try:
            self.conf_get("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.Error 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,
                path=self.dns_rec_name(),
            )
        except Exception as exc:
            raise ex.Error("dns update failed: "+str(exc))
        if "error" in data:
            raise ex.Error(data["error"])

        self.log.info("dns updated")

    def dns_rec_name(self):
        return self.svc.path

    def stop(self):
        """
        Stop the resource.
        """
        if self.ipname is None:
            self.log.info("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.Error

        try:
            ret = self.stopip_cmd()[0]
        except ex.NotSupported:
            self.log.info("stop ip not supported")
            return

        if ret != 0:
            self.log.error("failed")
            raise ex.Error

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

        self.del_link()

    def allocate(self):
        """
        Request an ip in the ipdev network from the collector.
        """
        if self.svc.node.collector_env.dbopensvc is None:
            return

        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.conf_get("network")
        except ex.OptNotFound:
            network = None

        if network is None:
            # implicit network: the network of the first ipdev ip
            ifconfig = self.get_ifconfig()
            intf = ifconfig.interface(self.ipdev)
            try:
                if isinstance(intf.ipaddr, list):
                    baseaddr = intf.ipaddr[0]
                else:
                    baseaddr = intf.ipaddr
                network = str(utilities.net.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,
                path=self.dns_rec_name(),
            )
        except Exception as exc:
            raise ex.Error("ip allocation failed: "+str(exc))
        if "error" in data:
            raise ex.Error(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.netmask 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.netmask = str(netmask)
        self.log.info("ip %s allocated", self.ipname)
        record_name = data["data"].get("record_name")
        if record_name:
            self.log.info("record %s created", record_name)

    def release(self):
        """
        Release an allocated ip a collector managed network.
        """
        if self.svc.node.collector_env.dbopensvc is None:
            return

        if self.ipname is None:
            self.log.info("skip release: no ipname set")
            return

        try:
            self.getaddr()
        except ex.Error:
            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,
                path=self.dns_rec_name(),
            )
        except Exception as exc:
            raise ex.Error("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_multi(["%s.ipname" % self.rid])
        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 = {}
        if "#" in expose:
           # expose data via reference
           resource = self.svc.get_resource(expose)
           data["type"] = resource.driver_basename
           data["port"] = resource.options.port
           data["protocol"] = resource.options.protocol
           try:
               data["host_port"] = resource.options.host_port
           except AttributeError:
               pass
           return data

        # expose data inline
        words = expose.split(":")
        if len(words) == 2:
            try:
                data["host_port"] = int(words[1])
            except ValueError:
                raise ex.Error("invalid host port format %s. expected integer" % words[1])
        words = re.split("[-/]", words[0])
        if len(words) != 2:
            raise ex.Error("invalid expose format %s. expected <nsport>/<proto>[:<hostport>]" % expose)
        try:
            data["port"] = int(words[0])
        except ValueError:
            raise ex.Error("invalid expose port format %s. expected integer" % words[0])
        if words[1] not in ("tcp", "udp"):
            raise ex.Error("invalid expose protocol %s. expected tcp or udp" % words[1])
        data["protocol"] = words[1]
        return data

    def provisioned(self):
        try:
            self.conf_get("ipname")
            return True
        except ex.OptNotFound:
            return False

    def provisioner(self):
        """
        Provision the ip resource, allocate an ip collector's side, and
        start it.
        """
        if self.kw_provisioner != "collector":
            return
        self.allocate()

    def unprovisioner(self):
        """
        Unprovision the ip resource, meaning unplumb and release collector's
        side.
        """
        if self.kw_provisioner != "collector":
            return
        self.release()

    def _current_time(self):
        return time.time()


# helper for mock
wait_dns_records_delay_func = delay
 0707010001f3d1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/ip/gce    0707010001f3d2000081a40000000000000000000000016a100daf00001a95000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/ip/gce/__init__.py    import json

import core.exceptions as ex
import core.status
from .. import \
    Ip, \
    KW_IPNAME, \
    KW_IPDEV, \
    KW_NETMASK, \
    KW_GATEWAY, \
    COMMON_KEYWORDS
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.subsystems.gce import GceMixin


DRIVER_GROUP = "ip"
DRIVER_BASENAME = "gce"
KEYWORDS = [
    KW_IPNAME,
    KW_IPDEV,
    KW_NETMASK,
    KW_GATEWAY,
    {
        "keyword": "routename",
        "at": True,
        "text": "Set the gce route name",
        "example": "rt-ip1"
    },
    {
        "keyword": "gce_zone",
        "at": True,
        "text": "Set the gce ip route next hop zone",
        "example": "europe-west1-b"
    },
] + COMMON_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("gcloud") and which("ip"):
        return ["ip.gce"]
    return []


class IpGce(Ip, GceMixin):
    def __init__(self,
                 routename=None,
                 gce_zone=None,
                 **kwargs):
        Ip.__init__(self, type="ip.gce", **kwargs)
        self.label = "gce ip %s@%s" % (self.ipname, self.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, Env.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": Env.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, Env.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", Env.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.Error("gcloud route describe returned with error: %s, %s" % (out, err))
        try:
            data = json.loads(out)
        except:
            raise ex.Error("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] != Env.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.Error 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.Error as e:
            self.status_log(str(e))
            gce_status = False

        s = local_status & gce_status
        if s:
            return core.status.UP
        else:
            return core.status.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.

   0707010001f3e3000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/ip/zone   0707010001f3e4000081a40000000000000000000000016a100daf00000777000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/ip/zone/__init__.py   import time

from subprocess import *

import core.exceptions as ex
import utilities.ifconfig

from drivers.resource.ip import KW_ZONE
from drivers.resource.ip.host.sunos import IpHost
from env import Env

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "zone"
KEYWORDS = [
    KW_ZONE,
]

def driver_capabilities(node=None):
    if Env.sysname == "SunOS":
        return ["ip.zone"]
    return []


class IpZone(IpHost):
    def __init__(self, zone=None, **kwargs):
        super(IpZone, self).__init__(type="ip.zone", **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 = utilities.ifconfig.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

 0707010001f3cb000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/ip/amazon 0707010001f3cc000081a40000000000000000000000016a100daf00002164000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/ip/amazon/__init__.py import core.exceptions as ex
import core.status
import utilities.ifconfig

from .. import Ip, COMMON_KEYWORDS, KW_IPNAME, KW_IPDEV, KW_NETMASK, KW_GATEWAY
from core.objects.svcdict import KEYS
from utilities.net.getaddr import getaddr
from utilities.subsystems.amazon import AmazonMixin

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "amazon"
KEYWORDS = [
    KW_IPNAME,
    KW_IPDEV,
    KW_NETMASK,
    KW_GATEWAY,
    {
        "keyword": "eip",
        "at": True,
        "text": "The public elastic ip to associate to :kw:`ipname`. The special ``allocate`` value tells the provisioner to assign a new public address.",
        "example": "52.27.90.63"
    },
    {
        "keyword": "cascade_allocation",
        "convert": "list",
        "default": [],
        "provisioning": True,
        "at": True,
        "text": "Set new allocated ip as value to other ip resources :kw:`ipname` parameter. The syntax is a whitespace separated list of ``<rid>.ipname[@<scope>]``.",
        "example": "ip#1.ipname ip#1.ipname@nodes"
    },
    {
        "keyword": "docker_daemon_ip",
        "provisioning": True,
        "at": False,
        "candidates": [True, False],
        "text": "Set new allocated ip as value as a :opt:`--ip <addr>` argument in the :kw:`DEFAULT.docker_daemon_args` parameter.",
        "example": "True"
    },

] + COMMON_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    import os
    from utilities.proc import which
    data = []
    if not which("ec2"):
        return data
    if os.path.exists("/sys/hypervisor/uuid"):
        with open("/sys/hypervisor/uuid", "r") as f:
           uuid = f.read().lower()
        if not uuid.startswith("ec2"):
           return data
    if os.path.exists("/sys/devices/virtual/dmi/id/product_uuid"):
        with open("/sys/devices/virtual/dmi/id/product_uuid", "r") as f:
           uuid = f.read().lower()
        if not uuid.startswith("ec2"):
           return data
    data.append("ip.amazon")
    return data


class IpAmazon(Ip, AmazonMixin):
    def __init__(self, eip=None, **kwargs):
        Ip.__init__(self, type="ip.amazon", **kwargs)
        self.eip = eip
        self.label = "ec2 ip %s@%s" % (self.ipname, self.ipdev)
        if eip:
            self.label += ", eip %s" % 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.Error("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 = utilities.ifconfig.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.Error("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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if s:
            return core.status.UP
        else:
            return core.status.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.Error("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.Error("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.Error("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


    def provisioner(self):
        self.provisioner_private()
        self.provisioner_public()
        self.provisioner_docker_ip()
        self.cascade_allocation()
        self.log.info("provisioned")
        self.start()
        return True

    def cascade_allocation(self):
        cascade = self.oget("cascade_allocation")
        if not cascade:
            return
        changes = []
        for e in cascade:
            try:
                rid, param = e.split(".")
            except:
                self.log.warning("misformatted cascade entry: %s (expected <rid>.<param>[@<scope>])" % e)
                continue
            if not rid in self.svc.cd:
                self.log.warning("misformatted cascade entry: %s (rid does not exist)" % e)
                continue
            self.log.info("cascade %s to %s" % (self.ipname, e))
            changes.append("%s.%s=%s" % (rid, param, self.ipname))
            self.svc.resources_by_id[rid].ipname = self.svc.conf_get(rid, param)
            self.svc.resources_by_id[rid].addr = self.svc.resources_by_id[rid].ipname
        self.svc.set_multi(changes)

    def provisioner_docker_ip(self):
        if not self.oget("docker_daemon_ip"):
            return
        args = self.svc.oget('DEFAULT', 'docker_daemon_args')
        args += ["--ip", self.ipname]
        self.svc.set_multi(["DEFAULT.docker_daemon_args=%s" % " ".join(args)])
        for r in self.svc.get_resources("container.docker"):
            # reload docker daemon args
            r.on_add()

    def provisioner_private(self):
        if self.ipname != "<allocate>":
            self.log.info("private ip already provisioned")
            return

        eni = self.get_network_interface()
        if eni is None:
            raise ex.Error("could not find ec2 network interface for %s" % self.ipdev)

        ips1 = set(self.get_instance_private_addresses())
        data = self.aws([
          "ec2", "assign-private-ip-addresses",
          "--network-interface-id", eni,
          "--secondary-private-ip-address-count", "1"
        ])
        ips2 = set(self.get_instance_private_addresses())
        new_ip = list(ips2 - ips1)[0]

        self.svc.set_multi(["%s.ipname=%s" % (self.rid, new_ip)])
        self.ipname = new_ip

    def provisioner_public(self):
        if self.eip != "<allocate>":
            self.log.info("public ip already provisioned")
            return

        data = self.aws([
          "ec2", "allocate-address",
          "--domain", "vpc",
        ])

        self.svc.set_multi(["%s.eip=%s" % (self.rid, data["PublicIp"])])
        self.eip = data["PublicIp"]

0707010001f3df000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/ip/route  0707010001f3e0000081a40000000000000000000000016a100daf00001153000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/ip/route/__init__.py  import os

import core.exceptions as ex
import core.status
import utilities.ifconfig

from env import Env
from core.objects.svcdict import KEYS
from core.resource import Resource
from utilities.lazy import lazy
from utilities.proc import justcall, which

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "route"
KEYWORDS = [
    {
        "keyword": "netns",
        "at": True,
        "required": True,
        "text": "The resource id of the container to plumb the ip into.",
        "example": "container#0"
    },
    {
        "keyword": "spec",
        "at": True,
        "required": True,
        "convert": "shlex",
        "text": "The route specification, passed to the ip route commands, with the appropriate network namespace set.",
    },
]
KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if Env.sysname == "Linux" and which("ip"):
        return ["ip.route"]
    return []

class IpRoute(Resource):
    def __init__(self,
                 netns=None,
                 spec=None,
                 **kwargs):
        super(IpRoute, self).__init__(type="ip.route", **kwargs)
        self.container_rid = str(netns)
        self.spec = spec
        self.tags = self.tags | set(["docker"])
        self.tags.add(self.container_rid)
        self.label = " ".join(spec)

    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)

    def start_route(self):
        if self.netns is None:
            raise ex.Error("unable to find the network namespace")
        try:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "replace"] + self.spec
            ret, out, err = self.vcall(cmd)
        except ex.Error:
            pass
        if ret != 0:
            raise ex.Error

    def stop_route(self):
        if self.netns is None:
            self.log.info("skip: unable to find the network namespace")
            return
        try:
            cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "del"] + self.spec
            ret, out, err = self.vcall(cmd)
        except ex.Error:
            pass
        if ret != 0:
            raise ex.Error

    def is_up(self):
        try:
            self.netns
        except ex.Error:
            return False
        if self.netns is None:
            return False
        cmd = [Env.syspaths.nsenter, "--net="+self.netns, "ip", "route", "list"] + self.spec
        out, err, ret = justcall(cmd)
        return ret == 0 and out.strip() != ""

    def _status(self, verbose=False):
        self.unset_lazy("netns")
        if self.is_up():
            return core.status.UP
        return core.status.DOWN

    def start(self):
        if self.is_up():
            self.log.info("route is already up")
            return
        self.start_route()

    def stop(self):
        if not self.is_up():
            self.log.info("route is already down")
            return
        self.stop_route()

    @lazy
    def container(self):
        if self.container_rid not in self.svc.resources_by_id:
            raise ex.Error("rid %s not found" % self.container_rid)
        return self.svc.resources_by_id[self.container_rid]

    @lazy
    def netns(self):
        if self.container.type in ("container.docker", "container.podman"):
            path = self.sandboxkey()
            if os.path.exists(path):
                return path
            # compat with older netns location
            path = path.replace("/services/", "/svc/")
            if os.path.exists(path):
                return path
            return
        elif self.container.type in ("container.lxd", "container.lxc"):
            return self.container.cni_netns()
        raise ex.Error("unsupported container type: %s" % self.container.type)

    def sandboxkey(self):
        sandboxkey = self.container.container_sandboxkey()
        if sandboxkey is None:
            raise ex.Error("failed to get sandboxkey")
        sandboxkey = str(sandboxkey).strip()
        if "'" in sandboxkey:
            sandboxkey = sandboxkey.replace("'","")
        if sandboxkey == "":
            raise ex.Error("sandboxkey is empty")
        return sandboxkey

 0707010001f3d3000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host   0707010001f3d5000081a40000000000000000000000016a100daf000003bd000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/aix.py    import utilities.ping

from .. import Ip
from utilities.net.converters import to_cidr, to_dotted

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.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.netmask)])]
        else:
            cmd = ['ifconfig', self.ipdev, self.addr, 'netmask', to_dotted(self.netmask), '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)

   0707010001f3db000081a40000000000000000000000016a100daf00000398000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/sunos.py  import utilities.ping
from utilities.net.converters import cidr_to_dotted, to_cidr

from .. import Ip

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(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=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.check_ping(self.addr, timeout=timeout)

    def startip_cmd(self):
        cmd = [
            "/usr/sbin/ifconfig", self.stacked_dev,
            "plumb", self.addr,
            "netmask", cidr_to_dotted(to_cidr(self.netmask)), "broadcast", "+", "up",
        ]
        return self.vcall(cmd)

    def stopip_cmd(self):
        cmd = ["/usr/sbin/ifconfig", self.stacked_dev, "unplumb"]
        return self.vcall(cmd)
0707010001f3da000081a40000000000000000000000016a100daf000003cb000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/osf1.py   import utilities.ping

from .. import Ip
from utilities.net.converters import to_cidr, to_dotted

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.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.netmask)]), 'add']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', 'alias', self.addr, 'netmask', to_dotted(self.netmask)]
        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)

 0707010001f3d7000081a40000000000000000000000016a100daf000003c9000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/freebsd.py    import utilities.ping

from .. import Ip
from utilities.net.converters import to_cidr, to_dotted

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.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.netmask)]), 'add']
        else:
            cmd = ['ifconfig', self.ipdev, 'inet', self.addr, 'netmask', to_dotted(self.netmask), '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)

   0707010001f3d9000081a40000000000000000000000016a100daf00000dc0000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/linux.py  import core.exceptions as ex
import utilities.ping

from .. import Ip
from env import Env
from utilities.net.converters import to_cidr, to_dotted
from utilities.proc import which, justcall

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.check_ping(self.addr, timeout=timeout, count=count)

    def has_macvtap_link(self):
        cmd = [Env.syspaths.ip, "link", "ls", "dev", self.ipdev]
        out, err, ret = justcall(cmd)
        mark = " %s@%s: " % (self.ipdev, self.base_ipdev)
        return ret == 0 and mark in out

    def del_macvtap_link(self):
        if not self.base_ipdev:
            return
        if not self.has_macvtap_link():
            return
        cmd = [Env.syspaths.ip, "link", "del", "link", "dev", self.ipdev, "type", "macvtap"]
        ret, out, err = self.vcall(cmd)
        if ret:
            raise ex.Error

    def add_macvtap_link(self, mode="bridge"):
        if not self.base_ipdev:
            return
        if self.has_macvtap_link():
            return
        cmd = [Env.syspaths.ip, "link", "add", "link", self.base_ipdev, "name", self.ipdev, "type", "macvtap", "mode", mode]
        ret, out, err = self.vcall(cmd)
        if ret:
            raise ex.Error

    def add_link(self):
        self.add_macvtap_link()

    def del_link(self):
        self.del_macvtap_link()

    def start_link(self):
        if which(Env.syspaths.ip):
           cmd = [Env.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 stop_link(self):
        self.del_macvtap_link()

    def startip_cmd(self):
        if which("ifconfig") and self.alias:
            if ':' in self.addr:
                cmd = ['ifconfig', self.ipdev, 'inet6', 'add', '/'.join([self.addr, to_cidr(self.netmask)])]
            else:
                cmd = ['ifconfig', self.stacked_dev, self.addr, 'netmask', to_dotted(self.netmask), 'up']
        else:
            cmd = [Env.syspaths.ip, "addr", "add", '/'.join([self.addr, to_cidr(self.netmask)]), "dev", self.ipdev]

        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 utilities.ping.check_ping(self.addr, timeout=1, count=1):
                return ret, out, err
        self.log.error("timed out waiting for ip activation")
        raise ex.Error

    def stopip_cmd(self):
        if which("ifconfig") and self.alias:
            if ':' in self.addr:
                cmd = ['ifconfig', self.ipdev, 'inet6', 'del', '/'.join([self.addr, to_cidr(self.netmask)])]
            else:
                if self.stacked_dev is None:
                    return 1, "", "no stacked dev found"
                if ":" in self.stacked_dev:
                    cmd = ['ifconfig', self.stacked_dev, 'down']
                else:
                    cmd = [Env.syspaths.ip, "addr", "del", '/'.join([self.addr, to_cidr(self.netmask)]), "dev", self.ipdev]
        else:
            cmd = [Env.syspaths.ip, "addr", "del", '/'.join([self.addr, to_cidr(self.netmask)]), "dev", self.ipdev]
        return self.vcall(cmd)

0707010001f3dc000081a40000000000000000000000016a100daf000006c1000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/windows.py    """
This module implements the Windows ip resource driver
"""

import time

import core.exceptions as ex
import utilities.ping

from .. import Ip

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"


class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.check_ping(self.addr, timeout=timeout, count=count)

    def startip_cmd(self):
        #netsh interface ip add address "Local Area Connection" 33.33.33.33 255.255.255.255
        if ":" in self.addr:
            if "." in self.netmask:
                self.log.error("netmask parameter is mandatory for ipv6 adresses")
                raise ex.Error
            cmd = ["netsh", "interface", "ipv6", "add", "address", self.ipdev, self.addr, self.netmask]
        else:
            cmd = ["netsh", "interface", "ipv4", "add", "address", self.ipdev, self.addr, self.netmask]

        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 utilities.ping.check_ping(self.addr, timeout=1, count=1):
                return ret, out, err
            time.sleep(1)
        self.log.error("timed out waiting for ip activation")
        raise ex.Error

    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)

   0707010001f3d4000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/__init__.py   0707010001f3d6000081a40000000000000000000000016a100daf000003b3000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/darwin.py import utilities.ping

from .. import Ip
from utilities.net.converters import to_cidr

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.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.netmask)]), '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)

 0707010001f3d8000081a40000000000000000000000016a100daf000004c6000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/ip/host/hpux.py   import core.exceptions as ex
import utilities.ping

from .. import Ip
from utilities.net.converters import to_cidr, to_dotted

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "host"

class IpHost(Ip):
    def check_ping(self, count=1, timeout=5):
        self.log.info("checking %s availability (%ss)", self.addr, timeout)
        return utilities.ping.check_ping(self.addr, count=count, timeout=timeout)

    def arp_announce(self):
        """
        Noop. The arp announce is already 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.Error
            cmd = ['ifconfig', self.stacked_dev, 'inet6', self.addr+'/'+to_cidr(self.netmask), 'up']
        else:
            cmd = ['ifconfig', self.stacked_dev, self.addr, 'netmask', to_dotted(self.netmask), '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)

  0707010001f3cf000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/ip/crossbow   0707010001f3d0000081a40000000000000000000000016a100daf000018d9000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/ip/crossbow/__init__.py   import time

from subprocess import *

import core.exceptions as ex

from drivers.resource.ip.host.sunos import IpHost
from drivers.resource.ip import COMMON_KEYWORDS, KW_IPNAME, KW_IPDEV, KW_NETMASK, KW_GATEWAY
from env import Env
from core.objects.svcdict import KEYS
from utilities.net.converters import to_cidr
from utilities.proc import justcall, which

DRIVER_GROUP = "ip"
DRIVER_BASENAME = "crossbow"
KEYWORDS = [
    KW_IPNAME,
    KW_IPDEV,
    {
        "keyword": "ipdevext",
        "at": True,
        "example": "v4",
        "default": "v4",
        "text": "The interface name extension for crossbow ipadm configuration."
    },
    KW_NETMASK,
    KW_GATEWAY,
] + COMMON_KEYWORDS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("ipadm"):
        return ["ip.crossbow"]
    return []

class IpCrossbow(IpHost):
    def __init__(self, ipdevext="v4", **kwargs):
        self.ipdevext = ipdevext
        super(IpCrossbow, self).__init__(type="ip.crossbow", **kwargs)
        if 'noalias' not in self.tags:
            self.tags.add('noalias')

    def set_label(self):
        """
        Set the resource label property.
        """
        try:
             self.get_mask()
        except ex.Error:
             pass
        try:
            self.getaddr()
            addr = self.addr
        except ex.Error:
            addr = self.ipname
        self.label = "%s/%s %s/%s" % (addr, to_cidr(self.netmask), self.ipdev, self.ipdevext)
        if self.ipname != addr:
            self.label += " " + self.ipname

    def stopip_cmd(self):
        if not which(Env.syspaths.ipadm):
            raise ex.Error("crossbow ips are not supported on this system")
        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=[Env.syspaths.ipadm, 'delete-addr', self.stacked_dev+'/'+self.ipdevext]
        r, o, e =  self.vcall(cmd)
        ret += r
        out += o
        err += e
        cmd = [Env.syspaths.ipadm, 'show-addr', '-p', '-o', 'state', self.stacked_dev ]
        _out, _, _ = justcall(cmd)
        _out = _out.strip().split("\n")
        if len(_out) > 0:
            self.log.info("skip delete-ip because addrs still use the ip")
            return ret, out, err
        cmd=[Env.syspaths.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]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return "undef"
        return out.strip()

    def startip_cmd(self):
        if not which(Env.syspaths.ipadm):
            raise ex.Error("crossbow ips are not supported on this system")
        if self.netmask is None:
            raise ex.Error("netmask not specified nor guessable")
        self.wait_net_smf()
        ret, out, err = (0, '', '')
        cmd = [Env.syspaths.ipadm, 'show-if', '-p', '-o', 'state', self.stacked_dev]
        _out, err, ret = justcall(cmd)
        _out = _out.strip().split("\n")
        if len(_out) == 0:
            cmd=[Env.syspaths.ipadm, 'create-ip', '-t', self.stacked_dev ]
            r, o, e = self.vcall(cmd)
        cmd=[Env.syspaths.ipadm, 'create-addr', '-t', '-T', 'static', '-a', self.addr+"/"+to_cidr(self.netmask), self.stacked_dev+'/'+self.ipdevext]
        r, o, e = self.vcall(cmd)
        if r != 0:
            cmd=[Env.syspaths.ipadm, 'show-if' ]
            self.vcall(cmd)
            raise ex.Error("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 = [Env.syspaths.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.netmask is None:
            self.status_log("netmask not specified nor guessable")
        elif _mask != to_cidr(self.netmask):
            self.status_log("wrong netmask: %s, expected %s" % (_mask, to_cidr(self.netmask)))
            return True
        return True

   0707010001f3f0000041ed0000000000000000000000176a102a9300000000000000e600010003ffffffffffffffff0000003500000000root/usr/share/opensvc/opensvc/drivers/resource/sync  0707010001f3f1000081a40000000000000000000000016a100daf000024cb000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/sync/__init__.py  import datetime
import json
import os

import core.exceptions as ex
import core.status
from utilities.converters import convert_speed, print_size
from env import Env
from core.scheduler import SchedOpts
from utilities.lazy import lazy
from core.resource import Resource
from utilities.string import bdecode

def notify(func):
    """
    A decorator in charge of notifying the daemon of sync task
    termination.
    """
    def _func(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        finally:
            self.notify_done()
    return _func

class Sync(Resource):
    default_optional = True

    def __init__(self,
                 sync_max_delay=None,
                 schedule=None,
                 **kwargs):
        self.pausable = True
        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": {},
        }

        Resource.__init__(self, **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.Error("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=600):
        """ Return False if timestamp is fresher than now-interval
            Return True otherwize.
            Zero is a infinite interval
        """
        if delay == 0:
            raise ex.Error("sync_max_delay cannot be 0")
        limit = ts + datetime.timedelta(seconds=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 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.
        """
        try:
            dst = getattr(self, "dst")
        except AttributeError:
            raise ex.Error("the 'dst' attribute is not set")
        try:
            dstfs = getattr(self, "dstfs")
        except AttributeError:
            raise ex.Error("the 'dstfs' attribute is not set")
        if dstfs is None:
            # No dstfs check has been configured. Assume the admin knows better.
            return True
        ruser = self.svc.node.get_ruser(node)
        cmd = Env.rsh.split(' ')+['-l', ruser, node, '--', 'LANG=C', 'df', dstfs]
        (ret, out, err) = self.call(cmd, cache=True, errlog=False)
        if ret != 0:
            raise ex.Error

        """
        # 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 dstfs+'(' not in out and 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"%(dstfs, node, dst))
            return False
        return True

    def pre_sync_check_svc_not_up(self):
        s = self.svc.group_status(excluded_groups=set(["app", "sync", "task", "disk.scsireserv"]))
        if s['avail'].status == core.status.UP:
            return
        if s['avail'].status == core.status.NA and \
           s['overall'].status == core.status.UP:
            return
        if self.svc.options.force and \
           s['avail'].status not in (core.status.DOWN, core.status.NA) and \
           s['overall'].status not in (core.status.DOWN, core.status.NA):
            self.log.info("allow sync, even though reference resources "
                          "aggregated status is %s/%s, because --force is set"
                          "" % (s['avail'], s['overall']))
            return
        if not self.svc.options.cron:
            self.log.info("skip: reference resources aggregated status "
                          "is %s/%s" % (s['avail'], s['overall']))
        raise ex.AbortAction

    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 != Env.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.AbortAction

    def pre_sync_check_prd_svc_on_non_prd_node(self):
        if self.svc.svc_env == 'PRD' and self.svc.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.AbortAction

    def sync_status(self, *args, **kwargs):
        """
        Placeholder
        """
        return core.status.UNDEF

    def _status(self, **kwargs):
        if self.svc.running_action in ("stop", "shutdown") and not self.svc.command_is_scoped():
            return core.status.NA
        if not self.svc.running_action and self.paused():
            # status eval
            return core.status.NA
        return self.sync_status(**kwargs)

    def paused(self):
        """
        Return True if the aggregated service status is not up, in which
        case we don't care about computing a status for the sync resource.

        Drivers with pausable=False are never paused.
        """
        if not self.pausable:
            return False
        try:
            data = self.svc.node._daemon_status(selector=self.svc.path)
        except Exception:
            data = None
        try:
            paths = data["monitor"]["services"]
        except (KeyError, TypeError):
            # the daemon is not returning proper status data
            paths = {}
        if self.svc.path in paths:
            avail = paths[self.svc.path]["avail"]
            if avail != "up":
                self.status_log("paused, service not up", "info")
                return True
        return False

    @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 = {}
        if not buff:
            return 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, *args, **kwargs):
        try:
            self._update_stats(*args, **kwargs)
        except (KeyError, AttributeError):
            pass

    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

    def notify_done(self):
        self.svc.notify_done("sync_all", rids=[self.rid])

    def schedule_options(self):
        return {
            "sync_all": SchedOpts(
                self.rid,
                fname="last_syncall_"+self.rid,
                schedule_option="sync_schedule" if self.rid != "sync#i0" else "sync#i0_schedule"
            )
        }
 0707010001f40b000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/radossnap    0707010001f40c000081a40000000000000000000000016a100daf00001846000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/radossnap/__init__.py    import datetime
import json

import core.exceptions as ex
import core.status
from .. import Sync, notify
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "radossnap"
KEYWORDS = [
    {
        "keyword": "images",
        "convert": "list",
        "required": True,
        "text": "The rados image names handled by this sync resource. whitespace separated."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("rbd"):
        data.append("sync.radossnap")
    return data


class SyncRadossnap(Sync):
    def __init__(self,
                 images=None,
                 client_id=None,
                 keyring=None,
                 **kwargs):
        super(SyncRadossnap, self).__init__(type="sync.radossnap", **kwargs)

        if images is None:
            images = []
        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 __str__(self):
        return "%s images=%s" % (
            super(SyncRadossnap, self).__str__(),
            ", ".join(self.images)
        )

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

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

    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 core.status.WARN

        try:
            data = self.get_last()
        except Exception as e:
            self.status_log(str(e))
            return core.status.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(seconds=self.sync_max_delay):
                expired.append(image)
            else:
                ok.append(image)

        r = core.status.UP

        if len(nosnap) > 0:
            self.status_log("no snap found for images: "+", ".join(nosnap))
            r = core.status.WARN
        if len(expired) > 0:
            self.status_log("snap too old for images: "+", ".join(expired))
            r = core.status.WARN

        return r

    @notify
    def sync_update(self):
        self.recreate()

    def sync_resync(self):
        self.recreate()

    def validate_image_fmt(self):
        l = []
        for image in self.images:
            if image.count("/") != 1:
                l.append(image)
        if len(l) > 0:
            raise ex.Error("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]+"..."
  0707010001f3f8000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/docker   0707010001f3f9000081a40000000000000000000000016a100daf000019e0000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/sync/docker/__init__.py   from subprocess import Popen, PIPE

import core.status
import core.exceptions as ex
import utilities.subsystems.docker as dockerlib

from .. import Sync, notify
from utilities.lazy import lazy
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.subsystems.docker import has_docker

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "docker"
DRIVER_BASENAME_ALIASES = ["oci"]
KEYWORDS = [
    {
        "keyword": "target",
        "convert": "list",
        "at": True,
        "required": True,
        "candidates": ["nodes", "drpnodes"],
        "text": "Destination nodes of the sync."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)

def driver_capabilities(node=None):
    data = []
    if has_docker(["docker", "docker.io"]):
        data.append("container.docker")
    return data

class SyncDocker(Sync):
    def __init__(self, target=None, **kwargs):
        super(SyncDocker, self).__init__(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()

    @lazy
    def lib(self):
        """
        Lazy allocator for the dockerlib object.
        """
        try:
            return self.svc.dockerlib
        except AttributeError:
            self.svc.dockerlib = dockerlib.DockerLib(self.svc)
            return self.svc.dockerlib

    def handled_resources(self):
        for res in self.svc.get_resources("container.docker"):
            yield res

    def _info(self):
        data = [
          ["target", " ".join(self.target) if self.target else ""],
        ]
        data += self.stats_keys()
        return data

    def get_container_data_dir_svc_fs(self):
        l = []
        for r in self.svc.get_resources("fs"):
            l.append(r.mount_point)
        l = sorted(l)
        v = self.lib.container_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.handled_resources():
            image_id = self.lib.get_image_id(r.image)
            if image_id is None:
                continue
            self.image_id_name[image_id] = r.image
            self.images.append(image_id)

    def get_remote_images(self, node):
        ruser = self.svc.node.get_ruser(node)
        cmd = Env.rsh.split()+['-l', ruser, node, '--', Env.paths.om, "svc", "-s", self.svc.path, "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_container_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([Env.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.Error:
                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.Error:
            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 = [Env.paths.om, "svc", "-s", self.svc.path, "docker", "save", self.image_id_name[image]]
        load_cmd = Env.rsh.split(' ')+['-l', ruser, node, '--', Env.paths.om, "svc", "-s", self.svc.path, "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.Error("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 core.status.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 core.status.WARN
        return core.status.UP

    @notify
    def sync_all(self):
        self.sync_nodes()
        self.sync_drp()
0707010001f3f6000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/dds  0707010001f3f7000081a40000000000000000000000016a100daf00003e13000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/sync/dds/__init__.py  import datetime
import os

from subprocess import *

import core.exceptions as ex
import core.status
import utilities.devices.linux

from .. import Sync, notify
from utilities.converters import print_duration
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import which
from utilities.lazy import lazy

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "dds"
KEYWORDS = [
    {
        "keyword": "src",
        "required": True,
        "text": "Points the origin of the snapshots to replicate from."
    },
    {
        "keyword": "dst",
        "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."
    },
    {
        "keyword": "target",
        "convert": "list",
        "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."
    },
    {
        "keyword": "snap_size",
        "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."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("dds") and which("lvcreate"):
        return ["sync.dds"]
    return []

class SyncDds(Sync):
    def __init__(self,
                 target=None,
                 src=None,
                 dst=None,
                 snap_size=0,
                 **kwargs):
        super(SyncDds, self).__init__(type="sync.dds", **kwargs)
        self.label = "dds of %s to %s" % (src, ", ".join(target))
        self.target = target
        self.src = src
        self.dst = dst
        self.snap_size = snap_size

    def __str__(self):
        return "%s target=%s src=%s" % (
            super(SyncDds, self).__str__(),
            self.target,
            self.src
        )

    @lazy
    def dsts(self):
        data = {}
        for node in self.svc.nodes | self.svc.drpnodes:
            data[node] = self.oget("dst", impersonate=node)

        if len(data) == 0:
            for node in self.svc.nodes | self.svc.drpnodes:
                data[node] = self.src
        return data

    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.remove_snap1()
                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.Error
                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 = [Env.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.Error
        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.Error

    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.Error
        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) = utilities.devices.linux.lv_info(self, self.src)
        if self.src_lv is None:
            self.log.error("unable to fetch source logical volume information")
            raise ex.Error
        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([Env.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([Env.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.AbortAction:
            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 = Env.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)
        buff = p2.communicate()
        if p2.returncode == 0:
            stats_buff = buff[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.Error
        self.push_statefile(node)

    def get_snap1_uuid(self):
        cmd = [Env.syspaths.lvs, '--noheadings', '-o', 'uuid', self.snap1]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.Error
        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 = Env.rcp.split() + [self.statefile, node+':'+self.statefile]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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.Error("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 = Env.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.Error
        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.Error

    def rename_snap2(self):
        if not self.snap_exists(self.snap2):
            self.log.error("%s should exist"%self.snap2)
            raise ex.Error
        if self.snap_exists(self.snap1):
            self.log.error("%s should not exist"%self.snap1)
            raise ex.Error
        cmd = ['lvrename', self.src_vg, self.snap2_lv, self.snap1_lv]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

    def get_remote_state(self, node):
        self.set_statefile()
        cmd1 = ['env', 'LANG=C', 'cat', self.statefile]
        cmd = Env.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.Error
        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 = Env.nodename
        lines = out.strip().split('\n')
        if len(lines) != 1:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.Error
        fields = lines[0].split(';')
        if len(fields) != 2:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.Error
        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 != Env.nodename:
            cmd = Env.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(Env.nodename, self.snap1)
        self.log.info("md5 %s: %s"%(Env.nodename, self.checksums[Env.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.Error
        err = False
        for n in self.targets:
            if self.checksums[Env.nodename] != self.checksums[n]:
                self.log.error("src/dst checksums differ for %s/%s"%(Env.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):
        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(seconds=self.sync_max_delay)
        except ex.Error:
            self.status_log("failed to get status")
            return core.status.WARN
        except IOError:
            self.status_log("dds state file not found")
            return core.status.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return core.status.WARN
        if last < now - delay:
            self.status_log("Last sync on %s older than %s"%(last, print_duration(self.sync_max_delay)))
            return core.status.WARN
        return core.status.UP

    def _info(self):
        data = [
          ["src", self.src],
          ["target", " ".join(self.target) if self.target else ""],
        ]
        data += self.stats_keys()
        return data

    @notify
    def sync_all(self):
        self.sync_nodes()
        self.sync_drp()
 0707010001f40d000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/rsync    0707010001f40e000081a40000000000000000000000016a100daf00004fc7000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/sync/rsync/__init__.py    import os
import glob

import core.exceptions as ex
import core.status
import datetime

from .. import Sync, notify
from utilities.converters import convert_speed
from env import Env
from utilities.cache import cache
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which, drop_option
from utilities.string import bdecode

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "rsync"
KEYWORDS = [
    {
        "keyword": "src",
        "convert": "list",
        "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."
    },
    {
        "keyword": "dst",
        "required": True,
        "text": "Destination of the sync. Beware of the meaningful ending '/'. Refer to the rsync man page for details."
    },
    {
        "keyword": "tags",
        "convert": "set",
        "default": set(),
        "default_text": "",
        "example": "delay_snap",
        "at": True,
        "text": "The sync resource supports the :c-tag:`delay_snap` tag. This tag is used to delay the snapshot creation just before the rsync, thus after :kw:`postsnap_trigger` execution. The default behaviour (no tags) is to group all snapshots creation before copying data to remote nodes, thus between :kw:`presnap_trigger` and :kw:`postsnap_trigger`."
    },
    {
        "keyword": "options",
        "convert": "shlex",
        "default": [],
        "default_text": "",
        "example": "--acls --xattrs --exclude foo/bar",
        "at": True,
        "text": "A whitespace-separated list of params passed unchanged to rsync. Typical usage is ACL preservation activation."
    },
    {
        "keyword": "reset_options",
        "convert": "boolean",
        "default": False,
        "at": True,
        "text": "Use options as-is instead of appending options to default hardcoded options. Can be used to disable --xattr or --acls for example."
    },
    {
        "keyword": "target",
        "convert": "list",
        "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'."
    },
    {
        "keyword": "snap",
        "at": True,
        "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."
    },
    {
        "keyword": "dstfs",
        "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)."
    },
    {
        "keyword": "bwlimit",
        "convert": "integer",
        "text": "Bandwidth limit in KB applied to this rsync transfer. Leave empty to enforce no limit. Takes precedence over :kw:`bwlimit` set in [DEFAULT]."
    },
]
DEPRECATED_KEYWORDS = {
    "sync.rsync.exclude": None,
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("rsync"):
        data.append("sync.rsync")

    cmd = ['rsync', '--version']
    out, _, ret = justcall(cmd)
    if ret != 0:
        return data

    if 'no xattrs' not in out:
        data.append("sync.rsync.xattrs")
    if 'no ACLs' not in out:
        data.append("sync.rsync.acls")
    return data


def snap_factory():
    if Env.sysname == 'Linux':
        from utilities.snap.lvm.linux import Snap
    elif Env.sysname == 'HP-UX':
        from utilities.snap.vxfs.hpux import Snap
    elif Env.sysname == 'AIX':
        from utilities.snap.jfs2.aix import Snap
    elif Env.sysname in ['SunOS', 'FreeBSD']:
        from utilities.snap.zfs.sunos import Snap
    elif Env.sysname in ['OSF1']:
        from utilities.snap.advfs.osf1 import Snap
    else:
        raise ex.Error
    return Snap

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.Error("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 SyncRsync(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 __init__(self,
                 src=None,
                 dst=None,
                 options=None,
                 target=None,
                 dstfs=None,
                 snap=False,
                 bwlimit=None,
                 internal=False,
                 reset_options=False,
                 **kwargs):
        super(SyncRsync, self).__init__(type="sync.rsync", **kwargs)

        if target is None:
            target = []
        if options is None:
            options = []
        if src is None:
            src = []
        glob_src = []
        for s in src:
            glob_src += glob.glob(s)
        src = glob_src
        if internal:
            if Env.paths.drp_path in dst:
                self.label = "rsync system files to drpnodes"
            else:
                self.label = "rsync svc config to %s"%(', '.join(sorted(sorted(target))))
        else:
            _src = ', '.join(sorted(src))
            if len(_src) > 300:
                _src = _src[0:300]
            _dst = ', '.join(sorted(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
        self.reset_options = reset_options
        self.presync_done = False

    def __str__(self):
        return "%s src=%s dst=%s options=%s target=%s" % (
            super(SyncRsync, self).__str__(),
            self.src,
            self.dst,
            self.full_options,
            self.target
        )

    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()

        if Env.nodename in self.svc.drpnodes:
            self.log.debug("drp node not allowed to sync nodes nor 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([Env.nodename])

        if len(targets) == 0:
            return set()

        for node in targets.copy():
            if state == "late" and not self.node_need_sync(node):
                targets.remove(node)
                continue

        if len(targets) == 0:
            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 Env.rsh.startswith("/usr/bin/ssh") and Env.sysname == "SunOS":
            # SunOS "ssh -n" doesn't work with rsync
            rsh = Env.rsh.replace("-n", "")
        else:
            rsh = Env.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, Env.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):
        if target not in self.target:
            return

        targets = self.nodes_to_sync(target)

        if len(targets) == 0:
            raise ex.syncNoNodesToSync

        self.add_resource_files_to_sync()
        self.resources_presync()

        if "delay_snap" in self.tags:
            if not hasattr(self.rset, 'snaps'):
                Snap = snap_factory()
                self.rset.snaps = 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") and self.rid != "sync#i0":
            # The pre_action() has provided us with a better source
            # to sync from. Use that
            src = getattr(self, "alt_src")
        else:
            src = self.src

        if len(src) == 0:
            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)
            stats = self.parse_rsync(out)
            self.update_stats(stats, target=node)
            self.remote_postsync(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()
        self.pre_sync_check_svc_not_up()

        """ 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_all":
                rtargets[i] |= r.nodes_to_sync('nodes')
                rtargets[i] |= r.nodes_to_sync('drpnodes')
            elif action == "sync_nodes":
                rtargets[i] |= r.nodes_to_sync('nodes')
            elif action == "sync_drp":
                rtargets[i] |= r.nodes_to_sync('drpnodes')
            for node in rtargets[i].copy():
                if 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.AbortAction

        if not need_snap:
            self.rset.log.debug("snap not needed")
            return

        Snap = snap_factory()
        try:
            self.rset.snaps = Snap(self.rid)
            self.rset.snaps.set_logger(self.rset.log)
            self.rset.snaps.try_snap(self.rset, action)
        except ex.syncNotSnapable:
            raise ex.Error

    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 remote_postsync(self, nodename):
        """
        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.svc.daemon_service_action(action="postsync", options={"waitlock": "1h"},
                                       server=nodename, sync=False,
                                       collect=False)

    def sync_nodes(self):
        self.pre_sync_check_svc_not_up()
        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):
        self.pre_sync_check_svc_not_up()
        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
        """
        if len(self.src) == 0:
            self.status_log("no files to sync", "info")
            return core.status.NA

        target = set()
        for i in self.target:
            target |= self.target_nodes(i)
        if len(target - set([Env.nodename])) == 0:
            self.status_log("no destination nodes", "info")
            return core.status.NA

        try:
            options = [] + self.full_options
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN

        """ sync state on nodes where the service is not UP
        """
        s = self.svc.group_status(excluded_groups=set(["app", "sync", "task", "disk.scsireserv"]))
        if s['avail'].status != core.status.UP or \
           (self.svc.topology == 'flex' and \
            Env.nodename != self.svc.flex_primary and \
            s['avail'].status == core.status.UP):
            if Env.nodename not in target:
                self.status_log("passive node not in destination nodes", "info")
                return core.status.NA
            if self.node_need_sync(Env.nodename):
                self.status_log("passive node needs update")
                return core.status.WARN
            else:
                return core.status.UP

        """ sync state on DRP nodes where the service is UP
        """
        if 'drpnodes' in self.target and Env.nodename in self.target_nodes('drpnodes'):
            self.status_log("service up on drp node, sync disabled", "info")
            return core.status.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 core.status.UP

        self.status_log("%s need update"%', '.join(sorted(nodes)))
        return core.status.DOWN

    @lazy
    def full_options(self):
        if self.reset_options:
            options = self.options
        else:
            options = ["-HAXpogDtrlvx", "--stats", "--delete", "--force"] + self.options
        if not self.has_capability("sync.rsync.xattrs"):
            options = drop_option("-X", options)
        if not self.has_capability("sync.rsync.acls"):
            options = drop_option("-A", options)
        options += ["--timeout=%s" % self.timeout]
        return options

    def add_resource_files_to_sync(self):
        if self.rid != "sync#i0":
            return
        for r in self.svc.get_resources():
            self.src += r.files_to_sync()

    def resources_presync(self):
        if self.rid != "sync#i0":
            return
        if self.presync_done:
            return
        for r in self.svc.get_resources():
            if r.disabled:
                continue
            if r.encap and not self.svc.encap:
                continue
            if not hasattr(r, "presync"):
                continue
            r.presync()
        self.presync_done = True

    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)],
          ["reset_options", str(self.reset_options)],
        ]
        data += self.stats_keys()
        return data

    @notify
    def sync_all(self):
        self.sync_nodes()
        self.sync_drp()
 0707010001f406000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/nexenta  0707010001f407000081a40000000000000000000000016a100daf00002469000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/nexenta/__init__.py  import datetime

import core.exceptions as ex
import core.status
import drivers.array.nexenta as array_driver

from .. import Sync, notify
from env import Env
from core.objects.svcdict import KEYS
from utilities.lazy import lazy

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "nexenta"
KEYWORDS = [
    {
        "keyword": "name",
        "at": True,
        "required": True,
        "text": "The name of the Nexenta autosync configuration."
    },
    {
        "keyword": "filer",
        "at": True,
        "required": True,
        "text": "The name of the Nexenta local head. Must be set for each node using the scoping syntax."
    },
    {
        "keyword": "path",
        "at": True,
        "required": True,
        "text": "The path of the zfs to synchronize, as seen by the Nexenta heads."
    },
    {
        "keyword": "reversible",
        "at": True,
        "candidates": [True, False],
        "required": True,
        "text": "Defines if the replication link can be reversed. Set to ``false`` for prd to drp replications to protect production data."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncNexenta(Sync):
    def __init__(self,
                 name=None,
                 path=None,
                 filer=None,
                 reversible=False,
                 **kwargs):
        super(SyncNexenta, self).__init__(type="sync.nexenta", **kwargs)
        self.pausable = False
        self.label = "nexenta autosync %s" % name
        self.autosync = name
        self.path = path
        self.reversible = reversible
        self.filer = filer
        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" % (
            super(SyncNexenta, self).__str__(),
            self.autosync
        )

    @lazy
    def filers(self):
        data = {}
        for n in self.svc.nodes | self.svc.drpnodes:
            data[n] = self.oget("filer", impersonate=n)
        return data

    def can_sync(self, target=None):
        try:
            self.get_endpoints()
        except ex.Error as e:
            self.log.error(str(e))
            raise ex.Error

        if self.ts is None:
            self.get_props()
        return True

    def sync_swap(self):
        # only available from CLI ?
        pass

    @notify
    def sync_update(self):
        try:
            self.get_endpoints()
        except ex.Error as e:
            self.log.error(str(e))
            raise ex.Error

        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.Error as e:
            self.log.error(str(e))
            raise ex.Error

    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.Error as e:
            self.log.error(str(e))
            raise ex.Error

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

    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.Error as e:
            self.log.error(str(e))
            raise ex.Error

    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.Error("can not parse last sync date: %s"%self.props['zfs/time_started'])
        self.age = now - self.ts

    def _status(self, verbose=False):
        ret = core.status.UP
        try:
            self.get_endpoints()
            self.status_log("master head is %s"%self.master.head)
            self.get_props()
        except ex.Error as e:
            if 'message' in e.value:
                msg = e.value['message']
            else:
                msg = str(e)
            self.status_log(msg)
            return core.status.WARN
        except:
            self.status_log("unexpected error")
            self.save_exc()
            return core.status.WARN

        limit = datetime.timedelta(seconds=self.sync_max_delay)
        if self.age > limit:
            self.status_log("last sync too old: %s ago"%str(self.age))
            ret = core.status.WARN
        s = self.master.autosync_get_state(self.autosync)
        if s not in ['online', 'running']:
            self.status_log("runner in '%s' state"%s)
            ret = core.status.WARN
        if ret == core.status.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.Error("two heads need to be setup")

        self.local = array_driver.Nexenta(self.filer, self.log)
        self.remote = array_driver.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.Error as e:
            if 'does not exist' in str(e):
                path_props = self.local.get_props(self.path)
                if path_props is None:
                    raise ex.Error("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.Error as e:
            if 'does not exist' in str(e):
                path_props = self.remote.get_props(self.path)
                if path_props is None:
                    raise ex.Error("path '%s' not found on remote head '%s'"%(self.path, self.filer))
                self.slave = self.remote
                self.master = self.local
            elif localdown:
                raise ex.Error("both heads unreachable")

   0707010001f3f4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap    0707010001f3f5000081a40000000000000000000000016a100daf00002a04000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/btrfssnap/__init__.py    import datetime
import os
import subprocess

import core.status
import utilities.subsystems.btrfs
import core.exceptions as ex
from .. import Sync, notify
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.files import makedirs

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "btrfssnap"
KEYWORDS = [
    {
        "keyword": "name",
        "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"
    },
    {
        "keyword": "subvol",
        "convert": "list",
        "at": True,
        "required": True,
        "example": "svc1fs:data svc1fs:log",
        "text": "A whitespace separated list of ``<label>:<subvol>`` to snapshot."
    },
    {
        "keyword": "keep",
        "at": True,
        "default": 3,
        "convert": "integer",
        "example": "3",
        "text": "The maximum number of snapshots to retain."
    },
    {
        "keyword": "recursive",
        "at": True,
        "default": False,
        "convert": "boolean",
        "candidates": [True, False],
        "text": "Also replicate subvolumes in the src tree."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

TIMEFMT = "%Y-%m-%dT%H:%M:%S.%fZ"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("btrfs"):
        return ["sync.btrfssnap"]
    return []


class SyncBtrfssnap(Sync):
    def __init__(self,
                 name=None,
                 subvol=None,
                 keep=1,
                 recursive=False,
                 **kwargs):
        super(SyncBtrfssnap, self).__init__(type="sync.btrfssnap", **kwargs)

        if subvol is None:
            subvol = []
        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.recursive = recursive
        self.btrfs = {}

    def on_add(self):
        pass

    def __str__(self):
        return "%s subvol=%s keep=%s" % (
            super(SyncBtrfssnap, self).__str__(),
            self.subvol,
            self.keep
        )

    def test_btrfs(self, label):
        cmd = [Env.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] = utilities.subsystems.btrfs.Btrfs(label=label, resource=self)
        except utilities.subsystems.btrfs.ExecError as e:
            raise ex.Error(str(e))
        return self.btrfs[label]

    def src(self, label, path):
        return os.path.join(self.btrfs[label].rootdir, path)

    def subvols(self, label, path):
        """
        sort by path so the subvols are sorted by path depth
        """
        btr = self.get_btrfs(label)
        src = self.src(label, path)
        if not src:
            return []
        if not self.recursive:
            sub = btr.get_subvol(src)
            if not sub:
                return []
            return [sub]
        subvols = []
        for subvol in btr.get_subvols().values():
            if subvol["path"] == path:
                subvols.append(subvol)
            elif subvol["path"].startswith(path + "/"):
                subvols.append(subvol)
        subvols = sorted(subvols, key=lambda x: x["path"])
        return subvols

    def create_snaps(self, label, subvol):
        cmds = []
        for sv in self.subvols(label, subvol):
            if "/.snap/" in sv["path"]:
                continue
            cmds += self.create_snap(label, sv["path"])
        return cmds

    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)
        snap += "/.snap/"
        snap += datetime.datetime.utcnow().isoformat("T")+"Z"
        if self.name:
            snap += "," + self.name
        try:
            makedirs(os.path.dirname(snap))
        except OSError as e:
            self.log.debug("skip %s snap: readonly", subvol)
            return []
        cmd = btrfs.snapshot_cmd(orig, snap, readonly=True)
        if not cmd:
            return []
        return [subprocess.list2cmdline(cmd)]

    def remove_snaps(self, label, subvol):
        cmds = []
        for sv in self.subvols(label, subvol):
            if "/.snap/" in sv["path"]:
                continue
            cmds += self._remove_snaps(label, sv["path"])
        return cmds

    def _remove_snaps(self, label, subvol):
        btrfs = self.get_btrfs(label)
        snaps = {
            datetime.datetime.utcnow().strftime(TIMEFMT): {"path": ""},
        }
        # does not contain the one we created due to cache
        for sv in btrfs.get_subvols().values():
            if not sv["path"].startswith(subvol+"/.snap/"):
                continue
            if not self.match_snap_name(sv["path"]):
                continue
            ds = sv["path"].replace(subvol+"/.snap/", "")
            ds = ds.split(",")[0] # discard optional name
            try:
                d = datetime.datetime.strptime(ds, TIMEFMT)
                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])
        cmds = []
        for path in sorted_snaps[self.keep:]:
            cmd = btrfs.subvol_delete_cmd(os.path.join(btrfs.rootdir, path))
            if cmd:
                cmds.append(subprocess.list2cmdline(cmd))
        return cmds

    def match_snap_name(self, path):
        if self.name:
            if not path.endswith(","+self.name):
                return False
        else:
            if not path.endswith("Z"):
                return False
        return True

    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
        snaps = []
        for sv in btrfs.get_subvols().values():
            if not sv["path"].startswith(subvol+"/.snap/"):
                continue
            if not self.match_snap_name(sv["path"]):
                continue
            ds = sv["path"].replace(subvol+"/.snap/", "")
            ds = ds.split(",")[0] # discard optional name
            try:
                d = datetime.datetime.strptime(ds, TIMEFMT)
                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/%d snaps" % (label, subvol, len(snaps), self.keep))
        last = sorted(snaps, reverse=True)[0]
        limit = datetime.datetime.now() - datetime.timedelta(seconds=self.sync_max_delay)
        if last < limit:
            self.status_log("%s:%s last snap is too old (%s)" % (label, subvol, last.strftime(TIMEFMT)))

    def _status(self, verbose=False):
        not_found = []
        for s in self.subvol:
            try:
                label, subvol = s.split(":")
            except:
                self.status_log("misformatted subvol entry %s (expected <label>:<subvol>)" % s)
                continue
            try:
                subvols = self.subvols(label, subvol)
            except Exception as e:
                if "mount" in str(e):
                    self.status_log("%s not found" % subvol, "info")
                    not_found.append(subvol)
                    continue
                else:
                    self.status_log(str(e), "error")
                    continue
            for sv in subvols:
                if "/.snap/" in sv["path"]:
                    continue
                self._status_one(label, sv["path"])
        messages = set(self.status_logs_get(["warn"])) - set([''])
        not_writable = set([r for r in messages if "not writable" in r or "not found" in r])
        issues = messages - not_writable

        if len(not_writable) > 0 and len(not_writable) == len(messages):
            return core.status.NA
        if len(not_found) == len(self.subvol):
            return core.status.NA
        if len(not_found) > 0:
            return core.status.WARN
        if len(issues) == 0:
            return core.status.UP
        return core.status.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
        cmds = []
        cmds += self.create_snaps(label, subvol)
        cmds += self.remove_snaps(label, subvol)
        if not cmds:
            return
        self.do_cmds(label, cmds)

    def do_cmds(self, label, cmds):
        o = self.get_btrfs(label)
        ret, out, err = o.vcall(" && ".join(cmds), shell=True)
        if ret != 0:
            raise ex.Error

    @notify
    def sync_update(self):
        for subvol in self.subvol:
            self._sync_update(subvol)

0707010001f408000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/sync/radosclone   0707010001f409000081a40000000000000000000000016a100daf00001190000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/radosclone/__init__.py   import datetime

import core.exceptions as ex
import core.status
from ..radossnap import SyncRadossnap
from core.objects.svcdict import KEYS

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "radosclone"
KEYWORDS = [
    {
        "keyword": "pairs",
        "convert": "list",
        "required": True,
        "at": True,
        "text": "The rados clone device pairs."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("rbd"):
        data.append("sync.radosclone")
    return data


class SyncRadosclone(SyncRadossnap):
    def __init__(self,
                 type="sync.radosclone",
                 client_id=None,
                 keyring=None,
                 pairs=None,
                 **kwargs):
        pairs = pairs or []
        images = map(lambda x: x.split(":")[0], pairs)
        super(SyncRadosclone, self).__init__(type=type, images=images, **kwargs)
        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)
        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.Error
        cmd = self.rbd_cmd()+['snap', 'protect', image+"@"+snapname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

        cmd = self.rbd_cmd()+['clone', image+"@"+snapname, clone]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

        for snapname in snapnames:
            try:
                self.unprotect(snapname)
                self.rm(snapname)
            except:
                pass

    def validate_pair_fmt(self):
        l = []
        image = ""
        for pair in self.pairs:
            try:
                image, clone = pair.split(":")
            except:
                l.append(pair)
                continue
            if image.count("/") != 1 or clone.count("/") != 1:
                l.append(pair)
        if len(l) > 0:
            raise ex.Error("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 core.status.WARN

        try:
            data = self.get_last()
        except Exception as e:
            self.status_log(str(e))
            return core.status.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 = core.status.UP

        if len(nosnap) > 0:
            self.status_log("no snap found for images: "+", ".join(nosnap))
            r = core.status.WARN
        if len(expired) > 0:
            self.status_log("snap too old for images: "+", ".join(expired))
            r = core.status.WARN
        if len(noclone) > 0:
            self.status_log("no clone found for pairs: "+", ".join(noclone))
            r = core.status.WARN
        if len(invclone) > 0:
            self.status_log("clone invalid for pairs: "+", ".join(invclone))
            r = core.status.WARN

        return r
0707010001f41d000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap  0707010001f41e000081a40000000000000000000000016a100daf00001ef0000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/zfssnap/__init__.py  import datetime

import core.exceptions as ex
import core.status

from .. import Sync, notify
from utilities.cache import cache, clear_cache
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.subsystems.zfs import Dataset

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "zfssnap"
KEYWORDS = [
    {
        "keyword": "recursive",
        "at": True,
        "example": "true",
        "default": True,
        "convert": "boolean",
        "text": "Set to true to snap recursively the datasets."
    },
    {
        "keyword": "name",
        "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"
    },
    {
        "keyword": "dataset",
        "convert": "list",
        "at": True,
        "required": True,
        "example": "svc1fs/data svc1fs/log",
        "text": "A whitespace separated list of datasets to snapshot."
    },
    {
        "keyword": "keep",
        "at": True,
        "default": 3,
        "convert": "integer",
        "example": "3",
        "text": "The maximum number of snapshots to retain."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("zfs"):
        data.append("sync.zfssnap")
    return data

class SyncZfssnap(Sync):
    def __init__(self,
                 name=None,
                 dataset=None,
                 keep=1,
                 recursive=True,
                 **kwargs):
        super(SyncZfssnap, self).__init__(type="sync.zfssnap", **kwargs)

        if dataset is None:
            dataset = []
        try:
            self.dataset = [ds.strip("/") for ds in dataset]
        except Exception:
            self.dataset = dataset
        if name:
            self.label = "zfs '%s' snapshot %s" % (name, ", ".join(self.dataset))
        else:
            self.label = "zfs snapshot %s" % ", ".join(self.dataset)
        self.recursive = recursive
        self.keep = keep
        self.name = name
        self.zfs = {}

    def __str__(self):
        return "%s dataset=%s keep=%s" % (
            super(SyncZfssnap, self).__str__(),
            self.dataset,
            self.keep
        )

    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 = 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)
            clear_cache("zfs.list.snapshots.name")
        except Exception as e:
            raise ex.Error(str(e))

    @cache("zfs.list.snapshots.name")
    def list_snaps(self):
        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()
        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 = Dataset(path, log=self.log)
                if self.recursive:
                    options = ["-r"]
                else:
                    options = []
                ds.destroy(options=options)
                clear_cache("zfs.list.snapshots.name")
            except Exception as e:
                raise ex.Error(str(e))

    def get_snaps(self, dataset):
        snaps = []
        for sv in self.list_snaps():
            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
        return snaps

    def last_snap_date(self, snaps):
        try:
            return sorted(snaps, reverse=True)[0]
        except IndexError:
            return

    def _status_one(self, dataset):
        if not self.has_pool(dataset):
            return
        try:
            ds = Dataset(dataset, log=self.log)
        except Exception as e:
            self.status_log("%s %s" % (dataset, str(e)))
            return
        snaps = self.get_snaps(dataset)
        if len(snaps) == 0:
            self.status_log("%s has no snap" % dataset)
            return
        if len(snaps) > self.keep + 1:
            self.status_log("%s has %d too many snaps" % (dataset, len(snaps)-self.keep))
        last = self.last_snap_date(snaps)
        limit = datetime.datetime.now() - datetime.timedelta(seconds=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 has_pool(self, dataset):
        cmd = ["zfs", "list", "-H", "-o", "name", dataset.split("/")[0]]
        _, _, ret = justcall(cmd)
        return ret == 0

    def sync_status(self, verbose=False):
        self.remove_snaps()
        for dataset in self.dataset:
            self._status_one(dataset)
        issues = set(self.status_logs_get(["warn"])) - set([''])
        if len(issues) == 0:
            return core.status.UP
        return core.status.WARN

    def can_update(self, dataset):
        s = self.svc.group_status(excluded_groups=set(["app", "sync", "task", "disk.scsireserv"]))
        if not self.svc.options.force and \
           s['avail'].status not in [core.status.UP, core.status.NA]:
            if not self.svc.options.cron:
                self.log.info("skip snapshot creation on instance not up")
            return False
        return True

    def _sync_update(self, dataset):
        if self.can_update(dataset):
            self.create_snap(dataset)
        self.remove_snap(dataset)

    @notify
    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
        """
        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]

        for resource in sorted(resources):
            for dataset in resource.dataset:
                resource._sync_update(dataset)

    def remove_snaps(self):
        for dataset in self.dataset:
            self.remove_snap(dataset)
0707010001f404000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/netapp   0707010001f405000081a40000000000000000000000016a100daf00002aca000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/sync/netapp/__init__.py   import datetime
import time

import core.exceptions as ex
import core.status
from .. import Sync, notify
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.lazy import lazy

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "netapp"
KEYWORDS = [
    {
        "keyword": "filer",
        "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."
    },
    {
        "keyword": "path",
        "required": True,
        "text": "Specifies the volume or qtree to drive snapmirror on."
    },
    {
        "keyword": "user",
        "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."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("ssh"):
        data.append("sync.netapp")
    return data


class SyncNetapp(Sync):
    def __init__(self, filer=None, path=None, user=None, **kwargs):
        super(SyncNetapp, self).__init__(type="sync.netapp", **kwargs)
        self.pausable = False
        self.label = "netapp %s on %s" % (path, filer)
        self.filer = filer
        self.path = path
        self.user = user
        self.path_short = self.path.replace('/vol/','')

    def __str__(self):
        return "%s filers=%s user=%s path=%s" % (
            super(SyncNetapp, self).__str__(),
            self.filers,
            self.user,
            self.path
        )

    @lazy
    def filers(self):
        data = {}
        for n in self.svc.nodes | self.svc.drpnodes:
            data[n] = self.oget("filer", impersonate=n)
        return data

    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 Env.nodename in self.filers:
            return self.filers[Env.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.Error("unable to find the %s filer"%target)

        _cmd = Env.rsh.split() + [self.user+'@'+filer] + cmd

        if info:
            self.log.info(' '.join(_cmd))

        out, err, ret = justcall(Env.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.Error("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):
        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.Error("unexpected lag format")
        if int(l[0]) * 60 * 60 + int(l[1]) * 60 > 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.Error

    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.Error
        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.Error(err)
        (ret, buff, err) = self._cmd(['snapmirror', 'release', self.path_short, src], master, info=True)
        if ret != 0:
            raise ex.Error(err)
        (ret, buff, err) = self._cmd(['snapmirror', 'status', '-l', dst], slave, info=False)
        if ret != 0:
            raise ex.Error(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.Error
        time.sleep(5)
        (ret, buff, err) = self._cmd(['snap', 'delete', self.path_short, snap], slave, info=True)
        if ret != 0:
            raise ex.Error(err)

    @notify
    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.Error

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

    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.Error
        if s['status'] == "Pending":
            self.log.error("Can not quiesce: volume in snapmirror Pending status")
            raise ex.Error
        (ret, buff, err) = self.cmd_slave(['snapmirror', 'quiesce', self.slave()+':'+self.path_short], info=True)
        if ret != 0:
            raise ex.Error
        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.Error
        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.Error

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

    def snapmirror_status(self, filer):
        (ret, buff, err) = self._cmd(['snapmirror', 'status'], filer, info=False)
        if ret != 0:
            raise ex.Error("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.Error("%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.Error("%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.Error
            self.sync_break()
        if self.svc.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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if s['state'] == "Snapmirrored":
            if "Transferring" in s['status']:
                self.log.debug("snapmirror transfer in progress")
                return core.status.WARN
            elif self.lagged(s['lag']):
                self.log.debug("snapmirror lag beyond sync_max_delay")
                return core.status.WARN
            else:
                return core.status.UP
        return core.status.DOWN
  0707010001f411000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symclone 0707010001f412000081a40000000000000000000000016a100daf00002e46000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symclone/__init__.py import datetime
import os
import time
import xml.etree.ElementTree as ElementTree

import core.exceptions as ex
import core.status
from .. import Sync, notify
from utilities.converters import print_duration
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "symclone"
KEYWORDS = [
    {
        "keyword": "precopy",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "Use :opt:`-precopy` on recreate."
    },
    {
        "keyword": "recreate_timeout",
        "at": True,
        "default": 300,
        "convert": "duration",
        "text": "Maximum wait time for the clone to reach the created state."
    },
    {
        "keyword": "restore_timeout",
        "at": True,
        "default": 300,
        "convert": "duration",
        "text": "Maximum wait time for the clone to reach the restored state."
    },
    {
        "keyword": "symid",
        "required": True,
        "text": "Identifier of the symmetrix array hosting the source and target devices pairs pointed by :kw:`pairs`."
    },
    {
        "keyword": "pairs",
        "convert": "list",
        "required": True,
        "at": True,
        "text": "Whitespace-separated list of devices ``<src>:<dst>`` devid pairs to drive with this resource.",
        "example": "00B60:00B61 00B62:00B63",
    },
    {
        "keyword": "consistent",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "Use :opt:`-consistent` in symclone commands.",
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("symdg"):
        data.append("sync.symclone")
    return data


class SyncSymclone(Sync):
    def __init__(self,
                 type="sync.symclone",
                 symid=None,
                 pairs=None,
                 precopy=True,
                 consistent=True,
                 restore_timeout=None,
                 recreate_timeout=None,
                 **kwargs):
        super(SyncSymclone, self).__init__(type=type, **kwargs)

        if pairs is None:
            pairs = []
        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.InitError("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" % (
            super(SyncSymclone, self).__str__(),
            self.symid,
            str(self.pairs)
        )

    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.Error("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.Error("%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.Error("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.Error
        self.wait_for_active()
        self.wait_for_devs_ready()

    def can_sync(self, target=None):
        try:
            self.check_requires("sync_update")
        except ex.Error as e:
            self.log.debug(e)
            return False
        return True

    def recreate(self):
        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.Error

    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.Error(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.Error(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.Error("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 core.status.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 core.status.WARN
        elif self.last < datetime.datetime.now() - datetime.timedelta(seconds=self.sync_max_delay):
            self.status_log("Last sync on %s older than %s"%(self.last, print_duration(self.sync_max_delay)))
            return core.status.WARN
        else:
            self.status_log("Last sync on %s" % self.last, "info")
            return core.status.UP

    def sync_break(self):
        self.activate()

    def sync_resync(self):
        self.recreate()

    @notify
    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.Error("%s not in active state (%s)" % (ina, ass))
        self.restore()
        self.wait_restored()
        self.terminate_restore()

    def start(self):
        self.activate()
  0707010001f413000081a40000000000000000000000016a100daf000008f7000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symclone/linux.py    import time

import core.exceptions as ex

from . import \
    SyncSymclone as BaseSyncSymclone, \
    KEYWORDS, \
    DRIVER_GROUP, \
    DRIVER_BASENAME, \
    driver_capabilities
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import which

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncSymclone(BaseSyncSymclone):
    def __init__(self,
                 type="sync.symclone",
                 symid=None,
                 pairs=None,
                 precopy=True,
                 consistent=True,
                 **kwargs):
        super(SyncSymclone, self).__init__(type=type, **kwargs)
        if pairs is None:
            pairs = []

    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(Env.syspaths.multipath) is None:
            return
        cmd = [Env.syspaths.multipath, '-v0', '-r', dev]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

    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.Error("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)


 0707010001f400000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap    0707010001f401000081a40000000000000000000000016a100daf000026a6000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/ibmdssnap/__init__.py    import datetime

import core.exceptions as ex
import core.status
import drivers.array.ibmds as array_driver
from .. import Sync, notify
from utilities.converters import print_duration
from core.objects.svcdict import KEYS

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "ibmdssnap"
KEYWORDS = [
    {
        "keyword": "pairs",
        "convert": "list",
        "at": True,
        "required": True,
        "text": "Whitespace-separated list of device pairs.",
        "example": "0065:0073 0066:0074"
    },
    {
        "keyword": "array",
        "at": True,
        "required": True,
        "text": "The name of the array holding the source devices and their paired devices.",
        "example": "IBM.2243-12ABC00"
    },
    {
        "keyword": "bgcopy",
        "at": True,
        "candidates": [True, False],
        "required": True,
        "convert": "boolean",
        "text": "Initiate a background copy of the source data block to the paired devices upon resync."
    },
    {
        "keyword": "recording",
        "at": True,
        "candidates": [True, False],
        "required": True,
        "convert": "boolean",
        "text": "Track only changed data blocks instead of copying the whole source data to the paired devices."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkflash"):
        return ["sync.ibmdssnap"]
    return []

class SyncIbmdssnap(Sync):
    def __init__(self,
                 pairs=None,
                 array=None,
                 bgcopy=True,
                 recording=True,
                 **kwargs):
        super(SyncIbmdssnap, self).__init__(type="sync.ibmdssnap", **kwargs)

        if pairs is None:
            pairs = []
        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 __str__(self):
        return "%s pairs=%s" % (super(SyncIbmdssnap, self).__str__(), ','.join(self.pairs))

    def resyncflash(self):
        if self.array is None:
            self.array = array_driver.IbmDss(node=self.svc.node).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.Error("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.Error(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.Error(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.Error(err)

    def can_sync(self, target=None):
        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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        r = core.status.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 = core.status.WARN
        elif not self.recording and len(record_enabled) > 0:
            self.status_log("Recording enabled on %s"%','.join(record_enabled))
            r = core.status.WARN
        if len(state_invalid) > 0:
            self.status_log("State not valid on %s"%','.join(state_invalid))
            r = core.status.WARN
        if len(persist_disabled) > 0:
            self.status_log("Persistent disabled on %s"%','.join(persist_disabled))
            r = core.status.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 = core.status.WARN

        if self.last is None:
            return core.status.WARN
        elif self.last < datetime.datetime.now() - datetime.timedelta(seconds=self.sync_max_delay):
            self.status_log("Last sync on %s older than %s"%(self.last, print_duration(self.sync_max_delay)))
            return core.status.WARN
        elif r == core.status.WARN:
            return core.status.WARN
        self.status_log("Last sync on %s"%self.last)
        return core.status.UP

    def sync_break(self):
        pass

    def sync_resync(self):
        self.resyncflash()

    @notify
    def sync_update(self):
        self.resyncflash()

    def start(self):
        pass

    def lsflash(self):
        if self.array is None:
            self.array = array_driver.IbmDss(node=self.svc.node).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.Error("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
  0707010001f417000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx    0707010001f418000081a40000000000000000000000016a100daf0000264f000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsnapvx/__init__.py    import os
import core.status
import core.exceptions as ex
import datetime
import xml.etree.ElementTree as ElementTree

from .. import Sync, notify
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.lazy import lazy
from utilities.cache import cache, clear_cache

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "symsnapvx"
KEYWORDS = [
    {
        "keyword": "symid",
        "required": True,
        "text": "Identifier of the symmetrix array hosting the devices pointed by :kw:`devs` and `devs_from`.",
        "at": True,
    },
    {
        "keyword": "devs",
        "convert": "list",
        "at": True,
        "text": "Whitespace-separated list of devices ``<src>`` devid to drive with this resource. The destination devices are only needed when the snapshot needs presenting.",
        "example": "00B60 00B63",
    },
    {
        "keyword": "devs_from",
        "convert": "list",
        "at": True,
        "text": "Whitespace-separared list of resource identifiers. The list of symmetrix device identifiers is looked up from the sub devices of the resources referenced here.",
        "example": "disk#0 disk#1",
    },
    {
        "keyword": "secure",
        "default": True,
        "convert": "boolean",
        "text": "Use :opt:`-secure` in symsnapvx commands.",
        "at": True,
    },
    {
        "keyword": "absolute",
        "text": "Use :opt:`-absolute` in symsnapvx commands.",
        "at": True,
    },
    {
        "keyword": "delta",
        "text": "Use :opt:`-delta` in symsnapvx commands.",
        "at": True,
    },
    {
        "keyword": "name",
        "text": "Use :opt:`-name` in symsnapvx commands.",
        "at": True,
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

CACHE_SIG = "{args[0].svc.path}-{args[0].rid}"
CACHE_SIG_CLEAR = "%s-%s"

# Cache vxlist outputs for 30min, to not overload the array controler
MIN_VX_LIST_PERIOD = 1600

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("symsnapvx"):
        data.append("sync.symsnapvx")
    return data

"""
Example: symsnapvx list -output xml_e

<?xml version="1.0" standalone="yes" ?>
<SymCLI_ML>
  <Symmetrix>
    <Symm_Info>
      <symid>000111111111</symid>
      <microcode_version>5978</microcode_version>
    </Symm_Info>
    <Snapvx>
      <Snapshot>
        <source>00097</source>
        <snapshot_name>SNAP_1</snapshot_name>
        <last_timestamp>Fri May 31 06:15:05 2024</last_timestamp>
        <num_generations>20</num_generations>
        <link>No</link>
        <restore>No</restore>
        <failed>No</failed>
        <error_reason>NA</error_reason>
        <GCM>False</GCM>
        <zDP>False</zDP>
        <secured>Yes</secured>
        <expanded>No</expanded>
        <bgdefinprog>No</bgdefinprog>
        <policy>No</policy>
        <persistent>No</persistent>
        <cloud>No</cloud>
      </Snapshot>

"""
def parse_vx_list(s):
    l = []
    etree = ElementTree.fromstring(s)
    for e in etree.findall("Symmetrix/Snapvx/Snapshot"):
        d = {}
        for sub in e.iter():
            d[sub.tag] = sub.text
        d["last"] = d.get("last_timestamp")
        l.append(d)
    return l

def devid_of(symid, devpath):
    from env import Env
    if Env.sysname == "Linux":
        devpath = os.path.realpath(devpath)
    cmd = ["syminq", "-pdevfile", devpath, "-output", "xml_e"]
    out, err, ret = justcall(cmd)
    if ret != 0:
        raise ex.Error("%s: %s" % (devpath, err))
    l = parse_syminq(out)
    for e in l:
        if e["symid"] == symid:
            return e["dev_name"]

"""
    <?xml version="1.0" standalone="yes" ?>
    <SymCLI_ML>
      <Inquiry>
        <Device>
          <symid>000297600016</symid>
          <pd_name>/dev/rdsk/c0t60000970000297600016533030334233d0s2</pd_name>
          <dev_name>003B3</dev_name>
          <director>1D</director>
          <port>8</port>
        </Device>
      </Inquiry>
    </SymCLI_ML>
"""
def parse_syminq(s):
    l = []
    etree = ElementTree.fromstring(s)
    for e in etree.findall("Inquiry/Device"):
        d = {}
        for sub in e.iter():
            d[sub.tag] = sub.text
        l.append(d)
    return l


class SyncSymsnapvx(Sync):
    def __init__(self,
                 type="sync.symsnapvx",
                 symid=None,
                 devs=None,
                 devs_from=None,
                 secure=None,
                 absolute=None,
                 delta=None,
                 name=None,
                 **kwargs):
        super(SyncSymsnapvx, self).__init__(type=type, **kwargs)

        self.symid = symid
        self.devs = devs or []
        self.devs_from = devs_from or []
        self.secure = secure or False
        self.absolute = absolute
        self.delta = delta
        self.name = name
        self.default_schedule = "@0"

        if name:
            self.label = "symsnapvx symid %s %s" % (symid, name)
        else:
            self.label = "symsnapvx symid %s devs %s" % (symid, " ".join(devs))
        if len(self.label) > 80:
            self.label = self.label[:76] + "..."

    def __str__(self):
        return "%s symid=%s devs=%s devs_from=%s" % (
            super(SyncSymsnapvx, self).__str__(),
            self.symid,
            str(self.devs),
            str(self.devs_from),
        )

    def _info(self):
        data = [
          ["devs", ",".join(self.devs)],
          ["delta", str(self.delta)],
          ["absolute", str(self.absolute)],
          ["secure", str(self.secure)],
          ["name", str(self.name)],
          ["symid", str(self.symid)],
          ["devids", ",".join(self.merged_devs)],
        ]
        snap = self.get_snap()
        if snap:
            data += [
              ["num_generations", snap.get("num_generations", "0")],
              ["last_timestamp", snap.get("last_timestamp", "")],
            ]
        return data

    @lazy
    def merged_devs(self):
        l = self.devs
        for res in self.svc.get_resources():
            if res.rid in self.devs_from:
                for devpath in res.sub_devs():
                    devid = devid_of(self.symid, devpath)
                    if devid is not None:
                        l.append(devid)
        return sorted(list(set(l)))

    def vx_cmd(self):
        return ["/usr/symcli/bin/symsnapvx", "-sid", self.symid]

    @cache(CACHE_SIG+".vx.list", ttl=MIN_VX_LIST_PERIOD, sid=DRIVER_BASENAME)
    def list_cached(self):
        """
        Return the list of snapshot volumes

        returned value must support JSON serialization to enable caching
        """
        return self.list()

    def list(self):
        if not self.merged_devs:
            raise ex.Error("no devices")
        cmd = self.vx_cmd() + ["list", "-devs", ",".join(self.merged_devs), "-output", "xml_e"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            if "No snapshot" in err:
                return []
            if err:
                raise ex.Error("vx_list: %s" % err)
        return parse_vx_list(out)

    def get_snap(self):
        snaps = self.list_cached()
        name = self.format_name()
        for snap in snaps:
            if snap["snapshot_name"] == name:
                return snap

    def establish(self):
        cmd = self.vx_cmd() + ["establish", "-noprompt"]
        if self.secure:
            cmd += ["-secure"]
        elif self.delta or self.absolute:
            cmd += ["-ttl"]
        if self.delta and self.absolute:
            raise ex.Error("set delta or absolute, not both")
        if self.delta:
            cmd += ["-delta", str(self.delta)]
        if self.absolute:
            cmd += ["-absolute", str(self.absolute)]
        cmd += ["-name", self.format_name()]
        cmd += ["-devs", ",".join(self.merged_devs)]
        ret, out, err = self.vcall(cmd, warn_to_info=True)
        if ret != 0:
            raise ex.Error(err)

    def format_name(self):
        if self.name:
            return self.name
        else:
            # 1.svc1.ns1.svc.cluster1
            ns = "root" if self.svc.namespace is None else self.svc.namespace
            return "%s.%s.%s.%s.%s" % (self.rid.split("#")[-1], self.svc.name, ns, self.svc.kind, self.svc.node.cluster_name)

    def can_sync(self, target=None):
        try:
            self.check_requires("sync_update")
        except (ex.Error, ex.ContinueAction) as e:
            self.log.debug(e)
            return False
        return True

    def snap_error(self, snap):
        err = snap.get("error_reason")
        if err != "NA":
            source = snap.get("source")
            self.status_log("%s: %s" % (source, err))

    def _status(self, verbose=False):
        snap = self.get_snap()
        if not snap:
            self.status_log("no snapshot yet", "info")
            return core.status.DOWN
        self.snap_error(snap)
        gens = snap.get("num_generations", "0")
        last = snap.get("last")
        if last is None:
            return core.status.DOWN
        delay = datetime.timedelta(seconds=self.sync_max_delay)
        # last format: Thu Feb 25 10:20:56 2010
        last = datetime.datetime.strptime(last, "%a %b %d %H:%M:%S %Y")
        self.status_log("last snapshot on %s, %s generations" % (last, gens), "info")
        if last < datetime.datetime.now() - delay:
            self.status_log("last snapshot too old (<%s)" % delay, "warn")
            return core.status.WARN
        return core.status.UP

    @notify
    def sync_update(self):
        try:
            self.establish()
        finally:
            # CACHE_SIG_CLEAR = "%s-%s"
            clear_cache(CACHE_SIG_CLEAR % (self.svc.path, self.rid) + ".vx.list", sid=DRIVER_BASENAME)
 0707010001f3f2000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/btrfs    0707010001f3f3000081a40000000000000000000000016a100daf000053ad000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/resource/sync/btrfs/__init__.py    import os
import time
import json
import subprocess
import datetime

import core.status
import utilities.subsystems.btrfs
import core.exceptions as ex
from .. import Sync, notify
from env import Env
from utilities.chunker import chunker
from utilities.converters import print_duration
from utilities.string import bdecode
from utilities.files import makedirs, protected_dir
from core.objects.svcdict import KEYS

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "btrfs"
KEYWORDS = [
    {
        "keyword": "src",
        "at": True,
        "required": True,
        "text": "Source subvolume of the sync."
    },
    {
        "keyword": "dst",
        "at": True,
        "required": True,
        "text": "Destination subvolume of the sync."
    },
    {
        "keyword": "target",
        "convert": "list",
        "at": True,
        "required": True,
        "candidates": ["nodes", "drpnodes"],
        "text": "Destination nodes of the sync."
    },
    {
        "keyword": "recursive",
        "at": True,
        "default": False,
        "convert": "boolean",
        "candidates": [True, False],
        "text": "Also replicate subvolumes in the src tree."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("btrfs"):
        return ["sync.btrfs"]
    return []


class SyncBtrfs(Sync):
    """
    Define btrfs sync resource to be btrfs send/btrfs receive between nodes
    """
    def __init__(self,
                 target=None,
                 src=None,
                 dst=None,
                 sender=None,
                 recursive=False,
                 snap_size=0,
                 **kwargs):
        super(SyncBtrfs, self).__init__(type="sync.btrfs", **kwargs)

        self.target = target or []
        self.label = "btrfs of %s to %s"%(src, ", ".join(self.target))
        self.src = src
        self.sender = sender
        self.recursive = recursive

        if ":" not in src or src.index(":") == len(src) - 1:
            raise ex.InitError("malformed src value")
        if ":" not in dst or dst.index(":") == len(dst) - 1:
            raise ex.InitError("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 on_add(self):
        self.statefile = os.path.join(self.var_d, "btrfs_state")

    def __str__(self):
        return "%s target=%s src=%s" % (
            super(SyncBtrfs, self).__str__(),\
            self.target, self.src
        )

    def sort_rset(self, rset):
        rset.resources.sort(key=lambda x: getattr(x, "src_subvol", x.rid))

    def _info(self):
        data = [
          ["src", self.src],
          ["target", subprocess.list2cmdline(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 = utilities.subsystems.btrfs.Btrfs(label=self.src_label, resource=self)
        except utilities.subsystems.btrfs.ExecError as e:
            raise ex.Error(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()
            r.remove_src_snap_next()

        for i, r in enumerate(resources):
            tosends = []


            for subvol in r.subvols():
                src = r.src_btrfs.rootdir + "/" + subvol["path"]
                dst = r.src_snap_next(subvol)
                tosends.append((src, dst))

            r.recreate_snaps(tosends)

    def all_subvols(self):
        """
        sort by path so the subvols are sorted by path depth
        """
        if not self.src:
            return []
        if not self.recursive:
            sub = self.src_btrfs.get_subvol(self.src)
            if not sub:
                return []
            return [sub]
        subvols = self.src_btrfs.get_subvols().values()
        subvols = sorted(subvols, key=lambda x: x["path"])
        return subvols

    def subvols(self):
        """
        sort by path so the subvols are sorted by path depth
        """
        if not self.src:
            return []
        if not self.recursive:
            sub = self.src_btrfs.get_subvol(self.src)
            if not sub:
                return []
            return [sub]
        subvols = []
        for subvol in self.src_btrfs.get_subvols().values():
            if subvol["path"].endswith("@tosend"):
                continue
            if subvol["path"].endswith("@sent"):
                continue
            if subvol["path"] == self.src_subvol:
                subvols.append(subvol)
            elif subvol["path"].startswith(self.src_subvol + "/"):
                subvols.append(subvol)
        subvols = sorted(subvols, key=lambda x: x["path"])
        return subvols

    def remote_subvols(self, node):
        if not self.dst:
            return []
        if not self.recursive:
            sub = self.dst_btrfs[node].get_subvol(self.dst)
            if not sub:
                return []
            return [sub]
        subvols = self.dst_btrfs[node].get_subvols().values()
        subvols = sorted(subvols, key=lambda x: x["path"])
        return subvols

    def recreate_snaps(self, snaps):
        self.make_src_workdirs()
        self.init_src_btrfs()
        self.src_btrfs.subvol_delete([snap[1] for snap in snaps if os.path.exists(snap[1])])
        try:
            self.src_btrfs.snapshots(snaps, readonly=True)
        except utilities.subsystems.btrfs.ExistError:
            self.log.error("%s should not exist"%snaps)
            raise ex.Error
        except utilities.subsystems.btrfs.ExecError:
            raise ex.Error

    def get_src_info(self):
        self.init_src_btrfs()
        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] = utilities.subsystems.btrfs.Btrfs(label=self.dst_label, resource=self, node=node)
            except utilities.subsystems.btrfs.ExecError as e:
                raise ex.Error(str(e))
        self.dst = os.path.join(self.dst_btrfs[node].rootdir, self.dst_subvol)

    def src_temp_dir(self):
        return os.path.join(self.src_btrfs.rootdir, ".osync", self.svc.fullname, self.rid, "temp")

    def dst_temp_dir(self, node):
        return os.path.join(self.dst_btrfs[node].rootdir, ".osync", self.svc.fullname, self.rid, "temp")

    def src_next_dir(self):
        return os.path.join(self.src_btrfs.rootdir, ".osync", self.svc.fullname, self.rid, "next")

    def dst_next_dir(self, node):
        return os.path.join(self.dst_btrfs[node].rootdir, ".osync", self.svc.fullname, self.rid, "next")

    def src_last_dir(self):
        return os.path.join(self.src_btrfs.rootdir, ".osync", self.svc.fullname, self.rid, "last")

    def dst_last_dir(self, node):
        return os.path.join(self.dst_btrfs[node].rootdir, ".osync", self.svc.fullname, self.rid, "last")

    def rel_snap_last(self, subvol):
        p = subvol["path"].replace("/","_")
        return  os.path.join(".osync", self.svc.fullname, self.rid, "last", p)

    def rel_snap_next(self, subvol):
        p = subvol["path"].replace("/","_")
        return  os.path.join(".osync", self.svc.fullname, self.rid, "next", p)

    def rel_tmp(self, subvol):
        p = self.dst_subvol + subvol["path"][len(self.src_subvol):]
        return  os.path.join(".osync", self.svc.fullname, self.rid, "temp", p)

    def dst_tmp(self, subvol, node):
        p = self.rel_tmp(subvol)
        return os.path.join(self.dst_btrfs[node].rootdir, p)

    def src_snap_last(self, subvol):
        p = self.rel_snap_last(subvol)
        return os.path.join(self.src_btrfs.rootdir, p)

    def src_snap_next(self, subvol):
        p = self.rel_snap_next(subvol)
        return os.path.join(self.src_btrfs.rootdir, p)

    def dst_snap_next(self, subvol, node):
        p = self.rel_snap_next(subvol)
        return os.path.join(self.dst_btrfs[node].rootdir, p)

    def dst_snap_last(self, subvol, node):
        p = self.rel_snap_last(subvol)
        return os.path.join(self.dst_btrfs[node].rootdir, p)

    def get_peersenders(self):
        self.peersenders = set()
        if "nodes" == self.sender:
            self.peersenders |= self.svc.nodes
            self.peersenders -= set([Env.nodename])

    def get_targets(self, action=None):
        self.targets = set()
        if "nodes" in self.target and action in (None, "sync_nodes", "sync_full", "sync_all"):
            self.targets |= self.svc.nodes
        if "drpnodes" in self.target and action in (None, "sync_drp", "sync_full", "sync_all"):
            self.targets |= self.svc.drpnodes
        self.targets -= set([Env.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 btrfs_send(self, subvols, node, incremental=True):
        if len(subvols) == 0:
            return

        send_cmd = ["btrfs", "send"]
        for subvol in subvols:
            if incremental:
                send_cmd += ["-c", self.src_snap_last(subvol)]
            send_cmd += [self.src_snap_next(subvol)]

        if node is not None:
            receive_cmd = Env.rsh.strip(" -n").split() + [node]
            receive_cmd += self.make_dst_workdirs(node) + [";"] + ["btrfs", "receive", self.dst_next_dir(node)]
        else:
            receive_cmd = ["btrfs", "receive", self.dst_next_dir(node)]

        self.log.info(subprocess.list2cmdline(send_cmd) + " | " + subprocess.list2cmdline(receive_cmd))
        p1 = subprocess.Popen(send_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        pi = subprocess.Popen(["dd", "bs=4096"], stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        p2 = subprocess.Popen(receive_cmd, stdin=pi.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        buff = p2.communicate()
        send_buff = p1.stderr.read()
        if send_buff is not None and len(send_buff) > 0:
            for line in bdecode(send_buff).split("\n"):
                if line:
                    self.log.info("| " + line)
        if p2.returncode == 0:
            stats_buff = pi.communicate()[1]
            stats = self.parse_dd(stats_buff)
            self.update_stats(stats, target=node)
            if buff[1] is not None and len(buff[1]) > 0:
                for line in bdecode(buff[1]).split("\n"):
                    if line:
                        self.log.info("| " + line)
        else:
            if buff[1] is not None and len(buff[1]) > 0:
                for line in bdecode(buff[1]).split("\n"):
                    if line:
                        self.log.error("| " + line)
            self.log.error("sync failed")
            raise ex.Error
        if buff[0] is not None and len(buff[0]) > 0:
            for line in bdecode(buff[0]).split("\n"):
                if line:
                    self.log.info("| " + line)

    def cleanup_remote(self, node, remote_subvols):
        self.init_src_btrfs()
        o = self.get_btrfs(node)
        l = []
        candidates = []
        for subvol in self.subvols():
            candidates.append(self.rel_snap_next(subvol))
            candidates.append(self.rel_tmp(subvol))

        for subvol in remote_subvols:
            if subvol["path"] in candidates:
                l.append(subvol["path"])

        l = [o.rootdir+"/"+p for p in l]

        try:
            o.subvol_delete(l)
        except utilities.subsystems.btrfs.ExecError:
            raise ex.Error()

    def remove_dst_snap_temp(self, node):
        return [subprocess.list2cmdline(["rm", "-rf", self.dst_temp_dir(node)])]

    def remove_dst_snap_next(self, node):
        o = self.get_btrfs(node)
        p = self.dst_next_dir(node)
        subvols = o.get_subvols_in_path(p)
        cmd = o.subvol_delete_cmd(subvols) or []
        if not cmd:
            return []
        return [subprocess.list2cmdline(cmd)]

    def remove_dst_snap_last(self, node):
        o = self.get_btrfs(node)
        p = self.dst_last_dir(node)
        subvols = o.get_subvols_in_path(p)
        cmd = o.subvol_delete_cmd(subvols) or []
        if not cmd:
            return []
        return [subprocess.list2cmdline(cmd)]

    def remove_src_snap_next(self):
        o = self.get_btrfs()
        p = self.src_next_dir()
        subvols = o.get_subvols_in_path(p)
        cmds = []
        for bunch in chunker(subvols, 20):
            cmd = o.subvol_delete_cmd(bunch) or []
            if not cmd:
                continue
            cmds = [subprocess.list2cmdline(cmd)]
            self.do_cmds(cmds)

    def remove_src_snap_last(self):
        o = self.get_btrfs()
        p = self.src_last_dir()
        subvols = o.get_subvols_in_path(p)
        cmd = o.subvol_delete_cmd(subvols) or []
        if not cmd:
            return []
        return [subprocess.list2cmdline(cmd)]

    def remove_dst(self, node):
        o = self.get_btrfs(node)
        p = self.dst
        subvols = o.get_subvols_in_path(p)
        cmd = o.subvol_delete_cmd(subvols) or []
        if not cmd:
            return []
        return [subprocess.list2cmdline(cmd)]

    def rename_src_snap_next(self):
        src = self.src_next_dir()
        dst = self.src_last_dir()
        return [
                subprocess.list2cmdline(["rm", "-rf", dst]),
                subprocess.list2cmdline(["mv", "-v", src, dst]),
        ]

    def rename_dst_snap_next(self, node):
        src = self.dst_next_dir(node)
        dst = self.dst_last_dir(node)
        cmds = [
            subprocess.list2cmdline(["rm", "-rf", dst]),
            subprocess.list2cmdline(["mv", "-v", src, dst]),
        ]
        return cmds

    def install_final(self, node):
        head_subvol = self.subvols()[0]
        src = os.path.join(self.dst_temp_dir(node), head_subvol["path"])
        if protected_dir(self.dst):
            raise ex.Error("%s is a protected dir. refuse to remove" % self.dst)
        cmds = self.remove_dst(node)
        cmds += [
            subprocess.list2cmdline(["rm", "-rf", self.dst]),
            subprocess.list2cmdline(["mv", "-v", src, self.dst]),
        ]
        return cmds

    def install_dst(self, subvols, node):
        cmds = []
        for subvol in subvols:
            src = self.dst_snap_last(subvol, node)
            dst = self.dst_tmp(subvol, node)
            cmd = self.dst_btrfs[node].snapshot_cmd(src, dst, readonly=False)
            if not cmd:
                continue
            cmds.append(subprocess.list2cmdline(["mkdir", "-p", os.path.dirname(dst)]))
            cmds.append(subprocess.list2cmdline(cmd))
        return cmds

    def make_src_workdirs(self):
        makedirs(self.src_last_dir())
        makedirs(self.src_next_dir())
        makedirs(self.src_temp_dir())

    def make_dst_workdirs(self, node):
        return [ "mkdir -p %s %s %s" % (self.dst_last_dir(node), self.dst_next_dir(node), self.dst_temp_dir(node)) ]

    def get_btrfs(self, node=None):
        if node:
            o = self.dst_btrfs[node]
        else:
            o = self.src_btrfs
        return o

    def do_cmds(self, cmds, node=None):
        o = self.get_btrfs(node)
        ret, out, err = o.vcall(" && ".join(cmds), shell=True)
        if ret != 0:
            raise ex.Error

    def _sync_update(self, action, full=False):
        self.init_src_btrfs()
        try:
            self.sanity_checks()
        except ex.Error:
            return
        self.get_targets(action)
        if len(self.targets) == 0:
            return
        self.get_src_info()
        self.make_src_workdirs()
        subvols = self.subvols()
        src_cmds = []

        for n in self.targets:
            self.get_dst_info(n)
            remote_btrfs = self.get_btrfs(n)
            remote_subvols = self.remote_subvols(n)
            self.cleanup_remote(n, remote_subvols)
            dst_cmds = []
            incrs = []
            fulls = []
            for subvol in subvols:
                src_snap_path = self.src_snap_last(subvol)
                src_snap = self.src_btrfs.get_subvol(src_snap_path)
                dst_snap_path = self.dst_snap_last(subvol, n)
                dst_snap = remote_btrfs.get_subvol(dst_snap_path)
                if not src_snap:
                    self.log.info("upgrade %s to full copy because %s was not found", subvol["path"], src_snap_path)
                    fulls.append(subvol)
                elif not dst_snap:
                    self.log.info("upgrade %s to full copy because %s on %s was not found", subvol["path"], dst_snap_path, n)
                    fulls.append(subvol)
                elif dst_snap["received_uuid"] == "-":
                    self.log.info("upgrade %s to full copy because %s on %s has been turned rw", subvol["path"], dst_snap_path, n)
                    fulls.append(subvol)
                elif src_snap["uuid"] != dst_snap["received_uuid"]:
                    self.log.info("upgrade %s to full copy because %s on %s has been received from a different subset %s", subvol["path"], dst_snap_path, n, dst_snap["received_uuid"])
                    fulls.append(subvol)
                else:
                    incrs.append(subvol)
            dst_cmds += self.remove_dst_snap_last(n)
            dst_cmds += self.remove_dst_snap_temp(n)
            dst_cmds += self.rename_dst_snap_next(n)
            dst_cmds += self.install_dst(subvols, n)
            dst_cmds += self.install_final(n)
            self.btrfs_send(incrs, n, incremental=True)
            self.btrfs_send(fulls, n, incremental=False)
            self.do_cmds(dst_cmds, n)

        src_cmds += self.remove_src_snap_last()
        src_cmds += self.rename_src_snap_next()
        self.do_cmds(src_cmds)
        self.write_statefile()
        for n in self.targets:
            self.push_statefile(n)
        self.write_stats()

    def sync_full(self):
        self._sync_update(None, full=True)

    def can_sync(self, target=None):
        return True

    def sync_status(self, verbose=False):
        self.init_src_btrfs()
        try:
            last = os.path.getmtime(self.statefile)
            now = time.time()
            delay = self.sync_max_delay
        except (KeyError, TypeError):
            self.status_log("btrfs state file is corrupt")
            return core.status.WARN
        except IOError:
            self.status_log("btrfs state file not found")
            return core.status.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return core.status.WARN
        if last < now - delay:
            self.status_log("last sync on %s older than %s"%(datetime.datetime.fromtimestamp(last), print_duration(self.sync_max_delay)))
            return core.status.WARN
        return core.status.UP

    def get_remote_state(self, node):
        cmd1 = ["cat", self.statefile]
        cmd = Env.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.Error
        return json.loads(out)

    def get_local_state(self):
        with open(self.statefile, "r") as f:
            out = f.read()
        return json.loads(out)

    def write_statefile(self):
        data = self.all_subvols(),
        with open(self.statefile, "w") as f:
            json.dump(data, f)

    def _push_statefile(self, node):
        cmd = Env.rcp.split() + [self.statefile, node+":"+self.statefile.replace("#", r"\#")]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def push_statefile(self, node):
        self._push_statefile(node)
        self.get_peersenders()
        for s in self.peersenders:
            self._push_statefile(s)

    @notify
    def sync_all(self):
        self.sync_nodes()
        self.sync_drp()
   0707010001f414000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsnap  0707010001f415000081a40000000000000000000000016a100daf000005e4000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/__init__.py  from ..symclone import SyncSymclone
from core.objects.svcdict import KEYS

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "symsnap"
KEYWORDS = [
    {
        "keyword": "recreate_timeout",
        "at": True,
        "default": 300,
        "convert": "duration",
        "text": "Maximum wait time for the clone to reach the created state."
    },
    {
        "keyword": "restore_timeout",
        "at": True,
        "default": 300,
        "convert": "duration",
        "text": "Maximum wait time for the clone to reach the restored state."
    },
    {
        "keyword": "symid",
        "required": True,
        "text": "Identifier of the symmetrix array hosting the source and target devices pairs pointed by :kw:`pairs`."
    },
    {
        "keyword": "pairs",
        "convert": "list",
        "required": True,
        "at": True,
        "text": "Whitespace-separated list of devices ``<src>:<dst>`` devid pairs to drive with this resource.",
        "example": "00B60:00B61 00B62:00B63",
    },
    {
        "keyword": "consistent",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "Use :opt:`-consistent` in symclone commands.",
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("symdg"):
        data.append("sync.symsnap")
    return data


class SyncSymsnap(SyncSymclone):
    pass
0707010001f416000081a40000000000000000000000016a100daf00000131000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsnap/linux.py from ..symclone.linux import SyncSymclone
from . import KEYWORDS, DRIVER_GROUP, DRIVER_BASENAME, driver_capabilities
from core.objects.svcdict import KEYS

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncSymsnap(SyncSymclone):
    pass
   0707010001f3fe000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap   0707010001f3ff000081a40000000000000000000000016a100daf00000eb9000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/hp3parsnap/__init__.py   import datetime

import core.exceptions as ex
import core.status
import drivers.array.hp3par as array_driver
from .. import Sync, notify
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "hp3parsnap"
KEYWORDS = [
    {
        "keyword": "array",
        "required": True,
        "at": True,
        "text": "Name of the HP 3par array to send commands to."
    },
    {
        "keyword": "vv_names",
        "convert": "list",
        "required": True,
        "at": True,
        "text": "The names of snapshot VV or sets of VV to update."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncHp3parsnap(Sync):
    def __init__(self, array=None, vv_names=None, **kwargs):
        super(SyncHp3parsnap, self).__init__(type="sync.hp3parsnap", **kwargs)
        if vv_names is None:
            vv_names = []
        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 = array_driver.Hp3pars(objects=[self.array], log=self.log, node=self.svc.node)
        except Exception as e:
            raise ex.InitError(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.path = self.svc.path

    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'])
        try:
            self.check_requires("sync_update")
        except ex.Error:
            return False
        return True

    def updatevv(self):
        self.array_obj.updatevv(vvnames=self.vv_names, log=self.log)

    @notify
    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 core.status.WARN
        if not self.array_obj.has_virtualcopy():
            self.status_log("array %s has no virtual copy license" % self.array)
            return core.status.WARN

        try:
            data = self.showvv()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.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 = core.status.WARN

        elapsed = datetime.datetime.utcnow() - datetime.timedelta(seconds=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 = core.status.WARN

        if r is not None:
            return r

        return core.status.UP

   0707010001f3fc000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/hp3par   0707010001f3fd000081a40000000000000000000000016a100daf00002c01000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/resource/sync/hp3par/__init__.py   import datetime

import core.exceptions as ex
import core.status
import drivers.array.hp3par as array_driver
from .. import Sync, notify
from env import Env
from core.objects.svcdict import KEYS
from utilities.lazy import lazy

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "hp3par"
KEYWORDS = [
    {
        "keyword": "array",
        "required": True,
        "at": True,
        "text": "Name of the HP 3par array to send commands to."
    },
    {
        "keyword": "method",
        "candidates": ["ssh", "cli"],
        "default": "ssh",
        "at": True,
        "text": "The method to use to submit commands to the arrays."
    },
    {
        "keyword": "mode",
        "required": True,
        "candidates": ["async", "sync"],
        "text": "Replication mode: Synchronous or Asynchronous"
    },
    {
        "keyword": "rcg",
        "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."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncHp3par(Sync):
    def __init__(self,
                 array=None,
                 method=None,
                 mode=None,
                 rcg=None,
                 **kwargs):
        super(SyncHp3par, self).__init__(type="sync.hp3par", **kwargs)
        self.pausable = False
        self.array = array
        self.rcg = rcg
        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" % (
            super(SyncHp3par, self).__str__(),
            self.array,
            self.method,
            self.mode,
            self.rcg
        )

    def on_add(self):
        try:
            arrays = array_driver.Hp3pars(objects=[self.array], log=self.log, node=self.svc.node)
        except Exception as e:
            raise ex.Error(str(e))
        if len(arrays.arrays) == 1:
            self.array_obj = arrays.arrays[0]
        if self.array_obj is None:
            raise ex.Error("array %s is not accessible" % self.array)
        self.array_obj.path = self.svc.path

    @lazy
    def rcg_names(self):
        data = {}
        for node in self.svc.nodes | self.svc.drpnodes:
            array = self.oget("array", impersonate=node)
            rcg = self.oget("rcg", impersonate=node)
            data[array] = rcg
        return data

    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 = array_driver.Hp3pars(objects=[target], log=self.log, node=self.svc.node).arrays[0]
                    if self.remote_array_obj is None:
                        raise ex.Error("array %s is not accessible" % array_name)
                    self.remote_array_obj.path = self.svc.path
                    return self.remote_array_obj
                except Exception as e:
                    raise ex.Error(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.Error()
        return out, err

    def can_sync(self, target=None, s=None):
        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.Error()
        self.stoprcopygroup()
        self.setrcopygroup_reverse()
        self.startrcopygroup()

    @notify
    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()
        if Env.nodename in self.svc.nodes:
            self.sync_resume()

    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' and self.svc.options.cron:
            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.Error()
        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']
            if target not in self.rcg_names:
                raise ex.Error("target %s not found in rcg names (%s)" % (target, ", ".join([name for name in self.rcg_names])))
            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 core.status.WARN

        try:
            data = self.showrcopy()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN

        elapsed = datetime.datetime.utcnow() - datetime.timedelta(seconds=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 = core.status.WARN
        if self.mode == "async" and data['rcg']['Mode'] != "Periodic":
            self.status_log("rcopy group mode is not Periodic (%s)"%data['rcg']['Mode'])
            r = core.status.WARN
        if self.mode == "sync" and data['rcg']['Mode'] != "Sync":
            self.status_log("rcopy group mode is not Sync (%s)"%data['rcg']['Mode'])
            r = core.status.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 = core.status.WARN
        if 'auto_recover' not in data['rcg']['Options']:
            self.status_log("rcopy group auto_recover option is not set")
            r = core.status.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 = core.status.WARN
            if vv.get('LastSyncTime') and vv['LastSyncTime'] < elapsed:
                self.status_log("vv %s last sync too old (%s)"%(vv['LocalVV'], vv['LastSyncTime'].strftime("%Y-%m-%d %H:%M")))
                r = core.status.WARN

        if r is not None:
            return r

        return core.status.UP

   0707010001f40f000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/sync/s3   0707010001f410000081a40000000000000000000000016a100daf00002c11000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/sync/s3/__init__.py   import datetime
import glob
import os

from foreign.six.moves import configparser as ConfigParser
from subprocess import *

import core.exceptions as ex
import core.status
from .. import Sync, notify
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "s3"
KEYWORDS = [
    {
        "keyword": "snar",
        "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."
    },
    {
        "keyword": "src",
        "convert": "list",
        "at": True,
        "required": True,
        "example": "/srv/mysvc/tools /srv/mysvc/apps*",
        "text": "Source globs as passed as paths to archive to a tar command."
    },
    {
        "keyword": "options",
        "convert": "shlex",
        "at": True,
        "example": "--exclude *.pyc",
        "text": "Options passed to GNU tar for archiving."
    },
    {
        "keyword": "bucket",
        "at": True,
        "required": True,
        "example": "opensvc-myapp",
        "text": "The name of the S3 bucket to upload the backup to."
    },
    {
        "keyword": "full_schedule",
        "at": True,
        "example": "@1441 sun",
        "text": "The schedule of full backups. :c-action:`sync_update` actions are triggered according to the resource :kw:`schedule` parameter, and do a full backup if the current date matches the :kw:`full_schedule` parameter or an incremental backup otherwise."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("gof3r") and which("tar"):
        data.append("sync.s3")
    return data


class SyncS3(Sync):
    def __init__(self,
                 src=None,
                 options=None,
                 bucket=None,
                 snar=None,
                 full_schedule="@1441 sun",
                 **kwargs):
        super(SyncS3, self).__init__(type="sync.s3", **kwargs)
        if options is None:
            options = []
        if src is None:
            src = []
        self.label = "s3 backup"
        self.src = src
        self.bucket = bucket
        self.options = options
        self.full_schedule = full_schedule
        self.snar = snar

    def __str__(self):
        return "%s src=%s bucket=%s" % (
            super(SyncS3, self).__str__(),
            self.src,
            self.bucket
        )

    def on_add(self):
        if self.svc.namespace:
            self.prefix = "/".join(("", self.svc.namespace.lower(), self.svc.name, self.rid.replace("#",".")))
        else:
            self.prefix = "/".join(("", self.svc.name, 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.Error("key %s not found in bucket" % key)
        try:
            _d = datetime.datetime.strptime(e["date"], "%Y-%m-%d %H:%M:%S")
        except:
            raise ex.Error("undecodable date %s" % e["date"])
        return _d

    def _status(self, verbose=False):
        try:
            self.check_bin()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        try:
            l = self.ls(refresh=True)
            n = self.get_n_incr()
        except Exception as e:
            self.status_log(str(e))
            return core.status.WARN

        if n is None:
            self.status_log("no backup found")
            return core.status.WARN

        if n > 0 and not os.path.exists(self.snar):
            self.status_log("snar file not found at %s" % self.snar)
            return core.status.WARN

        try:
            last = self.sync_date(n)
        except Exception as e:
            self.status_log(str(e))
            return core.status.WARN

        if self.sync_date(n) < datetime.datetime.now() - datetime.timedelta(seconds=self.sync_max_delay):
            self.status_log("last backup too old (%s)" % last.strftime("%Y-%m-%d %H:%M:%S"))
            return core.status.WARN

        self.status_log("last backup on %s" % last.strftime("%Y-%m-%d %H:%M:%S"))
        return core.status.UP

    def check_bin(self):
        if not which("gof3r"):
            raise ex.Error("could not find gof3r binary")
        if not which("tar"):
            raise ex.Error("could not find tar binary")

    def sync_full(self):
        self.check_bin()
        self.tar_full()

    @notify
    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 getattr(self, "ls_cache")
        cmd = ["aws", "s3", "ls", "s3://"+self.bucket+"/"+os.path.dirname(self.prefix)+"/"]
        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):
        aws_cf_f = "/root/.aws/config"
        try:
            aws_cf = ConfigParser.RawConfigParser()
            aws_cf.read(aws_cf_f)
        except:
            raise ex.Error("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.Error("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.Error("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 core.scheduler import Schedule, SchedNotAllowed, SchedSyntaxError
        now = datetime.datetime.now()
        try:
            sched = Schedule(self.full_schedule)
            return sched.validate(now)
        except SchedNotAllowed:
            return False
        except SchedSyntaxError as e:
            raise ex.Error(str(e))

    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)
   0707010001f41b000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/zfs  0707010001f41c000081a40000000000000000000000016a100daf0000494c000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/sync/zfs/__init__.py  import datetime
import os

from subprocess import *

import core.exceptions as ex
import core.status
from .. import Sync, notify
from env import Env
from utilities.subsystems.zfs import a2pool_dataset, Dataset
from utilities.lazy import lazy
from utilities.converters import print_duration
from core.objects.svcdict import KEYS
from utilities.proc import justcall
from utilities.string import bdecode

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "zfs"
KEYWORDS = [
    {
        "keyword": "src",
        "at": True,
        "required": True,
        "text": "Source dataset of the sync."
    },
    {
        "keyword": "dst",
        "at": True,
        "required": True,
        "text": "Destination dataset of the sync."
    },
    {
        "keyword": "target",
        "convert": "list",
        "required": True,
        "candidates": ['nodes', 'drpnodes', 'local'],
        "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'."
    },
    {
        "keyword": "intermediary",
        "at": True,
        "default": True,
        "convert": "boolean",
        "candidates": (True, False),
        "text": "Send snapshots created since the last update."
    },
    {
        "keyword": "recursive",
        "at": True,
        "default": True,
        "convert": "boolean",
        "candidates": (True, False),
        "text": "Send datasets hosted under the src dataset."
    },
    {
        "keyword": "tags",
        "convert": "set",
        "default": set(),
        "default_text": "",
        "example": "delay_snap",
        "at": True,
        "text": "The zfs sync resource supports the :c-tag:`delay_snap` tag. This tag is used to delay the snapshot creation just before the sync, thus after :kw:`postsnap_trigger` execution. The default behaviour (no tags) is to group all snapshots creation before copying data to remote nodes, thus between :kw:`presnap_trigger` and :kw:`postsnap_trigger`."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("zfs"):
        data.append("sync.zfs")
    return data

class SyncZfs(Sync):
    """define zfs sync resource to be zfs send/zfs receive between nodes
    """
    def __init__(self,
                 target=None,
                 src=None,
                 dst=None,
                 recursive = True,
                 intermediary = True,
                 snap_size=0,
                 **kwargs):
        super(SyncZfs, self).__init__(type="sync.zfs", **kwargs)
        self.label = "zfs of %s to %s"%(src, ",".join(target))
        self.target = target
        self.recursive = recursive
        self.intermediary = intermediary
        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 __str__(self):
        return "%s target=%s src=%s" % (
            super(SyncZfs, self).__str__(),
            self.target,
            self.src
        )

    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 sanity_check(self):
        if self.src_pool == self.dst_pool and 'local' in self.target:
            self.log.error('zfs send/receive in same pool not allowed')
            raise ex.Error

    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
        """
        self.sanity_check()
        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 r.target != ['nodes']:
                    return
                if action == 'sync_drp' and r.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 snap_exists(self, snapname, node=None):
        cmd = [Env.syspaths.zfs, 'list', '-t', 'snapshot', snapname]
        if node is not None:
            cmd = Env.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.Error
        if self.recursive :
            cmd = [Env.syspaths.zfs, 'snapshot' , '-r' , snap]
        else:
            cmd = [Env.syspaths.zfs, 'snapshot' , snap]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def get_upper_fs(self, fs):
        fsmembers = fs.split('/')
        if len(fsmembers) > 1:
            fsmembers = fsmembers[:len(fsmembers)-1]
            upperfs = "/".join(fsmembers)
        else:
            upperfs = '/'
        return upperfs

    def fs_exists(self, fsname, node=None):
        cmd = [Env.syspaths.zfs, 'list', '-t', 'filesystem', fsname]
        if node is not None:
            cmd = Env.rsh.split() + [node] + cmd
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        else:
            return False

    def create_fs(self, fs, node=None):
        if self.fs_exists(fs, node):
            msg = None
            if node is not None:
                msg = 'on node %s'%node
            self.log.info('%s already exist %s'%(fs, msg))
            return
        fsmembers = fs.split('/')
        if len(fsmembers) > 1:
            upperfs = self.get_upper_fs(fs)
            if not self.fs_exists(upperfs, node):
                self.create_fs(upperfs, node)
        cmd = [Env.syspaths.zfs, 'create' , fs]
        if node is not None:
            cmd = Env.rsh.split() + [node] + cmd
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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 Env.nodename not in self.svc.nodes or self.target != ["drpnodes"]:
            return
        self.peersenders |= self.svc.nodes
        self.peersenders -= set([Env.nodename])

    def get_targets(self):
        self.targets = set()
        if len(self.target) > 1 and 'local' in self.target:
            self.status_log("incompatible targets %s" % self.target, 'WARN')
            self.targets = set()
            return
        if 'local' in self.target:
            self.targets |= set([Env.nodename])
            return
        if 'nodes' in self.target:
            self.targets |= self.svc.nodes
        if 'drpnodes' in self.target:
            self.targets |= self.svc.drpnodes
        self.targets -= set([Env.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"] or self.target == ["local"]:
            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.Error("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.dst_snap_sent, node):
            return self.zfs_send_initial(node)

        send_cmd = [Env.syspaths.zfs, "send"]

        if self.recursive:
            send_cmd.append("-R")

        if self.intermediary:
            send_cmd.append("-I")
        else:
            send_cmd.append("-i")

        send_cmd += [self.src_snap_sent, self.src_snap_tosend]

        if self.src_ds == self.dst_ds or ( self.src_ds == self.src_pool and self.dst_ds == self.dst_pool ):
            receive_cmd = [Env.syspaths.zfs, "receive", "-dF", self.dst_pool]
        else:
            fspath = self.get_upper_fs(self.dst_ds)
            receive_cmd = [Env.syspaths.zfs, "receive", "-eF", fspath]
        if node is not None and 'local' not in self.target:
            _receive_cmd = Env.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 = [Env.syspaths.zfs, "send", "-R",
                        self.src_snap_tosend]
        else:
            send_cmd = [Env.syspaths.zfs, "send", "-p",
                        self.src_snap_tosend]

        if self.src_ds == self.dst_ds or ( self.src_ds == self.src_pool and self.dst_ds == self.dst_pool ):
            receive_cmd = [Env.syspaths.zfs, "receive", "-dF", self.dst_pool]
        else:
            fspath = self.get_upper_fs(self.dst_ds)
            self.create_fs(fspath, node)
            receive_cmd = [Env.syspaths.zfs, "receive", "-eF", fspath]
        if node is not None and 'local' not in self.target:
            _receive_cmd = Env.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.Error:
            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 = [Env.syspaths.zfs, 'destroy', '-r', snap]
        else:
            cmd = [Env.syspaths.zfs, 'destroy', snap]
        if node is not None:
            cmd = Env.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.Error

    def rename_snap(self, src, dst,  node=None):
        if self.snap_exists(dst, node):
            self.log.error("%s should not exist"%dst)
            raise ex.Error
        if self.recursive :
            cmd = [Env.syspaths.zfs, 'rename', '-r', src, dst]
        else:
            cmd = [Env.syspaths.zfs, 'rename', src, dst]

        if node is not None:
            cmd = Env.rsh.split() + [node] + cmd
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def rotate_snaps(self, src, dst, node=None):
        self.remove_snap(dst, node)
        self.rename_snap(src, dst, node)

    @notify
    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
        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(seconds=self.sync_max_delay)
        except IOError:
            self.status_log("zfs state file not found")
            return core.status.WARN
        except:
            import sys
            import traceback
            e = sys.exc_info()
            print(e[0], e[1], traceback.print_tb(e[2]))
            return core.status.WARN
        if last < now - delay:
            self.status_log("Last sync on %s older than %s" % (last, print_duration(self.sync_max_delay)))
            return core.status.WARN
        return core.status.UP

    def check_remote(self, node):
        rs = self.get_remote_state(node)
        if self.snap_uuid != rs['uuid']:
            self.log.error("%s last update uuid doesn't match snap uuid"%(node))
            raise ex.Error

    def get_remote_state(self, node):
        cmd1 = ['cat', self.statefile]
        cmd = Env.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.Error
        return self.parse_statefile(out, node=node)

    def get_local_state(self):
        with open(self.statefile, 'r') as f:
            out = f.read()
        return self.parse_statefile(out)

    def get_snap_uuid(self, snap):
        cmd = ['env', 'LC_ALL=C', Env.syspaths.zfs, 'list', '-H', '-o', 'creation', '-t', 'snapshot', snap]
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.Error
        self.snap_uuid = out.strip()

    @lazy
    def statefile(self):
        return os.path.join(self.var_d, 'zfs_state')

    def write_statefile(self):
        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 = Env.rcp.split() + [self.statefile, node+':'+self.statefile]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def push_statefile(self, node):
        self._push_statefile(node)
        self.get_peersenders()
        for node in self.peersenders:
            self._push_statefile(node)

    def parse_statefile(self, out, node=None):
        if node is None:
            node = Env.nodename
        lines = out.strip().split('\n')
        if len(lines) != 1:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.Error
        fields = lines[0].split(';')
        if len(fields) != 2:
            self.log.error("%s:%s is corrupted"%(node, self.statefile))
            raise ex.Error
        return dict(date=fields[0], uuid=fields[1])

0707010001f402000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap   0707010001f403000081a40000000000000000000000016a100daf000017e2000000e600010003ffffffffffffffff0000004c00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/necismsnap/__init__.py   import datetime
import time

import core.exceptions as ex
import core.status
import drivers.array.necism as array_driver
from .. import Sync, notify
from core.objects.svcdict import KEYS

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "necismsnap"
KEYWORDS = [
    {
        "keyword": "array",
        "required": True,
        "text": "Name of the NEC ISM array to send commands to."
    },
    {
        "keyword": "devs",
        "required": True,
        "text": "A whitespace-separated list of ``SV:LD``."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("iSMsc_query"):
        data.append("sync.necismsnap")
    return data


class SyncNecismsnap(Sync):
    def __init__(self,
                 array_name=None,
                 devs=None,
                 **kwargs):
        super(SyncNecismsnap, self).__init__(type="sync.necismsnap", **kwargs)

        self.devs = devs or []
        self.array = array_driver.NecIsm(array_name)
        self.parse_devs(devs)
        self.default_schedule = "@0"
        self.label = "NecIsm snapshot %s" % array_name

    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.InitError("%s: malformed devs" % self.rid)
            if sv in self.sv:
                raise ex.InitError("%s: duplicate sv %s in devs" % (self.rid, sv))
            if ld in self.ld:
                raise ex.InitError("%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.Error:
            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):
        return True

    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 core.status.WARN
        return core.status.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 core.status.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.Error:
            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()

    @notify
    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.Error("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(["app", "sync", "task", "disk.scsireserv"]))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()

  0707010001f419000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs 0707010001f41a000081a40000000000000000000000016a100daf00005866000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/symsrdfs/__init__.py import datetime
import json
import os
from xml.etree.ElementTree import XML

import core.status
from env import Env
from drivers.array.symmetrix import set_sym_env
from utilities.lazy import lazy

import core.exceptions as ex
import utilities.devices.linux

from .. import Sync
from core.objects.svcdict import KEYS

os.environ['PATH'] += ":/usr/symcli/bin"
set_sym_env()

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "symsrdfs"
KEYWORDS = [
    {
        "keyword": "symid",
        "at": True,
        "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."
    },
    {
        "keyword": "symdg",
        "at": False,
        "required": True,
        "text": "Name of the symmetrix device group where the source and target devices are grouped."
    },
    {
        "keyword": "rdfg",
        "at": False,
        "convert": "integer",
        "required": True,
        "text": "Name of the RDF group pairing the source and target devices."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("symdg"):
        data.append("sync.symsrdfs")
    return data


class SyncSymsrdfs(Sync):
    def __init__(self,
                 symid=None,
                 symdg=None,
                 rdfg=None,
                 symdevs=None,
                 precopy_timeout=300,
                 **kwargs):
        super(SyncSymsrdfs, self).__init__(type="sync.symsrdfs", **kwargs)

        if symdevs is None:
            symdevs = []
        self.pausable = False
        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" % (
            super(SyncSymsrdfs, self).__str__(),
            self.symdg,
            self.symdevs,
            self.rdfg
        )

    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.Error("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.lstrip("0")
            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.Error("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.lstrip("0")
            if dev_name in inq:
                devs += inq[dev_name]

        return devs

    def promote_devs_rw(self):
        if Env.sysname != "Linux":
            return
        devs = self.list_pd()
        devs = [d for d in devs if d.startswith("/dev/mapper/") or d.startswith("/dev/dm-") or d.startswith("/dev/rdsk/")]
        for dev in devs:
            self.promote_dev_rw(dev)

    def promote_dev_rw(self, dev):
        utilities.devices.linux.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(["app", "sync", "task", "disk.scsireserv"]))
        if self.svc.options.force or s['avail'].status == core.status.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.Error("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.Error("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.Error("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.Error("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.Error("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.Error
            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.Error("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.Error("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.Error

        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.Error("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.Error("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.Error("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.Error("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.Error("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.Error
        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.Error
        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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        state = self.get_dg_state()
        self.status_log("current state %s"%state, "info")
        if self.is_synchronous_and_synchronized_state():
            return core.status.UP
        self.status_log("expecting Synchronous/Synchronized")
        return core.status.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 Env.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.Error("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.Error("symrdf dg %s is RDF1 on drp node, you have to manually return to a sane SRDF status.")
        elif Env.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.Error("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.Error("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.Error("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(["app", "sync", "task", "disk.scsireserv"]))

    def get_svcstatus(self):
        if len(self.svcstatus) == 0:
            self.refresh_svcstatus()
  0707010001f3fa000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/sync/evasnap  0707010001f3fb000081a40000000000000000000000016a100daf00002bc4000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/sync/evasnap/__init__.py  import datetime
import json
import os
import subprocess
import xml.etree.ElementTree as ET

import core.exceptions as ex
import core.status
from .. import Sync, notify
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import which

DRIVER_GROUP = "sync"
DRIVER_BASENAME = "evasnap"
KEYWORDS = [
    {
        "section": "sync",
        "keyword": "eva_name",
        "rtype": "evasnap",
        "required": True,
        "text": "Name of the HP EVA array hosting the source and snapshot devices."
    },
    {
        "section": "sync",
        "keyword": "snap_name",
        "rtype": "evasnap",
        "required": True,
        "text": "Name of the snapshot objectname as seen in sssu."
    },
    {
        "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."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class SyncEvasnap(Sync):
    def __init__(self,
                 pairs=None,
                 eva_name="",
                 snap_name="",
                 **kwargs):
        super(SyncEvasnap, self).__init__(type="sync.evasnap", **kwargs)

        if pairs is None:
            pairs = []
        else:
            try:
                pairs = json.loads(pairs)
            except TypeError:
                pass
            except ValueError:
                pairs = []
                self.status_log("invalid pairs format")
        self.label = "EVA snapshot %s" % eva_name
        self.eva_name = eva_name
        self.snap_name = snap_name
        self.pairs = pairs
        self._lun_info = {}
        self.default_schedule = "@0"

    def __str__(self):
        return "%s eva_name=%s pairs=%s" % (
            super(SyncEvasnap, self).__str__(),
            self.eva_name, str(self.pairs)
        )

    def wait_for_devs_ready(self):
        pass

    def can_sync(self, target=None):
        return True

    def recreate(self):
        def snapname(info):
            return info['objectname'].split('\\')[-2]+'_'+self.snap_name

        try:
            self.prereq()
        except ex.Error as e:
            self.log.error(str(e))
            raise ex.Error

        status = self._status(skip_prereq=True)

        if not self.can_sync():
            return

        if not self.svc.options.force and status == core.status.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=None, verbose=False, check=True):
        if cmd is None:
            cmd = []
        os.chdir(Env.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.Error("sssu command execution error")
        return ret, out, err

    def lun_info(self, wwid):
        if wwid in self._lun_info:
            return self._lun_info[wwid]

        _wwid = wwid

        if '-' not in 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.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if not self.pairs:
            self.status_log("no pairs")
        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(seconds=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 core.status.WARN
        return core.status.UP

    def sync_resync(self):
        self.recreate()

    @notify
    def sync_update(self):
        self.recreate()

    def refresh_svcstatus(self):
        self.svcstatus = self.svc.group_status(excluded_groups=set(["app", "sync", "task", "disk.scsireserv"]))

    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):
        s = "array#" + self.eva_name
        try:
            self.svc.node.oget(s, "type")
        except ex.RequiredOptNotFound:
            raise ex.Error("no credentials for array %s in node or cluster configuration" % self.eva_name)
        try:
            self.manager = self.svc.node.oget(s, "manager")
        except ex.RequiredOptNotFound:
            raise ex.Error("no manager set for array %s in node or cluster configuration" % self.eva_name)
        try:
            self.username = self.svc.node.oget(s, "username")
        except ex.RequiredOptNotFound:
            raise ex.Error("no username set for array %s in node or cluster configuration" % self.eva_name)
        try:
            self.password = self.svc.node.oget(s, "password")
        except ex.RequiredOptNotFound:
            raise ex.Error("no password set for array %s in node or cluster configuration" % self.eva_name)
        self.sssubin = self.svc.node.oget(s, "bin")
        if not self.sssubin:
            self.sssubin = which(self.sssubin)
        if not self.sssubin:
            raise ex.Error("missing sssu binary")

        for pair in self.pairs:
            if 'src' not in pair or 'dst' not in pair or 'mask' not in pair:
                raise ex.Error("missing parameter in pair %s"%str(pair))
        ret, out, err = self.sssu(check=False)
        if "Error opening https connection" in out:
            raise ex.Error("error login to %s"%self.manager)
        elif "Error" in out:
            raise ex.Error("eva %s is not managed by %s"%(self.eva_name, self.manager))
0707010001f42b000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/volume    0707010001f42c000081a40000000000000000000000016a100daf0000713b000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/volume/__init__.py    """
Volume resource driver module.
"""
import os
import pwd
import grp

import core.exceptions as ex
import core.status
import utilities.lock
from env import Env
from utilities.converters import print_size
from utilities.naming import fmt_path, split_path, factory
from utilities.files import makedirs
from utilities.lazy import lazy
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.string import is_glob, is_string

DRIVER_GROUP = "volume"
DRIVER_BASENAME = None
KEYWORDS = [
    {
        "keyword": "name",
        "at": True,
        "required": False,
        "default": "{name}-vol-{rindex}",
        "text": "The volume service name. A service can only reference volumes in the same namespace."
    },
    {
        "keyword": "type",
        "protoname": "pooltype",
        "provisioning": True,
        "at": True,
        "text": "The type of the pool to allocate from. The selected pool will be the one matching type"
                " and capabilities and with the maximum available space."
    },
    {
        "keyword": "access",
        "default": "rwo",
        "candidates": ["rwo", "roo", "rwx", "rox"],
        "provisioning": True,
        "at": True,
        "text": "The access mode of the volume. "
                "``rwo`` is Read Write Once, "
                "``roo`` is Read Only Once, "
                "``rwx`` is Read Write Many, "
                "``rox`` is Read Only Many. "
                "``rox`` and ``rwx`` modes are served by flex volume services.",
    },
    {
        "keyword": "size",
        "at": True,
        "convert": "size",
        "provisioning": True,
        "text": "The size to allocate in the pool."
    },
    {
        "keyword": "pool",
        "at": True,
        "provisioning": True,
        "inheritance": "leaf",
        "text": "The name of the pool to allocate from."
    },
    {
        "keyword": "format",
        "at": True,
        "provisioning": True,
        "default": True,
        "convert": "boolean",
        "text": "If true the volume translator will also produce a fs resource layered over the disk allocated in the"
                " pool."
    },
    {
        "keyword": "configs",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<config name>/<key>:<volume relative path>:<options>``.",
        "example": "conf/mycnf:/etc/mysql/my.cnf:ro conf/sysctl:/etc/sysctl.d/01-db.conf"
    },
    {
        "keyword": "secrets",
        "at": True,
        "rtype": ["shm"],
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<secret name>/<key>:<volume relative path>:<options>``.",
        "example": "cert/pem:server.pem cert/key:server.key"
    },
    {
        "keyword": "directories",
        "at": True,
        "convert": "list",
        "default": [],
        "text": "The whitespace separated list of directories to create in the volume.",
        "example": "a/b/c d /e"
    },
    {
        "keyword": "user",
        "at": True,
        "text": "The user name or id that will own the volume root and installed files and directories.",
        "example": "1001"
    },
    {
        "keyword": "group",
        "at": True,
        "text": "The group name or id that will own the volume root and installed files and directories.",
        "example": "1001"
    },
    {
        "keyword": "perm",
        "at": True,
        "text": "The permissions, in octal notation, to apply to the installed files.",
        "example": "660"
    },
    {
        "keyword": "dirperm",
        "at": True,
        "text": "The permissions, in octal notation, to apply to the volume root and installed directories.",
        "example": "750"
    },
    {
        "keyword": "signal",
        "at": True,
        "text": "A <signal>:<target> whitespace separated list, where signal is a signal name or number "
                "(ex. 1, hup or sighup), and target is the comma separated list of resource ids to send the signal "
                "to (ex: container#1,container#2). If only the signal is specified, all candidate resources will be "
                "signaled. This keyword is usually used to reload daemons on certicate or configuration files changes.",
        "example": "hup:container#1"
    },
    {
        "keyword": "nodes",
        "at": True,
        "inheritance": "leaf",
        "text": "A node selector expression filtering the creator nodes to determine the volume nodes. "
                "If not set, all the creator nodes will be volume nodes.",
        "example": "site=p17",
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class Volume(Resource):
    """
    Volume resource class.

    A volume resource is linked to a volume object named <name> in the
    namespace of the service.

    The volume object contains disk and fs resources configured by the
    <pool> that created it, so the service doesn't have to embed 
    driver keywords that would prevent the service from being run on
    another cluster with different capabilities.

    Access:
    * rwo  Read Write Once
    * rwx  Read Write Many
    * roo  Read Only Once
    * rox  Read Only Many
    """

    def __init__(self, name=None, pool=None, pooltype=None, size=None,
                 format=True, access="rwo", secrets=None, configs=None,
                 user=None, group=None, perm=None, dirperm=None,
                 signal=None, directories=None, nodes=None, **kwargs):
        super(Volume, self).__init__(type="volume", **kwargs)
        self.pooltype = pooltype
        self.access = access
        self.name = name
        self.pool = pool
        self.size = size
        self.format = format
        self.secrets = secrets
        self.configs = configs
        self.directories = directories or []
        self.refresh_provisioned_on_provision = True
        self.refresh_provisioned_on_unprovision = True
        self.user = user
        self.group = group
        self.perm = perm
        self.dirperm = dirperm
        self.signal = signal
        self.nodes = nodes
        self.can_rollback_vol_instance = False
        self.can_rollback_flag = False

    def __str__(self):
        return "%s name=%s" % (super(Volume, self).__str__(), self.volname)

    def set_label(self):
        self.label = self.volname

    def on_add(self):
        self.set_label()
        if self.access and self.svc.topology == "flex":
            self.access = self.access[:2] + "x"

    @lazy
    def signal_data(self):
        import signal as signal_mod
        data = {}
        if self.signal is None:
            return data
        for e in self.signal.split():
            try:
                sig, tgt = e.split(":", 1)
            except Exception:
                continue
            try:
                sig = int(sig)
            except ValueError:
                sig = sig.upper()
                if not sig.startswith("SIG"):
                    sig = "SIG%s" % sig
                try:
                    sig = int(getattr(signal_mod, sig))
                except AttributeError:
                    continue
            if sig not in data:
                data[sig] = []
            for rid in tgt.split(","):
                if rid in data[sig]:
                    continue
                res = self.svc.get_resource(rid)
                if not res:
                    continue
                if hasattr(res, "send_signal"):
                    data[sig].append(rid)
        return data

    @lazy
    def volname(self):
        if self.name:
            return self.name
        else:
            return "%s-vol-%s" % (self.svc.name, self.rid.split("#")[-1])

    @lazy
    def volsvc(self):
        volume = factory("vol")(name=self.volname, namespace=self.svc.namespace, log_handlers=self.svc.log_handlers, node=self.svc.node)
        if not volume.exists():
            volume = factory("vol")(name=self.volname, namespace=self.svc.namespace, node=self.svc.node, log_handlers=self.svc.log_handlers, volatile=True)
            try:
                volume = self._configure_volume(volume, usage=False)
            except ex.Error:
                raise
            except Exception:
                import traceback
                traceback.print_exc()
        return volume

    @lazy
    def mount_point(self):
        try:
            return self.volsvc.mount_point()
        except ex.Error:
            return

    def mnt(self):
        """
        Expose the mount_point lazy as a callable for the '<volrid>.mnt'
        reference.
        """
        return self.mount_point

    @lazy
    def device(self):
        try:
            return self.volsvc.device()
        except ex.Error:
            return

    def chown(self):
        if self.mount_point is None:
            return
        uid = self.uid if self.uid is not None else -1
        gid = self.gid if self.gid is not None else -1
        os.chown(self.mount_point, uid, gid)
        if self.octal_dirmode:
            os.chmod(self.mount_point, self.octal_dirmode)

    def pre_provision_stop(self):
        self._stop(force=True)

    def stop(self):
        self._stop()

    def _stop(self, force=False):
        self.uninstall_flag()
        self.stop_vol_instance(force=force)

    def stop_vol_instance(self, force=False):
        try:
            self.volsvc
        except ex.Error:
            self.log.info("volume %s does not exist (and no pool can create it)", self.volname)
            return
        if not self.volsvc.exists():
            self.log.info("volume %s does not exist", self.volname)
            return
        users = self.volsvc.users(exclude=[self.svc.path])
        if users:
            self.log.info("skip %s stop: active users: %s",
                          self.volsvc.path, ",".join(users))
            return
        self.log.info("last user of %s on this node: stop the volume instance",
                      self.volsvc.path)
        if self.volsvc.action("stop", options={"local": True, "leader": self.svc.options.leader, "force": force}) != 0:
            raise ex.Error

    def rollback(self):
        if self.can_rollback_flag:
            self.uninstall_flag()
        if self.can_rollback_vol_instance:
            self.stop_vol_instance()

    def start(self):
        try:
            self.volsvc
        except ex.Error:
            raise ex.Error("volume %s does not exist (and no pool can create it)" % self.volname)
        if not self.volsvc.exists():
            raise ex.Error("volume %s does not exist" % self.volname)
        if self.volsvc.action("start", options={"local": True, "leader": self.svc.options.leader}) != 0:
            raise ex.Error
        self.can_rollback_vol_instance |= any([r.can_rollback for r in self.volsvc.resources_by_id.values()])
        self.can_rollback |= self.can_rollback_vol_instance
        self.chown()
        self.install_flag()
        self.can_rollback_flag = True
        self.can_rollback |= self.can_rollback_flag
        self.install_directories()
        self.install_secrets()
        self.install_configs()
        self.unset_lazy("device")
        self.unset_lazy("mount_point")

    @lazy
    def flag_path(self):
        return os.path.join(self.var_d, "flag")

    def flag_installed(self):
        return os.path.exists(self.flag_path)

    def uninstall_flag(self):
        try:
            os.unlink(self.flag_path)
        except Exception:
            pass

    def install_flag(self):
        makedirs(os.path.dirname(self.flag_path))
        with open(self.flag_path, "w"):
            pass

    def boot(self):
        self.uninstall_flag()

    def _status(self, verbose=False):
        self.data_status()
        try:
            self.volsvc
        except ex.Error:
            self.status_log("volume %s does not exist (and no pool can provision it)" % self.volname, "info")
            return core.status.DOWN
        if not self.volsvc.exists():
            self.status_log("volume %s does not exist" % self.volname, "info")
            return core.status.DOWN
        status = core.status.Status(self.volsvc.print_status_data()["avail"])
        if self.volsvc.print_status_data()["overall"] == "warn":
            self.status_log("%s has warnings" % self.volsvc.path)
        if not self.flag_installed():
            level = "warn" if status == "warn" else "info"
            self.status_log("%s avail %s" % (self.volsvc.path, status), level)
            return core.status.DOWN
        return status

    def exposed_devs(self):
        try:
            dev = self.volsvc.device()
            if dev is None:
                return set()
            return set([dev])
        except ex.Error:
            return set()

    def data_data(self, kind):
        """
        Transform the secrets/configs mappings list into a list of data structures.
        """
        if not self.mount_point:
            # volume not yet provisioned
            return []
        if kind == "sec" and self.secrets:
            refs = self.secrets
        elif kind == "cfg" and self.configs:
            refs = self.configs
        else:
            refs = []
        data = []
        for ref in refs:
            try:
                datapath, path = ref.split(":", 1)
            except Exception:
                continue
            datapath = datapath.lstrip("/")
            if datapath.startswith("usr/"):
                kind = "usr"
                datapath = datapath[4:]
            elif datapath.startswith("sec/"):
                datapath = datapath[4:]
            elif datapath.startswith("cfg/"):
                datapath = datapath[4:]
            try:
                datapath, key = datapath.split("/", 1)
            except Exception:
                continue
            try:
                if path in ("", os.sep):
                    path = self.mount_point.rstrip(os.sep) + os.sep
                else:
                    path = os.path.join(self.mount_point.rstrip(os.sep), path.lstrip(os.sep))
            except AttributeError:
                # self.mount_point changed to None since tested, so has no rstrip()
                continue
            data.append({
                "obj": fmt_path(datapath, namespace=self.svc.namespace, kind=kind),
                "key": key,
                "path": path,
            })
        return data

    def data_status(self):
        self._data_status("cfg")
        self._data_status("sec")

    def _data_status(self, kind):
        for data in self.data_data(kind):
            name, _, kind = split_path(data["obj"])
            obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node)
            if not obj.exists():
                self.status_log("referenced %s %s does not exist: "
                                "expected data %s can not be installed in the volume" % (kind, name, data["key"]),
                                "warn")
                continue
            keys = obj.resolve_key(data["key"])
            if not keys and not is_glob(data["key"]):
                self.status_log("%s %s has no key %s. "
                                "expected data can not be installed in the volume" % (kind, name, data["key"]), "warn")

    @lazy
    def octal_mode(self):
        try:
            return int(self.perm, 8)
        except TypeError:
            return None

    @lazy
    def octal_dirmode(self):
        try:
            return int(self.dirperm, 8)
        except TypeError:
            return None

    @lazy
    def uid(self):
        if self.user is None:
            return
        try:
            return int(self.user)
        except ValueError:
            pass
        try:
            info = pwd.getpwnam(self.user)
            return info.pw_uid
        except Exception:
            pass

    @lazy
    def gid(self):
        if self.group is None:
            return
        try:
            return int(self.group)
        except ValueError:
            pass
        try:
            info = grp.getgrnam(self.group)
            return info.gr_gid
        except Exception:
            pass
        return

    def _install_data(self, kind):
        changed = False
        for data in self.data_data(kind):
            name, _, kind = split_path(data["obj"])
            obj = factory(kind)(name, namespace=self.svc.namespace, volatile=True, node=self.svc.node)
            if not obj.exists():
                self.log.warning("referenced %s %s does not exist: "
                                 "expected data %s can not be installed in the volume",
                                 kind, name, data["key"])
                continue
            keys = obj.resolve_key(data["key"])
            if not keys and not is_glob(data["key"]):
                self.log.warning("%s %s has no key %s. "
                                 "expected data can not be installed in the volume",
                                 kind, name, data["key"])
                continue
            self.log.debug("install ./%s/%s/%s in %s", kind, name, data["key"], data["path"])
            for key in keys:
                changed |= obj.install_key(key, data["path"], uid=self.uid, gid=self.gid, mode=self.octal_mode,
                                           dirmode=self.octal_dirmode)
        return changed

    def install_directories(self):
        for d in self.directories:
            p = os.path.join(self.mount_point, d.strip("/"))
            if not os.path.exists(p):
                makedirs(p, uid=self.uid, gid=self.gid, mode=self.octal_dirmode)
                continue
            if not os.path.isdir(p):
                raise ex.Error("directory path %s is already occupied by a non-directory"%p)

    def has_data(self, kind, name, key=None):
        for data in self.data_data(kind):
            if data["obj"] != name:
                continue
            if key and data["key"] != key:
                continue
            return True
        return False

    def install_data(self):
        self.install_directories()
        changed = self.install_secrets()
        changed |= self.install_configs()
        if changed:
            self.send_signals()

    def send_signals(self):
        for sig, rids in self.signal_data.items():
            for rid in rids:
                res = self.svc.get_resource(rid)
                res.send_signal(sig)

    def install_configs(self):
        return self._install_data("cfg")

    def install_secrets(self):
        return self._install_data("sec")

    def has_config(self, name, key=None):
        return self.has_data("cfg", name, key)

    def has_secret(self, name, key=None):
        return self.has_data("sec", name, key)

    def provisioned(self):
        try:
            self.volsvc
        except ex.Error:
            return False
        if not self.volsvc.exists():
            return False
        if not self.owned():
            return False
        return self.volsvc.print_status_data().get("provisioned")

    def claimed(self, volume=None):
        if not volume:
            volume = self.volsvc
        if volume.children:
            return True
        return False

    def owned(self, volume=None):
        if not volume:
            volume = self.volsvc
        if not self.claimed(volume):
            return False
        if self.svc.path not in volume.children:
            return False
        return True

    def incompatible_claims(self, volume=None):
        if not volume:
            volume = self.volsvc
        volume_children_not_in_svc_parents = set()
        for e in volume.children:
            c1 = e + "@" + Env.nodename
            c2 = split_path(e)[0] + "@" + Env.nodename
            if c1 not in self.svc.parents and \
               c2 not in self.svc.parents:
                volume_children_not_in_svc_parents.add(e)
        return volume_children_not_in_svc_parents

    def claim(self, volume):
        if self.shared:
            if self.owned(volume):
                self.log.info("shared volume %s is already claimed by %s",
                              volume.path, self.svc.path)
                return
            parents = self.incompatible_claims(volume)
            if parents:
                raise ex.Error("shared volume %s is already claimed by %s, not local parents of this service"
                               % (volume.name, ",".join(parents)))
            else:
                self.log.info("shared volume %s current claims are compatible: %s",
                              volume.path, volume.children)
        else:
            if self.owned(volume):
                self.log.info("volume %s is already claimed by %s", volume.path, self.svc.path)
                return
        self.log.info("claim volume %s", volume.name)
        volume.set_multi(["DEFAULT.children+=%s" % self.svc.path])

    def unclaim(self):
        self.log.info("unclaim volume %s", self.volsvc.name)
        self.volsvc.set_multi(["DEFAULT.children-=%s" % self.svc.path], validation=False)

    def unprovisioner(self):
        try:
            self.volsvc
        except ex.Error:
            return
        if not self.volsvc.exists():
            return
        self.unclaim()

    def provisioner_shared_non_leader(self):
        self.provisioner()

    def provisioner(self):
        """
        Create a volume service with resources definitions deduced from the storage
        pool translation rules.
        """
        volume = self.create_volume()
        self.claim(volume)
        self.log.info("provision the %s volume service instance", self.volname)

        # will be rolled back by the volume resource. for now, the remaining
        # resources might need the volume for their provision
        #
        # if multiple svc declare using the same vol, the 1st provisioning svc
        # triggers the volume provisioning ... following svc provisioning must
        # wait (via the action lock) for the vol provisioning to finish.
        ret = volume.action("provision", options={
            "disable_rollback": True,
            "local": True,
            "leader": self.svc.options.leader,
            "notify": True,
            "waitlock": "5m",
        }) 
        if ret != 0:
            raise ex.Error("volume provision returned %d" % ret)
        self.can_rollback = True
        self.can_rollback_vol_instance = True
        self.post_provision_reset_lazy()

    def post_provision_reset_lazy(self):
        self.unset_lazy("device")
        self.unset_lazy("mount_point")
        self.unset_lazy("volsvc")

    def create_volume(self):
        """
        Another service provision may try to create the same volume simultaneously.
        Protect this method with a global lock.
        """
        lockfile = os.path.join(self.volsvc.var_d, "create_volume.lock")
        try:
            with utilities.lock.cmlock(lockfile=lockfile, timeout=20):
                return self.create_volume_locked()
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.Error("acquire create volume lock error: %s" % str(exc))

    def create_volume_locked(self):
        # the volsvc has initialized the volume logger as volatile already.
        # reset the logger handlers so we have the provisioning logs available
        # for post-mortem analysis.
        map(self.volsvc.logger.removeHandler, self.volsvc.logger.handlers[:])
        self.volsvc.logger.handlers = []

        volume = factory("vol")(name=self.volname, namespace=self.svc.namespace, node=self.svc.node)

        # force logger lazy eval now, to be sure its is configured non-volatile
        volume.logger

        if volume.exists():
            self.log.info("volume %s already exists", self.volname)
            data = volume.print_status_data(mon_data=True)
            if not data or "cluster" not in data:
                return volume
            if not self.svc.node.get_pool(volume.pool):
                raise ex.Error("pool %s not found on this node" % volume.pool)
            if self.svc.options.leader and volume.topology == "failover" and \
               (self.owned() or not self.claimed(volume)) and \
               data["avail"] != "up":
                cluster_avail = data["cluster"].get("avail")
                if cluster_avail is None:
                    self.log.info("no take over decision, we are leader but unknown cluster avail for volume %s",
                                  self.volname)
                elif cluster_avail == "up":
                    self.log.info("volume %s is up on, peer, we are leader: take it over", self.volname)
                    volume.action("takeover", options={"wait": True, "time": 60})
            return volume
        elif not self.svc.options.leader:
            self.log.info("volume %s does not exist, we are not leader: wait its propagation", self.volname)
            self.wait_for_fn(lambda: volume.exists(),
                             10,
                             1,
                             "non leader instance waited for too long for the volume to appear")
            return volume
        self.check_configure_requirements()
        self.log.info("create new volume %s (pool name: %s, pool type: %s, "
                      "access: %s, size: %s, format: %s, shared: %s)",
                      self.volname, self.pool, self.pooltype,
                      self.access, print_size(self.size, unit="B", compact=True), self.format, self.shared)
        return self._configure_volume(volume)

    def check_configure_requirements(self):
        missing = []
        if not self.size:
            missing.append("size")
        if missing:
            raise ex.Error("missing provisioning keywords required to create "
                           "the volume object: %s. if another svc is responsible "
                           "for creating the volume add a parent relation."
                           "" % ",".join(missing))

    def volume_env_data(self, pool):
        env = {}
        env.update(self._volume_env_data(pool.volume_env, optional=False))
        env.update(self._volume_env_data(pool.optional_volume_env, optional=True))
        return env

    def _volume_env_data(self, mappings, optional=False):
        env = {}
        for mapping in mappings:
            try:
                src, dst = mapping.split(":", 1)
            except Exception:
                continue
            args = src.split(".", 1)
            val = self.svc.oget(*args)
            if val is None:
                if optional:
                    continue
                else:
                    raise ex.Error("missing mapped key in %s: %s" % (self.svc.path, mapping))
            if is_string(val) and ".." in val:
                raise ex.Error("the '..' substring is forbidden in volume env keys: %s=%s" % (mapping, val))
            env[dst] = val
        return env

    def find_pool(self, usage=True, shared=None):
        return self.svc.node.find_pool(
            poolname=self.pool,
            pooltype=self.pooltype,
            access=self.access,
            size=self.size,
            fmt=self.format,
            shared=shared,
            usage=usage
        )

    def _configure_volume(self, volume, usage=True):
        if self.nodes:
            nodes = self.nodes
        else:
            try:
                nodes = self.svc._get("DEFAULT.nodes")
            except ex.OptNotFound:
                nodes = None
        pool = None
        try:
            pool = self.find_pool(usage=usage, shared=self.shared)
        except ex.Error as exc:
            if self.shared and nodes and len(nodes) == 1:
                try:
                    pool = self.find_pool(usage=usage, shared=False)
                    self.log.warning("could not find a shared-capable pool matching criteria. fallback to the non shared-capable pool %s. beware: if you add nodes later you will have to provision a new shared volume and move the data.", pool.name)
                except ex.Error as exc:
                    raise ex.Error("could not find a pool matching criteria:\n%s" % exc)
        if not pool:
            raise ex.Error("could not find a pool matching criteria")
        pool.log = self.log
        env = self.volume_env_data(pool)
        if env and not volume.volatile:
            self.log.info("volume env from mapping: %s", ", ".join(["%s=%s" % (k, v) for k, v in env.items()]))
        volume = pool.configure_volume(volume,
                                       fmt=self.format,
                                       size=self.size,
                                       access=self.access,
                                       nodes=nodes,
                                       shared=self.shared,
                                       env=env)
        return volume

 0707010001f394000041ed00000000000000000000000e6a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/resource/fs    0707010001f396000081a40000000000000000000000016a100daf00000fa1000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/aix.py import os
from stat import *

import core.exceptions as ex
from utilities.files import protected_mount, getmount
from utilities.mounts.aix import Mounts
from utilities.proc import qcall
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""


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 Fs(BaseFs):
    """
    AIX fs resource driver.
    """
    def __init__(self, **kwargs):
        self.mounts = None
        super(Fs, self).__init__(**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 = 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 = 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_mount(self):
        if self.mounts is None:
            self.mounts = Mounts()
        self.prepare_mount()
        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.Error
        self.mounts = None
        self.can_rollback = True

    def stop(self):
        if self.mounts is None:
            self.mounts = 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.Error
        self.mounts = None
   0707010001f3a1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext3   0707010001f3a2000081a40000000000000000000000016a100daf000000a9000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext3/__init__.py   from drivers.resource.fs import KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "ext3",
    name=__name__,
    keywords=KEYWORDS,
)

   0707010001f3a3000081a40000000000000000000000016a100daf0000012c000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext3/linux.py  from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "ext3"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkfs.ext3"):
        return ["fs.ext3"]
    return []

class FsExt3(Fs):
    mkfs = ['mkfs.ext3', '-F', '-q']
    queryfs = ['tune2fs', '-l']

0707010001f39c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/docker 0707010001f39d000081a40000000000000000000000016a100daf000010e6000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/fs/docker/__init__.py import os

import core.status
import utilities.subsystems.docker as dockerlib
import core.exceptions as ex
from utilities.lazy import lazy
from core.resource import Resource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "docker"
KEYWORDS = [
    {
        "keyword": "driver",
        "default": "local",
        "at": True,
        "text": "The docker volume driver to use for the resource.",
        "example": "tmpfs"
    },
    {
        "keyword": "options",
        "at": True,
        "convert": "shlex",
        "text": "The docker volume create options to use for the resource. :opt:`--label` and :opt:`--opt`",
        "example": "--opt o=size=100m,uid=1000 --opt type=tmpfs --opt device=tmpfs"
    },
    {
        "section": "fs",
        "keyword": "populate",
        "at": True,
        "convert": "list",
        "provisioning": True,
        "text": "The list of modulesets providing files to install in the volume.",
        "example": "configmap.redis configmap.global"
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = []
    if dockerlib.has_docker(["docker", "docker.io"]):
        data.append("fs.docker")
    return data


class FsDocker(Resource):
    def __init__(self, driver=None, options=None, populate=None, **kwargs):
        super(FsDocker, self).__init__(type='fs.docker', **kwargs)
        self.driver = driver
        self.options = options
        self.populate = populate or []

    @lazy
    def lib(self):
        """
        Lazy allocator for the dockerlib object.
        """
        try:
            return self.svc.dockerlib
        except AttributeError:
            self.svc.dockerlib = dockerlib.DockerLib(self.svc)
            return self.svc.dockerlib

    @lazy
    def label(self): # pylint: disable=method-hidden
        return "%s volume %s" % (self.driver, self.volname)

    def _status(self, verbose=False):
        return core.status.NA

    def _info(self):
        data = [
          ["name", self.volname],
          ["driver", self.driver],
          ["options", self.options],
          ["vol_path", self.vol_path],
        ]
        return data

    @lazy
    def mount_point(self):
        mount_point = self.lib.container_data_dir
        if mount_point is None:
            mount_point = "/var/tmp"
        return mount_point

    @lazy
    def vol_path(self):
        return self.lib.docker_volume_inspect(self.volname).get("Mountpoint")

    def has_it(self):
        try:
            data = self.lib.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):
        if self.svc.namespace:
            return ".".join([self.svc.namespace.lower(), self.svc.name, self.rid.replace("#", ".")])
        else:
            return ".".join([self.svc.name, self.rid.replace("#", ".")])

    def create_vol(self):
        if self.has_it():
            return 0
        cmd = self.lib.docker_cmd + ["volume", "create", "--name", self.volname]
        if self.options:
            cmd += self.options
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def start(self):
        pass

    def stop(self):
        pass

    def provisioned(self):
        return self.has_it()

    def provisioner(self):
        self.lib.docker_start()
        self.create_vol()
        self.populatefs()

    def populatefs(self):
        modulesets = self.populate
        if not modulesets:
            return
        try:
            os.environ["OPENSVC_VOL_PATH"] = self.vol_path
            self.svc.compliance.options.moduleset = ",".join(modulesets)
            ret = self.svc.compliance.do_run("fix")
        finally:
            del os.environ["OPENSVC_VOL_PATH"]
        if ret:
            raise ex.Error

    def unprovisioner(self):
        if not self.has_it():
            return
        cmd = self.lib.docker_cmd + ["volume", "rm", "-f", self.volname]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

  0707010001f3c0000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/fs/zfs    0707010001f3c1000081a40000000000000000000000016a100daf00001384000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/fs/zfs/__init__.py    import os

import core.exceptions as ex

from utilities.converters import convert_size
from env import Env
from utilities.subsystems.zfs import Dataset
from core.objects.svcdict import KEYS
from core.capabilities import capabilities
from .. import KWS_POOLING

KEYWORDS = KWS_POOLING + [
    {
        "section": "fs",
        "rtype": "zfs",
        "keyword": "size",
        "required": False,
        "convert": "size",
        "at": True,
        "text": "Used by default as the refquota of the provisioned dataset. The quota, refquota, reservation and refreservation values can be expressed as a multiplier of size (example: quota=x2).",
        "provisioning": True
    },
    {
        "section": "fs",
        "rtype": "zfs",
        "keyword": "refquota",
        "required": False,
        "default": "x1",
        "at": True,
        "text": "The dataset 'refquota' property value to set on provision. The value can be 'none', or a size expression, or a multiplier of the size keyword value (ex: x2).",
        "provisioning": True
    },
    {
        "section": "fs",
        "rtype": "zfs",
        "keyword": "quota",
        "required": False,
        "at": True,
        "text": "The dataset 'quota' property value to set on provision. The value can be 'none', or a size expression, or a multiplier of the size keyword value (ex: x2).",
        "provisioning": True
    },
    {
        "section": "fs",
        "rtype": "zfs",
        "keyword": "refreservation",
        "required": False,
        "at": True,
        "text": "The dataset 'refreservation' property value to set on provision. The value can be 'none', or a size expression, or a multiplier of the size keyword value (ex: x2).",
        "provisioning": True
    },
    {
        "section": "fs",
        "rtype": "zfs",
        "keyword": "reservation",
        "required": False,
        "at": True,
        "text": "The dataset 'reservation' property value to set on provision. The value can be 'none', or a size expression, or a multiplier of the size keyword value (ex: x2).",
        "provisioning": True
    },
]

KEYS.register_driver(
    "fs",
    "zfs",
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("zfs"):
        data.append("fs.zfs")
    return data

class FsZfsMixin():
    @property
    def poolname(self):
        return self.device.split("/")[0]

    def unprovisioner(self):
        if "node.x.zfs" not in capabilities:
            self.log.error("zfs command not found")
            raise ex.Error
        import core.status
        need_stop = None
        for r in self.svc.get_resources(["volume", "disk.zfs"]):
            if r.device != self.poolname:
                continue
            if r.status() not in (core.status.UP, core.status.STDBY_UP):
                r.start()
                need_stop = r
        dataset = Dataset(self.device, log=self.log)
        if dataset.exists():
            dataset.destroy(["-r"])
        if os.path.exists(self.mount_point) and os.path.isdir(self.mount_point):
            try:
                os.rmdir(self.mount_point)
                self.log.info("rmdir %s", self.mount_point)
            except OSError as exc:
                self.log.warning("failed to rmdir %s: %s", self.mount_point, exc)
        if need_stop:
            need_stop.stop()

    def provisioner(self):
        if "node.x.zfs" not in capabilities:
            self.log.error("zfs command not found")
            raise ex.Error
        dataset = Dataset(self.device, log=self.log)
        mkfs_opt = ["-p"]
        mkfs_opt += self.oget("mkfs_opt")

        if not any([True for e in mkfs_opt if e.startswith("mountpoint=")]):
            mkfs_opt += ['-o', 'mountpoint='+self.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)

        def convert(x, size):
            val = self.oget(x)
            if val in (None, "none", ""):
                return
            if val[0] == "x":
                if not size:
                    return
                try:
                    m = float(val[1:])
                except Exception:
                    raise ex.Error("%s set to a multiplier of size, but invalid: %s" % (x, val))
                return int(size * m)
            return convert_size(val, _to="m")

        nv_list = dict()
        size = self.oget("size")
        if size:
            size = convert_size(size, _to="m")
        for prop in ("refquota", "quota", "reservation", "refreservation"):
            val = convert(prop, size)
            if val:
                nv_list[prop] = "%dM" % val
        if not nv_list:
            return
        dataset.verify_prop(nv_list)

    def provisioned(self):
        dataset = Dataset(self.device, log=self.log)
        return dataset.exists()
0707010001f3c4000081a40000000000000000000000016a100daf00000085000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/fs/zfs/sunos.py   from . import FsZfsMixin
from ..sunos import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "zfs"

class FsZfs(FsZfsMixin, Fs):
    pass

   0707010001f3c2000081a40000000000000000000000016a100daf00000087000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/fs/zfs/freebsd.py from . import FsZfsMixin
from ..freebsd import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "zfs"

class FsZfs(FsZfsMixin, Fs):
    pass

 0707010001f3c3000081a40000000000000000000000016a100daf00000085000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/fs/zfs/linux.py   from . import FsZfsMixin
from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "zfs"

class FsZfs(FsZfsMixin, Fs):
    pass

   0707010001f3a4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext4   0707010001f3a6000081a40000000000000000000000016a100daf0000012c000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext4/linux.py  from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "ext4"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkfs.ext4"):
        return ["fs.ext4"]
    return []

class FsExt4(Fs):
    mkfs = ['mkfs.ext4', '-F', '-q']
    queryfs = ['tune2fs', '-l']

0707010001f3a5000081a40000000000000000000000016a100daf000000a9000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext4/__init__.py   from drivers.resource.fs import KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "ext4",
    name=__name__,
    keywords=KEYWORDS,
)

   0707010001f3a7000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag   0707010001f3ab000081a40000000000000000000000016a100daf000000ef000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag/linux.py  import os

from . import BaseFsFlag
from utilities.lazy import lazy

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "flag"

class FsFlag(BaseFsFlag):
    @lazy
    def base_flag_d(self):
        return os.path.join(os.sep, "dev", "shm", "opensvc")
 0707010001f3aa000081a40000000000000000000000016a100daf000000ef000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag/freebsd.py    import os

from . import BaseFsFlag
from utilities.lazy import lazy

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "flag"

class FsFlag(BaseFsFlag):
    @lazy
    def base_flag_d(self):
        return os.path.join(os.sep, "var", "run", "opensvc")
 0707010001f3ac000081a40000000000000000000000016a100daf0000019a000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag/sunos.py  import os

from . import BaseFsFlag
from utilities.lazy import lazy

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "flag"


class FsFlag(BaseFsFlag):
    @lazy
    def base_flag_d(self):
        flag_dir = os.path.join(os.sep, "system", "volatile")
        if os.path.exists(flag_dir):
            return os.path.join(flag_dir, "opensvc")
        else:
            return os.path.join(os.sep, "var", "run", "opensvc")
  0707010001f3a8000081a40000000000000000000000016a100daf00000a60000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag/__init__.py   import os

from drivers.resource.fs import KW_STAT_TIMEOUT
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "flag",
    name=__name__,
    keywords=[KW_STAT_TIMEOUT],
)

import core.status
from core.resource import Resource
from env import Env
from utilities.files import makedirs
from utilities.lazy import lazy
from utilities.proc import justcall


class BaseFsFlag(Resource):
    def __init__(self, type='fs.flag', **kwargs):
        super(BaseFsFlag, self).__init__(type=type, **kwargs)

    @lazy
    def base_flag_d(self):
        "return directory where flag files are created"
        return os.path.join(os.sep, 'tmp', 'opensvc')

    @lazy
    def flag_f(self):
        flag_name = os.path.join(self.svc.kind, self.svc.name, self.rid+".flag")
        if self.svc.namespace:
            flag_name = os.path.join(self.svc.namespace, flag_name)
        return os.path.join(self.base_flag_d, flag_name)

    @lazy
    def flag_d(self):
        return os.path.dirname(self.flag_f)

    def touch(self, fpath):
        if os.path.exists(fpath):
            os.utime(fpath, None)
        else:
            open(fpath, "a").close()

    def has_it(self):
        return os.path.exists(self.flag_f)

    def _status(self, verbose=False):
        return core.status.UP if self.has_it() else core.status.DOWN

    def start(self):
        if not self.has_it():
            self.log.info("create flag %s", self.flag_f)
            makedirs(self.flag_d, mode=0o700)
            self.touch(self.flag_f)
            self.can_rollback = True

    def stop(self):
        if self.has_it():
            self.log.info("unlink flag %s", self.flag_f)
            os.unlink(self.flag_f)

    def abort_start(self):
        if self.svc.topology != "failover":
            return
        if self.is_standby:
            return
        if self.svc.kind == "vol":
            # volumes are slaves of their consumer svc
            return
        try:
            for node in self.svc.nodes:
                if node == Env.nodename:
                    continue
                cmd = Env.rsh.split() + [node, "test", "-f", self.flag_f]
                out, err, ret = justcall(cmd)
                if ret == 0:
                    self.log.error("already up on %s", node)
                    return True
            return False
        except Exception as exc:
            self.log.exception(exc)
            return True

    def provisioner(self):
        pass

    def unprovisioner(self):
        pass

    def provisioned(self):
        flag = self.is_provisioned_flag()
        if flag is None:
            return False
        return flag

0707010001f3a9000081a40000000000000000000000016a100daf000000ef000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/fs/flag/darwin.py import os

from . import BaseFsFlag
from utilities.lazy import lazy

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "flag"

class FsFlag(BaseFsFlag):
    @lazy
    def base_flag_d(self):
        return os.path.join(os.sep, "var", "run", "opensvc")
 0707010001f397000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/fs/btrfs  0707010001f398000081a40000000000000000000000016a100daf00001085000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/fs/btrfs/__init__.py  import os
import tempfile
import time

import core.exceptions as ex

from .. import KEYWORDS
from ..linux import Fs
from core.objects.svcdict import KEYS
from core.capabilities import capabilities
from utilities.subsystems.btrfs import Btrfs
from utilities.lazy import lazy
from utilities.proc import justcall

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "btrfs"

KEYS.register_driver(
    "fs",
    "btrfs",
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("btrfs"):
        data.append("fs.btrfs")
    return data

class FsBtrfs(Fs):
    queryfs = ['btrfs', 'device', 'ready']

    @lazy
    def mkfs(self):
        return ['mkfs.btrfs', '-f', '-L', self.btrfs_label]

    @lazy
    def raw_btrfs_label(self):
        return '{name}.' + self.rid.replace("#", ".")

    @lazy
    def btrfs_label(self):
        return self.svc.name + '.' + self.rid.replace("#", ".")

    def provisioned(self):
        ret = super(FsBtrfs, self).provisioned()
        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(self.subvol)
        finally:
            self.cleanup(mnt)

    def current_label(self, mnt):
        cmd = ["btrfs", "filesystem", "label", mnt]
        ret, out, err = self.call(cmd, errlog=False)
        if ret == 0 and len(out.strip()) > 0:
            return out.strip()

    @lazy
    def subvol(self):
        l = self.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.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_btrfs_label = current_label.replace(self.svc.name, "{name}")
        else:
            label = self.btrfs_label
            raw_btrfs_label = self.raw_btrfs_label
        self.svc.set_multi(["%s.dev=%s" % (self.rid, "LABEL="+raw_btrfs_label)])
        self.unset_lazy("device")
        self.wait_label(label)

    def wait_label(self, label):
        if "node.x.findfs" not in capabilities:
            self.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.log.debug("%s\n%s\n%s" % (" ".join(cmd), out, err))
            if ret == 0:
                return
            self.log.info("label is not usable yet (%s)" % err.strip())
            time.sleep(2)
        raise ex.Error("timeout waiting for label to become usable")

    def mount(self, mnt):
        self.set_loopdevice()
        if self.loopdevice is None:
            device = self.device
        else:
            device = self.loopdevice
        cmd = ["mount", "-t", "btrfs", "-o", "subvolid=0", device, mnt]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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.log.info("subvolume %s provisioned" % self.subvol)
            self.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.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def provisioner(self):
        if self.device.startswith("LABEL=") or self.device.startswith("UUID="):
            self.log.info("skip formatting because dev is specified by LABEL or UUID")
        else:
            self.provisioner_fs()
        self.create_subvol()


   0707010001f3b0000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/none   0707010001f3b2000081a40000000000000000000000016a100daf0000008e000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/none/linux.py  from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "none"

class FsNone(Fs):
    def check_stat_device(self):
        return True

  0707010001f3b1000081a40000000000000000000000016a100daf000000b8000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/none/__init__.py   from drivers.resource.fs import KWS_VIRTUAL as KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "none",
    name=__name__,
    keywords=KEYWORDS,
)

0707010001f3b4000081a40000000000000000000000016a100daf000016ef000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/sunos.py   import os
import re
import time

import core.exceptions as ex
from env import Env
from utilities.subsystems.zfs import zfs_getprop, zfs_setprop
from utilities.mounts.sunos import Mounts
from . import BaseFs
from utilities.proc import justcall
from utilities.lazy import lazy

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""

class Fs(BaseFs):
    """
    SunOS fs resource driver.
    """
    def __init__(self, **kwargs):
        super(Fs, self).__init__(**kwargs)

    @lazy
    def rdevice(self):
        return self.device.replace('/dsk/', '/rdsk/', 1)

    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):
        mounts = Mounts()
        return mounts.has_mount(self.device, self.mount_point)

    def start_mount(self):
        self.prepare_mount()
        m = re.match(r"<(\w+)>", self.raw_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.raw_mount_point = re.sub(r"<\w+>", zonepath, self.raw_mount_point)
                    self.unset_lazy("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':
            return self.mount_zfs()
        return self.mount_generic()

    def mount_zfs(self):
        zone = self.svc.oget(self.rid, "zone")
        if not self.encap and not zone and zfs_getprop(self.device, 'zoned') != 'off':
            if zfs_setprop(self.device, 'zoned', 'off', log=self.log):
                raise ex.Error
        if zfs_getprop(self.device, 'mountpoint') == "legacy":
            self.mount_generic()
        else:
            self.mount_zfs_native()

    def mount_zfs_native(self):
        if zfs_getprop(self.device, 'mountpoint') != self.mount_point:
            if not zfs_setprop(self.device, 'mountpoint', self.mount_point, log=self.log):
                raise ex.Error

        if self.is_up() is True:
            return

        try:
            os.unlink(self.mount_point+"/.opensvc")
        except:
            pass
        ret, out, err = self.vcall([Env.syspaths.zfs, 'mount', self.device])
        if ret != 0:
            ret, out, err = self.vcall([Env.syspaths.zfs, 'mount', '-O', self.device])
            if ret != 0:
                raise ex.Error
        self.can_rollback = True

    def mount_generic(self):
        if 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)


        if ret != 0:
            raise ex.Error

        self.can_rollback = True

    def can_check_writable(self):
        if self.fs_type != 'zfs':
            return True
        pool = self.device.split("/")[0]
        cmd = [Env.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' and zfs_getprop(self.device, 'mountpoint') != "legacy":
            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.Error
            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.Error

    def stop(self):
        if self.is_up() is False:
            self.log.info("%s is already umounted" % self.label)
            return

        try:
            self.try_umount()
        except:
            self.log.error("failed")
            raise ex.Error
 0707010001f3bd000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/resource/fs/xfs    0707010001f3bf000081a40000000000000000000000016a100daf00000129000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/resource/fs/xfs/linux.py   from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "xfs"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkfs.xfs"):
        return ["fs.xfs"]
    return []

class FsXfs(Fs):
    queryfs = ['xfs_admin', '-l']
    mkfs = ['mkfs.xfs', '-f', '-q']

   0707010001f3be000081a40000000000000000000000016a100daf000000a8000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/fs/xfs/__init__.py    from drivers.resource.fs import KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "xfs",
    name=__name__,
    keywords=KEYWORDS,
)

0707010001f39a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/directory  0707010001f39b000081a40000000000000000000000016a100daf00001a39000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/fs/directory/__init__.py  import grp
import os
import pwd
import shutil
import stat

import core.exceptions as ex
import core.status
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.files import protected_dir
from utilities.lazy import lazy
from utilities.string import is_string

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "directory"
KEYWORDS = [
    {
        "keyword": "path",
        "at": True, 
        "required": True,
        "text": "The fullpath of the directory to create."
    },
    {
        "keyword": "user",
        "at": True,
        "example": "root",
        "text": "The user that should be owner of the directory. Either in numeric or symbolic form."
    },
    {
        "keyword": "group",
        "at": True,
        "example": "sys",
        "text": "The group that should be owner of the directory. Either in numeric or symbolic form."
    },
    {
        "keyword": "perm",
        "at": True,
        "example": "1777",
        "text": "The permissions the directory should have. A string representing the octal permissions."
    },
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


class FsDirectory(Resource):
    """Define a mount resource
    """

    def __init__(self,
                 path=None,
                 user=None,
                 group=None,
                 perm=None,
                 zone=None,
                 **kwargs):
        super(FsDirectory, self).__init__(type="fs.directory", **kwargs)
        self.path = path
        self.user = user
        self.group = group
        self.perm = perm
        self.zone = zone

    @lazy
    def mount_point(self):
        return self.path

    def on_add(self):
        if self.zone is None:
            return
        zp = None
        for r in [r for r in self.svc.resources_by_id.values() if r.type == "container.zone"]:
            if r.name == self.zone:
                try:
                    zp = r.zonepath
                except:
                    zp = "<%s>" % self.zone
                break
        if zp is None:
            raise ex.Error("zone %s, referenced in %s, not found" % (self.zone, self.rid))
        self.path = zp + "/root" + self.path
        if "<%s>" % self.zone != zp:
            self.path = os.path.realpath(self.path)
        self.tags.add(self.zone)
        self.tags.add("zone")

    @lazy
    def label(self): # pylint: disable=method-hidden
        if self.path:
            return "dir " + self.path
        else:
            return "dir"

    def start(self):
        self.create()

    def get_gid(self):
        if is_string(self.group):
            try:
                info = grp.getgrnam(self.group)
                self.gid = info[2]
            except KeyError:
                self.gid = None
        else:
            self.gid = int(self.group)

    def get_uid(self):
        if is_string(self.user):
            try:
                info = pwd.getpwnam(self.user)
                self.uid = info[2]
            except KeyError:
                self.uid = None
        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()
        if self.uid is None:
            self.status_log('user %s does not exist' % self.user)
            return True
        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()
        if self.gid is None:
            self.status_log('group %s does not exist' % self.group)
            return True
        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 self.path is None:
            self.status_log("path is not defined", "error")
            return core.status.UNDEF
        if not os.path.exists(self.path):
            self.log.debug("dir %s does not exist" % self.path)
            return core.status.DOWN
        self.check_uid()
        self.check_gid()
        self.check_perm()
        if self.status_logs_count(["warn", "error"]) > 0:
            return core.status.WARN
        else:
            return core.status.NA

    def __str__(self):
        return "%s path=%s user=%s group=%s perm=%s" % (
            super(FsDirectory, self).__str__(),\
            self.path, str(self.user), str(self.group), str(self.perm)
        )

    def __lt__(self, other):
        """
        Order so that deepest mountpoint can be umount first.
        If no ordering constraint, honor the rid order.
        """
        try:
            smnt = os.path.dirname(self.mount_point)
            omnt = os.path.dirname(other.mount_point)
        except AttributeError:
            return self.rid < other.rid
        return (smnt, self.rid) < (omnt, other.rid)

    def provisioned(self):
        return os.path.exists(self.path)

    def provisioner(self):
        pass

    def unprovisioner(self):
        if not os.path.exists(self.path):
            return
        if protected_dir(self.path):
            self.log.warning("cowardly refuse to purge %s", self.path)
        self.log.info("purge %s", self.path)
        shutil.rmtree(self.path)

   0707010001f3b8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/vxfs   0707010001f3bb000081a40000000000000000000000016a100daf00000140000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/sunos.py  from ..sunos import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "vxfs"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("newfs"):
        return ["fs.vxfs"]
    return []

class FsVxfs(Fs):
    mkfs = ['newfs', '-F', 'vxfs', '-o', 'largefiles', '-b', '8192']
    queryfs = ['fstyp']

0707010001f3b9000081a40000000000000000000000016a100daf000000a9000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/__init__.py   from drivers.resource.fs import KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "vxfs",
    name=__name__,
    keywords=KEYWORDS,
)

   0707010001f3ba000081a40000000000000000000000016a100daf00000137000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/vxfs/linux.py  from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "vxfs"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkfs.vxfs"):
        return ["fs.vxfs"]
    return []

class FsVxfs(Fs):
    mkfs = ['mkfs.vxfs', '-o', 'largefiles,bsize=8192']
    queryfs = ['fstyp']

 0707010001f3b3000081a40000000000000000000000016a100daf00000f87000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/osf1.py    import os
from stat import *

import core.exceptions as ex
from env import Env
from utilities.files import protected_mount
from utilities.mounts.osf1 import Mounts
from utilities.proc import qcall
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""


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 Fs(BaseFs):
    def __init__(self, **kwargs):
        self.Mounts = None
        super(Fs, self).__init__(**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 = Mounts()
        ret = self.Mounts.has_mount(self.device, self.mount_point)
        if ret:
            return True

        if self.fs_type not in ["advfs"] + Env.fs_net:
            # 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 utilities.subsystems.advfs
            try:
                o = utilities.subsystems.advfs.Fdmns()
                d = o.get_fdmn(dom)
            except utilities.subsystems.advfs.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_mount(self):
        if self.Mounts is None:
            self.Mounts = Mounts()
        self.prepare_mount()

        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.Error
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = 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.Error
        self.Mounts = None
 0707010001f3b5000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs  0707010001f3b7000081a40000000000000000000000016a100daf00000090000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/linux.py from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "tmpfs"

class FsTmpfs(Fs):
    def check_stat_device(self):
        return True

0707010001f3b6000081a40000000000000000000000016a100daf000000b9000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/resource/fs/tmpfs/__init__.py  from drivers.resource.fs import KWS_VIRTUAL as KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "tmpfs",
    name=__name__,
    keywords=KEYWORDS,
)

   0707010001f3bc000081a40000000000000000000000016a100daf00000e37000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/windows.py import os

import core.exceptions as ex
from utilities.lazy import lazy
from utilities.mounts.windows import Mounts
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""

def diskpartfile_name(self):
    return os.path.join(self.var_d, 'diskpart')

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.Error("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.Error("Failed to run command %s"% ' '.join(cmd) )

class Fs(BaseFs):
    """
    The Windows fs class
    """

    @lazy
    def drive(self):
        return self.mount_point.split(":", 1)[0] + ":"

    @lazy
    def volume(self):
        vols = self.svc.node.wmi().Win32_Volume()
        for vol in vols:
            if vol.DeviceId == self.device_id:
                return vol
        raise ex.Error("volume %s not found" % self.device)

    @lazy
    def device_id(self):
        if os.sep not in self.device:
            return os.sep+os.sep+os.path.join("?", "Volume{%s}" % self.device, "")
        else:
            return self.device

    def mount(self):
        ret = 0
        changed = False

        if self.volume.DriveLetter == self.drive:
            self.log.info("drive %s already assigned", self.drive)
        else:
            self.log.info("assign drive %s", self.drive)
            self.volume.DriveLetter = self.drive
            changed = True

        if not self.volume.Automount:
            self.log.info("mount volume %s", self.device)
            ret, = self.volume.Mount()
            changed = True

        if changed:
            self.can_rollback = True
            self.unset_lazy("volume")

        return ret

    def try_umount(self):
        if self.volume.DriveLetter is None:
            self.log.info("drive %s already unassigned", self.drive)
            return 0
        self.log.info("unassign drive %s", self.drive)
        self.volume.DriveLetter = None
        self.unset_lazy("volume")
        if self.volume.DriveLetter is None:
            return 0
        return 1

    # a completer
    def match_mount(self, i, dev, mnt):
        return True

    # a completer
    def is_online(self):
        return True

    def is_up(self):
        return Mounts(wmi=self.svc.node.wmi()).has_mount(self.device_id, self.mount_point)

    def start_mount(self):
        if self.is_online():
            self.log.info("%s is already online", self.device)
        if self.is_up():
            self.log.info("%s is already mounted", self.device)
            return 0
        ret = self.mount()
        if ret != 0:
            return 1
        return 0

    def stop(self):
        if self.is_up() is False:
            self.log.info("%s is already umounted", self.device)
            return
        ret = self.try_umount()
        if ret != 0:
            raise ex.Error("failed to umount %s" % self.device)
        return 0
 0707010001f39e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext2   0707010001f39f000081a40000000000000000000000016a100daf000000a9000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext2/__init__.py   from drivers.resource.fs import KEYWORDS
from core.objects.svcdict import KEYS

KEYS.register_driver(
    "fs",
    "ext2",
    name=__name__,
    keywords=KEYWORDS,
)

   0707010001f3a0000081a40000000000000000000000016a100daf0000012c000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/fs/ext2/linux.py  from ..linux import Fs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = "ext2"

def driver_capabilities(node=None):
    from utilities.proc import which
    if which("mkfs.ext2"):
        return ["fs.ext2"]
    return []

class FsExt2(Fs):
    mkfs = ['mkfs.ext2', '-F', '-q']
    queryfs = ['tune2fs', '-l']

0707010001f3ae000081a40000000000000000000000016a100daf000009e8000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/hpux.py    import os

import core.exceptions as ex
from utilities.files import protected_mount
from utilities.mounts.hpux import Mounts
from utilities.proc import qcall
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""


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 Fs(BaseFs):
    def set_fsck_h(self):
        self.fsck_h = {
            'vxfs': {
                'bin': 'fsck',
                'cmd': ['fsck', '-F', 'vxfs', '-y', self.device]
            },
        }

    def is_up(self):
        return Mounts().has_mount(self.device, self.mount_point)

    def start_mount(self):
        self.prepare_mount()
        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.Error
        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.Error

    def lv_name(self):
        return os.path.basename(self.device)
0707010001f395000081a40000000000000000000000016a100daf000062bb000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/__init__.py    import os
import subprocess
import shutil
import time

import core.exceptions as ex
import core.status
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from env import Env
from utilities.files import protected_dir
from utilities.drivers import driver_import
from utilities.lazy import lazy
from utilities.proc import justcall, which
from .directory import FsDirectory


KW_PRKEY = {
    "keyword": "prkey",
    "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."
}
KW_CREATE_OPTIONS = {
    "keyword": "create_options",
    "convert": "shlex",
    "default": [],
    "at": True,
    "provisioning": True,
    "text": "Additional options to pass to the logical volume create command. Size and name are alread set.",
    "example": "--contiguous y"
}
KW_SCSIRESERV = {
    "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, :kw:`scsireserv` can be activated on a per-resource basis."
}
KW_NO_PREEMPT_ABORT = {
    "keyword": "no_preempt_abort",
    "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, :kw:`no_preempt_abort` can be activated on a per-resource basis."
}
KW_DEV = {
    "keyword": "dev",
    "protoname": "device",
    "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"
}
KW_VG = {
    "keyword": "vg",
    "required": False,
    "at": True,
    "text": "The name of the disk group the filesystem device should be provisioned from.",
    "provisioning": True
}
KW_SIZE = {
    "keyword": "size",
    "required": False,
    "convert": "size",
    "at": True,
    "text": "The size of the logical volume to provision for this filesystem. On linux, can also be expressed as <n>%{FREE|PVS|VG}.",
    "provisioning": True
}
KW_MKFS_OPT = {
    "keyword": "mkfs_opt",
    "convert": "shlex",
    "default": [],
    "provisioning": True,
    "at": True,
    "text": "Eventual mkfs additional options."
}
KW_STAT_TIMEOUT = {
    "keyword": "stat_timeout",
    "convert": "duration",
    "default": 5,
    "at": True,
    "text": "The maximum wait time for a stat call to respond. When expired, the resource status is degraded is to warn, which might cause a TOC if the resource is monitored."
}
KW_SNAP_SIZE = {
    "keyword": "snap_size",
    "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."
}
KW_MNT = {
    "keyword": "mnt",
    "protoname": "mount_point",
    "at": True,
    "required": True,
    "text": "The mount point where to mount the filesystem."
}
KW_MNT_OPT = {
    "keyword": "mnt_opt",
    "protoname": "mount_options",
    "at": True,
    "text": "The mount options."
}
KW_PROMOTE_RW = {
    "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."
}
KW_ZONE = {
    "keyword": "zone",
    "at": True,
    "text": "The zone name the fs refers to. If set, the fs mount point is reparented into the zonepath rootfs."
}

KW_USER = {
    "keyword": "user",
    "at": True,
    "example": "root",
    "text": "The user that should be owner of the mnt directory. Either in numeric or symbolic form."
}

KW_GROUP = {
    "keyword": "group",
    "at": True,
    "example": "sys",
    "text": "The group that should be owner of the mnt directory. Either in numeric or symbolic form."
}

KW_PERM = {
    "keyword": "perm",
    "at": True,
    "example": "1777",
    "text": "The permissions the mnt directory should have. A string representing the octal permissions."
}

KW_CHECK_READ = {
    "keyword": "check_read",
    "default": False,
    "convert": "boolean",
    "candidates": (True, False),
    "text": "If set to ``true`` then during resource status check the ``file system read check`` is used when"
            " the file system is mounted and the ``file system write check`` is disabled."
            " This can help detection of nfs stale file systems."
            " The ``file system read check`` is: 'timeout {stat_timeout} stat -f {mnt}'"
            " and requires following capability: ``drivers.resource.fs.check_readable``."
            " The ``file system write check`` is disabled when ``fs_type`` is one of %s or mnt_opt contains ``ro``)."
            % (Env.fs_net + ["tmpfs"])
}

KWS_VIRTUAL = [
    KW_MNT,
    KW_MNT_OPT,
    KW_SIZE,
    KW_DEV,
    KW_STAT_TIMEOUT,
    KW_ZONE,
    KW_CHECK_READ,
]

KEYWORDS = [
    KW_MNT,
    KW_DEV,
    KW_MNT_OPT,
    KW_SIZE,
    KW_STAT_TIMEOUT,
    KW_SNAP_SIZE,
    KW_PRKEY,
    KW_SCSIRESERV,
    KW_NO_PREEMPT_ABORT,
    KW_MKFS_OPT,
    KW_CREATE_OPTIONS,
    KW_VG,
    KW_ZONE,
    KW_USER,
    KW_GROUP,
    KW_PERM,
    KW_CHECK_READ,
]

KWS_POOLING = [
    KW_MNT,
    KW_DEV,
    KW_MNT_OPT,
    KW_STAT_TIMEOUT,
    KW_SNAP_SIZE,
    KW_PRKEY,
    KW_SCSIRESERV,
    KW_NO_PREEMPT_ABORT,
    KW_MKFS_OPT,
    KW_ZONE,
    KW_USER,
    KW_GROUP,
    KW_PERM,
    KW_CHECK_READ,
]

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""

KEYS.register_driver(
    "fs",
    "",
    name=__name__,
    keywords=KEYWORDS,
)


class BaseFs(Resource):
    """Define a mount resource
    """

    def __init__(self,
                 mount_point=None,
                 device=None,
                 fs_type=None,
                 mount_options=None,
                 stat_timeout=5,
                 snap_size=None,
                 zone=None,
                 vg=None,
                 size=None,
                 mkfs_opt=None,
                 user=None,
                 group=None,
                 perm=None,
                 check_read=False,
                 **kwargs):
        super(BaseFs, self).__init__(type="fs", **kwargs)
        self.raw_mount_point = mount_point
        self._device = device
        self.fs_type = fs_type or ""
        self.stat_timeout = stat_timeout
        self.mount_options = mount_options or ""
        self.snap_size = snap_size
        self.zone = zone
        self.fsck_h = {}
        self.vg = vg
        self.size = size
        self.mkfs_opt = mkfs_opt or []
        if self.zone is not None:
            self.tags.add(self.zone)
            self.tags.add("zone")
        self.user = user
        self.group = group
        self.perm = perm
        self.check_readable_enabled = check_read

    @lazy
    def mnt_dir(self):
        mnt_dir = FsDirectory(path=self.mount_point, user=self.user, group=self.group, perm=self.perm)
        mnt_dir.svc = self.svc
        mnt_dir.rid = self.rid
        return mnt_dir

    @lazy
    def mount_point(self):
        try:
            mount_point = self.raw_mount_point.rstrip(os.sep)
        except AttributeError:
            return self.raw_mount_point
        if self.zone is None:
            return self.raw_mount_point
        zp = None
        for r in [r for r in self.svc.resources_by_id.values() if r.type in ("container.lxc", "container.zone")]:
            if r.name == self.zone:
                try:
                    zp = r.zonepath
                except:
                    zp = "<%s>" % self.zone
                break
        if zp is None:
            raise ex.Error("zone %s, referenced in %s, not found" % (self.zone, self.rid))
        if r.type == "container.zone":
            mount_point = zp + "/root" + self.raw_mount_point
        else:
            mount_point = zp + self.raw_mount_point
        if "<%s>" % self.zone != zp:
            mount_point = os.path.realpath(mount_point)
        return mount_point

    @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 None:
            # lazy ref support, like {<rid>.exposed_devs[<n>]}
            self._device = self.conf_get("dev")
        if self._device is not None:
            if self.fs_type == "lofs" and not self._device.startswith(os.sep):
                l = self._device.split("/")
                vol = self.svc.get_volume(l[0])
                if vol.mount_point is not None:
                    l[0] = vol.mount_point
                    return "/".join(l)
            return self._device
        return self._device

    @lazy
    def label(self): # pylint: disable=method-hidden
        if self.device is None:
            label = self.svc._get(self.rid+".dev", evaluate=False)
        else:
            label = self.device
        try:
            mount_point = self.mount_point
        except:
            # accept undef mount point (for example when zone is down)
            mount_point = "undef"
        if mount_point is not None:
            label += "@" + mount_point
        if self.fs_type not in ("tmpfs", "shm", "shmfs", "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.start_mount()
        self.mnt_dir.start()

    def prepare_mount(self):
        self.validate_dev()
        self.promote_rw()
        self.create_mntpt()

    def start_mount(self):
        pass

    def validate_dev(self):
        if self.fs_type in ["zfs", "advfs"] + Env.fs_net:
            return
        if self.fs_type in ["bind", "lofs"] or "bind" in self.mount_options:
            return
        if self.device in ("tmpfs", "shm", "shmfs", "none"):
            # pseudo fs have no dev
            return
        if not self.device:
            raise ex.Error("device keyword not set or evaluates to None")
        if self.device.startswith("UUID=") or self.device.startswith("LABEL="):
            return
        if not os.path.exists(self.device):
            raise ex.Error("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 ("", "tmpfs", "shm", "shmfs", "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.Error

    def need_check_writable(self):
        if 'ro' in self.mount_options.split(','):
            return False
        if self.fs_type in Env.fs_net + ["tmpfs"]:
            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_device(self):
        _, ok = stat_with_timeout(self.device, self.stat_timeout)
        return ok

    def check_writable(self):
        if not self.can_check_writable():
            return False
        try:
            return self.check_writable_xattr()
        except Exception:
            return self.check_writable_file()

    def check_writable_xattr(self):
        try:
            os.setxattr(self.mount_point, "user.opensvc", str(time.time()).encode())
            return True
        except IOError as exc:
            if exc.errno == 28:
                self.log.error('No space left on device. Invalidate writable test.')
                return True
            return False
        except (AttributeError, Exception):
            raise

    def check_writable_file(self):
        if self.testfile is None:
            return True
        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 need_check_readable(self):
        if self.check_readable_enabled:
            if 'nointr' in self.mount_options.split(','):
                """nointr is ignored after Linux kernel 2.6.25, Solaris default is intr,
                having both nointr and check_readable_enabled is not accepted"""
                self.status_log("config has both check_read and 'nointr' mount option", "warn")
                self.log.debug("disable read check: mount options have 'nointr'")
                return False
            return True
        else:
            return False

    def check_readable(self):
        stat_code, ok = stat_with_timeout(self.mount_point, self.stat_timeout, ["-f"])
        return stat_code == 0 and ok

    def is_up(self):
        """
        Placeholder
        """
        return

    def _status(self, verbose=False):
        if self.is_up():
            if not self.check_stat_device():
                self.status_log("fs dev is not responding to stat")
                return core.status.WARN
            if self.need_check_writable():
                if not self.check_writable():
                    self.status_log("fs is not writable")
                    return core.status.WARN
            elif self.need_check_readable():
                if not self.check_readable():
                    self.status_log("fs is not readable")
                    return core.status.WARN
            if self.fs_type not in ["zfs", "advfs"]:
                self.mnt_dir._status()
                self.status_logs += self.mnt_dir.status_logs
            return core.status.UP
        else:
            return core.status.DOWN

    def sub_devs(self):
        pseudofs = [
          'lofs',
          'none',
          'proc',
          'sysfs',
        ]
        if self.fs_type in pseudofs + Env.fs_net:
            return set()
        if self.fs_type == "zfs":
            from utilities.subsystems.zfs import zpool_devs
            return set(zpool_devs(self.device.split("/")[0], self.svc.node))
        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" % (
            super(BaseFs, self).__str__(),
            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.
        """
        try:
            smnt = os.path.dirname(self.mount_point)
            omnt = os.path.dirname(other.mount_point)
        except AttributeError:
            return self.rid < other.rid
        return (smnt, self.rid) < (omnt, other.rid)

    def check_fs(self):
        if not hasattr(self, "queryfs"):
            return True
        if self.mkfs_dev is None:
            return True
        cmd = getattr(self, "queryfs") + [self.mkfs_dev]
        out, err, ret = justcall(cmd)
        if ret == 0:
            return True
        self.log.info("%s is not formatted"%self.mkfs_dev)
        return False

    def lv_name(self):
        raise ex.Error

    def loop_resource(self):
        size = self.oget("size")
        try:
            mod = driver_import("resource", "disk", "loop")
            if mod is None:
                return
        except ImportError:
            return
        res = mod.DiskLoop(rid="disk#", loopfile=self.device, size=size)
        res.svc = self.svc
        return res

    def provision_loop(self):
        res = self.loop_resource()
        if not res:
            return
        if not res.size:
            return
        res.provisioner()

    def unprovision_loop(self):
        res = self.loop_resource()
        if not res:
            return
        res.unprovisioner()

    def lv_resource(self):
        try:
            name = self.lv_name()  # pylint: disable=assignment-from-no-return
        except ex.Error:
            return
        vg = self.oget("vg")
        size = self.oget("size")
        try:
            mod = driver_import("resource", "disk", "lv")
            if mod is None:
                return
        except ImportError:
            return
        res = mod.DiskLv(rid="disk#", name=name, vg=vg, size=size)
        res.svc = self.svc
        return res

    def provision_lv(self):
        res = self.lv_resource()
        if not res:
            return
        res.provisioner()

    def unprovision_lv(self):
        res = self.lv_resource()
        if not res:
            return
        res.unprovisioner()

    def provisioned(self):
        if "bind" in self.mount_options or self.fs_type in ("bind", "lofs"):
            return
        try:
            self.dev = self._device or self.conf_get("dev")
            self.mnt = self.mount_point or self.conf_get("mnt")
        except ex.OptNotFound:
            return
        if self.dev is None:
            return
        if self.mnt is None:
            return
        if not os.path.exists(self.mnt):
            return False
        if self.fs_type in Env.fs_net + ["tmpfs"]:
            return
        try:
            self.get_mkfs_dev()
        except ex.Error:
            self.mkfs_dev = None
        if not os.path.exists(self.dev) and (self.mkfs_dev is None or not os.path.exists(self.mkfs_dev)):
            return False
        return self.check_fs()

    def get_mkfs_dev(self):
        self.mkfs_dev = self.dev
        if Env.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.Error("%s raw device does not exists"%self.mkfs_dev)
        elif Env.sysname == 'Darwin':
            if os.path.isfile(self.mkfs_dev):
                import utilities.devices.darwin
                devs = utilities.devices.darwin.file_to_loop(self.mkfs_dev)
                if len(devs) == 1:
                    self.mkfs_dev = devs[0]
                else:
                    raise ex.Error("unable to find a device associated to %s" % self.mkfs_dev)
        elif Env.sysname == 'Linux':
            if os.path.isfile(self.mkfs_dev):
                if "loop" in self.mount_options.split(","):
                    return
                import utilities.devices.linux
                devs = utilities.devices.linux.file_to_loop(self.mkfs_dev)
                if len(devs) == 1:
                    self.mkfs_dev = devs[0]
                else:
                    raise ex.Error("unable to find a device associated to %s" % self.mkfs_dev)

    def provisioner_fs(self):
        if self.fs_type in Env.fs_net + ["tmpfs"]:
            return
        if "bind" in self.mount_options or self.fs_type in ("bind", "lofs"):
            return

        self.dev = self._device or self.conf_get("dev")
        self.mnt = self.mount_point or self.conf_get("mnt")

        if self.dev is None:
            raise ex.Error("device %s not found. parent resource is down ?" % self.dev)
        if not os.path.exists(self.mnt):
            os.makedirs(self.mnt)
            self.log.info("%s mount point created"%self.mnt)

        if not os.path.exists(self.dev):
            try:
                vg = self.vg or self.conf_get("vg", verbose=False)
            except ValueError:
                # keyword not supported (ex. bind mounts)
                vg = None
            except ex.OptNotFound:
                vg = None
            if vg:
                self.provision_lv()
            elif "loop" in self.mount_options.split(","):
                self.provision_loop()

        self.get_mkfs_dev()

        if not os.path.exists(self.mkfs_dev):
            raise ex.Error("abort fs provisioning: %s does not exist" % self.mkfs_dev)

        if self.check_fs():
            self.log.info("already provisioned")
            return

        if hasattr(self, "do_mkfs"):
            getattr(self, "do_mkfs")()
        elif hasattr(self, "mkfs"):
            try:
                opts = self.svc.conf_get(self.rid, "mkfs_opt")
            except:
                opts = []
            cmd = getattr(self, "mkfs") + opts + [self.mkfs_dev]
            (ret, out, err) = self.vcall(cmd)
            if ret != 0:
                self.log.error('Failed to format %s'%self.mkfs_dev)
                raise ex.Error
        else:
            raise ex.Error("no mkfs method implemented")

    def provisioner_shared_non_leader(self):
        self.unset_lazy("device")
        self.unset_lazy("label")

    def provisioner(self):
        self.unset_lazy("device")
        self.unset_lazy("label")
        self.provisioner_fs()

    def purge_mountpoint(self):
        if self.mount_point is None:
            return
        if os.path.exists(self.mount_point) and not protected_dir(self.mount_point):
            self.log.info("rm -rf %s" % self.mount_point)
            try:
                shutil.rmtree(self.mount_point)
            except Exception as e:
                raise ex.Error(str(e))

    def unprovisioner_fs(self):
        pass

    def unprovisioner(self):
        if self.fs_type in Env.fs_net + ["tmpfs"]:
            return
        self.unprovisioner_fs()
        self.purge_mountpoint()
        try:
            vg = self.conf_get("vg", verbose=False)
        except ValueError:
            # keyword not supported (ex. bind mounts)
            vg = None
        except ex.OptNotFound:
            vg = None
        if vg:
            self.unprovision_lv()
        if "loop" in self.mount_options.split(","):
            if os.path.isfile(self.device):
                self.unprovision_loop()

def stat_with_timeout(name, timeout, options=None):
    """
    Run with timeout subprocess: stat [options] name

    :param name: the File
    :param timeout: maximum time to wait for stat subprocess completion
    :param options: list of stat options or None

    :return: stat exit code, True if stat command terminate before timeout
    else kill subprocess and return 1, False
    """
    if not capabilities.has("node.x.stat"):
        return 0, True

    if name is None or name == "":
        return 0, True
    """

    """
    if options is None:
        options = []
    elif not isinstance(options, list):
        raise ValueError("stat_with_timeout: invalid options")

    proc = subprocess.Popen(["stat"] + options + [name],
                            stderr=subprocess.PIPE,
                            stdout=subprocess.PIPE)
    for retry in range(timeout * 10, 0, -1):
        if proc.poll() is None:
            time.sleep(0.1)
        else:
            return proc.returncode, True
    try:
        proc.kill()
    except OSError:
        pass
    return 1, False
 0707010001f3af000081a40000000000000000000000016a100daf00005cdd000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/linux.py   """
Linux Fs resource driver module
"""
import os
from stat import ST_MODE, ST_INO, S_ISREG, S_ISBLK, S_ISDIR

import core.exceptions as ex
import utilities.devices.linux
from env import Env
from utilities.files import protected_mount, getmount
from utilities.cache import cache
from utilities.lazy import lazy
from utilities.subsystems.zfs import zfs_getprop, zfs_setprop, zpool_devs
from utilities.mounts.linux import Mounts
from . import BaseFs
from utilities.proc import justcall, qcall

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""

class Fs(BaseFs):
    """
    Linux Fs resource driver
    """
    def __init__(self, **kwargs):
        super(Fs, self).__init__(**kwargs)
        self.mounts = None
        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
            if self.fs_type == "bind" or "bind" in self.mount_options:
                if not self._device.startswith(os.sep):
                    l = self._device.split("/")
                    try:
                        vol = self.svc.get_volume(l[0])
                    except ex.Error:
                        return
                    if vol.mount_point is not None:
                        l[0] = vol.mount_point
                        return "/".join(l)
            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):
        cmd = ['umount', mnt]
        return self.vcall(cmd, err_to_warn=True)

    def try_umount(self, dev=None, mnt=None, fs_type=None):
        if dev is None:
            dev = self.device
        if mnt is None:
            mnt = self.mount_point
        if fs_type is None:
            fs_type = self.fs_type

        if fs_type == "zfs":
            ret, out, err = self.umount_zfs(dev, mnt)
        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(dev):
            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:
            self.status_log("dev is not defined", "info")
            return False
        if self.mount_point is None:
            self.status_log("mnt is not defined", "info")
            return False
        self.mounts = Mounts()
        for dev in [self.device] + utilities.devices.linux.udevadm_query_symlink(self.device):
            ret = self.mounts.has_mount(dev, 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 = utilities.devices.linux.loop_to_file(self.device)
            if backfile and self.mounts.has_mount(backfile, os.path.realpath(self.mount_point)):
                return True
        elif os.path.isfile(self.device) and "loop" in self.mount_options.split(","):
            return self.mounts.has_mount(self.device, os.path.realpath(self.mount_point))

        # 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 = utilities.devices.linux.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 self.device.startswith(os.sep) and 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 = utilities.devices.linux.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.fs_type in ("none", "tmpfs", "bind"):
            return
        if self.device is None:
            return
        if self.device.startswith("LABEL=") or self.device.startswith("UUID="):
            try:
                _dev = utilities.devices.linux.label_to_dev(self.device, self.svc.node.devtree)
            except ex.Error 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 = 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
        if dev in ("tmpfs", "shm", "shmfs", "none"):
            # bind mounts for ex.
            return
        return dev

    @cache("dmsetup.ls.multipath")
    def dmsetup_ls_multipath(self):
        cmd = [Env.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 = [Env.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 = [Env.syspaths.dmsetup, "table"]
        out, _, _ = justcall(cmd)
        data = {}
        for line in out.splitlines():
            v = line.split()
            data[v[0].rstrip(":")] = line[len(v[0]):].strip()
        return data

    @lazy
    def dm_major(self):
        try:
            return utilities.devices.linux.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 dm_table(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()
        return name, dmsetup_table.get(name)

    def is_multipath_simple(self, minor):
        name, table = self.dm_table(minor)
        if not table:
            return False
        elements = table.split()
        return 'multipath' in elements

    def is_multipath(self, minor):
        name, table = self.dm_table(minor)
        if not table:
            return False
        elements = table.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 utilities.subsystems.btrfs import btrfs_devs
            return set(btrfs_devs(self.mount_point))
        if self.fs_type == "zfs":
            if not self.device:
                return set()
            return set(zpool_devs(self.device.split("/")[0], self.svc.node))

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

        if not self.is_devmap(statinfo):
            return set([dev])

        if self.is_multipath_simple(os.minor(statinfo.st_rdev)):
            return set([dev])

        if utilities.devices.linux.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 = utilities.devices.linux.get_blockdev_sd_slaves(syspath)
        return devs

    def sub_disks(self):
        return utilities.devices.linux.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 = utilities.devices.linux.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.Error(str(exc))

    def start_mount(self):
        if self.mounts is None:
            self.mounts = Mounts()
        self.prepare_mount()

        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.Error(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 = [Env.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, dev, mnt):
        mntprop = zfs_getprop(dev, 'mountpoint')
        if mntprop == "legacy":
            return self.umount_generic(mnt)
        elif mntprop != mnt:
            # docker data dir case, ex: dev=data/svc1 mnt=/srv/svc1/docker/zfs
            # and mntprop=/srv/svc1
            return self.umount_generic(mnt)
        else:
            return self.umount_zfs_native(mnt)

    def umount_zfs_native(self, mnt):
        ret, out, err = self.vcall([Env.syspaths.zfs, 'umount', mnt], err_to_info=True)
        if ret != 0:
            ret, out, err = self.vcall([Env.syspaths.zfs, 'umount', '-f', mnt], err_to_info=True)
        return ret, out, err

    def mount_zfs(self):
        zone = self.zone or self.oget("zone")
        if not self.encap and not zone and \
           zfs_getprop(self.device, 'zoned') != 'off':
            if zfs_setprop(self.device, 'zoned', 'off', log=self.log):
                raise ex.Error
        try:
            os.unlink(self.mount_point+"/.opensvc")
        except:
            pass
        if zfs_getprop(self.device, 'mountpoint') == "legacy":
            return self.mount_generic()
        else:
            return self.mount_zfs_native()

    def mount_zfs_native(self):
        if zfs_getprop(self.device, 'mountpoint') != self.mount_point:
            if not zfs_setprop(self.device, 'mountpoint', self.mount_point, log=self.log):
                raise ex.Error
            # the prop change has mounted the dataset
            return
        ret, out, err = self.vcall([Env.syspaths.zfs, 'mount', self.device])
        if ret != 0:
            ret, out, err = self.vcall([Env.syspaths.zfs, 'mount', '-O', self.device])
            if ret != 0:
                raise ex.Error
        return ret, out, err

    def mount_generic(self):
        if self.fs_type and self.fs_type != "bind":
            fstype = ['-t', self.fs_type]
        else:
            fstype = []

        if self.mount_options:
            opt = self.mount_options.strip().split(",")
        else:
            opt = []
        if self.fs_type == "bind" and not "bind" in opt:
            opt.append("bind")
        if opt:
            mntopt = ['-o', ",".join(opt)]
        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.Error

    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 = Mounts()
        if self.is_up() is False:
            self.log.info("%s is already umounted", self.label)
            return
        try:
            os.stat(self.mount_point)
        except OSError as exc:
            if exc.errno in (5, 13):
                self.log.warning("I/O error on mount point. try to umount anyway")
                self.kill_users()
            else:
                raise ex.Error(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.Error('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.Error("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):
        if not self.svc:
            return
        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 = Mounts()
        mnt_realpath = os.path.realpath(self.mount_point)
        for mount in sorted(mounts, key=lambda x: x.mnt, reverse=True):
            _mnt_realpath = os.path.realpath(mount.mnt)
            if _mnt_realpath != mnt_realpath and \
               _mnt_realpath.startswith(mnt_realpath+"/"):
                ret = self.try_umount(dev=mount.dev, mnt=_mnt_realpath, fs_type=mount.type)
                if ret != 0:
                    break

    def lv_name(self):
        dev = self.oget("dev")

        if dev.startswith("LABEL=") or dev.startswith("UUID="):
            try:
                _dev = utilities.devices.linux.label_to_dev(dev, tree=self.svc.node.devtree)
            except ex.Error as exc:
                _dev = None
            if _dev is None:
                self.log.info("unable to find device identified by %s", dev)
                return
            dev = _dev

        vg = self.oget("vg")

        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

   0707010001f3ad000081a40000000000000000000000016a100daf00001102000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/freebsd.py import os
from stat import *

import core.exceptions as ex
from utilities.files import protected_mount, getmount
from utilities.mounts.freebsd import Mounts
from utilities.proc import qcall
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""


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 Fs(BaseFs):
    """ define FreeBSD mount/umount doAction """
    def __init__(self, **kwargs):
        self.Mounts = None
        super(Fs, self).__init__(**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 = 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 = 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.Error

        return set([dev])

    def can_check_writable(self):
        return True

    def start_mount(self):
        if self.Mounts is None:
            self.Mounts = Mounts()
        self.prepare_mount()
        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.Error
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = 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.Error
        self.Mounts = None
  0707010001f399000081a40000000000000000000000016a100daf00001921000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/fs/darwin.py  import os
from stat import *

import core.exceptions as ex
import utilities.devices.darwin
from env import Env
from utilities.files import protected_mount, getmount
from utilities.mounts.darwin import Mounts
from utilities.proc import qcall
from . import BaseFs

DRIVER_GROUP = "fs"
DRIVER_BASENAME = ""


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 Fs(BaseFs):
    """ define FreeBSD mount/umount doAction """
    def __init__(self, **kwargs):
        self.Mounts = None
        self.loopdevice = None
        self.isloop = False
        super(Fs, self).__init__(**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 = Mounts()
        ret = self.Mounts.has_mount(self.device, self.mount_point)
        if ret:
            return True

        if self.fs_type not in Env.fs_net:
            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 = utilities.devices.darwin.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 = 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.Error

        return set([dev])

    def can_check_writable(self):
        return True

    def start_mount(self):
        if self.Mounts is None:
            self.Mounts = Mounts()
        self.prepare_mount()

        if self.fs_type in Env.fs_net or self.device == "none":
            # TODO showmount -e
            pass
        else:
            try:
                mode = os.stat(self.device)[ST_MODE]
                if S_ISREG(mode):
                    devs = utilities.devices.darwin.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.Error
        self.Mounts = None
        self.can_rollback = True

    def stop(self):
        if self.Mounts is None:
            self.Mounts = 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.Error
        self.Mounts = None
   0707010001f3e9000041ed0000000000000000000000036a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/resource/share 0707010001f3ea000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/share/__init__.py 0707010001f3eb000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/share/nfs 0707010001f3ef000081a40000000000000000000000016a100daf00000f5c000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/share/nfs/sunos.py    import core.exceptions as ex
import core.status
from . import BASE_KEYWORDS
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.proc import justcall

DRIVER_GROUP = "share"
DRIVER_BASENAME = "nfs"

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=BASE_KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    from env import Env
    if Env.sysname != "SunOS":
        return []
    if which("share"):
        return ["share.nfs"]
    return []


class ShareNfs(Resource):
    def __init__(self, path=None, opts=None, **kwargs):
        Resource.__init__(self, type="share.nfs", **kwargs)

        if "node.x.share" not in capabilities:
            raise ex.InitError("share is not installed")
        self.label = "nfs:%s" % path
        self.path = path
        try:
            self.opts = self.parse_opts(opts)
        except ex.Error as e:
            raise ex.InitError(str(e))

    def get_opts(self):
        cmd = ["share", "-F", "nfs"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            return ""
        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.Error 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.Error(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.Error(err)

    def stop(self):
        try:
            up = self.is_up()
        except ex.Error 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.Error

    def _status(self, verbose=False):
        try:
            up = self.is_up()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if len(self.issues) > 0:
            self.status_log(self.issues)
            return core.status.WARN
        if up:
            return core.status.UP
        else:
            return core.status.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 post_provision_start(self):
        pass

0707010001f3ec000081a40000000000000000000000016a100daf000001a2000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/share/nfs/__init__.py DRIVER_GROUP = "share"
DRIVER_BASENAME = "nfs"
BASE_KEYWORDS = [
    {
        "keyword": "path",
        "at": True,
        "required": True,
        "text": "The fullpath of the directory to share."
    },
    {
        "keyword": "opts",
        "at": True,
        "required": True,
        "text": "The NFS share export options, as they woud be set in /etc/exports or passed to Solaris share command."
    },
]

  0707010001f3ed000081a40000000000000000000000016a100daf00001025000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/resource/share/nfs/hpux.py import os

import core.status
import core.exceptions as ex

from . import BASE_KEYWORDS
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "share"
DRIVER_BASENAME = "nfs"

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=BASE_KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    from env import Env
    if Env.sysname != "HP-UX":
        return []
    if which("share"):
        return ["share.nfs"]
    return []


class ShareNfs(Resource):
    def __init__(self, path=None, opts=None, **kwargs):
        Resource.__init__(self, type="share.nfs", **kwargs)
        self.sharetab = "/etc/dfs/sharetab"
        self.dfstab = "/etc/dfs/dfstab"

        if "node.x.share" not in capabilities:
            raise ex.InitError("share is not installed")
        self.label = "nfs:%s" % path
        self.path = path
        try:
            self.opts = self.parse_opts(opts)
        except ex.Error as e:
            raise ex.InitError(str(e))

    def get_opts(self):
        if not os.path.exists(self.sharetab):
            return ""
        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.Error 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.Error(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.Error(err)

    def stop(self):
        try:
            up = self.is_up()
        except ex.Error 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.Error

    def _status(self, verbose=False):
        try:
            up = self.is_up()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if len(self.issues) > 0:
            self.status_log(self.issues)
            return core.status.WARN
        if up:
            return core.status.UP
        else:
            return core.status.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 post_provision_start(self):
        pass
   0707010001f3ee000081a40000000000000000000000016a100daf00001998000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/resource/share/nfs/linux.py    import core.status
import core.exceptions as ex

from . import BASE_KEYWORDS
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.cache import cache, clear_cache
from utilities.proc import justcall

DRIVER_GROUP = "share"
DRIVER_BASENAME = "nfs"

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=BASE_KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    from env import Env
    if Env.sysname != "Linux":
        return []
    if which("exportfs"):
        return ["share.nfs"]
    return []


class ShareNfs(Resource):
    def __init__(self, path=None, opts=None, **kwargs):
        Resource.__init__(self, type="share.nfs", **kwargs)
        self.label = "nfs:%s" % path
        self.path = path
        l = opts.replace('\\', '').split()
        self.opts = {}
        for e in l:
            try:
                client, opts = self.parse_entry(e)
            except ex.Error as e:
                raise ex.InitError(str(e))
            self.opts[client] = opts

    @cache("showmount.e")
    def get_showmount(self):
        self.data = {}
        cmd = ["showmount", "-e", "--no-headers", "127.0.0.1"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("nfs server not operational")
        for line in out.splitlines():
            try:
                idx = line.rindex(" ")
            except IndexError:
                continue
            path = line[0:idx].strip()
            ips = line[idx+1:].split(",")
            if ips == ['(everyone)']:
                ips = '*'
            self.data[path] = ips
        return self.data

    @cache("exportfs.v")
    def get_exports(self):
        self.data = {}
        cmd = ["exportfs", "-v"]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        out = out.replace('\n ', '').replace('\n\t', '')
        for line in out.splitlines():
            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.Error 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
        try:
            showmount = self.get_showmount()
        except ex.Error as exc:
            self.status_log(str(exc), "info")
            return False
        if self.path not in showmount:
            self.status_log("%s in userland etab but not in kernel etab" % self.path)
            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 showmount[self.path] != "*" and client not in showmount[self.path]:
                self.issues[client] = "%s not exported to client %s in kernel etab"%(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):
        if "node.x.exportfs" not in capabilities:
            raise ex.Error("exportfs is not installed")
        try:
            up = self.is_up()
        except ex.Error 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", "-i", "-u", ":".join((client, self.path))]
                ret, out, err = self.vcall(cmd)

            cmd = ["exportfs", "-i", "-o", ",".join(opts), ":".join((client, self.path))]
            ret, out, err = self.vcall(cmd)
            clear_cache("exportfs.v")
            clear_cache("showmount.e")
            if ret != 0:
                raise ex.Error

    def stop(self):
        if "node.x.exportfs" not in capabilities:
            raise ex.Error("exportfs is not installed")
        try:
            up = self.is_up()
        except ex.Error 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)
            clear_cache("exportfs.v")
            clear_cache("showmount.e")
            if ret != 0:
                raise ex.Error

    def _status(self, verbose=False):
        if "node.x.exportfs" not in capabilities:
            self.status_log("exportfs is not installed")
            return core.status.NA
        try:
            up = self.is_up()
        except ex.Error as e:
            self.status_log(str(e))
            return core.status.WARN
        if len(self.issues) > 0:
            self.status_log('\n'.join(self.issues.values()))
            return core.status.WARN
        if up:
            return core.status.UP
        else:
            return core.status.DOWN

    def parse_entry(self, e):
        if '(' not in e or ')' not in e:
            raise ex.Error("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 post_provision_start(self):
        pass


0707010001f31b000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/resource/__init__.py   0707010001f32a000041ed0000000000000000000000146a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/resource/container 0707010001f332000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/container/hpvm    0707010001f333000081a40000000000000000000000016a100daf0000109c000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/container/hpvm/__init__.py    import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from drivers.resource.disk.hpvm import DiskHpvm
from env import Env
from core.resource import Resource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "container"
DRIVER_BASENAME = "hpvm"
KEYWORDS = [
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("/opt/hpvm/bin/hpvmstatus"):
        data.append("container.hpvm")
    return data

class ContainerHpvm(BaseContainer):
    def __init__(self, guestos="HP-UX", **kwargs):
        super(ContainerHpvm, self).__init__(type="container.hpvm", guestos=guestos, **kwargs)

    def on_add(self):
        self.vg = DiskHpvm(
            rid = 'vmdg#'+self.rid,
            container_name = self.name
        )
        self.vg.svc = self.svc

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)

    def files_to_sync(self):
        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)
        return a

    def ping(self):
        return utilities.ping.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.Error

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

    def container_forcestop(self):
        cmd = ['/opt/hpvm/bin/hpvmstop', '-F', '-P', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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 = Env.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.to]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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()
0707010001f32b000081a40000000000000000000000016a100daf00003d8c000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/resource/container/__init__.py import core.status
import core.exceptions as ex
import utilities.ping

from env import Env
from utilities.lazy import lazy
from core.resource import Resource
from utilities.proc import justcall
from utilities.net.getaddr import getaddr
from utilities.storage import Storage

KW_QGA = {
    "keyword": "qga",
    "candidates": (True, False),
    "convert": "boolean",
    "at": True,
    "text": "Use vsock or vserial to communicate with the container via the qemu guest agent. This option requires qemu guest agent to be installed in the container.",
}
KW_QGA_OPERATIONAL_DELAY = {
    "keyword": "qga_operational_delay",
    "convert": "duration",
    "at": True,
    "text": "Wait after we successfully tested a pwd in the container, so the os is sufficiently started to accept a encap start.",
    "default": "10",
}
KW_START_TIMEOUT = {   
    "keyword": "start_timeout",
    "convert": "duration",
    "at": True,
    "text": "Wait for <duration> before declaring the container action a failure.",
    "default": "240",
    "example": "180"
}
KW_STOP_TIMEOUT = {   
    "keyword": "stop_timeout",
    "convert": "duration",
    "at": True,
    "text": "Wait for <duration> before declaring the container action a failure.",
    "default": "120",
    "example": "180"
}
KW_SNAP = {
    "keyword": "snap",
    "text": "The target snapshot/clone full path containing the new container disk files.",
    "required": False,
    "provisioning": True
}
KW_SNAPOF = {
    "keyword": "snapof",
    "text": "The snapshot origin full path containing the reference container disk files.",
    "required": False,
    "provisioning": True
}
KW_VIRTINST = {
    "keyword": "virtinst",
    "text": "The :cmd:`virt-install` command to use to create the container.",
    "convert": "shlex",
    "required": True,
    "provisioning": True
}
KW_NO_PREEMPT_ABORT = {
    "keyword": "no_preempt_abort",
    "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, :kw:`no_preempt_abort` can be activated on a per-resource basis."
}
KW_NAME = {
    "keyword": "name",
    "at": True,
    "default_text": "The container name.",
    "text": "Set if the container hostname is different from the container name."
}
KW_HOSTNAME = {
    "keyword": "hostname",
    "at": True,
    "text": "This need to be set if the virtual machine hostname is different from the machine name."
}
KW_OSVC_ROOT_PATH = {
    "keyword": "osvc_root_path",
    "at": True,
    "example": "/opt/opensvc",
    "text": "If the OpenSVC agent is installed via package in the container, this parameter must not be set. Else the value can be set to the fullpath hosting the agent installed from sources."
}
KW_GUESTOS = {
    "keyword": "guestos",
    "at": True,
    "candidates": ["unix", "windows"],
    "text": "The operating system in the virtual machine."
}
KW_SHARED_IP_GROUP = {
    "keyword": "shared_ip_group",
    "at": True,
    "text": "The cloud shared ip group name to allocate a public ip from."
}
KW_SIZE = {
    "keyword": "size",
    "at": True,
    "text": "The cloud vm size, as known to the cloud manager.",
    "example": "tiny"
}
KW_KEY_NAME = {
    "keyword": "key_name",
    "at": True,
    "required": True,
    "text": "The key name, as known to the cloud manager, to trust in the provisioned vm."
}
KW_CLOUD_ID = {
    "keyword": "cloud_id",
    "required": True,
    "at": True,
    "text": "The cloud id as configured in ``node.conf``.",
    "example": "cloud#1"
}
KW_PROMOTE_RW = {
    "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."
}
KW_SCSIRESERV = {
    "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, :kw:`scsireserv` can be activated on a per-resource basis."
}


class BaseContainer(Resource):
    """
    The container base class.
    """

    def __init__(self,
                 name=None,
                 guestos=None,
                 type=None,
                 osvc_root_path=None,
                 start_timeout=600,
                 stop_timeout=60,
                 **kwargs):
        super(BaseContainer, self).__init__(type=type, **kwargs)
        self.start_timeout = start_timeout
        self.stop_timeout = stop_timeout
        self.osvc_root_path = osvc_root_path
        self.sshbin = '/usr/bin/ssh'
        self.raw_name = name
        self.guestos = guestos
        if guestos is not None:
            self.guestos = guestos.lower()
        self.booted = False
        self.custom_create_pg = False

    @lazy
    def name(self):
        return self.raw_name or self.svc.name

    @lazy
    def label(self):  # pylint: disable=method-hidden
        return self.name

    @lazy
    def runmethod(self):
        if self.guestos == "windows":
            return
        return Env.rsh.split() + [self.name]

    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.conf_get("hostname")
        except ex.OptNotFound:
            hostname = self.name
        return hostname

    def getaddr(self, cache_fallback=False):
        if hasattr(self, "qga") and getattr(self, "qga"):
            return
        if hasattr(self, 'addr'):
            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.Error("could not resolve name %s: %s" % (self.vm_hostname, str(e)))

    def __str__(self):
        return "%s name=%s" % (super(BaseContainer, self).__str__(), self.name)

    def operational(self):
        if not self.runmethod or not self.svc.has_encap_resources:
            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"]
        out, err, ret = justcall(cmd, stdin=self.svc.node.devnull)
        if ret == 0:
            return True
        return False

    def wait_for_startup(self):
        self.wait_for_up()
        self.wait_for_ping()
        self.wait_for_operational()

    def wait_for_up(self):
        def fn():
            if hasattr(self, "is_up_clear_cache"):
                getattr(self, "is_up_clear_cache")()
            return self.is_up()
        self.log.info("wait for up status")
        self.wait_for_fn(fn, self.start_timeout, 2)

    def wait_for_ping(self):
        """
        Wait for container to become alive, using a ping test.
        Also verify the container has not died since judged started.
        """
        if hasattr(self, "qga") and getattr(self, "qga"):
            return
        def fn():
            if hasattr(self, "is_up_clear_cache"):
                getattr(self, "is_up_clear_cache")()
            if not self.is_up():
                raise ex.Error("the container went down")
            return getattr(self, "ping")()
        if hasattr(self, 'ping'):
            self.log.info("wait for container ping")
            self.wait_for_fn(fn, self.start_timeout, 2)

    def wait_for_operational(self):
        """
        Wait for container to become operational, using a driver-specific
        test (usually ssh).
        Also verify the container has not died since judged started.
        """
        def fn():
            if hasattr(self, "is_up_clear_cache"):
                getattr(self, "is_up_clear_cache")()
            if not self.is_up():
                raise ex.Error("the container went down")
            return self.operational()
        self.log.info("wait for container operational")
        self.wait_for_fn(fn, self.start_timeout, 2)

    def wait_for_shutdown(self):
        def fn():
            if hasattr(self, "is_up_clear_cache"):
                getattr(self, "is_up_clear_cache")()
            return not self.is_up()
        self.log.info("wait for down status")
        self.wait_for_fn(fn, self.stop_timeout, 2, errmsg="waited too long for shutdown")

    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 Env.nodename
        if not hasattr(self, "is_up_on"):
            # to implement in Container child class
            return
        if Env.nodename in self.svc.nodes:
            nodes = self.svc.nodes - set([Env.nodename])
        elif Env.nodename in self.svc.drpnodes:
            nodes = self.svc.drpnodes - set([Env.nodename])
        else:
            nodes = []
        if len(nodes) == 0:
            return
        for node in nodes:
            if getattr(self, "is_up_on")(node):
                return node
        return

    def abort_start_ping(self):
        if hasattr(self, "qga") and getattr(self, "qga"):
            return
        if self.svc.get_resources("ip"):
            # we manage an ip, no need to try to ping the container
            return False
        try:
            self.getaddr()
            if not hasattr(self, 'addr'):
                BaseContainer.getaddr(self)
            if not hasattr(self, 'addr'):
                raise ex.Error()
        except:
            self.log.info("could not resolve %s to an ip address" % self.vm_hostname)
            return True

        self.log.info("test %s ip %s availability" % (self.name, self.addr))
        if utilities.ping.check_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 != Env.nodename:
            return True

        return False

    def start(self):
        self.promote_rw()
        self.getaddr()
        where = self.where_up()
        if not self.custom_create_pg:
            self.create_pg()
        if where is not None:
            self.log.info("container %s already started on %s" % (self.label, where))
            return
        if Env.nodename in self.svc.drpnodes:
            self.install_drp_flag()
        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.Error:
            self.container_forcestop()
            self.wait_for_shutdown()
        if hasattr(self, "post_container_stop"):
            getattr(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 core.status.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 core.status.WARN
        if not self.check_capabilities():
            self.status_log("insufficient node capabilities")
            return core.status.WARN
        if self.is_up():
            return core.status.UP
        if self.is_down():
            return core.status.DOWN
        else:
            self.status_log("container status is neither up nor down")
            return core.status.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:
            try:
                _name = self.svc.name[self.svc.name.index(".")+1:]
            except ValueError:
                raise ex.Error("misnamed scaler slave %s: should be <n>.<scalername>" % self.svc.name)
        else:
            _name = self.svc.name
        namespace = self.svc.namespace.lower() if self.svc.namespace else "root"
        elems = (
            "%s.%s.svc.%s" % (_name, namespace, self.svc.cluster_name.lower()),
            "%s.svc.%s" % (namespace, self.svc.cluster_name.lower()),
            "svc.%s" % self.svc.cluster_name.lower(),
        )
        return elems

    def dns_options(self, options):
        ndots_done = False
        edns0_done = False
        usevc_done = False
        for co, i in enumerate(options):
            try:
                if co.startswith("ndots:"):
                    ndots = int(co.replace("ndots:", ""), "")
                    if ndots < 2:
                        options[i] = "ndots:2"
                    ndots_done = True
            except Exception:
                pass
            if co == "edns0":
                edns0_done = True
            elif co == "use-vc":
                usevc_done = True
        if not ndots_done:
            options.append("ndots:2")
        if not edns0_done:
            options.append("edns0")
        if not usevc_done:
            options.append("use-vc")
        return options

    def send_containerinfo_arg(self):
        data = Storage({
            "vm_hostname": self.vm_hostname,
            "container.guestos": self.guestos,
        })
        try:
            container_info = self.get_container_info()
            data["vmem"] = container_info["vmem"]
            data["vcpu"] = container_info["vcpus"]
        except Exception:
            pass
        try:
            data["zonepath"] = getattr(self, "zonepath", "")
        except Exception:
            # zonepath needs rootfs, which is not always available
            pass
        return data
0707010001f348000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/container/vcloud  0707010001f349000081a40000000000000000000000016a100daf0000264e000000e600010003ffffffffffffffff0000004d00000000root/usr/share/opensvc/opensvc/drivers/resource/container/vcloud/__init__.py  import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_SHARED_IP_GROUP, \
    KW_SIZE, \
    KW_KEY_NAME, \
    KW_CLOUD_ID, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from utilities.lazy import lazy
from core.resource import Resource
from core.objects.svcdict import KEYS

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

DRIVER_GROUP = "container"
DRIVER_BASENAME = "vcloud"
KEYWORDS = [
    {
        "keyword": "vapp",
        "required": True,
        "at": True,
        "example": "MyVapp",
        "text": "The Vcloud Virtual App hosting the VM."
    },
    {
        "keyword": "template",
        "text": "The name of the vcloud template image to derive from.",
        "required": True,
        "provisioning": True
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_SHARED_IP_GROUP,
    KW_SIZE,
    KW_KEY_NAME,
    KW_CLOUD_ID,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    try:
        from libcloud.compute.types import Provider
        from libcloud.compute.providers import get_driver
        return ["container.vcloud"]
    except ImportError:
        return []


class ContainerVcloud(BaseContainer):
    save_timeout = 240

    def __init__(self,
                 vapp=None,
                 cloud_id=None,
                 size="tiny",
                 key_name=None,
                 shared_ip_group=None,
                 template=None,
                 **kwargs):
        super(ContainerVcloud, self).__init__(type="container.vcloud", **kwargs)
        self.cloud_id = cloud_id
        self.size_name = size
        self.key_name = key_name
        self.vapp = vapp
        self.template = template
        self.shared_ip_group = shared_ip_group

    @lazy
    def save_name(self):
        return "%s.save" % self.name

    def _vm_perform_power_operation(self, vapp_or_vm_id, operation):
        drv = self.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.Error("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):
        for size in self.cloud.driver.list_sizes():
            if size.name == self.size_name:
                return size
        raise ex.Error("%s size not found"%self.size_name)

    @lazy
    def cloud(self):
        return self.svc.node.cloud_get(self.cloud_id)

    def get_vapp(self):
        try:
            vapp = self.cloud.driver.ex_find_node(self.vapp)
        except Exception as e:
            print(e)
            raise
        return vapp

    def get_node(self):
        try:
            vapp = self.cloud.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):
        l = self.cloud.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.Error("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)
             self.cloud.driver.ex_delete_image(d[k])

    def get_last_save(self):
        return self.get_image(self.save_name)

    def get_template(self):
        return self.get_image(self.template)

    def get_image(self, name):
        l = self.cloud.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.Error("image %s not found"%name)
        for k in sorted(d.keys()):
             last = d[k]
        return last

    def has_image(self, name):
        l = self.cloud.driver.list_images()
        for image in l:
            if image.name == name:
                return True
        return False

    def __str__(self):
        return "%s name=%s" % (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 utilities.ping.check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

    def check_capabilities(self):
        return True

    def ping(self):
        if not hasattr(self, "addr"):
            return 0
        return utilities.ping.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 Env.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()
        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.start_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.Error:
            self.container_forcestop()
            self.wait_for_shutdown()

    def container_stop(self):
        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):
        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 = self.cloud.driver.ex_save_image(n, save_name)
        except Exception as e:
            raise ex.Error(str(e))
        import time
        delay = 5
        for i in range(self.save_timeout//delay):
            img = self.cloud.driver.ex_get_image(image.id)
            if img.extra['status'] != 'SAVING':
                break
            time.sleep(delay)
        if img.extra['status'] != 'ACTIVE':
            raise ex.Error("save failed, image status %s"%img.extra['status'])

    def container_forcestop(self):
        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'}
        n = self.get_node()
        top = self.cloud.driver._get_vm_elements(n['id'])[0]
        def recurse(x, info=None, desc=None):
            if info is None:
                info = {}
            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

    def provision(self):
        image = self.get_template()
        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))
        self.cloud.driver.create_node(name=self.name, size=size, image=image, ex_key_name=self.key_name, ex_shared_ip_group_id=self.shared_ip_group)
        self.wait_for_startup()

  0707010001f338000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/container/ldom    0707010001f339000081a40000000000000000000000016a100daf0000114c000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/container/ldom/__init__.py    import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.proc import qcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "ldom"
KEYWORDS = [
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("/usr/sbin/ldm"):
        data.append("container.ldom")
    return data


class ContainerLdom(BaseContainer):
    def __init__(self, guestos="SunOS", **kwargs):
        super(ContainerLdom, self).__init__(type="container.ldom", guestos=guestos, **kwargs)
        self.sshbin = '/usr/local/bin/ssh'

    def __str__(self):
        return "%s name=%s" % (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 utilities.ping.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.Error
        return None

    def container_start(self):
        """ ldm bind domain
            ldm start domain
        """
        state = self.state()
        if state == 'None':
            raise ex.Error
        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.Error:
                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.Error
        if state == 'inactive':
            return None
        if state == 'bound' :
            self.container_action('unbind')
        if state == 'active' :
            cmd = Env.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.stop_timeout, 2)
                except ex.Error:
                    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

0707010001f342000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/container/podman  0707010001f343000081a40000000000000000000000016a100daf000010a2000000e600010003ffffffffffffffff0000004d00000000root/usr/share/opensvc/opensvc/drivers/resource/container/podman/__init__.py  """
Docker container resource driver module.
"""
import core.status
import utilities.subsystems.docker as dockerlib
import core.exceptions as ex
from .. import \
    BaseContainer
from ..docker import KEYWORDS, ContainerDocker
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from core.resource import Resource
from utilities.proc import justcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "podman"
DRIVER_BASENAME_ALIASES = ["oci"]
DEPRECATED_KEYWORDS = {
    "container.podman.run_image": "image",
    "container.podman.run_command": "command",
    "container.podman.net": "netns",
    "container.oci.run_image": "image",
    "container.oci.run_command": "command",
    "container.oci.net": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "container.podman.image": "run_image",
    "container.podman.command": "run_command",
    "container.podman.netns": "net",
    "container.oci.image": "run_image",
    "container.oci.command": "run_command",
    "container.oci.netns": "net",
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("podman"):
        data += [
            "container.podman",
            "container.podman.registry_creds",
            "container.podman.signal",
        ]
    return data


class ContainerPodman(ContainerDocker):
    """
    Docker container resource driver.
    """
    default_net = ""
    dns_option_option = "--dns-opt"

    def __init__(self, *args, **kwargs):
        kwargs["type"] = "container.podman"
        super(ContainerPodman, self).__init__(*args, **kwargs)

    @lazy
    def lib(self):
        """
        Lazy allocator for the podmanlib object.
        """
        try:
            return self.svc.podmanlib
        except AttributeError:
            self.svc.podmanlib = dockerlib.PodmanLib(self.svc)
            return self.svc.podmanlib

    @lazy
    def label(self): # pylint: disable=method-hidden
        return "podman " + self.lib.image_userfriendly_name(self)

    def container_rm(self):
        """
        Remove the resource podman instance.
        """
        cmd = self.lib.docker_cmd + ["rm", self.container_name]
        out, err, ret = justcall(cmd)
        if ret != 0:
            if "unable to find" in err:
                pass
            elif "no such file" in err:
                pass
            elif "container has already been removed" in err:
                pass
            elif "has dependent containers which must be removed" in err:
                pass
            elif "no container with name" in err:
                pass
            elif "no container with ID or name" in err:
                pass
            elif "removal" in err and "already in progress" in err:
                self.wait_for_removed()
            else:
                self.log.info(" ".join(cmd))
                raise ex.Error(err)
        else:
            self.log.info(" ".join(cmd))
        self.is_up_clear_cache()

    def create_pg(self):
        Resource.create_pg(self, has_subtree=True)

    def _start(self):
        BaseContainer.start(self)
        self.is_up_clear_cache()

    def _stop(self):
        BaseContainer.stop(self)
        self.is_up_clear_cache()
        if self.rm:
            self.container_rm()

    def _status(self, verbose=False):
        if not self.detach:
            return core.status.NA
        try:
            self.lib.docker_exe
        except ex.InitError as exc:
            self.status_log(str(exc), "warn")
            return core.status.DOWN
        sta = BaseContainer._status(self, verbose)
        self._status_inspect()
        return sta

    def is_up(self):
        if self.container_id is None:
            self.status_log("can not find container id", "info")
            return False
        if self.container_id in self.lib.get_running_instance_ids():
            return True
        return False

    def cgroup_options(self):
        return ["--cgroup-parent", self.cgroup_dir+"/libpod"]

  0707010001f34c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/xen 0707010001f34d000081a40000000000000000000000016a100daf00000ec5000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/xen/__init__.py import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_SNAP, \
    KW_SNAPOF, \
    KW_VIRTINST, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from core.resource import Resource
from env import Env
from core.objects.svcdict import KEYS

DRIVER_GROUP = "container"
DRIVER_BASENAME = "xen"
KEYWORDS = [
    KW_SNAP,
    KW_SNAPOF,
    KW_VIRTINST,
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("xm"):
        data.append("container.xen")
    return data

class ContainerXen(BaseContainer):
    def __init__(self, **kwargs):
        super(ContainerXen, self).__init__(type="container.xen", **kwargs)

    def __str__(self):
        return "%s name=%s" % (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 utilities.ping.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.Error
        cmd = ['virsh', 'start', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_stop(self):
        cmd = ['virsh', 'shutdown', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_forcestop(self):
        cmd = ['virsh', 'destroy', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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 = Env.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
   0707010001f336000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/kvm 0707010001f337000081a40000000000000000000000016a100daf00005211000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/kvm/__init__.py from __future__ import print_function

import os
import json
import time
import base64

from xml.etree.ElementTree import ElementTree, SubElement

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_SNAP, \
    KW_SNAPOF, \
    KW_VIRTINST, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV, \
    KW_QGA, \
    KW_QGA_OPERATIONAL_DELAY
from core.resource import Resource
from env import Env
from utilities.cache import cache, clear_cache
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which
from utilities.string import bdecode

CAPABILITIES = {
    "partitions": "1.0.1",
}

DRIVER_GROUP = "container"
DRIVER_BASENAME = "kvm"
KEYWORDS = [
    KW_SNAP,
    KW_SNAPOF,
    KW_VIRTINST,
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
    KW_QGA,
    KW_QGA_OPERATIONAL_DELAY,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = []
    cmd = ['virsh', 'capabilities']
    out, err, ret = justcall(cmd)
    if ret == 0:
        data.append("container.kvm")
    return data


class ContainerKvm(BaseContainer):
    def __init__(self,
                 snap=None,
                 snapof=None,
                 virtinst=None,
                 qga=False,
                 qga_operational_delay=10,
                 **kwargs):
        super(ContainerKvm, self).__init__(type="container.kvm", **kwargs)
        self.refresh_provisioned_on_provision = True
        self.refresh_provisioned_on_unprovision = True
        self.snap = snap
        self.snapof = snapof
        self.virtinst = virtinst or []
        self.qga = qga
        self.qga_operational_delay = qga_operational_delay

    @lazy
    def cf(self):
        return os.path.join(os.sep, 'etc', 'libvirt', 'qemu', self.name+'.xml')

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)

    def list_kvmconffiles(self):
        if not self.shared and not self.svc.topology == "failover":
            # don't send the container cf to nodes that won't run it
            return []
        if os.path.exists(self.cf):
            return [self.cf] + self.firmware_files()
        return []

    def files_to_sync(self):
        return self.list_kvmconffiles()

    def capable(self, cap):
        if self.libvirt_version >= CAPABILITIES.get(cap, "0"):
            return True
        return False

    @cache("virsh.capabilities")
    def capabilities(self):
        cmd = ['virsh', 'capabilities']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return
        return out

    def check_capabilities(self):
        out = self.capabilities()
        if out is None:
            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):
        if self.qga:
            return
        return utilities.ping.check_ping(self.addr, timeout=1, count=1)

    def qga_exec_status(self, pid):
        payload = {
            "execute": "guest-exec-status",
            "arguments": {
                "pid": pid
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error(err)
        data = json.loads(out)["return"]
        data["out-data"] = bdecode(base64.b64decode(data.get("out-data", b"")))
        data["err-data"] = bdecode(base64.b64decode(data.get("err-data", b"")))
        return data

    def qga_operational(self):
        payload = {
            "execute": "guest-exec",
            "arguments": {
                "path": "/usr/bin/pwd",
                "arg": [],
                "capture-output": True
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.log.debug(err)
            return False
        return True

    def rcp_from(self, src, dst):
        if self.qga:
            # TODO
            return "", "", 0
        else:
            cmd = Env.rcp.split() + [self.name+":"+src, dst]
            return justcall(cmd)

    def rcp(self, src, dst):
        if self.qga:
            return self.qga_cp(src, dst)
        else:
            cmd = Env.rcp.split() + [src, self.name+':'+dst]
            return justcall(cmd)

    def qga_cp(self, src, dst):
        self.log.debug("qga cp: %s to %s", src, dst)
        payload = {
            "execute":"guest-file-open",
            "arguments":{
                "path": dst,
                "mode":"w"
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        self.log.debug("%s => out:%s err:%s ret:%d", payload, out, err, ret)
        if ret != 0:
            raise ex.Error(err)
        data = json.loads(out)
        handle = data["return"]

        with open(src, "rb") as f:
            buff = base64.b64encode(f.read()).decode()

        payload = {
            "execute":"guest-file-write",
            "arguments":{
                "handle": handle,
                "buf-b64": buff,
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        self.log.debug("%s => out:%s err:%s ret:%d", payload, out, err, ret)
        if ret != 0:
            raise ex.Error(err)
        data = json.loads(out)

        payload = {
            "execute":"guest-file-close",
            "arguments":{
                "handle": handle,
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        self.log.debug("%s => out:%s err:%s ret:%d", payload, out, err, ret)
        if ret != 0:
            raise ex.Error(err)
        return "", "", 0

    def qga_exec(self, cmd, verbose=False, timeout=60):
        if verbose:
            log = self.log.info
        else:
            log = self.log.debug
        log("qga exec: %s", " ".join(cmd))
        payload = {
            "execute": "guest-exec",
            "arguments": {
                "path": cmd[0],
                "arg": cmd[1:],
                "capture-output": True
            }
        }
        cmd = ["virsh", "qemu-agent-command", self.name, json.dumps(payload)]
        out, err, ret = justcall(cmd)
        if ret != 0:
            self.log.debug(err)
            return False
        data = json.loads(out)
        pid = data["return"]["pid"]
        log("qga exec: command started with pid %d", pid)
        for i in range(timeout):
            data = self.qga_exec_status(pid)
            if not data.get("exited"):
                time.sleep(1)
                continue
            log("qga exec: command exited with %d", data.get("exitcode"))
            #log("qga exec: out: %s", data.get("out-data"))
            #log("qga exec: err: %s", data.get("err-data"))
            return data
        raise ex.Error("timeout waiting for qemu guest exec result, pid %d" % pid)

    def rcmd(self, cmd):
        if self.qga:
            data = self.qga_exec(cmd)
            return data.get("out-data", ""), data.get("err-data", ""), data.get("exitcode", 1)
        elif hasattr(self, "runmethod"):
            cmd = self.runmethod + cmd
            return justcall(cmd, stdin=self.svc.node.devnull)
        else:
            raise ex.EncapUnjoinable("undefined rcmd/runmethod in resource %s" % self.rid)

    def operational(self):
        if self.guestos == "windows":
            return True
        if self.qga:
            v = self.qga_operational()
            if v:
                # qga is operational, but we have no generic method to ensure
                # the os is far enough in the boot for a encap start to succeed
                # (network, sssd, dockerd, ... may need to be started but we don't
                # know if they are managed by systemd, openrc, ...)
                time.sleep(self.qga_operational_delay)
            return v
        else:
            return BaseContainer.operational(self)

    def is_up_clear_cache(self):
        clear_cache("virsh.dom_state.%s@%s" % (self.name, Env.nodename))

    def virsh_define(self):
        cmd = ['virsh', 'define', self.cf]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def virsh_undefine(self):
        if self.has_efi():
            cmd = ['virsh', 'undefine', '--nvram', self.name]
        else:
            cmd = ['virsh', 'undefine', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_start(self):
        if self.svc.create_pg and which("machinectl") is None and self.capable("partitions"):
            self.set_partition()
        else:
            self.unset_partition()
        if not os.path.exists(self.cf):
            self.log.error("%s not found"%self.cf)
            raise ex.Error
        self.virsh_define()
        cmd = ['virsh', 'start', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        clear_cache("virsh.dom_state.%s@%s" % (self.name, Env.nodename))

    def start(self):
        super(ContainerKvm, self).start()

    def container_stop(self):
        state = self.dom_state()
        cmd = []
        if state == "running":
            cmd = ['virsh', 'shutdown', self.name]
        elif state in ("blocked", "paused", "crashed"):
            self.container_forcestop()
        else:
            self.log.info("skip stop, container state=%s", state)
            return
        ret, buff, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        clear_cache("virsh.dom_state.%s@%s" % (self.name, Env.nodename))

    def stop(self):
        super(ContainerKvm, self).stop()

    def container_forcestop(self):
        cmd = ['virsh', 'destroy', self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    @cache("virsh.dom_state.{args[1]}@{args[2]}")
    def _dom_state(self, vmname, nodename, cmd):
        ret, out, err = self.call(cmd, errlog=False)
        if ret != 0:
            return
        for line in out.splitlines():
            if line.startswith("State:"):
                return line.split(":", 1)[-1].strip()

    def dom_state(self, nodename=None):
        cmd = ['virsh', 'dominfo', self.name]
        if nodename is not None:
            cmd = Env.rsh.split() + [nodename] + cmd
        return self._dom_state(self.name, nodename if nodename else Env.nodename, cmd)

    def is_up(self, nodename=None):
        state = self.dom_state(nodename=nodename)
        if state == "running":
            return True
        return False

    def is_down(self, nodename=None):
        state = self.dom_state(nodename=nodename)
        if state in (None, "shut off", "no state"):
            return True
        return False

    def is_defined(self):
        if os.path.exists(self.cf):
            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

    @lazy
    def cgroup_dir(self):
        return "/"+self.svc.pg.get_cgroup_relpath(self)

    @lazy
    def libvirt_version(self):
        cmd = ["virsh", "--version"]
        out, _, _ = justcall(cmd)
        return out.strip()

    def unset_partition(self):
        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            raise ex.Error("container config parsing error: %s" % exc)
        root = tree.getroot()
        if root is None:
            raise ex.Error("invalid container config %s" % self.cf)
        resource = root.find("resource")
        if resource is None:
            return
        part = resource.find("partition")
        if part is None:
            return
        if part.text != self.cgroup_dir:
            return
        root.remove(resource)
        self.log.info("unset resource/partition = %s" % self.cgroup_dir)
        part.text = self.cgroup_dir
        tree.write(self.cf)

    def set_partition(self):
        self.svc.pg.create_pg(self)
        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            raise ex.Error("container config parsing error: %s" % exc)
        root = tree.getroot()
        if root is None:
            raise ex.Error("invalid container config %s" % self.cf)
        resource = root.find("resource")
        if resource is None:
            resource = SubElement(root, "resource")
        part = resource.find("partition")
        if part is None:
            print("create part")
            part = SubElement(resource, "partition")
        if part.text == self.cgroup_dir:
            return
        self.log.info("set resource/partition = %s" % self.cgroup_dir)
        part.text = self.cgroup_dir
        tree.write(self.cf)

    def install_drp_flag(self):
        flag_disk_path = os.path.join(Env.paths.pathvar, 'drp_flag.vdisk')

        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            raise ex.Error("container config parsing error: %s" % exc)

        # 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.iter("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):
        devs = set(map(lambda x: x[0], self.devmapping))
        return devs

    def firmware_files(self):
        l = []
        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            return l
        for xml_node in tree.findall("os"):
            s = xml_node.find("loader")
            if s is not None:
                l.append(s.text)
            s = xml_node.find("nvram")
            if s is not None:
                l.append(s.text)
        return l

    def has_efi(self):
        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            return False
        for xml_node in tree.findall("os"):
            if xml_node.attrib.get("firmware") == "efi":
                return True
            if xml_node.find("nvram") is not None:
                return True
        return False

    @lazy
    def devmapping(self):
        """
        Return a list of (src, dst) devices tuples fount in the container
        conifguration file.
        """
        if not os.path.exists(self.cf):
            # not yet received from peer node ?
            return []
        data = []

        tree = ElementTree()
        try:
            tree.parse(self.cf)
        except Exception as exc:
            return data
        for dev in tree.iter('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']
            data.append((src, dst))
        return data

    def _status(self, verbose=False):
        return super(ContainerKvm, self)._status(verbose=verbose)

    def check_kvm(self):
        if os.path.exists(self.cf):
            return True
        return False

    def setup_kvm(self):
        if self.virtinst is None:
            self.log.error("the 'virtinst' parameter must be set")
            raise ex.Error
        cmd = [] + self.virtinst
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def setup_ips(self):
        if self.qga:
            return
        self.purge_known_hosts()
        for resource in self.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.svc.name]
        else:
            cmd = ['ssh-keygen', '-R', ip]
        ret, out, err = self.vcall(cmd, err_to_info=True)

    def setup_snap(self):
        if self.snap is None and self.snapof is None:
            return
        elif self.snap and self.snapof is None:
            self.log.error("the 'snapof' parameter is required when 'snap' parameter present")
            raise ex.Error
        elif self.snapof and self.snap is None:
            self.log.error("the 'snap' parameter is required when 'snapof' parameter present")
            raise ex.Error

        if not which('btrfs'):
            self.log.error("'btrfs' command not found")
            raise ex.Error

        cmd = ['btrfs', 'subvolume', 'snapshot', self.snapof, self.snap]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def provisioner(self):
        self.setup_snap()
        self.setup_kvm()
        self.setup_ips()
        self.log.info("provisioned")
        return True

    def provisioned(self):
        cmd = ['virsh', 'dominfo', self.name]
        out, _, ret = justcall(cmd)
        if ret != 0:
            return False
        return True

    def unprovisioner(self):
        if not self.provisioned():
            self.log.debug("skip kvm unprovision: container is not provisioned")
            return
        if self.is_defined():
            self.virsh_undefine()
        self.log.info("unprovisioned")
        return True

    def unprovisioner_shared_non_leader(self):
        if not self.provisioned():
            self.log.debug("skip kvm unprovision: container is not provisioned")
            return
        if self.is_defined():
            self.virsh_undefine()
        self.log.info("unprovisioned")
        return True

    def wait_for_shutdown(self):
        """
        Defines dedicated wait_for_shutdown that waits for `is_down` instead of `not is_up`.
        The `not is_up` is not enough, `in shutdown` can take time...
        TODO: Improve method with extra wait for not more `Id` when `State` is `shut off`

        Example of shutdown state transitions during shutdown:
            Thu Nov 27 10:24:02 CET 2025
            Id:             19
            ...
            State:          running
            ...

            Thu Nov 27 10:24:31 CET 2025
            Id:             19
            ...
            State:          in shutdown
            ...

            Thu Nov 27 10:24:33 CET 2025
            Id:             19
            ...
            State:          shut off
            ...

            Thu Nov 27 10:24:33 CET 2025
            Id:             -
            ...
            State:          shut off
            ...
        """
        def fn():
            if hasattr(self, "is_up_clear_cache"):
                getattr(self, "is_up_clear_cache")()
            return self.is_down()
        self.log.info("wait for down status")
        self.wait_for_fn(fn, self.stop_timeout, 2, errmsg="waited too long for shutdown")
   0707010001f334000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/container/jail    0707010001f335000081a40000000000000000000000016a100daf0000106b000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/container/jail/__init__.py    import os

import core.exceptions as ex
import core.status
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from core.resource import Resource
from env import Env
from core.objects.svcdict import KEYS
from utilities.proc import qcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "jail"
KEYWORDS = [
    {
        "keyword": "jailroot",
        "text": "Sets the root fs directory of the container",
        "required": True,
    },
    {
        "keyword": "ips",
        "convert": "list",
        "at": True,
        "text": "The ipv4 addresses of the jail."
    },
    {
        "keyword": "ip6s",
        "convert": "list",
        "at": True,
        "text": "The ipv6 addresses of the jail."
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("jail"):
        data.append("container.jail")
    return data


class ContainerJail(BaseContainer):
    """ 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 __init__(self,
                 guestos="FreeBSD",
                 jailroot="/tmp",
                 ips=None,
                 ip6s=None,
                 **kwargs):
        super(ContainerJail, self).__init__(
            guestos=guestos,
            type="container.jail",
            **kwargs
        )
        if ip6s is None:
            ip6s = []
        if ips is None:
            ips = []
        self.jailroot = jailroot
        self.ips = ips
        self.ip6s = ip6s

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)

    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.name, '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.Error

    def container_stop(self):
        cmd = ['jail', '-r', self.name]
        (ret, out, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

    def ping(self):
        return utilities.ping.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 = Env.rsh.split() + [nodename] + cmd
        (ret, out, err) = self.call(cmd)
        if ret != 0:
            raise ex.Error
        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 core.status.UP
        else:
            return core.status.DOWN


 0707010001f344000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/srp 0707010001f345000081a40000000000000000000000016a100daf00002d0d000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/srp/__init__.py import os
import socket

import core.exceptions as ex

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from utilities.lazy import lazy
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall, qcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "srp"
KEYWORDS = [
    {
        "keyword": "prm_cores",
        "default": 1,
        "convert": "integer",
        "provisioning": True,
        "text": "The number of core to bind the SRP container to."
    },
    {
        "keyword": "ip",
        "at": True,
        "provisioning": True,
        "text": "The ip name or addr used to create the SRP container."
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("srp") and which("srp_su"):
        data.append("container.srp")
    return data


class ContainerSrp(BaseContainer):
    def __init__(self, guestos="HP-UX", prm_cores=1, ip=None, **kwargs):
        super(ContainerSrp, self).__init__(type="container.srp", guestos=guestos, **kwargs)
        self.prm_cores = prm_cores
        self.raw_ip = ip
        self.need_start = []

    @lazy
    def rootpath(self):
        return os.path.join(os.sep, 'var', 'hpsrp', self.name)

    @lazy
    def runmethod(self):
        return ['srp_su', self.name, 'root', '-c']

    def files_to_sync(self):
        return [self.export_file]

    def get_rootfs(self):
        return self.get_status()['state']

    def rcp_from(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.Error()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%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.Error()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%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.Error()

    def container_stop(self):
        cmd = ['srp', '-stop', self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error()

    def container_forcestop(self):
        raise ex.Error

    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.Error("srp -list returned %d:\n%s"%(ret, err))

        data = {}

        words = out.split()
        service = ""
        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.Error("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 = Env.rsh.split() + [nodename] + cmd

        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("srp -status returned %d:\n%s"%(ret, err))
        lines = out.split('\n')
        if len(lines) < 2:
            raise ex.Error("srp -status output too short:\n%s"%out)
        l = lines[1].split()
        if l[0] != self.name:
            raise ex.Error("srp -status second line, first entry does not match container name")
        if len(l) != 5:
            raise ex.Error("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.Error:
            return False
        if val == 'yes' or val == '1':
            return False
        return True

    def check_capabilities(self):
        if "node.x.srp" not in capabilities:
            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.Error("%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.Error()

    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.Error()


    @lazy
    def export_file(self):
        return os.path.join(self.var_d, self.name + '.xml')

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)


    @lazy
    def ip(self):
        return self.lookup(self.raw_ip)

    def lookup(self, ip):
        if ip is None:
            raise ex.Error("the ip provisioning keyword is not set")

        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.Error("could not resolve %s to an ip address"%ip)

    def validate(self):
        # False triggers provisioner, True skip provisioner
        if "node.x.srp" not in capabilities:
            self.log.error("this node is not srp capable")
            return True

        if self.check_srp():
            self.log.error("container is already created")
            return True

        return False

    def check_srp(self):
        try:
            self.get_status()
        except:
            return False
        return True

    def cleanup(self):
        rs = self.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.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.vcall(cmd)
        if ret != 0:
            raise ex.Error()
        self.restart_fs()

    def provisioner(self):
        self.add_srp()
        self.start()
        self.log.info("provisioned")
        return True
   0707010001f33e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/resource/container/openstack   0707010001f33f000081a40000000000000000000000016a100daf00002d89000000e600010003ffffffffffffffff0000005000000000root/usr/share/opensvc/opensvc/drivers/resource/container/openstack/__init__.py   import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_SHARED_IP_GROUP, \
    KW_SIZE, \
    KW_KEY_NAME, \
    KW_CLOUD_ID, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from utilities.lazy import lazy
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "openstack"
KEYWORDS = [
    {
        "keyword": "template",
        "text": "The name of the openstack template image to derive from.",
        "required": True,
        "provisioning": True
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_SHARED_IP_GROUP,
    KW_SIZE,
    KW_KEY_NAME,
    KW_CLOUD_ID,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    try:
        from libcloud.compute.types import Provider
        from libcloud.compute.providers import get_driver
        import libcloud.security
        return ["container.openstack"]
    except ImportError:
        return []

class ContainerOpenstack(BaseContainer):
    save_timeout = 240

    def __init__(self,
                 cloud_id=None,
                 size="tiny",
                 key_name=None,
                 shared_ip_group=None,
                 template=None,
                 **kwargs):
        super(ContainerOpenstack, self).__init__(type="container.openstack", **kwargs)
        self.cloud_id = cloud_id
        self.size_name = size
        self.key_name = key_name
        self.shared_ip_group = shared_ip_group
        self.template = template
        self.addr = None

    @lazy
    def save_name(self):
        return "%s.save" % self.name

    def keyfile(self):
        kf = [os.path.join(Env.paths.pathetc, self.key_name+'.pem'),
              os.path.join(Env.paths.pathetc, self.key_name+'.pub'),
              os.path.join(Env.paths.pathvar, self.key_name+'.pem'),
              os.path.join(Env.paths.pathvar, self.key_name+'.pub')]
        for k in kf:
            if os.path.exists(k):
                return k
        raise ex.Error("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.NotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%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.NotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%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.NotSupported("remote commands not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def get_size(self):
        for size in self.cloud.driver.list_sizes():
            if size.name == self.size_name:
                return size
        raise ex.Error("%s size not found"%self.size_name)

    @lazy
    def cloud(self):
        return self.svc.node.cloud_get(self.cloud_id)

    def get_node(self):
        l = self.cloud.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):
        l = self.cloud.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.Error("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)
             self.cloud.driver.ex_delete_image(d[k])

    def get_last_save(self):
        return self.get_image(self.save_name)

    def get_template(self):
        return self.get_image(self.template)

    def get_image(self, name):
        l = self.cloud.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.Error("image %s not found"%name)
        for k in sorted(d.keys()):
             last = d[k]
        return last

    def has_image(self, name):
        l = self.cloud.driver.list_images()
        for image in l:
            if image.name == name:
                return True
        return False

    def __str__(self):
        return "%s name=%s" % (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.Error("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 utilities.ping.check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

        return 0

    def check_capabilities(self):
        return True

    def ping(self):
        if self.addr is None:
            return 0
        return utilities.ping.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 Env.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.Error("abort reboot because vm is in state %d (!=4)"%n.state)
        else:
            self.container_restore()

    def container_reboot(self):
        n = self.get_node()
        try:
            self.cloud.driver.reboot_node(n)
        except Exception as e:
            raise ex.Error(str(e))

    def container_restore(self):
        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 = self.cloud.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.start_timeout, 5)
        #n = self.cloud.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.Error:
            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):
        n = self.get_node()
        self.container_save()
        self.cloud.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):
        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 = self.cloud.driver.ex_save_image(n, save_name)
        except Exception as e:
            raise ex.Error(str(e))
        import time
        delay = 5
        for i in range(self.save_timeout//delay):
            img = self.cloud.driver.ex_get_image(image.id)
            if img.extra['status'] != 'SAVING':
                break
            time.sleep(delay)
        if img.extra['status'] != 'ACTIVE':
            raise ex.Error("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'}
        n = self.get_node()
        try:
            size = self.cloud.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

    def provision(self):
        image = self.get_template()
        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))
        self.cloud.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.wait_for_startup()
        self.wait_for_fn(self.is_up, self.start_timeout, 5)

   0707010001f330000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/esx 0707010001f331000081a40000000000000000000000016a100daf00000e81000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/esx/__init__.py import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS

DRIVER_GROUP = "container"
DRIVER_BASENAME = "esx"
KEYWORDS = [
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("vmware-cmd"):
        data.append("container.esx")
    return data


class ContainerEsx(BaseContainer):
    def __init__(self, **kwargs):
        super(ContainerEsx, self).__init__(type="container.esx", **kwargs)
        self.vmx = None

    def __str__(self):
        return "%s name=%s" % (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 check_capabilities(self):
        return "node.x.vmware-cmd" in capabilities

    def ping(self):
        return utilities.ping.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.Error
        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.Error
        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.Error

    def container_stop(self):
        cmd = ['stop', 'soft']
        ret, buff, err = self.vmcmd(cmd, verbose=True)
        if ret != 0:
            raise ex.Error

    def container_forcestop(self):
        cmd = ['stop', 'hard']
        ret, buff, err = self.vmcmd(cmd, verbose=True)
        if ret != 0:
            raise ex.Error

    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
   0707010001f346000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/container/vbox    0707010001f347000081a40000000000000000000000016a100daf00001281000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/container/vbox/__init__.py    import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.proc import qcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "vbox"
KEYWORDS = [
    {
        "keyword": "headless",
        "at": True,
        "candidates": (True, False),
        "default": False,
        "convert": "boolean",
        "text": "Enable VM startup in headless mode",
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("VBoxManage"):
        data.append("container.vbox")
    return data

class ContainerVbox(BaseContainer):
    def __init__(self, headless=None, **kwargs):
        super(ContainerVbox, self).__init__(type="container.vbox", **kwargs)
        #self.sshbin = '/usr/local/bin/ssh'
        self.vminfo = None
        self.headless = headless

    def __str__(self):
        return "%s name=%s" % (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=None):
        cmd = ['VBoxManage', 'list', 'runningvms']
        if nodename is not None:
            cmd = Env.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.ping.check_ping(self.addr)

    def container_action(self, action, add=None):
        if add is None:
            add = []
        cmd = ['VBoxManage', action, self.name] + add
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_start(self):
        state = self.state()
        if state == 'None':
            raise ex.Error
        elif state == 'off':
            if self.headless:
                self.container_action('startvm', ['--type', 'headless'])
            else:
                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.Error
        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.stop_timeout, 2)
            except ex.Error:
                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

   0707010001f32e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/container/docker  0707010001f32f000081a40000000000000000000000016a100daf0000a305000000e600010003ffffffffffffffff0000004d00000000root/usr/share/opensvc/opensvc/drivers/resource/container/docker/__init__.py  """
Docker container resource driver module.
"""
import os
import re
import shlex

from itertools import chain

import core.status
import utilities.subsystems.docker as dockerlib
import core.exceptions as ex
import utilities.ping
from utilities.concurrent_futures import get_concurrent_futures

from .. import \
    KW_NO_PREEMPT_ABORT, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV, \
    BaseContainer
from utilities.lazy import unset_lazy, lazy
from utilities.naming import factory, svc_pathvar
from utilities.files import makedirs
from core.resource import Resource
from utilities.converters import print_duration
from core.objects.svcdict import KEYS
from utilities.proc import justcall, drop_option, has_option, get_option, get_options

DRIVER_GROUP = "container"
DRIVER_BASENAME = "docker"
DRIVER_BASENAME_ALIASES = ["oci"]
KEYWORDS = [
    {
        "keyword": "hostname",
        "at": True,
        "text": "Set the container hostname. If not set, a unique id is used."
    },
    {
        "keyword": "detach",
        "at": True,
        "default": True,
        "convert": "boolean",
        "text": "Run container in background. Set to ``false`` only for init containers, alongside :kw:`start_timeout` and the :c-tag:`nostatus` tag.",
    },
    {
        "keyword": "read_only",
        "at": True,
        "convert": "tristate",
        "text": "Mount the container's root filesystem as read only.",
    },
    {
        "keyword": "entrypoint",
        "at": True,
        "text": "The script or binary executed in the container. Args must be set in :kw:`command`.",
        "example": "/bin/sh"
    },
    {
        "keyword": "rm",
        "at": True,
        "default": False,
        "convert": "boolean",
        "text": "If set to ``true``, add :opt:`--rm` to the docker run args and make sure the instance is removed on resource stop.",
        "example": False
    },
    {
        "keyword": "volume_mounts",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<volume name|local dir>:<containerized mount path>:<mount options>``. "
                "When the source is a local dir, the default <mount option> is rw. "
                "When the source is a volume name, the default <mount option> is taken from volume access.",
        "example": "myvol1:/vol1 myvol2:/vol2:rw /localdir:/data:ro"
    },
    {
        "keyword": "environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<value>``. A shell expression spliter is applied, so double quotes can be around values only or whole ``<var>=<value>``. Variables are uppercased.",
        "example": """AA="a a" "BB=c c" CC=d """
    },
    {
        "keyword": "secrets_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "A whitespace separated list of ``<var>=<secret name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<secret name>/<key path>`` only or whole ``<var>=<secret name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "configs_environment",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "devices",
        "at": True,
        "convert": "shlex",
        "default": [],
        "text": "The whitespace separated list of ``<host devpath>:<containerized devpath>``, specifying the host devices the container should have access to.",
        "example": "myvol1:/dev/xvda myvol2:/dev/xvdb"
    },
    {
        "keyword": "netns",
        "at": True,
        "text": "Sets the :cmd:`docker run --net` argument. The default is ``none`` if :opt:`--net` is not specified in :kw:`run_args`, meaning the container will have a private netns other containers can share. A :c-res:`ip.netns` or :c-res:`ip.cni` resource can configure an ip address in this container. A container with ``netns=container#0`` will share the container#0 netns. In this case agent format a :opt:`--net=container:<name of container#0 docker instance>`. ``netns=host`` shares the host netns.",
        "example": "container#0"
    },
    {
        "keyword": "user",
        "at": True,
        "text": "Sets the :cmd:`docker run --user` argument.",
        "example": "guest"
    },
    {
        "keyword": "userns",
        "at": True,
        "candidates": ("host", None),
        "text": "Sets the :cmd:`docker run --userns` argument. If not set, the container will have a private userns other containers can share. A container with ``userns=host`` will share the host's userns.",
        "example": "container#0"
    },
    {
        "keyword": "pidns",
        "at": True,
        "text": "Sets the :cmd:`docker run --pid` argument. If not set, the container will have a private pidns other containers can share. Usually a pidns sharer will run a google/pause image to reap zombies. A container with ``pidns=container#0`` will share the container#0 pidns. In this case agent format a :opt:`--pid=container:<name of container#0 docker instance>`. Use ``pidns=host`` to share the host's pidns.",
        "example": "container#0"
    },
    {
        "keyword": "ipcns",
        "at": True,
        "text": "Sets the :cmd:`docker run --ipc` argument. If not set, the docker daemon's default value is used. ``ipcns=none`` does not mount /dev/shm. ``ipcns=private`` creates a ipcns other containers can not share. ``ipcns=shareable`` creates a netns other containers can share. ``ipcns=container#0`` will share the container#0 ipcns.",
        "example": "container#0"
    },
    {
        "keyword": "utsns",
        "at": True,
        "candidates": (None, "host"),
        "text": "Sets the :cmd:`docker run --uts` argument. If not set, the container will have a private utsns. A container with ``utsns=host`` will share the host's hostname.",
        "example": "container#0"
    },
    {
        "keyword": "privileged",
        "at": True,
        "convert": "tristate",
        "text": "Give extended privileges to the container.",
        "example": "container#0"
    },
    {
        "keyword": "interactive",
        "at": True,
        "convert": "tristate",
        "text": "Keep stdin open even if not attached. To use if the container entrypoint is a shell.",
    },
    {
        "keyword": "tty",
        "at": True,
        "convert": "tristate",
        "text": "Allocate a pseudo-tty.",
    },
    {
        "keyword": "name",
        "at": True,
        "default_text": "<autogenerated>",
        "text": "The name to assign to the container on docker run. If none is specified a ``<namespace>..<name>.container.<rid idx>`` name is automatically assigned.",
        "example": "osvcprd..rundeck.container.db"
    },
    {
        "keyword": "image",
        "at": True,
        "required": True,
        "text": "The docker image pull, and run the container with.",
        "example": "83f2a3dd2980 or ubuntu:latest"
    },
    {
        "keyword": "image_pull_policy",
        "at": True,
        "required": False,
        "default": "once",
        "candidates": ["once", "always"],
        "text": "The docker image pull policy. ``always`` pull upon each container start, ``once`` pull if not already pulled (default).",
        "example": "once"
    },
    {
        "keyword": "command",
        "protoname": "run_command",
        "at": True,
        "convert": "shlex",
        "text": "The command to execute in the docker container on run.",
        "example": "/opt/tomcat/bin/catalina.sh"
    },
    {
        "keyword": "run_args",
        "at": True,
        "convert": "expanded_shlex",
        "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"
    },
    {
        "keyword": "pull_timeout",
        "convert": "duration",
        "at": True,
        "text": "Wait for <duration> before declaring the container action a failure.",
        "default": "2m",
        "example": "2m"
    },
    {
        "keyword": "start_timeout",
        "convert": "duration",
        "at": True,
        "text": "Wait for <duration> before declaring the container action a failure.",
        "default": "5",
        "example": "5"
    },
    {
        "keyword": "stop_timeout",
        "convert": "duration",
        "at": True,
        "text": "Wait for <duration> before declaring the container action a failure.",
        "default": "120",
        "example": "180"
    },
    {
        "keyword": "registry_creds",
        "at": True,
        "text": "The name of a secret in the same namespace having a config.json key which value is used to login to the container image registry. If not specified, the node-level registry credential store is used.",
        "example": "creds-registry-opensvc-com"
    },
    KW_NO_PREEMPT_ABORT,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]
DEPRECATED_KEYWORDS = {
    "container.docker.run_image": "image",
    "container.docker.run_command": "command",
    "container.docker.net": "netns",
    "container.oci.run_image": "image",
    "container.oci.run_command": "command",
    "container.oci.net": "netns",
}
REVERSE_DEPRECATED_KEYWORDS = {
    "container.docker.image": "run_image",
    "container.docker.command": "run_command",
    "container.docker.netns": "net",
    "container.oci.image": "run_image",
    "container.oci.command": "run_command",
    "container.oci.netns": "net",
}

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
    deprecated_keywords=DEPRECATED_KEYWORDS,
    reverse_deprecated_keywords=REVERSE_DEPRECATED_KEYWORDS,
    driver_basename_aliases=DRIVER_BASENAME_ALIASES,
)


ATTR_MAP = {
    "hostname": {
        "attr": "vm_hostname",
        "path": ["Config", "Hostname"],
    },
    "privileged": {
        "path": ["HostConfig", "Privileged"],
    },
    "interactive": {
        "path": ["Config", "OpenStdin"],
    },
    "entrypoint": {
        "path": ["Config", "Entrypoint"],
        "cmp": "cmp_entrypoint",
    },
    "tty": {
        "path": ["Config", "Tty"],
    },
    #    "rm": {
    #        "path": ["HostConfig", "AutoRemove"],
    #    },
    "netns": {
        "path": ["HostConfig", "NetworkMode"],
        "cmp": "cmp_ns",
    },
    "pidns": {
        "path": ["HostConfig", "PidMode"],
        "cmp": "cmp_ns",
    },
    "ipcns": {
        "path": ["HostConfig", "IpcMode"],
        "cmp": "cmp_ns",
    },
    "utsns": {
        "path": ["HostConfig", "UTSMode"],
        "cmp": "cmp_ns",
    },
    "userns": {
        "path": ["HostConfig", "UsernsMode"],
        "cmp": "cmp_ns",
    },
}

def driver_capabilities(node=None):
    data = []
    if dockerlib.has_docker(["docker", "docker.io"]):
        data += [
            "container.docker",
            "container.docker.registry_creds",
            "container.docker.signal",
        ]
    return data

def alarm_handler(signum, frame):
    raise KeyboardInterrupt


class ContainerDocker(BaseContainer):
    """
    Docker container resource driver.
    """
    default_net = "none"
    dns_option_option = "--dns-option"

    def __init__(self,
                 name="",
                 type="container.docker",
                 hostname=None,
                 image=None,
                 image_pull_policy="once",
                 run_command=None,
                 run_args=None,
                 detach=True,
                 entrypoint=None,
                 read_only=None,
                 rm=None,
                 user=None,
                 netns=None,
                 userns=None,
                 pidns=None,
                 utsns=None,
                 ipcns=None,
                 privileged=None,
                 interactive=None,
                 tty=None,
                 volume_mounts=None,
                 devices=None,
                 environment=None,
                 secrets_environment=None,
                 configs_environment=None,
                 registry_creds=None,
                 guestos="Linux",
                 pull_timeout=120,
                 **kwargs):
        super(ContainerDocker, self).__init__(
            name="",
            type=type,
            guestos=guestos,
            **kwargs
        )
        self.user_defined_name = name
        self.hostname = hostname
        self.image = image
        self.image_pull_policy = image_pull_policy
        self.pull_timeout = pull_timeout
        self.run_command = run_command
        self.run_args = run_args
        self.detach = detach
        self.read_only = read_only
        self.entrypoint = entrypoint
        self.rm = rm
        self.user = user
        self.netns = netns
        self.userns = userns
        self.pidns = pidns
        self.utsns = utsns
        self.ipcns = ipcns
        self.privileged = privileged
        self.interactive = interactive
        self.tty = tty
        self.volume_mounts = volume_mounts if volume_mounts else []
        self.devices = devices if devices else []
        self.volumes = {}
        self.environment = environment
        self.secrets_environment = secrets_environment
        self.configs_environment = configs_environment
        self.registry_creds = registry_creds
        if not self.detach:
            self.rm = True

    @lazy
    def lib(self):
        """
        Lazy allocator for the dockerlib object.
        """
        try:
            return self.svc.dockerlib
        except AttributeError:
            self.svc.dockerlib = dockerlib.DockerLib(self.svc)
            return self.svc.dockerlib

    def on_add(self):
        try:
            self.volume_options()
        except ex.Error:
            # volume not created yet
            pass
        try:
            self.device_options()
        except ex.Error:
            # volume not created yet
            pass

    def abort_start_ping(self, *args, **kwargs):
        return False

    def getaddr(self, *args, **kwargs):
        return

    @lazy
    def container_name(self):
        """
        Format a docker container name
        """
        if self.user_defined_name:
            return self.user_defined_name
        if self.svc.namespace:
            container_name = self.svc.namespace + ".."
        else:
            container_name = ""
        container_name += self.svc.name + '.' + self.rid
        return container_name.replace('#', '.')

    @lazy
    def container_label_id(self):
        """
        Format a docker container name
        """
        return "com.opensvc.id=%s.%s" % (self.svc.id, self.rid)

    @lazy
    def name(self):  # pylint: disable=method-hidden
        return self.container_name

    @lazy
    def container_id(self):
        return self.lib.get_container_id(self)

    @lazy
    def label(self):  # pylint: disable=method-hidden
        return "docker " + self.lib.image_userfriendly_name(self)

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)

    def rcmd(self, cmd):
        cmd = self.lib.docker_cmd + ['exec', '-t', self.container_name] + cmd
        return justcall(cmd)

    def enter(self):
        for cmd in [["/bin/bash"], ["/bin/sh"]]:
            try:
                self.execute(interactive=True, tty=True, cmd=cmd)
                return
            except ValueError:
                continue
            else:
                return

    def execute(self, interactive=False, tty=False, cmd=None):
        import subprocess
        xcmd = self.lib.docker_cmd + ["exec"]
        if interactive:
            xcmd.append("-i")
        if tty:
            xcmd.append("-t")
        xcmd.append(self.container_name)
        xcmd += cmd
        proc = subprocess.Popen(xcmd)
        proc.communicate()
        if proc.returncode in [126, 127]:
            # executable not found
            raise ValueError

    def rcp_from(self, src, dst):
        """
        Copy <src> from the container's rootfs to <dst> in the host's fs.
        """
        cmd = self.lib.docker_cmd + ["cp", self.container_name + ":" + src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%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.
        """
        cmd = self.lib.docker_cmd + ["cp", src, self.container_name + ":" + dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%s' execution error:\n%s" % (" ".join(cmd), err))
        return out, err, ret

    def files_to_sync(self):
        """
        Files to contribute to sync#i0.
        """
        return []

    def operational(self):
        """
        Always return True for docker containers.
        """
        return True

    @lazy
    def vm_hostname(self):
        """
        The container hostname
        """
        try:
            hostname = self.hostname
        except ex.OptNotFound:
            hostname = ""
        return hostname

    def wait_for_startup(self):
        if not self.detach:
            return
        super(ContainerDocker, self).wait_for_startup()

    def wait_for_removed(self):
        def removed():
            self.is_up_clear_cache()
            if self.container_id:
                return False
            return True

        self.wait_for_fn(removed, 10, 1, errmsg="waited too long for container removal")

    def container_rm(self):
        """
        Remove the resource docker instance.
        """
        if self.container_id is None:
            return
        if not self.lib.docker_running():
            return
        if self.lib.docker_cmd is None:
            raise ex.Error("docker executable not found")
        cmd = self.lib.docker_cmd + ['rm', self.container_id]
        out, err, ret = justcall(cmd)
        if ret != 0:
            if "No such container" in err:
                pass
            elif "no such file" in err:
                pass
            elif "removal" in err and "already in progress" in err:
                self.wait_for_removed()
            else:
                self.log.info(" ".join(cmd))
                raise ex.Error(err)
        else:
            self.log.info(" ".join(cmd))
        self.is_up_clear_cache()

    def client_config(self):
        if not self.registry_creds:
            return []
        self.install_client_config()
        args = self.lib.client_config_args(self.registry_creds_path)
        return args

    def install_client_config(self):
        buff = self.registry_creds_sec.decode_key("config.json")
        makedirs(os.path.dirname(self.registry_creds_path), uid=0, gid=0, mode=0o600)
        self.registry_creds_sec.write_key(self.registry_creds_path, buff, uid=0, gid=0, mode=0o600)

    @lazy
    def registry_creds_path(self):
        if not self.registry_creds:
            return
        var_d = svc_pathvar(self.registry_creds_sec.path)
        return os.path.join(var_d, "registry_creds", "config.json")

    @lazy
    def registry_creds_sec(self):
        if not self.registry_creds:
            return
        return factory("sec")(self.registry_creds, namespace=self.svc.namespace, volatile=True)

    def docker(self, action):
        """
        Wrap docker commands to honor <action>.
        """
        if self.lib.docker_cmd is None:
            raise ex.Error("docker executable not found")
        sec_env = {}
        cfg_env = {}
        cmd = self.lib.docker_cmd + []
        if action == "start":
            if self.lib.config_args_position_head:
                cmd += self.client_config()
            if self.rm:
                self.container_rm()
            if self.container_id is None:
                self.is_up_clear_cache()
            if self.container_id is None:
                try:
                    image_id = self.lib.get_image_id(self.image)
                except ValueError as exc:
                    raise ex.Error(str(exc))
                if image_id is None:
                    if not self.registry_creds:
                        self.lib.docker_login(self.image)
                    if self.start_timeout:
                        self.log.info("push start timeout to %s (cached) + %s (pull)",
                                      print_duration(self.start_timeout),
                                      print_duration(self.pull_timeout))
                        self.start_timeout += self.pull_timeout
                sec_env = self.kind_environment_env("sec", self.secrets_environment)
                cfg_env = self.kind_environment_env("cfg", self.configs_environment)
                cmd += ["run"]
                if not self.lib.config_args_position_head:
                    cmd += self.client_config()
                cmd += self._add_run_args()
                for var in sec_env:
                    cmd += ["-e", var]
                for var in cfg_env:
                    cmd += ["-e", var]
                cmd += [self.image]
                if self.run_command:
                    cmd += self.run_command
            else:
                cmd += ["start", self.container_id]
        elif action == "stop":
            cmd += ["stop", self.container_id]
        elif action == "kill":
            cmd += ["kill", self.container_id]
        else:
            self.log.error("unsupported docker action: %s", action)
            return 1

        env = {}
        env.update(os.environ)
        env.update(sec_env)
        env.update(cfg_env)

        if action == "start":
            timeout = self.start_timeout or None
        else:
            timeout = None

        concurrent_futures = get_concurrent_futures()
        # defines max_workers to avoid ThreadPoolExecutor(max_worker=None) with python 3.4 to avoir following
        # exception:
        #   File "/usr/lib64/python3.4/concurrent/futures/thread.py", line 116, in _adjust_thread_count
        #     if len(self._threads) < self._max_workers:
        # TypeError: unorderable types: int() < NoneType()
        #
        try:
            from os import cpu_count
        except ImportError:
            from multiprocessing import cpu_count
        except ImportError:
            cpu_count = lambda: 1

        max_workers = min(32, (cpu_count() or 1) + 4)
        with concurrent_futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
            future = executor.submit(self.vcall, cmd, warn_to_info=True, env=env)
            try:
                ret = future.result(timeout=timeout)[0]
            except concurrent_futures.TimeoutError:
                self.log.error("%s timeout exceeded", print_duration(timeout))
                if action == "start":
                    cmd = self.lib.docker_cmd + ["kill", self.container_name]
                    self.vcall(cmd, warn_to_info=True, env=env)
                ret = 1

        if ret != 0:
            raise ex.ExecError("container command exec error", ret)

        if action == "start":
            self.is_up_clear_cache()
        elif action in ("stop", "kill"):
            if self.rm:
                self.container_rm()
            self.is_up_clear_cache()
            self.lib.docker_stop()

    def device_options(self, errors="raise"):
        if self.run_args is None:
            args = []
        else:
            args = [] + self.run_args
        devices = []
        for arg in chain(get_options("--device", args), iter(self.devices)):
            elements = arg.split(":")
            if not elements or len(elements) not in (2, 3):
                continue
            if not elements[0].startswith(os.sep):
                # vol service
                elements[0], vol = self.replace_volname(elements[0], mode="blk", strict=False, errors=errors)
                if not elements[0]:
                    continue
                devices.append(":".join(elements))
            elif not os.path.exists(elements[0]):
                # host path
                raise ex.Error("source dir of mapping %s does not "
                                  "exist" % arg)
            else:
                devices.append(arg)
        return devices

    def volume_options(self, errors="raise"):
        if self.run_args is None:
            args = []
        else:
            args = [] + self.run_args
        volumes = []
        dsts = []
        for volarg in chain(get_options("-v", args), get_options("--volume", args), iter(self.volume_mounts)):
            elements = volarg.split(":")
            if not elements or len(elements) not in (2, 3):
                continue
            if elements[1] in dsts:
                raise ex.Error("different volume mounts use the same destination "
                                  "mount point: %s" % elements[1])
            if not elements[0].startswith(os.sep):
                # vol service
                wants_ro = False
                elements[0], vol = self.replace_volname(elements[0], strict=False, errors=errors)
                if not elements[0]:
                    continue
                try:
                    options = elements[2].split(",")
                    if 'ro' in options:
                        wants_ro = True
                    options = drop_option("ro", options)
                    options = drop_option("rw", options)
                    del elements[2]
                except Exception:
                    options = []
                if wants_ro or (vol and vol.volsvc.access.startswith("ro")):
                    options.insert(0, "ro")
                else:
                    options.insert(0, "rw")
                elements.append(",".join(options))
                volumes.append(":".join(elements))
            elif not os.path.exists(elements[0]):
                # host path
                raise ex.Error("source dir of mapping %s does not "
                                  "exist" % volarg)
            else:
                volumes.append(volarg)
            dsts.append(elements[1])
        return volumes

    def environment_options(self):
        if self.environment is None:
            return []
        options = []
        for mapping in self.environment:
            try:
                var, val = mapping.split("=", 1)
            except Exception as exc:
                self.log.info("ignored environment mapping %s: %s", mapping, exc)
                continue
            var = var.upper()
            options += ["-e", "%s=%s" % (var, val)]
        return options

    def cgroup_options(self):
        if not self.lib.docker_min_version("1.7"):
            return []
        if self.lib.docker_info.get("CgroupDriver") != "cgroupfs":
            return []
        return ["--cgroup-parent", self.cgroup_dir]

    def _add_run_args(self, errors="raise"):
        if self.run_args is None:
            args = []
        else:
            args = [] + self.run_args

        args = drop_option("-d", args, drop_value=False)
        args = drop_option("--detach", args, drop_value=False)
        if self.detach:
            args += ["--detach"]

        # drop user specified --name. we set ours already
        args = drop_option("--name", args, drop_value=True)
        args = drop_option("-n", args, drop_value=True)
        args += ['--name=' + self.container_name]
        args += ['--label=' + self.container_label_id]
        args += ['--label=com.opensvc.path=' + self.svc.path]
        args += ['--label=com.opensvc.namespace=%s' % (self.svc.namespace if self.svc.namespace else "root")]
        args += ['--label=com.opensvc.name=' + self.svc.name]
        args += ['--label=com.opensvc.kind=' + self.svc.kind]
        args += ['--label=com.opensvc.rid=' + self.rid]

        args = drop_option("--hostname", args, drop_value=True)
        args = drop_option("-h", args, drop_value=True)
        if not self.netns or (self.netns != "host" and not self.netns.startswith("container#")):
            # only allow hostname setting if the container has a private netns
            if self.vm_hostname:
                args += ["--hostname", self.vm_hostname]
            elif not self.run_args:
                pass
            else:
                hostname = get_option("--hostname", self.run_args, boolean=False)
                if not hostname:
                    hostname = get_option("-h", self.run_args, boolean=False)
                if hostname:
                    args += ["--hostname", hostname]

        if self.entrypoint:
            args = drop_option("--entrypoint", args, drop_value=True)
            args += ["--entrypoint", self.entrypoint]

        if self.netns:
            args = drop_option("--net", args, drop_value=True)
            args = drop_option("--network", args, drop_value=True)
            if self.netns.startswith("container#"):
                res = self.svc.get_resource(self.netns)
                if res is not None:
                    args += ["--net=container:" + res.container_name]
                elif errors == "raise":
                    raise ex.Error("resource %s, referenced in %s.netns, does not exist" % (self.netns, self.rid))
            else:
                args += ["--net=" + self.netns]
        elif not has_option("--net", args) and self.default_net:
            args += ["--net=" + self.default_net]

        if self.pidns:
            args = drop_option("--pid", args, drop_value=True)
            if self.pidns.startswith("container#"):
                res = self.svc.get_resource(self.pidns)
                if res is not None:
                    args += ["--pid=container:" + res.container_name]
                elif errors == "raise":
                    raise ex.Error("resource %s, referenced in %s.pidns, does not exist" % (self.pidns, self.rid))
            else:
                args += ["--pid=" + self.pidns]

        if self.ipcns:
            args = drop_option("--ipc", args, drop_value=True)
            if self.ipcns.startswith("container#"):
                res = self.svc.get_resource(self.ipcns)
                if res is not None:
                    args += ["--ipc=container:" + res.container_name]
                elif errors == "raise":
                    raise ex.Error("resource %s, referenced in %s.ipcns, does not exist" % (self.ipcns, self.rid))
            else:
                args += ["--ipc=" + self.ipcns]

        if self.utsns == "host":
            args = drop_option("--uts", args, drop_value=True)
            args += ["--uts=host"]

        if self.userns is not None:
            args = drop_option("--userns", args, drop_value=True)
        if self.userns == "host":
            args += ["--userns=host"]

        if self.user is not None:
            args = drop_option("--user", args, drop_value=True)
            args += ["--user", self.user]

        if self.privileged is not None:
            args = drop_option("--privileged", args, drop_value=False)
        if self.privileged:
            args += ["--privileged"]

        if self.read_only is not None:
            args = drop_option("--read-only", args, drop_value=False)
        if self.read_only:
            args += ["--read-only"]

        if self.interactive is not None:
            args = drop_option("--interactive", args, drop_value=False)
            args = drop_option("-i", args, drop_value=False)
        if self.interactive:
            args += ["--interactive"]

        if self.tty is not None:
            args = drop_option("--tty", args, drop_value=False)
            args = drop_option("-t", args, drop_value=False)
        if self.tty:
            args += ["--tty"]

        drop_option("--rm", args, drop_value=False)

        drop_option("-v", args, drop_value=True)
        drop_option("--volume", args, drop_value=True)
        for vol in self.volume_options(errors=errors):
            args.append("--volume=%s" % vol)

        drop_option("--device", args, drop_value=True)
        for dev in self.device_options(errors=errors):
            args.append("--device=%s" % dev)

        args += self.cgroup_options()

        def dns_opts(args):
            if not self.svc.node.dns or "--dns" in args:
                return []
            net = get_option("--net", args, boolean=False)
            if net and net.startswith("container:"):
                return []
            if net == "host":
                return []
            l = []
            for dns in self.svc.node.dns:
                l += ["--dns", dns]
            for search in self.dns_search():
                l += ["--dns-search", search]
            dns_options = [o for o in get_options(self.dns_option_option, args)]
            args = drop_option(self.dns_option_option, args, drop_value=True)
            for o in self.dns_options(dns_options):
                l += [self.dns_option_option, o]
            return l

        args += dns_opts(args)
        args += self.environment_options()
        return args

    @lazy
    def cgroup_dir(self):
        return os.sep + self.svc.pg.get_cgroup_relpath(self)

    def image_pull(self):
        self.client_config()
        self.lib.image_pull(self.image, config=self.registry_creds_path)

    def container_start(self):
        self.docker('start')

    def _start(self):
        self.lib.docker_start()
        super(ContainerDocker, self).start()

    def provision(self):
        super(ContainerDocker, self).provision()
        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]))
        super(ContainerDocker, self).unprovision()
        self.container_rm()

    def provisioner_shared_non_leader(self):
        if self.lib.docker_daemon_private:
            return
        try:
            image_id = self.lib.get_image_id(self.image)
            return
        except ValueError as exc:
            pass
        try:
            self.image_pull()
            return
        except ex.Error as exc1:
            self.log.warning("could not pull image '%s': %s", self.image, str(exc1).strip())

    def start(self):
        if self.image_pull_policy == "always":
            self.image_pull()
        try:
            self._start()
        except KeyboardInterrupt:
            if not self.detach:
                self.is_up_clear_cache()
                self.container_forcestop()
                self.container_rm()
                raise ex.Error("timeout")
            else:
                raise ex.AbortAction
        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 _stop(self):
        if not self.lib.docker_running():
            return
        super(ContainerDocker, self).stop()
        if self.rm:
            self.container_rm()
        self.is_up_clear_cache()
        self.lib.docker_stop()

    def _info(self):
        """
        Return keys to contribute to resinfo.
        """
        data = self.lib._info()
        data.append([self.rid, "run_args", " ".join(self._add_run_args(errors="ignore"))])
        data.append([self.rid, "rm", str(self.rm)])
        data.append([self.rid, "container_name", str(self.container_name)])
        if self.netns:
            data.append([self.rid, "netns", str(self.netns)])
        if self.run_command:
            data.append([self.rid, "run_command", " ".join(self.run_command)])
        return data

    def _status_container_image(self, inspect_data):
        try:
            image_id = self.lib.get_image_id(self.image)
        except ValueError as exc:
            self.status_log(str(exc))
            return
        try:
            running_image_id = re.sub("^sha256:", "", inspect_data["Image"])
        except KeyError:
            return
        if image_id is None:
            self.status_log("image '%s' is not pulled yet." % self.image)
        elif image_id != running_image_id:
            self.status_log("the current container is based on image '%s' "
                            "instead of '%s'" % (running_image_id, image_id))

    def cmp_entrypoint(self, current, target, data):
        try:
            alt_target = shlex.split(target)
            if current == target or current == alt_target:
                return
            self.status_log("%s=%s, but %s=%s" % \
                            (".".join(data["path"]), current, data["attr"], target))
        except Exception:
            pass

    def cmp_ns(self, current, target, data):
        try:
            res = self.svc.get_resource(self.netns)
            target = "container:" + res.container_name
            if current == target:
                return
            target = "container:" + res.container_id
            if current != target:
                self.status_log("%s=%s, but %s=%s" % \
                                (".".join(data["path"]), current, data["attr"], target))
        except Exception:
            pass

    def _status_inspect(self):
        try:
            inspect_data = self.lib.docker_inspect(self.container_id)
        except Exception:
            return
        self._status_container_image(inspect_data)

        def get(path, data=None):
            try:
                return get(path[1:], data[path[0]])
            except IndexError:
                return data[path[0]]

        def validate(attr, data):
            try:
                current = get(data["path"], inspect_data)
            except KeyError:
                return
            _attr = data.get("attr", attr)
            _fn = data.get("cmp")
            target = getattr(self, _attr)
            if not target:
                return
            if _fn:
                _fn = getattr(self, _fn)
                return _fn(current, target, data)
            if current != target:
                self.status_log("%s=%s, but %s=%s" % \
                                (".".join(data["path"]), current, attr, target))

        if get(["State", "Status"], inspect_data) != "running":
            return
        for attr, data in ATTR_MAP.items():
            validate(attr, data)

    def _status(self, verbose=False):
        if not self.detach:
            return core.status.NA
        try:
            self.lib.docker_exe
        except ex.InitError as exc:
            self.status_log(str(exc), "warn")
            return core.status.DOWN
        if not self.lib.docker_running():
            self.status_log("docker daemon is not running", "info")
            return core.status.DOWN
        sta = super(ContainerDocker, self)._status(verbose)
        self._status_inspect()
        return sta

    def send_signal(self, sig):
        if not self.is_up():
            return
        cmd = self.lib.docker_cmd + ["kill", "-s", str(sig), self.container_id]
        self.vcall(cmd)

    def container_forcestop(self):
        self.docker('kill')

    def _ping(self):
        return utilities.ping.check_ping(self.addr, timeout=1)

    def is_down(self):
        return not self.is_up()

    def is_up_clear_cache(self):
        self.unset_lazy("container_id")
        unset_lazy(self.lib, "container_ps")
        unset_lazy(self.lib, "running_instance_ids")
        unset_lazy(self.lib, "container_by_label")
        unset_lazy(self.lib, "container_by_name")
        unset_lazy(self.lib, "containers_inspect")
        unset_lazy(self.lib, "images")

    def is_up(self):
        if self.lib.docker_daemon_private and \
                self.lib.container_data_dir is None:
            self.status_log("DEFAULT.container_data_dir must be defined")

        if not self.lib.docker_running():
            return False

        if self.container_id is None:
            self.status_log("can not find container id", "info")
            return False
        if self.container_id in self.lib.get_running_instance_ids():
            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

    def container_pid(self):
        try:
            data = self.lib.docker_inspect(self.container_id)
            return data["State"]["Pid"]
        except (IndexError, KeyError):
            return

    def container_sandboxkey(self):
        try:
            data = self.lib.docker_inspect(self.container_id)
            return data["NetworkSettings"]["SandboxKey"]
        except (AttributeError, IndexError, KeyError):
            return

    def cni_containerid(self):
        """
        Used by ip.cni
        """
        return self.container_pid()

    def cni_netns(self):
        """
        Used by ip.cni
        """
        return self.container_sandboxkey()

    def is_provisioned(self):
        return True

    def post_provision_start(self):
        self._start()
        self.status(resfresh=True)

    def pre_provision_stop(self):
        self._stop()
        self.status(resfresh=True)
   0707010001f340000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/ovm 0707010001f341000081a40000000000000000000000016a100daf00001585000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/ovm/__init__.py import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_SNAP, \
    KW_SNAPOF, \
    KW_VIRTINST, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.fcache import fcache

DRIVER_GROUP = "container"
DRIVER_BASENAME = "ovm"
KEYWORDS = [
    {
        "keyword": "uuid",
        "at": True,
        "required": True,
        "text": "The virtual machine unique identifier used to pass commands on the VM."
    },
    KW_SNAP,
    KW_SNAPOF,
    KW_VIRTINST,
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("xm") and os.path.exists("/OVS"):
        data.append("container.ovm")
    return data


class ContainerOvm(BaseContainer):
    def __init__(self, uuid=None, **kwargs):
        super(ContainerOvm, self).__init__(type="container.ovm", **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" % (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 utilities.ping.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.Error("no configuration file found in repositories")
        return l[0]

    def _migrate(self):
        cmd = ['xm', 'migrate', '-l', self.uuid, self.svc.options.to]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_start(self):
        cf = self.find_vmcf()
        cmd = ['xm', 'create', cf]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_stop(self):
        cmd = ['xm', 'shutdown', self.uuid]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def container_forcestop(self):
        cmd = ['xm', 'destroy', self.uuid]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

   0707010001f34e000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/resource/container/zone    0707010001f34f000081a40000000000000000000000016a100daf0000ae76000000e600010003ffffffffffffffff0000004b00000000root/usr/share/opensvc/opensvc/drivers/resource/container/zone/__init__.py    import os
import stat
import time
from datetime import datetime
from shutil import copy

import core.status
import core.exceptions as ex
import utilities.subsystems.zone
import utilities.lock
import utilities.os.sunos
from env import Env
from utilities.lazy import lazy
from utilities.net.converters import to_cidr, cidr_to_dotted
from utilities.net.getaddr import getaddr
from utilities.subsystems.zfs import zfs_setprop, Dataset
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.mounts import Mounts
from utilities.proc import justcall, qcall, which
from .. import \
    BaseContainer, \
    KW_SNAP, \
    KW_SNAPOF, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV

SYSIDCFG="/etc/sysidcfg"
ZONECFG = "/usr/sbin/zonecfg"
ZONEADM = "/usr/sbin/zoneadm"
ZLOGIN = "/usr/sbin/zlogin"
PGREP = "/usr/bin/pgrep"
PWAIT = "/usr/bin/pwait"
INIT = "/sbin/init"
SVCS = "/usr/bin/svcs"
SYS_UNCONFIG = "/usr/sbin/sys-unconfig"
MULTI_USER_SMF = "svc:/milestone/multi-user:default"
VALID_ACTIONS = [
    "ready",
    "boot",
    "shutdown",
    "halt",
    "attach",
    "detach",
    "install",
    "clone"
]

DRIVER_GROUP = "container"
DRIVER_BASENAME = "zone"
KEYWORDS = [
    {
        "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",
    },
    {
        "keyword": "zonepath",
        "at": True,
        "text": "The zone path used to provision the container.",
        "provisioning": True,
    },
    {
        "keyword": "container_origin",
        "text": "The origin container having the reference container disk files.",
        "provisioning": True
    },
    {
        "keyword": "rootfs",
        "text": "Sets the root fs directory of the container",
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "sc_profile",
        "text": "The system configuration profile xml file for container provisioning. If not set, a configuration profile will be automatically created.",
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "ai_manifest",
        "text": "The Automated Installer manifest xml file for container provisioning. If not set, default manifest will be used.",
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "brand",
        "text": "The zone brand. If not set default brand will be used",
        "candidates": ["solaris", "solaris10", "native"],
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "install_archive",
        "text": "The install archive to use during zonedm install '-a <archive>'. If both container_origin and install_archive are set, but container_origin is not yet provisioned, container_origin will be created from <install_archive>.",
        "required": False,
        "provisioning": True
    },
    KW_SNAP,
    KW_SNAPOF,
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)


PROVISIONED_STATES = ['installed', 'running']


def driver_capabilities(node=None):
    data = []
    if which("zoneadm"):
        data.append("container.zone")
    if os.path.exists('/etc/zones/SYSsolaris.xml'):
        data.append("container.zone.brand-solaris")
    elif os.path.exists('/etc/zones/SUNWdefault.xml'):
        data.append("container.zone.brand-native")
    if os.path.exists('/etc/zones/SYSsolaris10.xml'):
        data.append("container.zone.brand-solaris10")
    return data

class ContainerZone(BaseContainer):
    """
    Zone container resource driver.
    """
    def __init__(self,
                 delete_on_stop=False,
                 zonepath=None,
                 container_origin=None,
                 rootfs=None,
                 snap=None,
                 snapof=None,
                 sc_profile=None,
                 ai_manifest=None,
                 provision_net_type="anet",
                 brand=None,
                 install_archive=None,
                 **kwargs):
        super(ContainerZone, self).__init__(type="container.zone", **kwargs)
        self.sysidcfg = None
        self.delete_on_stop = delete_on_stop
        self.delayed_noaction = True
        self.container_origin = container_origin
        self.snapof = snapof
        self.snap = snap
        self.kw_zonepath = zonepath
        self.sc_profile = sc_profile
        self.ai_manifest = ai_manifest
        self.provision_net_type = provision_net_type
        if self.has_capability("container.zone.brand-solaris"):
            self.default_brand = 'solaris'
        elif self.has_capability("container.zone.brand-native"):
            self.default_brand = 'native'
        else:
            self.default_brand = None
        self.boot_config_file = None
        self.kw_brand = brand
        self.install_archive = install_archive

    @lazy
    def clone(self):
        if not self.snap:
            return "rpool/zones/" + self.name
        return self.snap

    @lazy
    def runmethod(self):
        return [ZLOGIN, self.name]

    @lazy
    def zone_cf(self):
        return "/etc/zones/%s.xml" % self.name

    @lazy
    def osver(self):
        return utilities.os.sunos.get_solaris_version()

    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.Error(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.Error("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.Error("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.Error("set zonepath command not found in %s" % fpath)

    def zonecfg(self, zonecfg_args=None):
        zonecfg_args = zonecfg_args or []
        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.Error(msg)
        else:
            msg = "%s done status: %i\n%s" % (" ".join(cmd), ret, out)
            self.log.info(msg)
        self.zone_refresh()
        return ret

    def is_zone_locked(self):
        zonelock = "/system/volatile/zones/%s.zoneadm.lock" % self.name
        if not os.path.exists(zonelock):
            self.log.debug("zone %s lockfile does not exist" % self.name)
            return False
        import fcntl
        locked = None
        try:
            fd = open(zonelock, "a", 1)
            fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
            self.log.debug("zone %s is not locked" % self.name)
            locked = False
        except IOError:
            self.log.debug("zone %s is locked" % self.name)
            locked = True
        finally:
            try:
                fcntl.flock(fd, fcntl.LOCK_UN)
            except:
                pass
            try:
                fd.close()
            except:
                pass
        return locked

    def is_zone_unlocked(self):
        return not self.is_zone_locked()

    def zoneadm(self, action, option=None):
        if action in VALID_ACTIONS:
            cmd = [ZONEADM, "-z", self.name, action]
        else:
            self.log.error("unsupported zone action: %s", action)
            return 1
        if option is not None:
            cmd += option

        begin = datetime.now()
        if os.environ.get("OSVC_ACTION_ORIGIN") == "daemon" and which("su"):
            # the zoneadm command gives an error when executed from osvcd.
            # su creates a clean execution context and makes zoneadm succeed.
            cmd = ["su", "root", "-c", " ".join(cmd)]
        self.log.info("%s", " ".join(cmd))
        ret = self.lcall(cmd, env={})
        duration = datetime.now() - begin
        if ret != 0:
            raise ex.Error("%s failed in %s - ret %i" % (" ".join(cmd), duration, ret))
        else:
            self.log.info("%s done in %s - ret %i" % (" ".join(cmd), duration, ret))
        self.zone_refresh()
        return ret

    def set_zonepath_perms(self):
        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[stat.ST_MODE]
        if (stat.S_IWOTH & mode) or (stat.S_IXOTH & mode) or (stat.S_IROTH & mode) or \
           (stat.S_IWGRP & mode) or (stat.S_IXGRP & mode) or (stat.S_IRGRP & mode):
            self.vcall(["chmod", "700", self.zonepath])

    def rcp_from(self, src, dst):
        src = os.path.realpath(self.zonepath + "/root/" + src)
        cmd = ["cp", src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%s' execution error:\n%s"%(" ".join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        dst = os.path.realpath(self.zonepath + "/root/" + dst)
        cmd = ["cp", src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%s' execution error:\n%s"%(" ".join(cmd), err))
        return out, err, ret

    def attach(self):
        if self.state in ("installed", "ready", "running"):
            self.log.info("zone container %s already installed" % self.name)
            return
        elif self.state is None:
            cmd = [ZONECFG, "-z", self.name, "-f", self.zone_cfg_path()]
            ret, out, err = self.vcall(cmd)
            self.zone_refresh()
            if ret != 0:
                raise ex.Error
        options = []
        if utilities.os.sunos.get_solaris_version() >= 11.3:
            options += ["-x", "deny-zbe-clone"]
        try:
            self.umount_fs_in_zonepath()
            self.zoneadm("attach", options)
        except ex.Error:
            options.append("-F")
            self.zoneadm("attach", options)
        self.can_rollback = True

    def delete(self):
        if not self.delete_on_stop:
            return 0
        return self.zone_unconfigure()

    def zone_unconfigure(self):
        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)
        self.zone_refresh()
        if ret != 0:
            raise ex.Error
        return 0

    def detach(self):
        if self.state == "configured":
            self.log.info("zone container %s already detached/configured" % self.name)
            return 0
        elif self.state is None:
            self.log.info("zone container %s has no state, skip detach" % self.name)
            return 0
        self.wait_for_fn(self.is_zone_unlocked, self.stop_timeout, 2)
        try:
            self.zoneadm("detach")
        except ex.Error:
            self.zoneadm("detach", option=["-F"])
        self.zone_refresh()

    def ready(self):
        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):
        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
        if current_value == value:
            return True
        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.start_timeout, 2)

    def zone_boot(self):
        """
        Return 0 if zone is running else return self.zoneadm("boot")
        """
        if self.state == "running":
            self.log.info("zone container %s already running" % self.name)
            return
        self.zoneadm("boot")
        if self.state != "running":
            raise ex.Error("zone should be running")
        self.wait_multi_user()

    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
        """
        if self.state is None:
            self.log.info("skip zone %s halt, no such zone", self.name)
            return 0
        elif self.state == "running":
            ret, out, err = self.vcall([ZLOGIN, self.name, "/sbin/init", "0"])
        elif self.state not in ["shutting_down", "down", "installed"]:
            self.log.warning("unable to halt zone %s state %s", self.name, self.state)
            return 0

        for _ in range(self.stop_timeout):
            self.zone_refresh()
            if self.state == "installed":
                for t2 in range(self.stop_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)
        return self.zoneadm("halt")

    def container_start(self):
        self.zone_boot()

    def _status(self, verbose=False):
        if self.state == "running":
            return core.status.UP
        return core.status.DOWN

    def zone_refresh(self):
        self.unset_lazy("zone_data")
        self.unset_lazy("state")
        self.unset_lazy("brand")
        self.unset_lazy("zonepath")

    @lazy
    def state(self):
        if self.zone_data is None:
            self.log.info("zone %s does not exist" % self.name)
            return
        else:
            return self.zone_data.get("state")

    @lazy
    def zonepath(self):
        """
        method that returns zonepath
        from zoneadm output
        or zonecfg info
        or zonecfg exported info
        or from resource kw_zonepath value
        else return None (when creating zone2clone, we don't know zonepath before
        zonecfg create command launched)
        """
        if self.zone_data is not None:
            zp = self.zone_data.get("zonepath")
            if zp:
                return zp
        try:
            return self.get_zonepath_from_zonecfg_cmd()
        except ex.Error:
            pass
        try:
            return self.get_zonepath_from_zonecfg_export()
        except ex.Error:
            pass
        if self.kw_zonepath:
            return self.kw_zonepath
        else:
            return None

    @lazy
    def brand(self):
        if self.zone_data:
            return self.zone_data.get("brand")
        else:
            return None

    @lazy
    def zone_data(self):
        """
        Refresh Zone object attributes:
                state
                zonepath
                brand
        from zoneadm -z zonename list -p
        zoneid:zonename:state:zonepath:uuid:brand:ip-type
        """
        out, err, ret = justcall([ZONEADM, "-z", self.name, "list", "-p"])
        if ret != 0:
            return None
        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
        elif n_fields ==  11:
            # since 11.4.43.0.1.113.3:
            # -:pioupiou:installed:/pioupiou/solaris11:adxxxxxx-4cxx-420b-855a-a5111e7xxxxx:solaris:excl:-:::
            (zoneid, zonename, state, zonepath, uuid, brand, iptype, rw, macp, dummy, dummy) = l
        else:
            raise ex.Error("Unexpected zoneadm list output: %s"%out)
        if zonename != self.name:
            return None
        return dict(state=state, zonepath=zonepath, brand=brand)

    def is_running(self):
        """
        Return True if zone is running else False"
        """
        return self.state == "running"

    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
        return False

    def boot_and_wait_reboot(self):
        """
        Boot freshly installed zones, then wait for automatic zone reboot
            boot zone
            ensure for zone init process is running
            wait for 2 'system boot' (this is only usable on freshly installed zones)
            wait for zone running
            wait for zone operational

        We wait for 2 'system boot', this is only usable on freshly installed zones
        """
        def wait_boot_count(count, max_retries=240):
            retries = 0
            self.log.info('wait for %s boot count is %s (max retries %s)', self.name, count, max_retries)
            cmd = [ZLOGIN, self.name, 'last', 'reboot']
            while retries < max_retries:
                out, err, st = justcall(cmd)
                if st == 0:
                    reboots = len([line for line in out.split('\n') if 'system boot' in line])
                    self.log.info('%s boot count: %s', self.name, reboots)
                    if reboots >= count:
                        return True
                time.sleep(1)
                retries += 1

        self.log.info("wait for zone %s boot and reboot...", self.name)
        self.zone_boot()
        if self.is_running is False:
            raise ex.Error("zone is not running")
        cmd = [PGREP, "-z", self.name, "-f", INIT]
        out, err, st = justcall(cmd)
        if st != 0:
            raise ex.Error("fail to detect zone init process")
        wait_boot_count(2)
        self.wait_for_fn(self.is_up, self.start_timeout, 2)
        self.log.info("wait for zone operational")
        self.wait_for_fn(self.operational, self.start_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.
        """
        if self.zonepath == "/":
            # sanity check
            return

        mounts = 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", log=self.log)

    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.zone_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:
            super(ContainerZone, self).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:
            super(ContainerZone, self).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" % (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}

    def pre_unprovision_stop(self):
        self._stop()

    def post_provision_start(self):
        pass

    def update_ip_tags(self):
        if self.osver < 11.0:
            return
        ip_kws = []
        need_save = False
        if self.brand in ['native', 'solaris10']:
            # pickup only first rid because of sysidcfg only setup 1st nic
            rids = self.get_encap_ip_rids()[:1]
        else:
            rids = self.get_encap_ip_rids()
        for rid in rids:
            # Add mandatory tags for sol11 zones
            tags = self.get_encap_conf(rid, 'tags')
            for tag in ['noaction', 'noalias', 'exclusive']:
                if tag not in tags:
                    tags.add(tag)
                    need_save = True
            for tag in ['preboot', 'postboot']:
                if tag in tags:
                    tags.remove(tag)
                    need_save = True

            try:
                self.get_encap_conf(rid, 'gateway')
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                # Add nonrouted tag if no gateway provisioning keyword is passed
                tags.add("nonrouted")
                need_save = True

            ip_kws += ["%s.tags=%s" % (rid, ' '.join(tags))]
        if need_save and ip_kws:
            # update service env file
            self.svc.set_multi(ip_kws)

    def get_encap_ip_rids(self):
        return [rid for rid in self.svc.conf_sections(cat='ip')
                if 'encap' in self.get_encap_conf(rid, 'tags')]

    def get_encap_conf(self, rid, kw):
        return self.svc.oget(rid, kw, impersonate=self.name)

    def get_install_ipv4_interfaces(self):
        """
         returns list of InstallIpv4Interface tha will be used to create sc_profile
        """
        from .configuration_profile import InstallIpv4Interface
        ipv4_interfaces = []
        for rid in self.get_encap_ip_rids():
            try:
                ipdevext = self.get_encap_conf(rid, 'ipdevext')
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                ipdevext = 'v4'
            ipdev = self.get_encap_conf(rid, 'ipdev')
            ipv4_name = '%s/%s' % (ipdev, ipdevext)

            try:
                default_route = self.get_encap_conf(rid, 'gateway')
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                default_route = None

            try:
                netmask = to_cidr(self.get_encap_conf(rid, 'netmask'))
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                continue
            ipname = self.get_encap_conf(rid, 'ipname')
            addr = getaddr(ipname, True)
            ipv4_interface = InstallIpv4Interface(ipv4_name,
                                                  static_address='%s/%s' % (addr, netmask),
                                                  address_type='static',
                                                  default_route=default_route,
                                                  id=len(ipv4_interfaces))
            ipv4_interfaces.append(ipv4_interface)
        return ipv4_interfaces

    def get_sysidcfg_network_interfaces(self):
        network_interfaces = []
        for rid in self.get_encap_ip_rids():
            ipdev = self.get_encap_conf(rid, 'ipdev')
            try:
                default_route = self.get_encap_conf(rid, 'gateway')
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                default_route = None

            try:
                netmask_conf = self.get_encap_conf(rid, 'netmask')
            except (ex.RequiredOptNotFound, ex.OptNotFound):
                netmask_conf = None
            if netmask_conf is None:
                netmask = None
            else:
                netmask = cidr_to_dotted(to_cidr(netmask_conf))
            ipname = self.get_encap_conf(rid, 'ipname')
            addr = getaddr(ipname, True)
            if len(network_interfaces) == 0:
                network_interface = "network_interface=PRIMARY {primary hostname=%s\n" % self.name
                network_interface += "    default_route=%s\n" % default_route
            else:
                network_interface = "network_interface=%s {\n" % ipdev
            network_interface += "    ip_address=%s\n" % addr
            if netmask:
                network_interface += "    netmask=%s\n" % netmask
            network_interface += "    protocol_ipv6=no}\n"
            network_interfaces.append(network_interface)
        return network_interfaces

    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, search) 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 prepare_boot_config(self):
        if self.brand in ['solaris']:
            self.create_sc_profile()
            self.boot_config_file = self.sc_profile
        elif self.brand in ['solaris10']:
            self.create_sysidcfg()
            self.boot_config_file = self.sysidcfg
        elif self.brand in ['native']:
            self.create_sysidcfg()

    def install_boot_config(self):
        if self.brand in ['native'] and self.sysidcfg:
            sysidcfg_filename = self.zonepath + "/root" + SYSIDCFG
            self.log.info('create %s', sysidcfg_filename)
            copy(self.sysidcfg, sysidcfg_filename)
            os.chmod(sysidcfg_filename, 0o0600)

    def create_sc_profile(self):
        if self.sc_profile:
            if not os.path.exists(self.sc_profile):
                message = 'sc_profile %s does not exists' % self.sc_profile
                self.log.warning(message)
                raise ex.Error(message)
            self.log.info('using provided sc_profile %s', self.sc_profile)
            return
        else:
            self.sc_profile = os.path.join(self.var_d, 'sc_profile.xml')
            self.log.info('creating sc_profile %s', self.sc_profile)
        try:
            from .configuration_profile import ScProfile, InstallIpv4Interface
            domain, nameservers, searchs = self.get_ns()
            sc_profile = ScProfile(sc_profile_file=self.sc_profile)
            sc_profile.set_nodename(self.name)
            sc_profile.set_localtime('Europe/Paris')
            for ipv4_interface in self.get_install_ipv4_interfaces():
                sc_profile.add_ipv4_interface(ipv4_interface)
            sc_profile.set_environment({'LANG': 'C'})
            if searchs or nameservers:
                sc_profile.set_dns_client(searches=searchs, nameservers=nameservers)
                sc_profile.set_name_service_switch({'host': 'files dns'})
            sc_profile.write()
        except Exception as e:
            self.svc.save_exc()
            raise ex.Error("exception from %s: %s during create_sc_profile" % (e.__class__.__name__, e.__str__()))

    def set_sysidcfg_unconfig(self):
        self.sysidcfg = os.path.join(self.var_d, 'sysidcfg.unconfig')
        self.log.info('creating sysidcfg %s', self.sysidcfg)
        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 += "auto_reg=disable\n"
        contents += "nfs4_domain=dynamic\n"
        contents += "network_interface=NONE {hostname=%s}\n" % self.name
        contents += "name_service=NONE\n"
        with open(self.sysidcfg, "w") as sysidcfg_file:
            sysidcfg_file.write(contents)

    def create_sysidcfg(self):
        if self.sysidcfg:
            if not os.path.exists(self.sysidcfg):
                message = 'sysidcfg %s does not exists' % self.sysidcfg
                self.log.warning(message)
                raise ex.Error(message)
            self.log.info('using provided sysidcfg %s', self.sc_profile)
            return
        else:
            sysidcfg_network_interfaces = self.get_sysidcfg_network_interfaces()
            if not sysidcfg_network_interfaces:
                self.log.info('no network interface found, use sysidcfg unconfig')
                self.set_sysidcfg_unconfig()
                return
            self.sysidcfg = os.path.join(self.var_d, 'sysidcfg')
            self.log.info('creating sysidcfg %s', self.sysidcfg)
        try:
            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 += "auto_reg=disable\n"
            contents += "nfs4_domain=dynamic\n"
            for network_interface in sysidcfg_network_interfaces[:1]:
                contents += network_interface + '\n'
            domain, nameservers, searchs = self.get_ns()
            if not nameservers:
                name_service = "name_service=NONE\n"
            elif searchs and domain:
                name_service = "name_service=DNS {domain_name=%s" % domain
                name_service += "\n    name_server=%s" % ','.join(nameservers)
                name_service += "\n    search=%s" % ','.join(searchs)
                name_service += "\n    }\n"
            elif searchs:
                # use first entry of search as domain_name
                name_service = "name_service=DNS {domain_name=%s" % searchs[0]
                name_service += "\n    name_server=%s" % ','.join(nameservers)
                name_service += "\n    search=%s" % ','.join(searchs)
                name_service += "\n    }\n"
            elif domain:
                name_service = "name_service=DNS {domain_name=%s" % domain
                name_service += "\n    name_server=%s" % ','.join(nameservers)
                name_service += "\n    }\n"
            else:
                name_service = "name_service=NONE\n"
            contents += name_service

            with open(self.sysidcfg, "w") as sysidcfg_file:
                sysidcfg_file.write(contents)
        except Exception as exc:
            raise ex.Error("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):
        if not self.provision_net_type:
            return
        if self.provision_net_type in ['no-anet', 'no-net']:
            self.zonecfg(['remove -F %s' % self.provision_net_type.replace('no-', '')])
            return

        for rid in self.svc.conf_sections(cat='ip'):
            ipdev = self.svc.oget(rid, 'ipdev', impersonate=self.name)
            if not self.test_net_interface(ipdev):
                raise ex.Error("Missing interface: %s" % ipdev)
            if self.provision_net_type == 'anet':
                cmd = [ZONECFG, "-z", self.name, 'select anet lower-link=%s linkname=%s; info;end' % (ipdev, ipdev)]
                out, err, ret = justcall(cmd)
                if ret != 0:
                    self.zonecfg(['add anet; set lower-link=%s; set linkname=%s; end' % (ipdev, ipdev)])
            elif self.provision_net_type == 'net':
                cmd = [ZONECFG, "-z", self.name, 'select net physical=%s; info; end' % ipdev]
                out, err, ret = justcall(cmd)
                if ret != 0:
                    self.zonecfg(['add net; set physical=%s; end' % ipdev])

    def _brand_to_create(self):
        return self.kw_brand or self.default_brand

    def zone_configure(self):
        "Ensure zone is at least configured"
        if self.state is None:
            if self.kw_brand == 'native' and not self.has_capability("container.zone.brand-native"):
                raise ex.Error("node has no capability to create brand %s zone" % self.kw_brand)
            if self.container_origin:
                cmd = "create -t " + self.container_origin
            elif self.kw_brand and self.kw_brand != self.default_brand and self.kw_brand not in ['native']:
                cmd = "create -t SYS"+ self.kw_brand
            else:
                cmd = "create"

            if self.zonepath:
                cmd += "; set zonepath=" + self.zonepath

            self.zonecfg([cmd])
            self.zone_refresh()
            if self.state != "configured":
                raise ex.Error("zone %s is not configured" % self.name)

        if self.brand in ['solaris', 'solaris10']:
            try:
                self.zone_configure_net()
            except:
                raise ex.Error('Error during zone_configure_net')

    def install_origin(self):
        """
        verify if self.container_origin zone is installed
        else configure container_origin if required
        then install container_origin if required
        """
        if self.state == "installed":
            return
        self.provision_zone()
        if self.brand == "native":
            self.boot_and_wait_reboot()
        else:
            self.zone_boot()
        self.wait_multi_user()
        if self.brand == "native":
            self.log.info('call in zone %s %s' % (self.name, SYS_UNCONFIG))
            justcall([ZLOGIN, self.name, SYS_UNCONFIG], input='y\n')
        self.halt()
        if self.state != "installed":
            raise(ex.Error("zone %s is not installed" % (self.name)))

    def create_cloned_zone(self):
        if self.state == "configured":
            if self.boot_config_file:
                self.zoneadm("clone", ['-c', self.boot_config_file, self.container_origin])
            else:
                self.zoneadm("clone", [self.container_origin])
            self.update_ip_tags()
        self.zone_refresh()
        if self.state != "installed":
            raise(ex.Error("zone %s is not installed" % self.name))

    def create_snaped_zone(self):
        self.create_zonepath()
        self.zoneadm("attach", ["-F"])
        self.zone_refresh()

    def install_zone(self):
        zonepath = self.zonepath
        if zonepath and os.path.exists(zonepath):
            try:
                os.chmod(zonepath, 0o0700)
            except:
                pass
        args = []
        if self.boot_config_file:
            args = ['-c', self.boot_config_file]
        if self.ai_manifest and self.brand in ['solaris']:
            args += ['-m', self.ai_manifest]
        if self.install_archive:
            args += ['-a', self.install_archive, '-u']
        self.zoneadm("install", args)
        self.zone_refresh()

    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.name
        source_ds = Dataset(self.snapof)
        if source_ds.exists(type="filesystem") is False:
            raise(ex.Error("source dataset doesn't exist " + self.snapof))
        snapshot = source_ds.snapshot(zonename)
        snapshot.clone(self.clone, ['-o', 'mountpoint=' + self.kw_zonepath])

    def create_container_origin(self):
        lockfile = os.path.join(self.var_d, 'create_zone2clone-' + self.container_origin)
        try:
            with utilities.lock.cmlock(timeout=1200, delay=4, lockfile=lockfile):
                container = self.origin_factory()
                container.install_origin()
        except utilities.lock.LOCK_EXCEPTIONS as exc:
            raise ex.AbortAction(str(exc))

    def origin_factory(self):
        name = self.container_origin
        kwargs = {'brand': self._brand_to_create()}
        if self._brand_to_create() == 'solaris':
            kwargs['provision_net_type'] = 'no-anet'
            kwargs['sc_profile'] = '/usr/share/auto_install/sc_profiles/unconfig.xml'
            if self.ai_manifest:
                kwargs['ai_manifest'] = self.ai_manifest
        if self._brand_to_create() in ['solaris10']:
            kwargs['provision_net_type'] = 'no-anet'
        if self._brand_to_create() in ['native']:
            kwargs['provision_net_type'] = 'no-net'
            kwargs['zonepath'] = '/zones/%s' % name
        if self.install_archive:
            kwargs['install_archive'] = self.install_archive
        origin = ContainerZone(rid="container#skelzone", name=name, **kwargs)
        origin.svc = self.svc
        if self._brand_to_create() in ['native', 'solaris10']:
            origin.set_sysidcfg_unconfig()
        return origin

    def provisioner(self, need_boot=True):
        """provision zone
        - if snapof and zone brand is native
           configure zone
           then create zonepath from snapshot of snapof
           then attach zone
        - else if container_origin
           create clone container_origin if not yet created
           configure zone
           clone container origin to zone

        - if need_boot boot and wait multiuser
        """
        state = self.state
        if state in PROVISIONED_STATES:
            self.log.info('zone already provisioned: state is %s', state)
            return True
        self.log.info('provisioner start')

        # failfast setting for provisioning
        if self.snapof and self.container_origin:
            self.log.error('provision error: container_origin is not compatible with snapof')
            return False
        elif self.snapof and self._brand_to_create() != 'native':
            msg = 'provision error: snapof is only available with native zone, try container_origin instead'
            self.log.error(msg)
            return False

        if self.container_origin:
            self.create_container_origin()
        self.provision_zone()
        self.can_rollback = True

        if need_boot is True:
            self.zone_boot()
            self.wait_multi_user()

        self.log.info("provisioned")
        return True

    def provision_zone(self):
        if self.state in PROVISIONED_STATES:
            self.log.info("zone %s already in state %s" % (self.name, self.state))
            return
        self.zone_configure()
        self.prepare_boot_config()
        self.make_zone_installed()
        self.install_boot_config()

    def make_zone_installed(self):
        if self.snapof:
            # we are on brand native
            self.create_snaped_zone()
        elif self.container_origin:
            self.create_cloned_zone()
        else:
            self.install_zone()

    def unprovisioner(self):
        self.log.info('unprovisioner start')
        state = self.state
        if state == 'configured':
            self.zone_unconfigure()
        elif state:
            msg = 'unable to unprovision zone in state %s' % state
            self.log.error(msg)
            raise ex.Error(msg)
        if os.path.exists(self.zone_cfg_path()):
            os.remove(self.zone_cfg_path())
  0707010001f350000081a40000000000000000000000016a100daf00001c43000000e600010003ffffffffffffffff0000005800000000root/usr/share/opensvc/opensvc/drivers/resource/container/zone/configuration_profile.py   import logging
from xml.etree.ElementTree import ElementTree, fromstring, Element

SC_CONFIG = '''
<service_bundle name="sysconfig" type="profile">
    <service name="system/identity" type="service" version="1">
        <instance enabled="true" name="cert"/>
        <instance enabled="true" name="node">
            <property_group name="config" type="application">
                <propval name="nodename" type="astring" value="skelzone"/>
            </property_group>
        </instance>
    </service>
    <service name="network/install" type="service" version="1">
        <instance enabled="true" name="default">
            <property_group name="install_ipv6_interface" type="application">
                <propval name="stateful" type="astring" value="yes"/>
                <propval name="name" type="astring" value="net1/v6"/>
                <propval name="stateless" type="astring" value="yes"/>
                <propval name="address_type" type="astring" value="addrconf"/>
            </property_group>
        </instance>
    </service>
    <service name="network/dns/client" type="service" version="1">
        <property_group name="config" type="application">
        </property_group>
        <instance enabled="true" name="default"/>
    </service>
    <service name="system/name-service/cache" type="service" version="1">
        <instance enabled="true" name="default"/>
    </service>
    <service name="system/name-service/switch" type="service" version="1">
        <property_group name="config" type="application">
        </property_group>
        <instance enabled="true" name="default"/>
    </service>
    <service name="system/environment" type="service" version="1">
        <instance enabled="true" name="init">
            <property_group name="environment" type="application">
            </property_group>
        </instance>
    </service>
    <service name="system/timezone" type="service" version="1">
        <instance enabled="true" name="default">
            <property_group name="timezone" type="application">
                <propval name="localtime" type="astring" value="Europe/Paris"/>
            </property_group>
        </instance>
    </service>
    <service name="system/config-user" type="service" version="1">
        <instance enabled="true" name="default">
            <property_group name="root_account" type="application">
                <propval name="password" type="astring" value="NP"/>
                <propval name="type" type="astring" value="normal"/>
                <propval name="login" type="astring" value="root"/>
            </property_group>
        </instance>
    </service>
</service_bundle>
'''


def property_group(name, attr_type):
    return Element('property_group', attrib={'name': name, 'type': attr_type})


def propval(name, attr_type, value):
    return Element('propval', attrib={'name': name, 'type': attr_type, 'value': value})


class InstallIpv4Interface:
    def __init__(self, name, static_address=None, address_type='static',
                 default_route=None, id=0):
        """
        create fragment of xml system configuration profile for network
        if id is not 0, then use 'multiple interface' fragment
        """
        self.name = name
        self.static_address = static_address
        self.address_type = address_type
        self.default_route = default_route
        self.id = id

    def element(self):
        if self.id == 0:
            attr_type = 'application'
            name = 'install_ipv4_interface'
        else:
            attr_type = 'ipv4_interface'
            name = 'install_ipv4_interface_%s' % str(self.id)
        prop_group = property_group(name, attr_type)
        if self.default_route and self.id == 0:
            prop_group.append(propval('default_route', 'net_address_v4', self.default_route))
        if self.static_address:
            prop_group.append(propval('static_address', 'net_address_v4', self.static_address))
        if self.name:
            prop_group.append(propval('name', 'astring', self.name))
        if self.address_type:
            prop_group.append(propval('address_type', 'astring', self.address_type))
        return prop_group


class ScProfile:
    def __init__(self, sc_profile_file='sc_profile.xml', template=SC_CONFIG, log=None):
        self.sc_profile_file = sc_profile_file
        self.root = fromstring(template)
        self.log = log or logging.getLogger()

    def write(self):
        self.log.info('create system configuration profile %s', self.sc_profile_file)
        tree = ElementTree(element=self.root)
        tree.write(self.sc_profile_file, encoding='US-ASCII', xml_declaration=True)

    def set_nodename(self, nodename):
        path = ("service/[@name='system/identity']/instance/[@name='node']"
                "/property_group/[@name='config']/propval/[@name='nodename']")
        self.root.find(path).set('value', nodename)

    def add_ipv4_interface(self, install_ipv4_interface=None):
        if install_ipv4_interface is None:
            return
        network_install = self.root.find("service/[@name='network/install']/instance")
        network_install.append(install_ipv4_interface.element())

    def set_name_service_switch(self, name_values=None):
        path = "service/[@name='system/name-service/switch']/property_group/[@name='config']"
        ns_switch = self.root.find(path)
        for name, value in name_values.items():
            ns_switch.append(propval(name, 'astring', value))
        ns_switch.append(propval('default', 'astring', 'files'))

    def set_localtime(self, localtime):
        path = ("service/[@name='system/timezone']/instance/[@name='default']"
                "/property_group/[@name='timezone']/propval/[@name='localtime']")
        self.root.find(path).set('value', localtime)

    def set_environment(self, name_values):
        path = ("service/[@name='system/environment']/instance/[@name='init']"
                "/property_group/[@name='environment']")
        environment = self.root.find(path)
        for name, value in name_values.items():
            environment.append(propval(name, 'astring', value))

    def set_dns_client(self, searches=None, nameservers=None):
        path = "service/[@name='network/dns/client']/property_group/[@name='config']"
        dns_config = self.root.find(path)
        if nameservers:
            net_address_list = Element('net_address_list')
            for nameserver in nameservers:
                net_address_list.append(Element('value_node', attrib={'value': nameserver}))
            nameserver_element = Element('property', attrib={'name': 'nameserver',
                                                             'type': 'net_address'})
            nameserver_element.append(net_address_list)
            dns_config.append(nameserver_element)

        if searches:
            astring_list = Element('astring_list')
            for search in searches:
                astring_list.append(Element('value_node', attrib={'value': search}))
            search_element = Element('property', attrib={'name': 'search', 'type': 'astring'})
            search_element.append(astring_list)
            dns_config.append(search_element)
 0707010001f32c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/resource/container/amazon  0707010001f32d000081a40000000000000000000000016a100daf00002c39000000e600010003ffffffffffffffff0000004d00000000root/usr/share/opensvc/opensvc/drivers/resource/container/amazon/__init__.py  import os

import core.exceptions as ex
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_SHARED_IP_GROUP, \
    KW_SIZE, \
    KW_KEY_NAME, \
    KW_CLOUD_ID, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from core.resource import Resource
from env import Env
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall


DRIVER_GROUP = "container"
DRIVER_BASENAME = "amazon"
KEYWORDS = [
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_SHARED_IP_GROUP,
    KW_SIZE,
    KW_KEY_NAME,
    KW_CLOUD_ID,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    try:
        from libcloud.compute.providers import get_driver
        from libcloud.compute.types import NodeState
        return ["container.amazon"]
    except ImportError:
        return []


class ContainerAmazon(BaseContainer):
    save_timeout = 240

    def __init__(self,
                 cloud_id=None,
                 image_id=None,
                 size="t2.micro",
                 key_name=None,
                 subnet=None,
                 **kwargs):
        super(ContainerAmazon, self).__init__(type="container.amazon", **kwargs)
        self.cloud_id = cloud_id
        self.size_id = size
        self.image_id = image_id
        self.key_name = key_name
        self.subnet_name = subnet
        self.addr = None

    @lazy
    def save_name(self):
        return "%s.save" % self.name

    def keyfile(self):
        kf = [os.path.join(Env.paths.pathetc, self.key_name+'.pem'),
              os.path.join(Env.paths.pathetc, self.key_name+'.pub'),
              os.path.join(Env.paths.pathvar, self.key_name+'.pem'),
              os.path.join(Env.paths.pathvar, self.key_name+'.pub')]
        for k in kf:
            if os.path.exists(k):
                return k
        raise ex.Error("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.NotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%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.NotSupported("remote copy not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%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.NotSupported("remote commands not supported on Windows")

        self.getaddr()
        if self.addr is None:
            raise ex.Error('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.Error("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def get_subnet(self):
        for subnet in self.cloud.driver.ex_list_subnets():
            if subnet.name == self.subnet_name:
                return subnet
        raise ex.Error("%s subnet not found"%self.subnet_name)

    def get_size(self):
        for size in self.cloud.driver.list_sizes():
            if size.id == self.size_id:
                return size
        raise ex.Error("%s size not found"%self.size_id)

    @lazy
    def cloud(self):
        c = self.svc.node.cloud_get(self.cloud_id)
        return c

    def get_node(self):
        l = self.cloud.list_nodes()
        for n in l:
            if n.name == self.name:
                return n
        return

    def get_image(self, image_id):
        l = self.cloud.driver.list_images(ex_image_ids=[image_id])
        d = {}
        for image in l:
            if image.id == image_id:
                # exact match
                return image
        raise ex.Error("image %s not found" % image_id)

    def has_image(self, image_id):
        l = self.cloud.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" % (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.Error("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 utilities.ping.check_ping(ip, timeout=1, count=1):
                self.addr = ip
                break

        return 0

    def check_capabilities(self):
        return True

    def ping(self):
        if self.addr is None:
            return 0
        return utilities.ping.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 Env.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
        """
        try:
            from libcloud.compute.types import NodeState
        except ImportError:
            raise ex.Error("missing required module libcloud.compute.types")
        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.start_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.start_timeout, 5)
            return
        elif n.state == NodeState().STOPPED:
            self.log.info("starting ebs ec2 instance through aws")
            self.cloud.driver.ex_start_node(n)
            self.log.info("wait for container up status")
            self.wait_for_fn(self.is_up, self.start_timeout, 5)
            return
        raise ex.Error("don't know what to do with node in state: %s"%NodeState().tostring(n.state))

    def container_reboot(self):
        n = self.get_node()
        try:
            self.cloud.driver.reboot_node(n)
        except Exception as e:
            raise ex.Error(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.Error:
            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):
        n = self.get_node()
        self.log.info("stopping ebs ec2 instance through aws")
        self.cloud.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):
        try:
            from libcloud.compute.types import NodeState
        except ImportError:
            raise ex.Error("missing required module libcloud.compute.types")
        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'}
        n = self.get_node()
        try:
            size = self.cloud.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


    def provisioner(self):
        prereq = True
        if self.image_id is None:
            self.log.error("the image keyword is mandatory for the provision action")
            prereq &= False
        if self.size_id is None:
            self.log.error("the size keyword is mandatory for the provision action")
            prereq &= False
        if self.subnet_name is None:
            self.log.error("the subnet keyword is mandatory for the provision action")
            prereq &= False
        if self.key_name is None:
            self.log.error("the key_name keyword is mandatory for the provision action")
            prereq &= False
        if not prereq:
            raise ex.Error()

        image = self.get_image(self.image_id)
        size = self.get_size()
        subnet = self.get_subnet()
        self.log.info("create instance %s, size %s, image %s, key %s, subnet %s"%(self.name, size.name, image.name, self.key_name, subnet.name))
        self.cloud.driver.create_node(name=self.name, size=size, image=image, ex_keyname=self.key_name, ex_subnet=subnet)
        self.log.info("wait for container up status")
        self.wait_for_fn(self.is_up, self.start_timeout, 5)

   0707010001f33c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/lxd 0707010001f33d000081a40000000000000000000000016a100daf00002b05000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/lxd/__init__.py import json
import os

from subprocess import PIPE

import core.exceptions as ex

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from utilities.lazy import lazy
from core.resource import Resource
from utilities.storage import Storage
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which

lxc = "/usr/bin/lxc"
lxd = "/usr/bin/lxd"

DRIVER_GROUP = "container"
DRIVER_BASENAME = "lxd"
KEYWORDS = [
    {
        "keyword": "launch_image",
        "at": True,
        "required": True,
        "provisioning": True,
        "text": "The lxd image to instantiate on provision.",
        "example": "ubuntu:16.04"
    },
    {
        "keyword": "launch_options",
        "at": True,
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "default_text": "",
        "text": "The :cmd:`lxc launch <image> ... <name>` options set on provision.",
        "example": "-p default"
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = []
    if os.path.exists(lxd) and os.path.exists(lxc):
        data.append("container.lxd")
    return data

class ContainerLxd(BaseContainer):
    refresh_provisioned_on_unprovision = True

    def __init__(self, launch_image=None, launch_options=None, **kwargs):
        super(ContainerLxd, self).__init__(type="container.lxd", **kwargs)
        self.getaddr = self.dummy
        self.launch_image = None
        self.launch_options = []

    @lazy
    def label(self):  # pylint: disable=method-hidden
        return "lxd %s" % self.name if self.name else "<undefined>"

    @lazy
    def runmethod(self):
        return ['lxc', 'exec', self.name, '--']

    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.Error("'%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.Error("'%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.Error

    def lxc_start(self):
        self.lxd_import()
        cmd = [lxc, "start", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def lxc_stop(self):
        cmd = [lxc, "stop", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    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.to+":"+self.name]
        (ret, buff, err) = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

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

    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 super(ContainerLxd, self)._status(verbose=verbose)

    def dummy(self, cache_fallback=False):
        pass

    def operational(self):
        if not super(ContainerLxd, self).operational():
            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" % (Resource.__str__(self), self.name)

    def abort_start(self):
        for nodename in self.svc.peers:
            if nodename == Env.nodename:
                continue
            if self.is_up_on(nodename):
                self.log.error("already up on %s", nodename)
                return True
        return False

    def cni_containerid(self):
        """
        Used by ip.cni
        """
        return self.name

    def cni_netns(self):
        """
        Used by ip.cni and ip.netns
        """
        try:
            return "/proc/%d/ns/net" % self.get_pid()
        except (TypeError, ValueError):
            return

    def get_pid(self):
        try:
            return self.lxc_info()["state"]["pid"]
        except (KeyError, TypeError) as exc:
            return

    def start(self):
        super(ContainerLxd, self).start()
        self.svc.sub_set_action("ip", "start", tags=set([self.rid]))

    def stop(self):
        self.svc.sub_set_action("ip", "stop", tags=set([self.rid]))
        super(ContainerLxd, self).stop()

    def provision(self):
        super(ContainerLxd, self).provision()
        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]))
        super(ContainerLxd, self).unprovision()

    def provisioned(self):
        return self.has_it()

    def provisioner(self):
        if self.has_it():
            return
        cmd = ["/usr/bin/lxc", "launch", self.launch_image] + self.launch_options + [self.name]
        ret, out, err = self.vcall(cmd, stdin=PIPE)
        if ret != 0:
            raise ex.Error
        self.wait_for_fn(self.is_up, self.start_timeout, 2)
        self.can_rollback = True

    def unprovisioner(self):
        cmd = ["/usr/bin/lxc", "delete", self.name]
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

   0707010001f34a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/resource/container/vz  0707010001f34b000081a40000000000000000000000016a100daf00001689000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/resource/container/vz/__init__.py  import os

import core.exceptions as ex

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from core.capabilities import capabilities
from core.resource import Resource
from core.objects.svcdict import KEYS
from utilities.lazy import lazy
from utilities.proc import justcall, qcall

DRIVER_GROUP = "container"
DRIVER_BASENAME = "vz"
KEYWORDS = [
    {
        "keyword": "rootfs",
        "text": "Sets the root fs directory of the container",
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "template",
        "text": "Sets the url of the template unpacked into the container root fs.",
        "required": True,
        "provisioning": True
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    from utilities.proc import which
    data = []
    if which("vzctl"):
        data.append("container.vz")
    return data


class ContainerVz(BaseContainer):
    def __init__(self, guestos="Linux", rootfs=None, template=None, **kwargs):
        super(ContainerVz, self).__init__(type="container.vz", guestos=guestos, **kwargs)
        self.rootfs = rootfs
        self.template = template

    @lazy
    def runmethod(self):
        return ["vzctl", "exec", self.name]

    @lazy
    def _cf(self):
        return os.path.join(os.sep, "etc", "vz", "conf", "%s.conf" % self.name)

    def __str__(self):
        return "%s name=%s" % (Resource.__str__(self), self.name)

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

    def rcp_from(self, src, dst):
        rootfs = self.get_rootfs()
        if len(rootfs) == 0:
            raise ex.Error()
        src = rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%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.Error()
        dst = rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%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=None):
        if options is None:
            options = []
        cmd = ['vzctl', action, self.name] + options
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error
        return out

    def container_start(self):
        self.vzctl('start')

    def container_stop(self):
        self.vzctl('stop')

    def container_forcestop(self):
        raise ex.Error

    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 = Env.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 "vzctl" not in capabilities:
            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.Error
        return self._cf


   0707010001f33a000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/resource/container/lxc 0707010001f33b000081a40000000000000000000000016a100daf00008a23000000e600010003ffffffffffffffff0000004a00000000root/usr/share/opensvc/opensvc/drivers/resource/container/lxc/__init__.py """
The lxc v1, v2, v3 resource driver
"""
import glob
import os
import shutil

from datetime import datetime

import core.exceptions as ex
import core.status
import utilities.ping

from .. import \
    BaseContainer, \
    KW_START_TIMEOUT, \
    KW_STOP_TIMEOUT, \
    KW_NO_PREEMPT_ABORT, \
    KW_NAME, \
    KW_HOSTNAME, \
    KW_OSVC_ROOT_PATH, \
    KW_GUESTOS, \
    KW_PROMOTE_RW, \
    KW_SCSIRESERV
from env import Env
from utilities.files import makedirs, protected_dir, rmtree_busy
from utilities.lazy import lazy
from core.objects.svcdict import KEYS
from utilities.proc import justcall, which

CAPABILITIES = {
    "cgroup_dir": "2.1",
}

DRIVER_GROUP = "container"
DRIVER_BASENAME = "lxc"
KEYWORDS = [
    {
        "keyword": "container_data_dir",
        "at": True,
        "text": "If this keyword is set, the service configures a resource-private container data store. This setup is allows stateful service relocalization.",
        "example": "/srv/svc1/data/containers"
    },
    {
        "keyword": "cf",
        "text": "Defines a lxc configuration file in a non-standard location.",
        "provisioning": True,
        "example": "/srv/mycontainer/config"
    },
    {
        "keyword": "rootfs",
        "text": "Sets the root fs directory of the container",
        "required": False,
        "provisioning": True
    },
    {
        "keyword": "template",
        "text": "Sets the url of the template unpacked into the container root fs or the name of the template passed to :cmd:`lxc-create`.",
        "required": True,
        "provisioning": True
    },
    {   
        "keyword": "template_options",
        "text": "The arguments to pass through :cmd:`lxc-create` to the per-template script.",
        "convert": "shlex",
        "default": [],
        "provisioning": True
    },
    {
        "keyword": "create_secrets_environment",
        "at": True,
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "text": "Set variables in the :cmd:`lxc-create` execution environment. A whitespace separated list of ``<var>=<secret name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<secret name>/<key path>`` only or whole ``<var>=<secret name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "create_configs_environment",
        "at": True,
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "text": "Set variables in the :cmd:`lxc-create` execution environment. The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "create_environment",
        "at": True,
        "provisioning": True,
        "convert": "shlex",
        "default": [],
        "text": "Set variables in the :cmd:`lxc-create` execution environment. The whitespace separated list of ``<var>=<config name>/<key path>``. A shell expression spliter is applied, so double quotes can be around ``<config name>/<key path>`` only or whole ``<var>=<config name>/<key path>``. Variables are uppercased.",
        "example": "CRT=cert1/server.crt PEM=cert1/server.pem"
    },
    {
        "keyword": "rcmd",
        "convert": "shlex",
        "at": True,
        "example": "lxc-attach -e -n osvtavnprov01 -- ",
        "text": "An container remote command override the agent default"
    },
    KW_START_TIMEOUT,
    KW_STOP_TIMEOUT,
    KW_NO_PREEMPT_ABORT,
    KW_NAME,
    KW_HOSTNAME,
    KW_OSVC_ROOT_PATH,
    KW_GUESTOS,
    KW_PROMOTE_RW,
    KW_SCSIRESERV,
]

DEFAULT_CONFIG = """\
lxc.utsname = %(hostname)s
lxc.tty = 4
lxc.pts = 1024
lxc.console = /var/log/opensvc/%(hostname)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
"""

KEYS.register_driver(
    DRIVER_GROUP,
    DRIVER_BASENAME,
    name=__name__,
    keywords=KEYWORDS,
)

def driver_capabilities(node=None):
    data = []
    cmd = ["lxc-info", "--version"]
    out, _, ret = justcall(cmd)
    if ret == 0:
        data.append("container.lxc")
        version = out.strip()
        if version >= CAPABILITIES.get("cgroup_dir", "0"):
            data.append("container.lxc.cgroup_dir")
    return data


class ContainerLxc(BaseContainer):
    """
     container status transition diagram :
       ---------
      | STOPPED |<---------------
       ---------                 |
           |                     |
         start                   |
           |                     |
           V                     |
       ----------                |
      | STARTING |--error-       |
       ----------         |      |
           |              |      |
           V              V      |
       ---------    ----------   |
      | RUNNING |  | ABORTING |  |
       ---------    ----------   |
           |              |      |
      no process          |      |
           |              |      |
           V              |      |
       ----------         |      |
      | STOPPING |<-------       |
       ----------                |
           |                     |
            ---------------------
    """

    def __init__(self,
                 guestos="Linux",
                 cf=None,
                 rcmd=None,
                 rootfs=None,
                 container_data_dir=None,
                 template=None,
                 template_options=None,
                 create_environment=None,
                 create_configs_environment=None,
                 create_secrets_environment=None,
                 **kwargs):
        super(ContainerLxc, self).__init__(
            type="container.lxc",
            guestos=guestos,
            **kwargs
        )
        self.raw_rootfs = rootfs
        self.refresh_provisioned_on_provision = True
        self.refresh_provisioned_on_unprovision = True
        self.always_pg = True
        self.container_data_dir = container_data_dir
        self.rcmd = rcmd
        self.template = template
        self.template_options = template_options or []
        self.links = None
        self.cf = cf
        self.create_environment = create_environment
        self.create_configs_environment = create_configs_environment
        self.create_secrets_environment = create_secrets_environment
        self.custom_create_pg = True

    def on_add(self):
        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

    @lazy
    def label(self):  # pylint: disable=method-hidden
        return "lxc " + self.name

    @lazy
    def runmethod(self):
        if self.rcmd is not None:
            runmethod = self.rcmd
        elif which('lxc-attach') and os.path.exists('/proc/1/ns/pid'):
            if self.lxcpath:
                runmethod = ['lxc-attach', '-n', self.name, '-P', self.lxcpath, '--clear-env', '--']
            else:
                runmethod = ['lxc-attach', '-n', self.name, '--clear-env', '--']
        else:
            runmethod = Env.rsh.split() + [self.name]
        return runmethod

    @lazy
    def lxc_version(self):
        cmd = ["lxc-info", "--version"]
        out, _, _ = justcall(cmd)
        return out.strip()

    def capable(self, cap):
        if self.lxc_version >= CAPABILITIES.get(cap, "0"):
            return True
        return False

    def files_to_sync(self):
        # Don't synchronize container.lxc config in /var/lib/lxc if not shared
        # Non shared container resource mandates a private container for each
        # service instance, thus synchronizing the lxc config is counter productive
        # and can even lead to provisioning failure on secondary nodes.
        if not self.shared:
            return []

        # 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
        self.find_cf()
        if not self.cf or not os.path.exists(self.cf):
            return []

        # The config file is hosted on a fs resource.
        # Let the user replicate it via a sync resource if the fs is not
        # shared. If the fs is shared, it must not be replicated to avoid
        # copying on the remote underlying fs (which may block a zfs dataset
        # mount).
        res = self.svc.resource_handling_file(self.cf)
        if res:
            return []

        # replicate the config file in the system standard path
        data = [self.cf]
        return data

    def enter(self):
        for cmd in [["/bin/bash"], ["/bin/sh"]]:
            try:
                os.system(" ".join(self.runmethod + cmd))
                return
            except ValueError:
                continue
            else:
                return

    def rcp_from(self, src, dst):
        if not self.rootfs:
            raise ex.Error
        src = self.rootfs + src
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def rcp(self, src, dst):
        if not self.rootfs:
            raise ex.Error
        dst = self.rootfs + dst
        cmd = ['cp', src, dst]
        out, err, ret = justcall(cmd)
        if ret != 0:
            raise ex.Error("'%s' execution error:\n%s"%(' '.join(cmd), err))
        return out, err, ret

    def set_encap_file_ownership(self, path):
        """
        Unprivileged containers may have a non zero root uid/gid
        """
        try:
            st = os.stat(self.rootfs)
        except OSError as exc:
            self.log.warning(exc)
            return
        path = os.path.join(self.rootfs, path.lstrip("/"))
        #self.log.info("set encap file ownership %s uid %d gid %d", path, st.st_uid, st.st_gid)
        try:
            os.chown(path, st.st_uid, st.st_gid)
        except OSError as exc:
            self.log.warning(exc)

    def lxc(self, action):
        self.find_cf()
        outf = None
        if action == 'start':
            outf = '/var/tmp/svc_'+self.name+'_lxc_'+action+'.log'
            if self.capable("cgroup_dir"):
                cmd = ["lxc-start", "-d", "-n", self.name, "-o", outf, "-s", "lxc.cgroup.dir="+self.cgroup_dir]
            else:
                cmd = ["lxc-start", "-d", "-n", self.name, "-o", outf]
            if self.cf:
                cmd += ['-f', self.cf]
            if self.lxcpath:
                makedirs(self.lxcpath)
                cmd += self.lxcpath_args
        elif action == 'stop':
            cmd = ['lxc-stop', '-n', self.name]
            cmd += self.lxcpath_args
        elif action == 'kill':
            cmd = ['lxc-stop', '--kill', '--name', self.name]
            cmd += self.lxcpath_args
        else:
            raise ex.Error("unsupported lxc action: %s" % action)

        def prex():
            os.umask(0o022)

        begin = datetime.now()
        ret, _, _ = self.vcall(cmd, preexec_fn=prex)
        duration = datetime.now() - begin
        loginfo = '%s done in %s - ret %i'%(action, duration, ret)
        if outf is not None:
            loginfo += ' - logs in %s'%outf
        self.log.info(loginfo)
        if ret != 0:
            raise ex.Error

    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 ofile:
            for line in ofile.readlines():
                if param not in line:
                    continue
                if line.strip()[0] == '#':
                    continue
                data = line.replace('\n', '').split('=')
                if len(data) < 2:
                    continue
                if data[0].strip() != param:
                    continue
                value = ' '.join(data[1:]).strip()
                break
        return value

    @lazy
    def rootfs(self):
        rootfs = self.raw_rootfs
        if rootfs:
            rootfs, vol = self.replace_volname(rootfs, strict=False)
        if rootfs:
            return rootfs
        if self.lxcpath:
            return os.path.join(self.lxcpath, "config")
        rootfs = self.get_cf_value("lxc.rootfs")
        if rootfs is None:
            rootfs = self.get_cf_value("lxc.rootfs.path")
        if rootfs is None:
            raise ex.Error("could not determine lxc container rootfs")
        if ":" in rootfs:
            # zfs:/tank/svc1, nbd:file1, dir:/foo ...
            rootfs = rootfs.split(":", 1)[-1]
            if rootfs and not rootfs.startswith("/"):
                # zfs:tank/svc1
                rootfs = "/" + rootfs
        return rootfs

    @property
    def zonepath(self):
        return self.rootfs

    @lazy
    def lxcpath(self):
        if self.container_data_dir:
            path, _ = self.replace_volname(self.container_data_dir, strict=False, errors="ignore")
            return path

    @lazy
    def lxcpath_args(self):
        if self.lxcpath:
            return ["-P", self.lxcpath]
        return []

    def install_drp_flag(self):
        flag = os.path.join(self.rootfs, ".drp_flag")
        self.log.info("install drp flag in container : %s", flag)
        with open(flag, 'w') as ofile:
            ofile.write(' ')

    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
        if self.capable("cgroup_dir"):
            path = os.path.join(ppath, self.cgroup_dir)
        else:
            path = os.path.join(ppath, "lxc")
        try:
            os.makedirs(path)
            self.log.info("mkdir %s", path)
        except (OSError, IOError):
            # errno 17: file exists
            pass
        paths = [path]
        for d in glob.glob(path+"/lxc.*"):
            try:
                os.rmdir(d)
                self.log.info("removed leftover cgroup %s", d)
            except Exception as exc:
                self.log.debug("failed to remove leftover cgroup %s: %s", d, str(exc))
        while path != ppath:
            path = os.path.dirname(path)
            paths.append(path)
        for path in sorted(paths):
            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)
                    if parent_val is None:
                        raise ex.Error("failed to read %s/%s" % (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:
                continue
            if current_val == "1":
                self.log.debug("set_cpuset_clone_children: %s/%s already set to 1", path, parm)
                continue
            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 None
        with open(fpath, "r") as ofile:
            current_val = ofile.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 ofile:
            ofile.write(val)

    def cleanup_cgroup(self, grp="*"):
        import glob
        for path in glob.glob("/sys/fs/cgroup/%s/lxc/%s-[0-9]" % (grp, self.name)) + \
                    glob.glob("/sys/fs/cgroup/%s/lxc/%s" % (grp, self.name)):
            try:
                os.rmdir(path)
                self.log.info("removed leftover cgroup %s", path)
            except Exception as exc:
                self.log.debug("failed to remove leftover cgroup %s: %s", path, str(exc))

    def container_start(self):
        self.cleanup_cgroup()
        self.set_cpuset_clone_children()
        self.create_pg()
        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):
        self.lxc('kill')

    def get_pid(self):
        cmd = ['lxc-info', '--name', self.name, '-p']
        cmd += self.lxcpath_args
        out, _, ret = justcall(cmd)
        if ret != 0:
            return
        try:
            return int(out.split()[-1])
        except IndexError:
            return

    def get_links(self):
        links = []
        cmd = ['lxc-info', '--name', self.name]
        cmd += self.lxcpath_args
        out, _, 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 utilities.ping.check_ping(self.addr, timeout=1)

    def is_up_on(self, nodename):
        return self.is_up(nodename)

    @lazy
    def cgroup_dir(self):
        return self.svc.pg.get_cgroup_relpath(self)

    def is_up(self, nodename=None):
        if which("lxc-ps"):
            return self.is_up_ps(nodename=nodename)
        return self.is_up_info(nodename=nodename)

    def is_up_info(self, nodename=None):
        cmd = ['lxc-info', '--name', self.name]
        cmd += self.lxcpath_args
        if nodename is not None:
            cmd = Env.rsh.split() + [nodename] + cmd
        out, _, 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 = Env.rsh.split() + [nodename] + cmd
        out, _, 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):
        self.find_cf()
        if self.cf is None:
            return
        cfg = self.get_cf_path()
        if cfg is None:
            self.log.debug("could not determine the config file standard hosting directory")
            return
        if self.cf == cfg:
            return
        cfg_d = os.path.dirname(cfg)
        if not os.path.isdir(cfg_d):
            try:
                os.makedirs(cfg_d)
            except Exception as exc:
                raise ex.Error("failed to create directory %s: %s"%(cfg_d, str(exc)))
        self.log.info("install %s as %s", self.cf, cfg)
        try:
            shutil.copy(self.cf, cfg)
        except Exception as exc:
            raise ex.Error(str(exc))

    def get_cf_path(self):
        if self.lxcpath:
            return os.path.join(self.lxcpath, self.name, "config")
        path = which('lxc-info')
        if path is None:
            return
        dpath = os.path.dirname(os.path.realpath(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):
        cfg = self.get_cf_path()
        if not self.is_provisioned():
            return True
        res = self.svc.resource_handling_file(cfg)
        if res and res.status() not in (core.status.NA, core.status.UP, core.status.STDBY_UP):
            return True
        if cfg is None:
            self.status_log("could not determine the config file standard hosting directory")
            return False
        if os.path.exists(cfg):
            return True
        self.status_log("config file is not installed as %s" % cfg)
        return False

    def _status(self, verbose=False):
        self.check_installed_cf()
        return super(ContainerLxc, self)._status(verbose=verbose)

    def find_cf(self):
        if self.cf is not None:
            self.cf, vol = self.replace_volname(self.cf, strict=False)
            return

        if self.lxcpath:
            d_lxc = self.lxcpath
            self.cf = os.path.join(d_lxc, self.name, 'config')
            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]:
            cfg = os.path.join(prefix, d_lxc, self.name, 'config')
            if os.path.exists(cfg):
                cfg_d = os.path.dirname(cfg)
                makedirs(cfg_d)
                self.cf = cfg
                return

        # on Oracle Linux, config is in /etc/lxc
        cfg = os.path.join(os.sep, 'etc', 'lxc', self.name, 'config')
        if os.path.exists(cfg):
            self.cf = cfg
            return

        self.cf = None
        raise ex.Error("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.Error("lxc install prefix not found")

    def dummy(self, cache_fallback=False):
        pass

    def operational(self):
        if not super(ContainerLxc, self).operational():
            return False

        cmd = self.runmethod + ['test', '-f', '/bin/systemctl']
        out, _, 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, _, 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 cni_containerid(self):
        """
        Used by ip.cni
        """
        return self.name

    def cni_netns(self):
        """
        Used by ip.cni and ip.netns
        """
        try:
            return "/proc/%d/ns/net" % self.get_pid()
        except (TypeError, ValueError):
            return

    def start(self):
        super(ContainerLxc, self).start()
        self.svc.sub_set_action("ip", "start", tags=set([self.rid]))
        self.svc.sub_set_action("disk.scsireserv", "start", tags=set([self.name]))
        self.svc.sub_set_action("disk.zpool", "start", tags=set([self.name]))
        self.svc.sub_set_action("fs", "start", tags=set([self.name]))

    def stop(self):
        self.svc.sub_set_action("fs", "stop", tags=set([self.name]))
        self.svc.sub_set_action("disk.zpool", "stop", tags=set([self.name]))
        self.svc.sub_set_action("disk.scsireserv", "stop", tags=set([self.name]))
        self.svc.sub_set_action("ip", "stop", tags=set([self.rid]))
        super(ContainerLxc, self).stop()

    def provision(self):
        super(ContainerLxc, self).provision()
        self.svc.sub_set_action("ip", "provision", tags=set([self.rid]))
        self.svc.sub_set_action("disk.scsireserv", "provision", tags=set([self.name]))
        self.svc.sub_set_action("disk.zpool", "provision", tags=set([self.name]))
        self.svc.sub_set_action("fs", "provision", tags=set([self.name]))

    def unprovision(self):
        self.svc.sub_set_action("fs", "unprovision", tags=set([self.name]))
        self.svc.sub_set_action("disk.zpool", "unprovision", tags=set([self.name]))
        self.svc.sub_set_action("disk.scsireserv", "unprovision", tags=set([self.name]))
        self.svc.sub_set_action("ip", "unprovision", tags=set([self.rid]))
        super(ContainerLxc, self).unprovision()




    def validate(self):
        if self.d_lxc is None:
            self.log.error("this node is not lxc capable")
            return True

        if not self.check_hostname():
            return False

        if not self.check_lxc():
            self.log.error("container is not created")
            return False

        return True

    def check_lxc(self):
        if os.path.exists(self.cf):
            return True
        return False

    def setup_lxc_config(self):
        import tempfile
        f = tempfile.NamedTemporaryFile(delete=False)
        f.write(DEFAULT_CONFIG % dict(hostname=self.vm_hostname, rootfs=self.rootfs))
        self.config = f.name
        f.close()

    def setup_lxc(self):
        if self.check_lxc():
            self.log.info("container is already created")
            return
        self.setup_lxc_config()
        with open(os.path.join(Env.paths.pathlog, "%s.console.log"%self.name), "a+") as f:
            f.write("")
        cmd = ['lxc-create', '-n', self.name, '-f', self.config]
        if self.lxcpath:
            makedirs(self.lxcpath)
            cmd += self.lxcpath_args
        ret, out, err = self.vcall(cmd)
        if ret != 0:
            raise ex.Error

    def check_hostname(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.log.error("can not get container hostname")
            raise ex.Error

        if h != self.vm_hostname:
            self.log.info("container hostname is not %s"%self.vm_hostname)
            return False

        return True

    def set_hostname(self):
        if self.check_hostname():
            self.log.info("container hostname already set")
            return
        with open(self.p_hostname, 'w') as f:
            f.write(self.vm_hostname+'\n')
        self.log.info("container hostname set to %s"%self.vm_hostname)

    def get_template(self):
        self.template_fname = os.path.basename(self.template)
        self.template_local = os.path.join(Env.paths.pathtmp, self.template_fname)
        if os.path.exists(self.template_local):
            self.log.info("template %s already downloaded"%self.template_fname)
            return
        from utilities.uri import Uri
        secure = self.svc.node.oget("node", "secure_fetch")
        try:
            with Uri(self.template, secure=secure).fetch() as fpath:
                shutil.copy(fpath, self.template_local)
        except IOError as e:
            self.log.error("download failed", ":", e)
            raise ex.Error

    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.log.info("template already unpacked")
            return
        tar.extractall()
        tar.close()

    def setup_template(self):
        self.set_hostname()

    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.vcall(['chroot', self.rootfs, 'update-rc.d', '-f', 'udev', 'remove'])
        elif os.path.exists(chkconfig):
            self.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.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_rsa.pub')
        authkeys = os.path.join(self.rootfs, 'root', '.ssh', 'authorized_keys')
        if not os.path.exists(pub):
            self.log.error("no rsa key found on node for root")
            return
        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.log.info("setup hypervisor root trust")

    def purge_known_hosts(self, ip=None):
        if ip is None:
            cmd = ['ssh-keygen', '-R', self.svc.name]
        else:
            cmd = ['ssh-keygen', '-R', ip]
        ret, out, err = self.vcall(cmd, err_to_info=True)

    def unprovisioner_shared_non_leader(self):
        self.purge_lxc_var()

    def unprovisioner(self):
        self.purge_lxc_var()

    def purge_lxc_var(self):
        self.set_d_lxc()
        path = os.path.join(self.d_lxc, self.name)
        if not os.path.exists(path):
            self.log.info("%s already cleaned up", path)
            return
        if protected_dir(path):
            self.log.warning("refuse to remove %s", path)
            return
        self.log.info("rm -rf %s", path)
        rmtree_busy(path)

    def set_d_lxc(self):
        # lxc root conf dir
        if self.lxcpath:
            self.d_lxc = self.lxcpath
            return
        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

    def provisioner(self):
        # hostname file in the container rootfs
        self.p_hostname = os.path.join(self.rootfs, 'etc', 'hostname')
        self.set_d_lxc()

        if not os.path.exists(self.rootfs):
            os.makedirs(self.rootfs)

        if self.template is None or "://" not in self.template:
            self.provisioner_lxc_create()
        else:
            self.provisioner_archive()

    def provisioner_lxc_create(self):
        cmd = ['lxc-create', '--name', self.name, "--dir", self.rootfs]
        if self.cf and os.path.exists(self.cf):
            cmd += ['-f', self.cf]
        if self.lxcpath:
            makedirs(self.lxcpath)
            cmd += self.lxcpath_args
            if not self.cf:
                cf = os.path.join(self.lxcpath, self.name, "config")
                if os.path.exists(cf):
                    cmd += ["-f", cf]
        if self.template:
            cmd += ['--template', self.template]
            if self.template_options:
                cmd += ["--"] + self.template_options
        env = {
            "DEBIAN_FRONTEND": "noninteractive",
            "DEBIAN_PRIORITY": "critical",
        }
        for key in ("http_proxy", "https_proxy", "ftp_proxy", "rsync_proxy"):
            if key in os.environ:
                env[key] = os.environ[key]
            key = key.upper()
            if key in os.environ:
                env[key] = os.environ[key]
        env.update(self.get_create_env())
        self.log.info(" ".join(cmd))
        ret = self.lcall(cmd, env=env)
        if ret != 0:
            raise ex.Error

    def get_create_env(self):
        env = {}
        env.update(self.direct_environment_env(self.create_environment))
        env.update(self.kind_environment_env("cfg", self.create_configs_environment))
        env.update(self.kind_environment_env("sec", self.create_secrets_environment))
        return env

    def provisioner_archive(self):
        # container config file
        if self.d_lxc is not None:
            self.config = os.path.join(self.d_lxc, self.name, 'config')

        self.get_template()
        self.unpack_template()
        self.setup_template()
        self.setup_lxc()
        self.disable_udev()
        self.setup_getty()
        self.setup_authkeys()

        self.start()
        self.log.info("provisioned")
        return True

    def provisioned(self):
        cmd = ['lxc-info', '--name', self.name, '-p']
        cmd += self.lxcpath_args
        out, _, ret = justcall(cmd)
        if ret != 0:
            return False
        return True
 0707010001f42d000041ed0000000000000000000000026a102a9300000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/drivers/sanswitch  0707010001f42f000081a40000000000000000000000016a100daf000012fb000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/sanswitch/brocade.py   import os
import telnetlib

import core.exceptions as ex
from core.node import Node
from env import Env
from utilities.naming import split_path, factory
from utilities.proc import justcall

if Env.paths.pathbin not in os.environ['PATH']:
    os.environ['PATH'] += ":"+Env.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=None, password=None):
    base = ['ssh', '-o', 'StrictHostKeyChecking=no',
                   '-o', 'ForwardX11=no',
                   '-o', 'ConnectTimeout=5']
    if password:
        _cmd = ["sshpass", "-d", "0"] + base + [
            '-l', username, switch
        ]
    else:
        _cmd = base + [
            '-o', 'PasswordAuthentication=no',
            '-l', username, '-i', key, switch
        ]
    out, err, ret = justcall(_cmd + [cmd], input=password)
    if "command not found" in err:
        # bogus firmware syntax
        out, err, ret = justcall(_cmd + ['bash --login -c '+cmd])
    if ret != 0:
        raise ex.Error("brocade command execution error: %s" % err)
    return out, err, ret

class Brocades(object):
    switchs = []

    def __init__(self, objects=None, node=None):
        if objects is None:
            objects = []
        self.objects = objects
        if len(objects) > 0:
            self.filtering = True
        else:
            self.filtering = False
        if node:
            self.node = node
        else:
            self.node = Node()
        done = []
        for s in self.node.conf_sections(cat="switch"):
            name = self.node.oget(s, "name")
            if not name:
                name = s.split("#", 1)[-1]
            if name in done:
                continue
            if self.filtering and name not in self.objects:
                continue
            try:
                stype = self.node.oget(s, "type")
            except:
                continue
            if stype != "brocade":
                continue
            key = self.node.oget(s, "key")
            method = self.node.oget(s, "method")
            password = self.node.oget(s, "password")
            if password and "sec/" in password:
                _name, _namespace, _ = split_path(password)
                sec = factory("sec")(name=_name, namespace=_namespace, volatile=True)
                if not sec.exists():
                    print("%s referenced in section %s does not exists" % (password, s))
                    continue
                if not sec.has_key("password"):
                    print("%s referenced in section %s has no 'password' key" % (password, s))
                    continue
                password = sec.decode_key("password")
            try:
                username = self.node.oget(s, "username")
            except:
                print("no 'username' parameter in section %s" % s)
                continue
            if key is None and password is None:
                print("no 'key' nor 'password' parameter in section %s" % s)
                continue
            self.switchs.append(Brocade(name, username, key, password, method=method, node=self.node))
            done.append(name)

    def __iter__(self):
        for switch in self.switchs:
            yield(switch)

class Brocade(object):
    def __init__(self, name, username, key, password, method=None, node=None):
        self.name = name
        self.username = username
        self.password = password
        self.method = method
        self.key = key
        self.keys = ['brocadeswitchshow', 'brocadensshow', 'brocadezoneshow']
        self.node = node

    def brocadecmd(self, cmd):
        if self.key is not None or self.method == "ssh":
            return brocadecmd(cmd, self.name, self.username, key=self.key, password=self.password)
        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())
 0707010001f42e000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/sanswitch/__init__.py  0707010001f297000041ed0000000000000000000000106a102a9200000000000000e600010003ffffffffffffffff0000002d00000000root/usr/share/opensvc/opensvc/drivers/check  0707010001f2cf000041ed0000000000000000000000046a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/check/mpath    0707010001f2d0000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/mpath/__init__.py    0707010001f2d9000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath  0707010001f2dd000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/linux.py from . import Check
0707010001f2db000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/aix.py   from . import Check
0707010001f2dc000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/hpux.py  from . import Check
0707010001f2da000081a40000000000000000000000016a100daf00000b30000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/__init__.py  import drivers.check

from core.capabilities import capabilities
from env import Env
from utilities.proc import justcall
from utilities.diskinfo import DiskInfo

_di = DiskInfo()

class Check(drivers.check.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.path
        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 "node.x.powermt" not in capabilities:
            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({"instance": name,
                              "value": str(n),
                              "path": 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
0707010001f2de000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/check/mpath/powerpath/sunos.py from . import Check
0707010001f2d1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native 0707010001f2d3000081a40000000000000000000000016a100daf000008d0000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/aix.py  import drivers.check
from utilities.proc import justcall

class Check(drivers.check.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.path
        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({"instance": wwid,
                          "value": str(n),
                          "path": 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({"instance": wwid,
                      "value": str(n),
                      "path": self.find_svc(dev),
                     })
        return r
0707010001f2d4000081a40000000000000000000000016a100daf00000931000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/hpux.py import drivers.check

from utilities.proc import justcall

class Check(drivers.check.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.path
        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
        n = 0
        proto = ""
        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({"instance": wwid,
                              "value": str(n),
                              "path": 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({"instance": wwid,
                      "value": str(n),
                      "path": self.find_svc(dev),
            })
        return r
   0707010001f2d7000081a40000000000000000000000016a100daf00000a30000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/sunos.py    import drivers.check

from utilities.proc import justcall

class Check(drivers.check.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.path
        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({"instance": wwid,
                              "value": str(n),
                              "path": self.find_svc(dev),
                             })
                    dev = None
                    n = 0
        return r

0707010001f2d8000081a40000000000000000000000016a100daf000005ac000000e600010003ffffffffffffffff0000004500000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/windows.py  import os
import tempfile
from subprocess import *

import drivers.check
import foreign.wmi as wmi

class Check(drivers.check.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.path
        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({"instance": name,
                          "value": str(n),
                          "path": "",
                         })
        return r
0707010001f2d2000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/__init__.py 0707010001f2d6000081a40000000000000000000000016a100daf00000343000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/osf1.py import drivers.check

from utilities.diskinfo import DiskInfo

class Check(drivers.check.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.path
        return ''

    def do_check(self):
        di = DiskInfo()
        r = []
        for dev, data in di.h.items():
            r.append({"instance": data['wwid'],
                      "value": str(data['path_count']),
                      "path": self.find_svc(dev),
                     })
        return r
 0707010001f2d5000081a40000000000000000000000016a100daf0000106b000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/check/mpath/native/linux.py    import drivers.check

from env import Env
from utilities.proc import justcall

class Check(drivers.check.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.path
        return ''

    def do_check_old(self):
        r"""
	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 = [Env.syspaths.multipath, '-l']
        (out, err, ret) = justcall(cmd)
        lines = out.split('\n')
        if len(lines) < 1:
            return self.undef
        r = []
        wwid = None
        dev = None
        n = 0
        for line in lines:
            if len(line) > 0 and not r'\_ ' in line and not line.startswith('['):
                # new mpath
                # - store previous
                # - reset path counter
                if wwid is not None:
                    r.append({"instance": wwid,
                              "value": str(n),
                              "path": 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({"instance": wwid,
                      "value": str(n),
                      "path": self.find_svc(dev),
                     })
        return r

    def do_check(self):
        cmd = [Env.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
        n = 0
        for line in lines:
            if ' dm-' in line:
                # new mpath
                # - store previous
                # - reset path counter
                if wwid is not None:
                    r.append({"instance": wwid,
                              "value": str(n),
                              "path": 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({"instance": wwid,
                      "value": str(n),
                      "path": self.find_svc(dev),
                     })
        return r
 0707010001f2b2000041ed0000000000000000000000046a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/fs_u 0707010001f2b8000081a40000000000000000000000016a100daf00000612000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/freebsd.py  import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
  0707010001f2b9000081a40000000000000000000000016a100daf00000612000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/hpux.py import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
  0707010001f2bd000081a40000000000000000000000016a100daf00000456000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/windows.py  import drivers.check
import utilities.devices.windows

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        return ''

    def do_check(self):
        try:
            import win32api
        except ImportError:
            return []
        cmd = ['df', '-lP']
        r = []
        for drive in utilities.devices.windows.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({
                      "instance": drive,
                      "value": str(pct),
                      "path": self.find_svc(drive),
                     })
        return r

if __name__ == "__main__":
    o = Check()
    print(o.do_check())
  0707010001f2bc000081a40000000000000000000000016a100daf000005fb000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/sunos.py    import drivers.check
from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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
            path = self.find_svc(l[5])
            r.append({
                      "instance": l[5],
                      "value": l[4],
                      "path": path,
                     })
            r.append({
                      "instance": l[5]+".free",
                      "value": l[3],
                      "path": path,
                     })
            r.append({
                      "instance": l[5]+".size",
                      "value": l[1],
                      "path": path,
                     })
        return r
 0707010001f2be000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/zfs 0707010001f2c1000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/linux.py    from . import Check
0707010001f2c2000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/sunos.py    from . import Check
0707010001f2c0000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004100000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/freebsd.py  from . import Check
0707010001f2bf000081a40000000000000000000000016a100daf00000a84000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/zfs/__init__.py import re

import drivers.check

from utilities.converters import convert_size
from env import Env
from utilities.proc import justcall, which

class Check(drivers.check.Check):
    def __init__(self, svcs=None):
        if svcs is None:
            svcs = []
        super(Check, self).__init__(svcs)
        self.zpcache = {}

    chk_type = "fs_u"

    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.path
            for resource in svc.get_resources('fs'):
                if hasattr(resource, "device") and resource.device == name:
                    return svc.path
        return ''

    def do_check(self):
        cmd = [Env.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]):
                # container id
                continue
            if "osvc_sync_" in l[0]:
                # do not report osvc sync snapshots fs usage
                continue
            used = convert_size(l[1], _to="KB")
            avail = convert_size(l[2], _to="KB")
            total = used + avail
            pct = round(used / total * 100)
            path = self.find_svc(l[0], l[3])
            r.append({
                      "instance": l[0],
                      "value": str(pct)+"%",
                      "path": path,
                     })
            r.append({
                      "instance": l[0]+".free",
                      "value": str(avail),
                      "path": path,
                     })
            r.append({
                      "instance": l[0]+".size",
                      "value": str(total),
                      "path": path,
                     })
        return r

0707010001f2b3000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/__init__.py 0707010001f2b6000081a40000000000000000000000016a100daf00000439000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/aix.py  import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
   0707010001f2b7000081a40000000000000000000000016a100daf00000612000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/darwin.py   import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
  0707010001f2b4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003800000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/advfs   0707010001f2b5000081a40000000000000000000000016a100daf000003f5000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/advfs/osf1.py   import drivers.check
import utilities.subsystems.advfs

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def __init__(self, svcs=None):
        super(Check, self).__init__(self, svcs)
        if svcs is None:
            svcs = []

    def find_svc(self, name):
        for svc in self.svcs:
            for resource in svc.get_resources("pool"):
                if not hasattr(resource, "poolname"):
                    continue
                if resource.poolname == name:
                    return svc.path
        return ""

    def do_check(self):
        o = utilities.subsystems.advfs.Fdmns()
        r = []
        for dom in o.list_fdmns():
            try:
                d = o.get_fdmn(dom)
                r.append({
                          "instance": dom,
                          "value": str(d.used_pct),
                          "path": self.find_svc(dom),
                         })
            except utilities.subsystems.advfs.ExInit:
                pass
        return r
   0707010001f2ba000081a40000000000000000000000016a100daf00000947000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/fs_u/linux.py    import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_u"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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[0] in ("overlay", "overlay2", "aufs"):
                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 l[5].startswith('/snap/'):
                continue
            if "/overlay2/" in l[5]:
                continue
            if "/snapd/" in l[5]:
                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
            path = self.find_svc(l[5])
            r.append({
                      "instance": l[5],
                      "value": l[4],
                      "path": path,
                     })
            r.append({
                      "instance": l[5]+".free",
                      "value": l[3],
                      "path": path,
                     })
            r.append({
                      "instance": l[5]+".size",
                      "value": l[1],
                      "path": path,
                     })
        return r
 0707010001f2e2000041ed0000000000000000000000056a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/raid 0707010001f2e3000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/raid/__init__.py 0707010001f2e4000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/raid/megaraid    0707010001f2e6000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/check/raid/megaraid/linux.py   from . import Check
0707010001f2e5000081a40000000000000000000000016a100daf00001191000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/check/raid/megaraid/__init__.py    import os

import drivers.check

from env import Env
from utilities.proc import justcall, which

class Check(drivers.check.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(Env.paths.pathtmp)
        logs = [os.path.join(Env.paths.pathtmp, 'MegaSAS.log'),
                os.path.join(Env.paths.pathtmp, 'MegaCli.log'),
                os.path.join(Env.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({
                             "instance": slot,
                              "value": str(errs),
                              "path": '',
                             })
                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({
                 "instance": slot,
                  "value": str(errs),
                  "path": '',
                 })
        return r

    def do_check_bbustatus(self):
        megacli = self.find_megacli()
        if megacli is None:
            return self.undef
        os.chdir(Env.paths.pathtmp)
        logs = [os.path.join(Env.paths.pathtmp, 'MegaSAS.log'),
                os.path.join(Env.paths.pathtmp, 'MegaCli.log'),
                os.path.join(Env.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({
                          "instance": '%s battery NoBattery'%slot,
                          "value": str(val),
                          "path": '',
                         })
            if line.startswith('Relative State of Charge:'):
                val = line.strip('%').split()[-1]
                r.append({
                          "instance": '%s battery charge'%slot,
                          "value": str(val),
                          "path": '',
                         })
            if line.startswith('Temperature:'):
                val = line.split()[-2]
                r.append({
                          "instance": '%s battery temp'%slot,
                          "value": str(val),
                          "path": '',
                         })
            if line.startswith('isSOHGood:'):
                if 'Yes' in line:
                    val = 0
                else:
                    val = 1
                r.append({
                          "instance": '%s battery isSOHGood'%slot,
                          "value": str(val),
                          "path": '',
                         })
        return r
   0707010001f2e7000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/check/raid/megaraid/sunos.py   from . import Check
0707010001f2e8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003700000000root/usr/share/opensvc/opensvc/drivers/check/raid/sas2    0707010001f2eb000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/check/raid/sas2/sunos.py   from . import Check
0707010001f2ea000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/check/raid/sas2/linux.py   from . import Check
0707010001f2e9000081a40000000000000000000000016a100daf00000e72000000e600010003ffffffffffffffff0000004300000000root/usr/share/opensvc/opensvc/drivers/check/raid/sas2/__init__.py    import os

import drivers.check

from env import Env
from utilities.proc import justcall, which

class Check(drivers.check.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(Env.paths.pathtmp)
        logs = [os.path.join(Env.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
            enc = ""
            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({ "instance": ctrl+','+slot, "value": '1', "path": '', })
                        errs += 1
                    else :
                        r.append({ "instance": ctrl+','+slot, "value": '0', "path": '', })
                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({ "instance": ctrl+','+slot, "value": '1', "path": '', })
                        errs += 1
                    else :
                        r.append({ "instance": ctrl+','+slot, "value": '0', "path": '', })
                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({ "instance": ctrl+','+slot, "value": '1', "path": '', })
                        errs += 1
                    else :
                        r.append({ "instance": ctrl+','+slot, "value": '0', "path": '', })
            r.append({ "instance": 'all SAS20*', "value": str(errs), "path": '', })
        return r
  0707010001f2ec000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/raid/smartarray  0707010001f2f0000081a40000000000000000000000016a100daf000001ff000000e600010003ffffffffffffffff0000004800000000root/usr/share/opensvc/opensvc/drivers/check/raid/smartarray/windows.py   import os

from . import Check as BaseCheck

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(BaseCheck):
    pass

 0707010001f2ed000081a40000000000000000000000016a100daf00000c1b000000e600010003ffffffffffffffff0000004900000000root/usr/share/opensvc/opensvc/drivers/check/raid/smartarray/__init__.py  import os

import drivers.check

from utilities.proc import justcall, which

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(drivers.check.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({
                          "instance": ".".join((slot, inst)),
                          "value": str(value),
                          "path": '',
                         })
        return r
 0707010001f2ef000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/check/raid/smartarray/sunos.py from . import Check
0707010001f2ee000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004600000000root/usr/share/opensvc/opensvc/drivers/check/raid/smartarray/linux.py from . import Check
0707010001f2cc000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003400000000root/usr/share/opensvc/opensvc/drivers/check/mcelog   0707010001f2cd000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000004000000000root/usr/share/opensvc/opensvc/drivers/check/mcelog/__init__.py   0707010001f2ce000081a40000000000000000000000016a100daf000006c7000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/mcelog/linux.py  import datetime
import os

import drivers.check

from env import Env
from utilities.proc import which

class Check(drivers.check.Check):
    chk_type = "mcelog"
    mcelog_p = "/var/log/mcelog"
    marker_p = os.path.join(Env.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({
                  "instance": "new lines",
                  "value": str(l),
                  "path": "",
                 })

        self.update_marker()
        return r
 0707010001f2f6000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/vg_u 0707010001f2f8000081a40000000000000000000000016a100daf00000536000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/vg_u/aix.py  import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "vg_u"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk.vg'):
                if not hasattr(resource, "name"):
                    continue
                if resource.name == vgname:
                    return svc.path
        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({"instance": vg,
                      "value": str(val),
                      "path": self.find_svc(l[0]),
                     }
                    )
        return r
  0707010001f2fa000081a40000000000000000000000016a100daf000004ae000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/vg_u/linux.py    import drivers.check

from env import Env
from utilities.proc import justcall

class Check(drivers.check.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.path
        return ''

    def do_check(self):
        cmd = [Env.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({"instance": l[0],
                      "value": str(val),
                      "path": self.find_svc(l[0]),
                     }
                    )
        return r
  0707010001f2f9000081a40000000000000000000000016a100daf0000073b000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/vg_u/hpux.py import drivers.check

from utilities.proc import call

class Check(drivers.check.Check):
    chk_type = "vg_u"

    def find_svc(self, vgname):
        for svc in self.svcs:
            for resource in svc.get_resources('disk.vg'):
                if not hasattr(resource, "name"):
                    continue
                if resource.name == vgname:
                    return svc.path
        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({"instance": instance,
                      "value": str(val),
                      "path": self.find_svc(instance),
                     }
                    )
        return r
 0707010001f2f7000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/vg_u/__init__.py 0707010001f2a1000041ed0000000000000000000000046a102a9200000000000000e600010003ffffffffffffffff0000003000000000root/usr/share/opensvc/opensvc/drivers/check/fm   0707010001f2a5000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/fm/openmanage    0707010001f2a6000081a40000000000000000000000016a100daf000005e5000000e600010003ffffffffffffffff0000004700000000root/usr/share/opensvc/opensvc/drivers/check/fm/openmanage/__init__.py    import os

import drivers.check

from env import Env
from utilities.proc import justcall, which

class Check(drivers.check.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({
                      "instance": inst,
                      "value": str(state),
                      "path": '',
                     })
        return r
   0707010001f2a7000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000004400000000root/usr/share/opensvc/opensvc/drivers/check/fm/openmanage/linux.py   from . import Check
0707010001f2a2000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003600000000root/usr/share/opensvc/opensvc/drivers/check/fm/fmadm 0707010001f2a3000081a40000000000000000000000016a100daf00000411000000e600010003ffffffffffffffff0000004200000000root/usr/share/opensvc/opensvc/drivers/check/fm/fmadm/__init__.py import os

import drivers.check

from env import Env
from utilities.proc import justcall, which

class Check(drivers.check.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(Env.paths.pathtmp)
        cmd = [fmadm, 'faulty']
        out, err, ret = justcall(cmd)
        if ret != 0:
            return self.undef
        r = []
        r.append({
              "instance": 'faults ',
              "value": str(len(out)),
              "path": '',
            })
        return r
   0707010001f2a4000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/fm/fmadm/sunos.py    from . import Check
0707010001f2c3000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/check/jstat    0707010001f2c5000081a40000000000000000000000016a100daf00001806000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/jstat/linux.py   from __future__ import print_function

import datetime
import json
import os
import sys
from optparse import Option, OptionParser
from subprocess import Popen, PIPE

import drivers.check
from utilities.proc import justcall
from utilities.storage import Storage

class Check(drivers.check.Check):
    chk_type = "jstat"

    def find_java(self):
        cmd = ["pgrep", "java"]
        out, err, ret = justcall(cmd)
        out = out.strip()
        if not out:
            return []
        return [int(pid) for pid in out.split()]

    def do_check(self):
        data = []
        for pid in self.find_java():
            ids = pid_to_ids(pid)
            if ids.instance is not None:
                instance = ids.instance
            elif ids.rid is not None:
                instance = ids.rid
            else:
                # not handled
                continue
            path = ids.path if ids.path else ids.name if ids.name else ""
            jstat = get_executable(Storage(), pid)
            if not jstat:
                continue
            for stat in STATS:
                for key, val in get_stat_metrics(jstat, pid, stat).items():
                    data.append({
                        "instance": "%s.%s.%s" % (instance, stat, key),
                        "value": val,
                        "path": path,
                    })
        #print(json.dumps(data, indent=4))
        return data

VERSION = "1.0"
USAGE = ""
STAT_HELP = """
Determines the statistics information that jstat displays. The following 
table lists the available options. Use the -options general option to 
display the list of options for a particular platform installation.

Option	          Displays...
======            ===========
class	          Statistics on the behavior of the class loader.
compiler          Statistics of the behavior of the HotSpot Just-in-Time
                  compiler.
gc                Statistics of the behavior of the garbage collected heap.
gccapacity        Statistics of the capacities of the generations and their
                  corresponding spaces.
gccause           Summary of garbage collection statistics (same as -gcutil),
                  with the cause of the last and current (if applicable)
                  garbage collection events.
gcnew             Statistics of the behavior of the new generation.
gcnewcapacity     Statistics of the sizes of the new generations and its
                  corresponding spaces.
gcold	          Statistics of the behavior of the old and permanent
                  generations.
gcoldcapacity     Statistics of the sizes of the old generation.
gcpermcapacity    Statistics of the sizes of the permanent generation.
gcutil            Summary of garbage collection statistics.
printcompilation  HotSpot compilation method statistics.
"""

STATS = [
    "class",
#    "compiler",
    "gc",
    "gccapacity",
#    "gccause",
    "gcnew",
    "gcnewcapacity",
    "gcold",
    "gcoldcapacity",
    "gcpermcapacity",
    "gcutil",
#    "printcompilation",
]
DEFAULT_STATS = [
    "gc",
]

OPTIONS = [
     Option(
        "-p", "--pid", action="store", dest="pid", type="int",
        help="The pid of the JVM to extract metrics from."),
     Option(
        "-x", "--executable", action="store", dest="executable",
        help="The jstat executable to use. Defaults to the jstat "
             "binary found in the same dir as the monitored java."),
     Option(
        "-s", "--stat", action="append", dest="stat",
        help=STAT_HELP),
]


def pid_to_ids(pid):
    data = Storage()

    try:
        with open("/proc/%d/environ" % pid, 'rb') as fp:
            buff = fp.read()
    except Exception:
        # OSError, IOError, FileNotFoundError ...
        return data

    try:
        buff = buff.decode('utf-8')
    except UnicodeDecodeError:
        buff = buff.decode('utf-8', errors='replace')  # or 'ignore'

    for line in buff.split('\0'):
        line = line.replace("\n", "")
        try:
            key, val = line.split("=", 1)
        except ValueError:
            continue
        if key == "OPENSVC_SVC_ID":
            data["svc_id"] = val
        elif key == "OPENSVC_SVCNAME":
            data["svcname"] = val
        elif key == "OPENSVC_SVCPATH":
            data["path"] = val
        elif key == "OPENSVC_RID":
            data["rid"] = val
        elif key == "OPENSVC_CHK_INSTANCE":
            data["instance"] = val
    return data

def get_pid(options):
    if options.pid:
        return options.pid

def get_executable(options, pid):
    if options.executable:
        return options.executable
    java = os.readlink("/proc/%d/exe" % pid)
    java_d = os.path.dirname(java)
    jstat = os.path.join(java_d, "jstat")
    if os.path.exists(jstat):
        return jstat

def get_stats(options):
    if options.stat:
        return list(set(options.stat)-set(STATS))
    return DEFAULT_STATS

def get_stat_metrics(jstat, pid, stat):
    cmd = [jstat, "-%s"%stat, str(pid)]
    proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
    out, err = proc.communicate()
    if proc.returncode != 0:
        return {}
    lines = out.decode().splitlines()
    headers = lines[0].split()
    metrics = [float(val) for val in lines[1].replace(",", ".").split()]
    data = {}
    for head, metric in zip(headers, metrics):
        data[head] = metric
    return data

def main(argv):
    parser = OptionParser(version=VERSION, usage=USAGE, option_list=OPTIONS)
    options, args = parser.parse_args(argv)

    pid = get_pid(options)
    if pid is None:
        print("pid not defined and not guessed", file=sys.stderr)
        return 1

    jstat = get_executable(options, pid)
    if jstat is None:
        print("jstat executable not defined and not guessed", file=sys.stderr)
        return 1

    stats = get_stats(options)
    data = {
        "timestamp": int(datetime.datetime.utcnow().timestamp()),
        "stats": {},
    }

    for stat in stats:
        data["stats"][stat] = get_stat_metrics(jstat, pid, stat)

    print(json.dumps(data, indent=4))
    

if __name__ == "__main__":
    ret = main(sys.argv)
    sys.exit(ret)

  0707010001f2c4000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/jstat/__init__.py    0707010001f2f1000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/sync 0707010001f2f2000081a40000000000000000000000016a100daf000004c7000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/sync/__init__.py import drivers.check

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

class Check(drivers.check.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.path
        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({
                "instance": resource.rid + ".bytes",
                "value": str(stats["bytes"]),
                "path": svc.path,
             })
        if "speed" in stats:
            data.append({
                "instance": resource.rid + ".speed",
                "value": str(stats["speed"]),
                "path": svc.path,
             })
        return data
 0707010001f2f3000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/sync/freebsd.py  from . import Check
0707010001f2f4000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/sync/linux.py    from . import Check
0707010001f2f5000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/sync/sunos.py    from . import Check
0707010001f29c000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/drivers/check/eth  0707010001f2a0000081a40000000000000000000000016a100daf00002753000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/eth/sunos.py import re

import drivers.check
import utilities.os.sunos

from utilities.proc import justcall

"""
# 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(drivers.check.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 = utilities.os.sunos.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({
                            "instance": '%s.%s.speed' % (ifn, self.phys[phy]["device"]),
                            "value": str(self.phys[phy]["speed"]),
                            "path": '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_speed')
            r.append({
                      "instance": '%s.speed'%ifn,
                      "value": str(val),
                      "path": '',
                     })
        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({
                            "instance": '%s.%s.duplex' % (ifn, self.phys[phy]["device"]),
                            "value": val,
                            "path": '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_duplex')
            r.append({
                      "instance": '%s.duplex'%ifn,
                      "value": str(val),
                      "path": '',
                     })
        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({
                            "instance": '%s.%s.link' % (ifn, self.phys[phy]["device"]),
                            "value": val,
                            "path": '',
                        })
            return r
        for ifn in self.ifs:
            val = self.get_param(ifn, 'link_status')
            r.append({
                      "instance": '%s.link'%ifn,
                      "value": str(val),
                      "path": '',
                     })
        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
 0707010001f29e000081a40000000000000000000000016a100daf00000a75000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/eth/hpux.py  import drivers.check

from utilities.proc import justcall

class Check(drivers.check.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({
                  "instance": inst,
                  "value": val,
                  "path": "",
                 })

        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({
                  "instance": inst,
                  "value": val,
                  "path": "",
                 })

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

        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({
                  "instance": inst,
                  "value": val,
                  "path": "",
                 })

        return r
   0707010001f29d000081a40000000000000000000000016a100daf00000001000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/eth/__init__.py  
   0707010001f29f000081a40000000000000000000000016a100daf00000b9f000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/eth/linux.py import glob

import drivers.check
import utilities.ifconfig
import utilities.subsystems.ethtool


class Check(drivers.check.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)
        ifconfig = utilities.ifconfig.Ifconfig()
        for i in ifconfig.intf:
            if not i.name.startswith("eth") and \
               not i.name.startswith("en"):
                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 = utilities.subsystems.ethtool.Ethtool(intf)
            ethtool.load()
        except utilities.subsystems.ethtool.LoadError:
            return []

        inst = intf + ".speed"
        val = ethtool.speed
        # discard unknown (kvm)
        if val is not None and "Unknown" not in val:
            val.replace('Mb/s', '')
            r.append({
                  "instance": inst,
                  "value": val,
                  "path": '',
                 })

        inst = intf + ".autoneg"
        # discard unknown (kvm)
        if val is not None and "Unknown" not in val:
            val = ethtool.auto_negotiation
            if val == 'on':
                val = '1'
            else:
                val = '0'
            r.append({
                  "instance": inst,
                  "value": val,
                  "path": '',
                 })

        inst = intf + ".duplex"
        val = ethtool.duplex
        # discard unknown (kvm)
        if val is not None and "Unknown" not in val:
            if val == 'Full':
                val = '1'
            else:
                val = '0'
            r.append({
                  "instance": inst,
                  "value": val,
                  "path": '',
                 })

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

        return r
 0707010001f2df000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/numa 0707010001f2e1000081a40000000000000000000000016a100daf0000058b000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/numa/linux.py    import os
import glob
import math

import drivers.check

class Check(drivers.check.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({
                  "instance": node+'.mem.leveling',
                  "value": str(deviation),
                  "path": '',
                 })
        return r

 0707010001f2e0000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/numa/__init__.py 0707010001f2c6000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003100000000root/usr/share/opensvc/opensvc/drivers/check/lag  0707010001f2cb000081a40000000000000000000000016a100daf00000301000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/lag/windows.py   import foreign.wmi as wmi

import drivers.check

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

    def do_check(self):
        try:
            self.w = wmi.WMI(namespace=r"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({
                "instance": inst+'.redundancy',
                "value": str(val),
                "path": '',
               })
        return r

if __name__ == "__main__":
    o = Check()
    o.do_check()
   0707010001f2ca000081a40000000000000000000000016a100daf00001a63000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/lag/sunos.py import drivers.check
import utilities.os.sunos

from utilities.proc import justcall, which

"""
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(drivers.check.Check):
    chk_type = "lag"
    chk_name = "Solaris network interface lag"

    def do_check(self):
        if not which("dladm"):
            return self.undef
        self.osver = utilities.os.sunos.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({
                                      "instance": '%s.%s.speed'%(lag, net),
                                      "value": str(val),
                                      "path": '',
                                     })
            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({
                      "instance": '%s.%d.speed'%(lag, i),
                      "value": str(val),
                      "path": '',
                     })
            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({
                                      "instance": '%s.%s.%s'%(lag, net, key),
                                      "value": str(val),
                                      "path": '',
                                     })
            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({
                          "instance": '%s.%d.%s'%(lag, i, key),
                          "value": str(val),
                          "path": '',
                         })
                i += 1
        return r
 0707010001f2c7000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003d00000000root/usr/share/opensvc/opensvc/drivers/check/lag/__init__.py  0707010001f2c8000081a40000000000000000000000016a100daf00000725000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/lag/hpux.py  import drivers.check

from utilities.proc import justcall, which

class Check(drivers.check.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({
                  "instance": inst,
                  "value": val,
                  "path": '',
                 })

        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
   0707010001f2c9000081a40000000000000000000000016a100daf00000fae000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/lag/linux.py import glob
import json
import os

import drivers.check
import utilities.ifconfig

from env import Env

"""
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(drivers.check.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 = utilities.ifconfig.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(Env.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({
                          "instance": inst+'.mii_status',
                          "value": val,
                          "path": '',
                         })
            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({
                          "instance": inst+'.link_failure_per_hour',
                          "value": "%.2f"%val,
                          "path": '',
                         })
        r.append({
                  "instance": lag+'.paths',
                  "value": str(n_slave),
                  "path": '',
                 })
        return r
  0707010001f2a8000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003200000000root/usr/share/opensvc/opensvc/drivers/check/fs_i 0707010001f2b0000081a40000000000000000000000016a100daf00000857000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/linux.py    import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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[0] in ("overlay", "overlay2", "aufs"):
                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 l[5].startswith('/snap/'):
                continue
            if "/overlay2/" in l[5]:
                continue
            if "/snapd/" in l[5]:
                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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
 0707010001f2af000081a40000000000000000000000016a100daf000005a0000000e600010003ffffffffffffffff0000003a00000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/hpux.py import drivers.check
from utilities.proc import call

class Check(drivers.check.Check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({"instance": inst,
                      "value": l[7],
                      "path": self.find_svc(inst),
                     }
                    )
            elif len(l) == 8:
                inst = ' '.join(l[7:])
                r.append({"instance": inst,
                      "value": l[6],
                      "path": self.find_svc(inst),
                     }
                    )
        return r
0707010001f2a9000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/__init__.py 0707010001f2ae000081a40000000000000000000000016a100daf00000415000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/darwin.py   import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[8],
                      "value": l[7],
                      "path": self.find_svc(l[8]),
                     })
        return r
   0707010001f2ac000081a40000000000000000000000016a100daf00000439000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/aix.py  import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[6],
                      "value": l[5],
                      "path": self.find_svc(l[6]),
                     })
        return r
   0707010001f2b1000081a40000000000000000000000016a100daf00000499000000e600010003ffffffffffffffff0000003b00000000root/usr/share/opensvc/opensvc/drivers/check/fs_i/sunos.py    import drivers.check

from utilities.proc import justcall

class Check(drivers.check.Check):
    chk_type = "fs_i"

    def find_svc(self, mountpt):
        for svc in self.svcs:
            for resource in svc.get_resources('fs'):
                if not hasattr(resource, "mount_point"):
                    continue
                if resource.mount_point == mountpt:
                    return svc.path
        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({
                      "instance": l[5],
                      "value": l[4],
                      "path": self.find_svc(l[5]),
                     })
        return r
   0707010001f298000081a40000000000000000000000016a100daf0000121b000000e600010003ffffffffffffffff0000003900000000root/usr/share/opensvc/opensvc/drivers/check/__init__.py  from __future__ import print_function

import glob
import importlib
import os
import pkgutil
import sys

from env import Env


class Check(object):
    def __init__(self, svcs=None):
        if svcs is None:
            svcs = []
        self.svcs = svcs
        if self.svcs is None:
            self.svcs = []

    @property
    def undef(self):
        return [{
            'path': '',
            'instance': 'undef',
            'value': '-1'
        }]

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

class Checks(Check):
    def __init__(self, svcs=None, node=None, checkers=None):
        if svcs is None:
            svcs = []
        self.check_list = []
        self.svcs = svcs
        self.node = node
        self.checkers = checkers or []
        self.register_internal_checkers()
        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_internal_checkers(self):
        def onerror(name):
            pass
        for modinfo in pkgutil.walk_packages(__path__, __name__ + '.', onerror=onerror):
            if hasattr(modinfo, "ispkg"):
                name = modinfo.name
                ispkg = modinfo.ispkg
            else:
                name = modinfo[1]
                ispkg = modinfo[2]
            if ispkg:
                continue
            if name.split(".")[-1] != Env.module_sysname:
                continue
            mod = importlib.import_module(name)
            o = mod.Check(svcs=self.svcs)
            if self.checkers and o.chk_type not in self.checkers:
                continue
            self += o

    def register_local_checkers(self):
        check_d = os.path.join(Env.paths.pathvar, 'check')
        if not os.path.exists(check_d):
            return
        sys.path.append(check_d)
        for f in glob.glob(os.path.join(check_d, 'check*.py')):
            if Env.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 do_checks(self):
        import datetime

        now = str(datetime.datetime.now())
        data = {}

        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 'instance' not in instance:
                    continue
                if instance['instance'] == 'undef':
                    continue
                if 'value' not in instance:
                    continue
                _instance = {
                    "instance": instance.get("instance", ""),
                    "value": instance.get("value", ""),
                    "path": instance.get("path", ""),
                    "driver": driver,
                }
                instances.append(_instance)

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

    def print_checks(self, data):
        from utilities.render.forest import Forest
        from utilities.render.color import color
        tree = Forest()
        head_node = tree.add_node()
        head_node.add_column(Env.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["path"])
                _node.add_column(str(instance["value"]))
                if instance["driver"] == "generic":
                    _node.add_column()
                else:
                    _node.add_column(instance["driver"])
        tree.out()

 0707010001f299000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/check/btrfs    0707010001f29a000081a40000000000000000000000016a100daf00000000000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/btrfs/__init__.py    0707010001f29b000081a40000000000000000000000016a100daf000009a2000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/btrfs/linux.py   import os

import drivers.check

from utilities.proc import justcall

class Check(drivers.check.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.path
        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({"instance": dev+'.'+err_type,
                          "value": val,
                          "path": self.find_svc(dev),
                        })
        return r

if __name__ == "__main__":
    o = Check()
    r = o.do_check()
    print(r)
  0707010001f2fb000041ed0000000000000000000000026a102a9200000000000000e600010003ffffffffffffffff0000003300000000root/usr/share/opensvc/opensvc/drivers/check/zpool    0707010001f2fe000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/zpool/linux.py   from . import Check
0707010001f2fd000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003e00000000root/usr/share/opensvc/opensvc/drivers/check/zpool/freebsd.py from . import Check
0707010001f2fc000081a40000000000000000000000016a100daf000008cc000000e600010003ffffffffffffffff0000003f00000000root/usr/share/opensvc/opensvc/drivers/check/zpool/__init__.py    import os

import drivers.check

from env import Env
from utilities.proc import justcall

class Check(drivers.check.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 not hasattr(res, "name"):
                    continue
                if res.name == pool:
                    return svc.path
        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({"instance": pool,
                          "value": str(stat_val),
                          "path": self.find_svc(pool),
                        })
        return r
0707010001f2ff000081a40000000000000000000000016a100daf00000014000000e600010003ffffffffffffffff0000003c00000000root/usr/share/opensvc/opensvc/drivers/check/zpool/sunos.py   from . import Check
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000b00000000TRAILER!!!                                                                                                                                                                                                                                            